revset: drop support for Git ref symbol resolution

Follows the same reason as the git_head()/git_refs() removal. This patch introduces
an immediate breaking change because there is no machinery to emit warnings during the
symbol resolution stage. Hopefully, Git ref symbols were rarely used.
This commit is contained in:
Yuya Nishihara
2025-12-08 20:23:18 +09:00
parent 1d5fb4b869
commit fdc6c5cff0
5 changed files with 7 additions and 133 deletions

View File

@@ -20,6 +20,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
* The deprecated `git_head()` and `git_refs()` functions have been removed from
revsets and templates.
* Git-like symbols (e.g. `refs/heads/main`) are no longer resolved to
revisions. Use the bookmark/tag `<name>` or `<name>@<remote>` syntax instead.
* The deprecated `ui.revsets-use-glob-by-default` option has been removed.
* `jj bookmark track`/`untrack` no longer supports `<kind>:<bookmark>@<remote>`

View File

@@ -53,8 +53,7 @@ Jujutsu attempts to resolve a symbol in the following order:
1. Tag name
2. Bookmark name
3. Git ref
4. Commit ID or change ID
3. Commit ID or change ID
To override the priority, use the appropriate [revset function](#functions). For
example, to resolve `abc` as a commit ID even if there happens to be a bookmark

View File

@@ -283,8 +283,8 @@ fn disambiguate_prefix_with_refs(view: &View, id_sym: &str, min_len: usize) -> u
debug_assert!(id_sym.is_ascii());
(min_len..id_sym.len())
.find(|&n| {
// Tags, bookmarks, and Git refs have higher priority, but Git refs
// should include "/" char. Extension symbols have lower priority.
// Tags and bookmarks have higher priority. Extension symbols have
// lower priority.
let prefix = &id_sym[..n];
view.get_local_tag(prefix.as_ref()).is_absent()
&& view.get_local_bookmark(prefix.as_ref()).is_absent()

View File

@@ -2683,27 +2683,7 @@ impl PartialSymbolResolver for BookmarkResolver {
}
}
struct GitRefResolver;
impl PartialSymbolResolver for GitRefResolver {
fn resolve_symbol(
&self,
repo: &dyn Repo,
symbol: &str,
) -> Result<Option<CommitId>, RevsetResolutionError> {
let view = repo.view();
for git_ref_prefix in &["", "refs/"] {
let target = view.get_git_ref([git_ref_prefix, symbol].concat().as_ref());
if let Some(id) = to_resolved_ref("git_ref", symbol, target)? {
return Ok(Some(id));
}
}
Ok(None)
}
}
const DEFAULT_RESOLVERS: &[&dyn PartialSymbolResolver] =
&[&TagResolver, &BookmarkResolver, &GitRefResolver];
const DEFAULT_RESOLVERS: &[&dyn PartialSymbolResolver] = &[&TagResolver, &BookmarkResolver];
struct CommitPrefixResolver<'a> {
context_repo: &'a dyn Repo,

View File

@@ -1051,114 +1051,6 @@ fn test_resolve_symbol_remote_tags_or_bookmarks() -> TestResult {
Ok(())
}
#[test]
fn test_resolve_symbol_git_refs() -> TestResult {
let test_repo = TestRepo::init();
let repo = &test_repo.repo;
let mut tx = repo.start_transaction();
let mut_repo = tx.repo_mut();
// Create some commits and refs to work with and so the repo is not empty
let commit1 = write_random_commit(mut_repo);
let commit2 = write_random_commit(mut_repo);
let commit3 = write_random_commit(mut_repo);
let commit4 = write_random_commit(mut_repo);
let commit5 = write_random_commit(mut_repo);
mut_repo.set_git_ref_target(
"refs/heads/bookmark1".as_ref(),
RefTarget::normal(commit1.id().clone()),
);
mut_repo.set_git_ref_target(
"refs/heads/bookmark2".as_ref(),
RefTarget::normal(commit2.id().clone()),
);
mut_repo.set_git_ref_target(
"refs/heads/conflicted".as_ref(),
RefTarget::from_legacy_form(
[commit2.id().clone()],
[commit1.id().clone(), commit3.id().clone()],
),
);
mut_repo.set_git_ref_target(
"refs/tags/tag1".as_ref(),
RefTarget::normal(commit2.id().clone()),
);
mut_repo.set_git_ref_target(
"refs/tags/remotes/origin/bookmark1".as_ref(),
RefTarget::normal(commit3.id().clone()),
);
// Nonexistent ref
assert_matches!(
resolve_symbol(mut_repo, "nonexistent"),
Err(RevsetResolutionError::NoSuchRevision{name, candidates})
if name == "nonexistent" && candidates.is_empty()
);
// Full ref
mut_repo.set_git_ref_target(
"refs/heads/bookmark".as_ref(),
RefTarget::normal(commit4.id().clone()),
);
assert_eq!(
resolve_symbol(mut_repo, "refs/heads/bookmark")?,
vec![commit4.id().clone()]
);
// Qualified with only heads/
mut_repo.set_git_ref_target(
"refs/heads/bookmark".as_ref(),
RefTarget::normal(commit5.id().clone()),
);
mut_repo.set_git_ref_target(
"refs/tags/bookmark".as_ref(),
RefTarget::normal(commit4.id().clone()),
);
// bookmark alone is not recognized
insta::assert_debug_snapshot!(
resolve_symbol(mut_repo, "bookmark").unwrap_err(), @r#"
NoSuchRevision {
name: "bookmark",
candidates: [],
}
"#);
// heads/bookmark does get resolved to the git ref refs/heads/bookmark
assert_eq!(
resolve_symbol(mut_repo, "heads/bookmark")?,
vec![commit5.id().clone()]
);
// Unqualified tag name
mut_repo.set_git_ref_target(
"refs/tags/tag".as_ref(),
RefTarget::normal(commit4.id().clone()),
);
assert_matches!(
resolve_symbol(mut_repo, "tag"),
Err(RevsetResolutionError::NoSuchRevision { .. })
);
// Unqualified remote-tracking bookmark name
mut_repo.set_git_ref_target(
"refs/remotes/origin/remote-bookmark".as_ref(),
RefTarget::normal(commit2.id().clone()),
);
assert_matches!(
resolve_symbol(mut_repo, "origin/remote-bookmark"),
Err(RevsetResolutionError::NoSuchRevision { .. })
);
// Conflicted ref is an error
assert_matches!(
resolve_symbol(mut_repo, "refs/heads/conflicted"),
Err(RevsetResolutionError::ConflictedRef { kind: "git_ref", symbol, targets })
if symbol == "refs/heads/conflicted"
&& targets == vec![commit1.id().clone(), commit3.id().clone()]
);
Ok(())
}
fn resolve_commit_ids(repo: &dyn Repo, revset_str: &str) -> Vec<CommitId> {
try_resolve_commit_ids(repo, revset_str).unwrap()
}