613 lines · 20.4 KB · Rust
RawDownload
// 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 as _;use jj_lib::backend::ChangeId;use jj_lib::backend::CommitId;use jj_lib::backend::MillisSinceEpoch;use jj_lib::backend::Signature;use jj_lib::backend::Timestamp;use jj_lib::config::ConfigLayer;use jj_lib::config::ConfigSource;use jj_lib::id_prefix::IdPrefixContext;use jj_lib::id_prefix::IdPrefixIndex;use jj_lib::object_id::HexPrefix;use jj_lib::object_id::ObjectId as _;use jj_lib::object_id::PrefixResolution::AmbiguousMatch;use jj_lib::object_id::PrefixResolution::NoMatch;use jj_lib::object_id::PrefixResolution::SingleMatch;use jj_lib::op_store::RefTarget;use jj_lib::repo::MutableRepo;use jj_lib::repo::Repo as _;use jj_lib::revset::RevsetExpression;use jj_lib::settings::UserSettings;use testutils::TestRepo;use testutils::TestRepoBackend; fn stable_settings() -> UserSettings {    let mut config = testutils::base_user_config();    let mut layer = ConfigLayer::empty(ConfigSource::User);    layer        .set_value("debug.commit-timestamp", "2001-02-03T04:05:06+07:00")        .unwrap();    config.add_layer(layer);    UserSettings::from_config(config).unwrap()} #[test]fn test_id_prefix() {    let test_repo = TestRepo::init_with_backend(TestRepoBackend::Git);    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();    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.repo_mut()            .new_commit(vec![parent_id.clone()], repo.store().empty_merged_tree_id())            .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("test").unwrap();     // 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"    0c8 9    18f 7    19a 10    37a 13    3b4 21    3c0 1    4ee 16    51f 4    56e 14    711 17    761 3    7b1 11    7c6 24    7f4 8    846 23    8d7 25    960 15    a30 12    b51 19    b97 22    b9d 5    bb4 2    c3a 18    c47 0    d3c 6    d54 20    ");    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::try_from_hex(x).unwrap();    let shortest_commit_prefix_len = |index: &IdPrefixIndex, commit_id| {        index            .shortest_commit_prefix_len(repo.as_ref(), commit_id)            .unwrap()    };    let resolve_commit_prefix = |index: &IdPrefixIndex, prefix: HexPrefix| {        index.resolve_commit_prefix(repo.as_ref(), &prefix).unwrap()    };    let shortest_change_prefix_len = |index: &IdPrefixIndex, change_id| {        index            .shortest_change_prefix_len(repo.as_ref(), change_id)            .unwrap()    };    let resolve_change_prefix = |index: &IdPrefixIndex, prefix: HexPrefix| {        index.resolve_change_prefix(repo.as_ref(), &prefix).unwrap()    };     // Without a disambiguation revset    // ---------------------------------------------------------------------------------------------    let context = IdPrefixContext::default();    let index = context.populate(repo.as_ref()).unwrap();     assert_eq!(shortest_commit_prefix_len(&index, commits[7].id()), 2);    assert_eq!(shortest_commit_prefix_len(&index, commits[16].id()), 1);    assert_eq!(resolve_commit_prefix(&index, prefix("1")), AmbiguousMatch);    assert_eq!(        resolve_commit_prefix(&index, prefix("18")),        SingleMatch(commits[7].id().clone())    );    assert_eq!(resolve_commit_prefix(&index, prefix("10")), NoMatch);    assert_eq!(resolve_commit_prefix(&index, prefix("180")), NoMatch);    assert_eq!(        shortest_change_prefix_len(&index, commits[2].change_id()),        2    );    assert_eq!(        shortest_change_prefix_len(&index, commits[6].change_id()),        1    );    assert_eq!(resolve_change_prefix(&index, prefix("4")), AmbiguousMatch);    assert_eq!(        resolve_change_prefix(&index, prefix("43")),        SingleMatch(vec![commits[2].id().clone()])    );    assert_eq!(resolve_change_prefix(&index, prefix("40")), NoMatch);    assert_eq!(resolve_change_prefix(&index, prefix("430")), NoMatch);     // Disambiguate within a revset    // ---------------------------------------------------------------------------------------------    let expression =        RevsetExpression::commits(vec![commits[7].id().clone(), commits[2].id().clone()]);    let context = context.disambiguate_within(expression);    let index = context.populate(repo.as_ref()).unwrap();    // The prefix is now shorter    assert_eq!(shortest_commit_prefix_len(&index, commits[7].id()), 1);    // Shorter prefix within the set can be used    assert_eq!(        resolve_commit_prefix(&index, prefix("1")),        SingleMatch(commits[7].id().clone())    );    // Can still resolve commits outside the set    assert_eq!(        resolve_commit_prefix(&index, prefix("19")),        SingleMatch(commits[10].id().clone())    );    assert_eq!(        shortest_change_prefix_len(&index, commits[2].change_id()),        1    );    assert_eq!(        resolve_change_prefix(&index, prefix("4")),        SingleMatch(vec![commits[2].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 context = context.disambiguate_within(expression);    let index = context.populate(repo.as_ref()).unwrap();    assert_eq!(shortest_commit_prefix_len(&index, root_commit_id), 1);    assert_eq!(resolve_commit_prefix(&index, prefix("")), AmbiguousMatch);    assert_eq!(        resolve_commit_prefix(&index, prefix("0")),        SingleMatch(root_commit_id.clone())    );    assert_eq!(shortest_change_prefix_len(&index, root_change_id), 1);    assert_eq!(resolve_change_prefix(&index, prefix("")), AmbiguousMatch);    assert_eq!(        resolve_change_prefix(&index, prefix("0")),        SingleMatch(vec![root_commit_id.clone()])    );     // Disambiguate within revset that fails to evaluate    // ---------------------------------------------------------------------------------------------    let expression = RevsetExpression::symbol("nonexistent".to_string());    let context = context.disambiguate_within(expression);    assert!(context.populate(repo.as_ref()).is_err());} #[test]fn test_id_prefix_divergent() {    let test_repo = TestRepo::init_with_backend(TestRepoBackend::Git);    let repo = &test_repo.repo;    let root_commit_id = repo.store().root_commit_id();     let mut tx = repo.start_transaction();    let mut create_commit_with_change_id =        |parent_id: &CommitId, description: &str, change_id: ChangeId| {            let signature = Signature {                name: "Some One".to_string(),                email: "some.one@example.com".to_string(),                timestamp: Timestamp {                    timestamp: MillisSinceEpoch(0),                    tz_offset: 0,                },            };            tx.repo_mut()                .new_commit(vec![parent_id.clone()], repo.store().empty_merged_tree_id())                .set_description(description)                .set_author(signature.clone())                .set_committer(signature)                .set_change_id(change_id)                .write()                .unwrap()        };     let first_change_id = ChangeId::from_hex("a5333333333333333333333333333333");    let second_change_id = ChangeId::from_hex("a5000000000000000000000000000000");     let first_commit = create_commit_with_change_id(root_commit_id, "first", first_change_id);    let second_commit =        create_commit_with_change_id(first_commit.id(), "second", second_change_id.clone());    let third_commit_divergent_with_second =        create_commit_with_change_id(first_commit.id(), "third", second_change_id);    let commits = [        first_commit.clone(),        second_commit.clone(),        third_commit_divergent_with_second.clone(),    ];    let repo = tx.commit("test").unwrap();     // Print the commit IDs and change IDs for reference    let change_prefixes = commits        .iter()        .enumerate()        .map(|(i, commit)| format!("{} {}", &commit.change_id().hex()[..4], i))        .join("\n");    insta::assert_snapshot!(change_prefixes, @r"    a533 0    a500 1    a500 2    ");    let commit_prefixes = commits        .iter()        .enumerate()        .map(|(i, commit)| format!("{} {}", &commit.id().hex()[..4], i))        .join("\n");    insta::assert_snapshot!(commit_prefixes, @r"    e2b9 0    f8d1 1    c596 2    ");     let prefix = |x| HexPrefix::try_from_hex(x).unwrap();    let shortest_change_prefix_len = |index: &IdPrefixIndex, change_id| {        index            .shortest_change_prefix_len(repo.as_ref(), change_id)            .unwrap()    };    let resolve_change_prefix = |index: &IdPrefixIndex, prefix: HexPrefix| {        index.resolve_change_prefix(repo.as_ref(), &prefix).unwrap()    };     // Without a disambiguation revset    // --------------------------------    let context = IdPrefixContext::default();    let index = context.populate(repo.as_ref()).unwrap();    assert_eq!(        shortest_change_prefix_len(&index, commits[0].change_id()),        3    );    assert_eq!(        shortest_change_prefix_len(&index, commits[1].change_id()),        3    );    assert_eq!(        shortest_change_prefix_len(&index, commits[2].change_id()),        3    );    assert_eq!(resolve_change_prefix(&index, prefix("a5")), AmbiguousMatch);    assert_eq!(        resolve_change_prefix(&index, prefix("a53")),        SingleMatch(vec![first_commit.id().clone()])    );    assert_eq!(        resolve_change_prefix(&index, prefix("a50")),        SingleMatch(vec![            second_commit.id().clone(),            third_commit_divergent_with_second.id().clone()        ])    );     // Now, disambiguate within the revset containing only the second commit    // ----------------------------------------------------------------------    let expression = RevsetExpression::commits(vec![second_commit.id().clone()]);    let context = context.disambiguate_within(expression);    let index = context.populate(repo.as_ref()).unwrap();    // The prefix is now shorter    assert_eq!(        shortest_change_prefix_len(&index, second_commit.change_id()),        1    );    // This tests two issues, both important:    // - We find both commits with the same change id, even though    // `third_commit_divergent_with_second` is not in the short prefix set    // (#2476).    // - The short prefix set still works: we do *not* find the first commit and the    //   match is not ambiguous, even though the first commit's change id would also    //   match the prefix.    assert_eq!(        resolve_change_prefix(&index, prefix("a")),        SingleMatch(vec![            second_commit.id().clone(),            third_commit_divergent_with_second.id().clone()        ])    );     // We can still resolve commits outside the set    assert_eq!(        resolve_change_prefix(&index, prefix("a53")),        SingleMatch(vec![first_commit.id().clone()])    );    assert_eq!(        shortest_change_prefix_len(&index, first_commit.change_id()),        3    );} #[test]fn test_id_prefix_hidden() {    let test_repo = TestRepo::init_with_backend(TestRepoBackend::Git);    let repo = &test_repo.repo;    let root_commit_id = repo.store().root_commit_id();     let mut tx = repo.start_transaction();    let mut commits = vec![];    for i in 0..10 {        let signature = Signature {            name: "Some One".to_string(),            email: "some.one@example.com".to_string(),            timestamp: Timestamp {                timestamp: MillisSinceEpoch(i),                tz_offset: 0,            },        };        let commit = tx            .repo_mut()            .new_commit(                vec![root_commit_id.clone()],                repo.store().empty_merged_tree_id(),            )            .set_author(signature.clone())            .set_committer(signature)            .write()            .unwrap();        commits.push(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"    3ae 6    64c 5    84e 2    906 8    912 7    9d1 3    a6b 1    c47 0    d9b 4    f5f 9    ");    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    1b5 6    26b 3    26c 8    439 2    781 0    871 7    896 5    a2c 1    b93 4    ");     let hidden_commit = &commits[8];    tx.repo_mut().record_abandoned_commit(hidden_commit);    tx.repo_mut().rebase_descendants().unwrap();    let repo = tx.commit("test").unwrap();     let prefix = |x: &str| HexPrefix::try_from_hex(x).unwrap();    let shortest_commit_prefix_len = |index: &IdPrefixIndex, commit_id| {        index            .shortest_commit_prefix_len(repo.as_ref(), commit_id)            .unwrap()    };    let resolve_commit_prefix = |index: &IdPrefixIndex, prefix: HexPrefix| {        index.resolve_commit_prefix(repo.as_ref(), &prefix).unwrap()    };    let shortest_change_prefix_len = |index: &IdPrefixIndex, change_id| {        index            .shortest_change_prefix_len(repo.as_ref(), change_id)            .unwrap()    };    let resolve_change_prefix = |index: &IdPrefixIndex, prefix: HexPrefix| {        index.resolve_change_prefix(repo.as_ref(), &prefix).unwrap()    };     // Without a disambiguation revset    // --------------------------------    let context = IdPrefixContext::default();    let index = context.populate(repo.as_ref()).unwrap();    assert_eq!(shortest_commit_prefix_len(&index, hidden_commit.id()), 2);    assert_eq!(        shortest_change_prefix_len(&index, hidden_commit.change_id()),        3    );    assert_eq!(        resolve_commit_prefix(&index, prefix(&hidden_commit.id().hex()[..1])),        AmbiguousMatch    );    assert_eq!(        resolve_commit_prefix(&index, prefix(&hidden_commit.id().hex()[..2])),        SingleMatch(hidden_commit.id().clone())    );    assert_eq!(        resolve_change_prefix(&index, prefix(&hidden_commit.change_id().hex()[..2])),        AmbiguousMatch    );    assert_eq!(

Lines 1–500 of 613.