Skip to content

Commit

Permalink
templater: expose RefTarget methods through RefName type
Browse files Browse the repository at this point in the history
I considered adding RefTarget template type, but some of the methods naturally
fit to RefName. For example, a conflicted branch name is decorated as "??", so
it makes sense to add branch.conflict() instead of branch.target().conflict().

I'm not pretty sure how many RefName methods we'll need to add to port the
current branch listing, but there will be .tracked(), .tracking_local_present(),
.ahead_by(), and .behind_by().
  • Loading branch information
yuja committed May 3, 2024
1 parent 6056b67 commit 4b993c6
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 16 deletions.
56 changes: 56 additions & 0 deletions cli/src/commit_templater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,10 @@ impl RefName {
self.remote.is_some()
}

fn is_present(&self) -> bool {
self.target.is_present()
}

/// Whether the ref target has conflicts.
fn has_conflict(&self) -> bool {
self.target.has_conflict()
Expand Down Expand Up @@ -805,6 +809,58 @@ fn builtin_ref_name_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Re
Ok(L::wrap_string(out_property))
},
);
map.insert(
"present",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property = self_property.map(|ref_name| ref_name.is_present());
Ok(L::wrap_boolean(out_property))
},
);
map.insert(
"conflict",
|_language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let out_property = self_property.map(|ref_name| ref_name.has_conflict());
Ok(L::wrap_boolean(out_property))
},
);
map.insert(
"normal_target",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let repo = language.repo;
let out_property = self_property.and_then(|ref_name| {
let maybe_id = ref_name.target.as_normal();
Ok(maybe_id.map(|id| repo.store().get_commit(id)).transpose()?)
});
Ok(L::wrap_commit_opt(out_property))
},
);
map.insert(
"removed_targets",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let repo = language.repo;
let out_property = self_property.and_then(|ref_name| {
let ids = ref_name.target.removed_ids();
Ok(ids.map(|id| repo.store().get_commit(id)).try_collect()?)
});
Ok(L::wrap_commit_list(out_property))
},
);
map.insert(
"added_targets",
|language, _build_ctx, self_property, function| {
template_parser::expect_no_arguments(function)?;
let repo = language.repo;
let out_property = self_property.and_then(|ref_name| {
let ids = ref_name.target.added_ids();
Ok(ids.map(|id| repo.store().get_commit(id)).try_collect()?)
});
Ok(L::wrap_commit_list(out_property))
},
);
map
}

Expand Down
67 changes: 51 additions & 16 deletions cli/tests/test_tag_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,44 +35,79 @@ fn test_tag_list() {
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "branch1"]);
test_env.jj_cmd_ok(&repo_path, &["new", "root()", "-mcommit2"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "branch2"]);
test_env.jj_cmd_ok(&repo_path, &["new", "root()", "-mcommit3"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "branch3"]);
test_env.jj_cmd_ok(&repo_path, &["git", "export"]);

copy_ref("refs/heads/branch1", "refs/tags/test_tag");
copy_ref("refs/heads/branch2", "refs/tags/test_tag2");
copy_ref("refs/heads/branch1", "refs/tags/conflicted_tag");
test_env.jj_cmd_ok(&repo_path, &["git", "import"]);
copy_ref("refs/heads/branch2", "refs/tags/conflicted_tag");
test_env.jj_cmd_ok(&repo_path, &["git", "import"]);
copy_ref("refs/heads/branch3", "refs/tags/conflicted_tag");
test_env.jj_cmd_ok(&repo_path, &["git", "import", "--at-op=@-"]);
test_env.jj_cmd_ok(&repo_path, &["status"]); // resolve concurrent ops

insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["tag", "list"]),
@r###"
test_tag
test_tag2
"###);
conflicted_tag
test_tag
test_tag2
"###);

insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["tag", "list", "--color=always"]),
@r###"
test_tag
test_tag2
"###);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["tag", "list", "--color=always"]),
@r###"
conflicted_tag
test_tag
test_tag2
"###);

// Test pattern matching.
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["tag", "list", "test_tag2"]),
@r###"
test_tag2
"###);
test_tag2
"###);

insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["tag", "list", "glob:test_tag?"]),
@r###"
test_tag2
"###);
test_tag2
"###);

let template = r#"'name: ' ++ name ++ "\n""#;
let template = r#"
concat(
"[" ++ name ++ "]\n",
separate(" ", "present:", present) ++ "\n",
separate(" ", "conflict:", conflict) ++ "\n",
separate(" ", "normal_target:", normal_target.description().first_line()) ++ "\n",
separate(" ", "removed_targets:", removed_targets.map(|c| c.description().first_line())) ++ "\n",
separate(" ", "added_targets:", added_targets.map(|c| c.description().first_line())) ++ "\n",
)
"#;
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["tag", "list", "-T", template]),
@r###"
name: test_tag
name: test_tag2
[conflicted_tag]
present: true
conflict: true
normal_target: <Error: No commit available>
removed_targets: commit1
added_targets: commit2 commit3
[test_tag]
present: true
conflict: false
normal_target: commit1
removed_targets:
added_targets: commit1
[test_tag2]
present: true
conflict: false
normal_target: commit2
removed_targets:
added_targets: commit2
"###);
}
8 changes: 8 additions & 0 deletions docs/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,14 @@ The following methods are defined.

* `.name() -> String`: Local branch or tag name.
* `.remote() -> String`: Remote name or empty if this is a local ref.
* `.present() -> Boolean`: True if the ref points to any commit.
* `.conflict() -> Boolean`: True if [the branch or tag is
conflicted](branches.md#conflicts).
* `.normal_target() -> Option<Commit>`: Target commit if the ref is not
conflicted and points to a commit.
* `.removed_targets() -> List<Commit>`: Old target commits if conflicted.
* `.added_targets() -> List<Commit>`: New target commits. The list usually
contains one "normal" target.

### ShortestIdPrefix type

Expand Down

0 comments on commit 4b993c6

Please sign in to comment.