mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-25 21:47:42 +00:00
5f3df4aaea
We resolve file paths into repo-relative paths while parsing the revset expression, so I think it's consistent to also resolve which workspace "@" refers to while parsing it. That means we won't need the workspace context both while parsing and while resolving symbols. In order to break things like `author("martinvonz@")` (thanks to @yuja for catching this), I also changed the parsing of working-copy expressions so they are not allowed to be quoted. `author(martinvonz@)` will therefore be an error now. That seems like a small improvement anyway, since we have recently talked about making `root` and `[workspace]@` not parsed as other symbols.
255 lines
7.2 KiB
Rust
255 lines
7.2 KiB
Rust
// Copyright 2023 The Jujutsu Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use itertools::Itertools;
|
|
use jj_lib::backend::{CommitId, MillisSinceEpoch, ObjectId, Signature, Timestamp};
|
|
use jj_lib::id_prefix::IdPrefixContext;
|
|
use jj_lib::index::HexPrefix;
|
|
use jj_lib::index::PrefixResolution::{AmbiguousMatch, NoMatch, SingleMatch};
|
|
use jj_lib::repo::Repo;
|
|
use jj_lib::revset::RevsetExpression;
|
|
use testutils::TestRepo;
|
|
|
|
#[test]
|
|
fn test_id_prefix() {
|
|
let settings = testutils::user_settings();
|
|
let test_repo = TestRepo::init(true);
|
|
let repo = &test_repo.repo;
|
|
let root_commit_id = repo.store().root_commit_id();
|
|
let root_change_id = repo.store().root_change_id();
|
|
|
|
let mut tx = repo.start_transaction(&settings, "test");
|
|
let mut create_commit = |parent_id: &CommitId| {
|
|
let signature = Signature {
|
|
name: "Some One".to_string(),
|
|
email: "some.one@example.com".to_string(),
|
|
timestamp: Timestamp {
|
|
timestamp: MillisSinceEpoch(0),
|
|
tz_offset: 0,
|
|
},
|
|
};
|
|
tx.mut_repo()
|
|
.new_commit(
|
|
&settings,
|
|
vec![parent_id.clone()],
|
|
repo.store().empty_tree_id().clone(),
|
|
)
|
|
.set_author(signature.clone())
|
|
.set_committer(signature)
|
|
.write()
|
|
.unwrap()
|
|
};
|
|
let mut commits = vec![create_commit(root_commit_id)];
|
|
for _ in 0..25 {
|
|
commits.push(create_commit(commits.last().unwrap().id()));
|
|
}
|
|
let repo = tx.commit();
|
|
|
|
// Print the commit IDs and change IDs for reference
|
|
let commit_prefixes = commits
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(i, commit)| format!("{} {}", &commit.id().hex()[..3], i))
|
|
.sorted()
|
|
.join("\n");
|
|
insta::assert_snapshot!(commit_prefixes, @r###"
|
|
11a 5
|
|
214 24
|
|
2a6 2
|
|
33e 14
|
|
3be 16
|
|
3ea 18
|
|
593 20
|
|
5d3 1
|
|
5f6 13
|
|
676 3
|
|
7b6 25
|
|
7da 9
|
|
81c 10
|
|
87e 12
|
|
997 21
|
|
9f7 22
|
|
a0e 4
|
|
a55 19
|
|
ac4 23
|
|
c18 17
|
|
ce9 0
|
|
d42 6
|
|
d9d 8
|
|
eec 15
|
|
efe 7
|
|
fa3 11
|
|
"###);
|
|
let change_prefixes = commits
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(i, commit)| format!("{} {}", &commit.change_id().hex()[..3], i))
|
|
.sorted()
|
|
.join("\n");
|
|
insta::assert_snapshot!(change_prefixes, @r###"
|
|
026 9
|
|
030 13
|
|
1b5 6
|
|
26b 3
|
|
26c 8
|
|
271 10
|
|
439 2
|
|
44a 17
|
|
4e9 16
|
|
5b2 23
|
|
6c2 19
|
|
781 0
|
|
79f 14
|
|
7d5 24
|
|
86b 20
|
|
871 7
|
|
896 5
|
|
9e4 18
|
|
a2c 1
|
|
a63 22
|
|
b19 11
|
|
b93 4
|
|
bf5 21
|
|
c24 15
|
|
d64 12
|
|
fee 25
|
|
"###);
|
|
|
|
let prefix = |x| HexPrefix::new(x).unwrap();
|
|
|
|
// Without a disambiguation revset
|
|
// ---------------------------------------------------------------------------------------------
|
|
let c = IdPrefixContext::default();
|
|
assert_eq!(
|
|
c.shortest_commit_prefix_len(repo.as_ref(), commits[2].id()),
|
|
2
|
|
);
|
|
assert_eq!(
|
|
c.shortest_commit_prefix_len(repo.as_ref(), commits[5].id()),
|
|
1
|
|
);
|
|
assert_eq!(
|
|
c.resolve_commit_prefix(repo.as_ref(), &prefix("2")),
|
|
AmbiguousMatch
|
|
);
|
|
assert_eq!(
|
|
c.resolve_commit_prefix(repo.as_ref(), &prefix("2a")),
|
|
SingleMatch(commits[2].id().clone())
|
|
);
|
|
assert_eq!(
|
|
c.resolve_commit_prefix(repo.as_ref(), &prefix("20")),
|
|
NoMatch
|
|
);
|
|
assert_eq!(
|
|
c.resolve_commit_prefix(repo.as_ref(), &prefix("2a0")),
|
|
NoMatch
|
|
);
|
|
assert_eq!(
|
|
c.shortest_change_prefix_len(repo.as_ref(), commits[0].change_id()),
|
|
2
|
|
);
|
|
assert_eq!(
|
|
c.shortest_change_prefix_len(repo.as_ref(), commits[6].change_id()),
|
|
1
|
|
);
|
|
assert_eq!(
|
|
c.resolve_change_prefix(repo.as_ref(), &prefix("7")),
|
|
AmbiguousMatch
|
|
);
|
|
assert_eq!(
|
|
c.resolve_change_prefix(repo.as_ref(), &prefix("78")),
|
|
SingleMatch(vec![commits[0].id().clone()])
|
|
);
|
|
assert_eq!(
|
|
c.resolve_change_prefix(repo.as_ref(), &prefix("70")),
|
|
NoMatch
|
|
);
|
|
assert_eq!(
|
|
c.resolve_change_prefix(repo.as_ref(), &prefix("780")),
|
|
NoMatch
|
|
);
|
|
|
|
// Disambiguate within a revset
|
|
// ---------------------------------------------------------------------------------------------
|
|
let expression =
|
|
RevsetExpression::commits(vec![commits[0].id().clone(), commits[2].id().clone()]);
|
|
let c = c.disambiguate_within(expression);
|
|
// The prefix is now shorter
|
|
assert_eq!(
|
|
c.shortest_commit_prefix_len(repo.as_ref(), commits[2].id()),
|
|
1
|
|
);
|
|
// Shorter prefix within the set can be used
|
|
assert_eq!(
|
|
c.resolve_commit_prefix(repo.as_ref(), &prefix("2")),
|
|
SingleMatch(commits[2].id().clone())
|
|
);
|
|
// Can still resolve commits outside the set
|
|
assert_eq!(
|
|
c.resolve_commit_prefix(repo.as_ref(), &prefix("21")),
|
|
SingleMatch(commits[24].id().clone())
|
|
);
|
|
assert_eq!(
|
|
c.shortest_change_prefix_len(repo.as_ref(), commits[0].change_id()),
|
|
1
|
|
);
|
|
assert_eq!(
|
|
c.resolve_change_prefix(repo.as_ref(), &prefix("7")),
|
|
SingleMatch(vec![commits[0].id().clone()])
|
|
);
|
|
|
|
// Single commit in revset. Length 0 is unambiguous, but we pretend 1 digit is
|
|
// needed.
|
|
// ---------------------------------------------------------------------------------------------
|
|
let expression = RevsetExpression::commit(root_commit_id.clone());
|
|
let c = c.disambiguate_within(expression);
|
|
assert_eq!(
|
|
c.shortest_commit_prefix_len(repo.as_ref(), root_commit_id),
|
|
1
|
|
);
|
|
assert_eq!(
|
|
c.resolve_commit_prefix(repo.as_ref(), &prefix("")),
|
|
AmbiguousMatch
|
|
);
|
|
assert_eq!(
|
|
c.resolve_commit_prefix(repo.as_ref(), &prefix("0")),
|
|
SingleMatch(root_commit_id.clone())
|
|
);
|
|
assert_eq!(
|
|
c.shortest_change_prefix_len(repo.as_ref(), root_change_id),
|
|
1
|
|
);
|
|
assert_eq!(
|
|
c.resolve_change_prefix(repo.as_ref(), &prefix("")),
|
|
AmbiguousMatch
|
|
);
|
|
assert_eq!(
|
|
c.resolve_change_prefix(repo.as_ref(), &prefix("0")),
|
|
SingleMatch(vec![root_commit_id.clone()])
|
|
);
|
|
|
|
// Disambiguate within revset that fails to evaluate
|
|
// ---------------------------------------------------------------------------------------------
|
|
// TODO: Should be an error
|
|
let expression = RevsetExpression::symbol("nonexistent".to_string());
|
|
let context = c.disambiguate_within(expression);
|
|
assert_eq!(
|
|
context.shortest_commit_prefix_len(repo.as_ref(), commits[2].id()),
|
|
2
|
|
);
|
|
assert_eq!(
|
|
context.resolve_commit_prefix(repo.as_ref(), &prefix("2")),
|
|
AmbiguousMatch
|
|
);
|
|
}
|