jj_lib/default_index/
mod.rs

1// Copyright 2023 The Jujutsu Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! An on-disk index of the commits in a repository.
16//!
17//! Implements an index of the commits in a repository that conforms to the
18//! trains in the [index module](crate::index). The index is stored on local
19//! disk and contains an entry for every commit in the repository. See
20//! [`DefaultReadonlyIndex`] and [`DefaultMutableIndex`].
21
22mod bit_set;
23mod changed_path;
24mod composite;
25mod entry;
26mod mutable;
27mod readonly;
28mod rev_walk;
29mod rev_walk_queue;
30mod revset_engine;
31mod revset_graph_iterator;
32mod store;
33
34pub use self::mutable::DefaultMutableIndex;
35pub use self::readonly::ChangedPathIndexLevelStats;
36pub use self::readonly::CommitIndexLevelStats;
37pub use self::readonly::DefaultReadonlyIndex;
38pub use self::readonly::DefaultReadonlyIndexRevset;
39pub use self::readonly::IndexStats;
40pub use self::readonly::ReadonlyIndexLoadError;
41pub use self::store::DefaultIndexStore;
42pub use self::store::DefaultIndexStoreError;
43pub use self::store::DefaultIndexStoreInitError;
44
45#[cfg(test)]
46#[rustversion::attr(
47    since(1.89),
48    expect(clippy::cloned_ref_to_slice_refs, reason = "makes tests more readable")
49)]
50mod tests {
51    use std::cmp::Reverse;
52    use std::convert::Infallible;
53    use std::ops::Range;
54    use std::sync::Arc;
55
56    use itertools::Itertools as _;
57    use smallvec::smallvec_inline;
58    use test_case::test_case;
59
60    use super::changed_path::CompositeChangedPathIndex;
61    use super::composite::AsCompositeIndex as _;
62    use super::composite::CommitIndexSegment as _;
63    use super::composite::CompositeCommitIndex;
64    use super::composite::DynCommitIndexSegment;
65    use super::entry::GlobalCommitPosition;
66    use super::entry::SmallGlobalCommitPositionsVec;
67    use super::mutable::MutableCommitIndexSegment;
68    use super::readonly::ReadonlyCommitIndexSegment;
69    use super::*;
70    use crate::backend::ChangeId;
71    use crate::backend::CommitId;
72    use crate::default_index::entry::LocalCommitPosition;
73    use crate::default_index::entry::SmallLocalCommitPositionsVec;
74    use crate::default_index::readonly::FieldLengths;
75    use crate::index::Index as _;
76    use crate::object_id::HexPrefix;
77    use crate::object_id::PrefixResolution;
78    use crate::revset::PARENTS_RANGE_FULL;
79    use crate::tests::new_temp_dir;
80
81    const TEST_FIELD_LENGTHS: FieldLengths = FieldLengths {
82        // TODO: align with commit_id_generator()?
83        commit_id: 3,
84        change_id: 16,
85    };
86
87    /// Generator of unique 16-byte CommitId excluding root id
88    fn commit_id_generator() -> impl FnMut() -> CommitId {
89        let mut iter = (1_u128..).map(|n| CommitId::new(n.to_le_bytes().into()));
90        move || iter.next().unwrap()
91    }
92
93    /// Generator of unique 16-byte ChangeId excluding root id
94    fn change_id_generator() -> impl FnMut() -> ChangeId {
95        let mut iter = (1_u128..).map(|n| ChangeId::new(n.to_le_bytes().into()));
96        move || iter.next().unwrap()
97    }
98
99    fn get_commit_index_stats(commits: &Arc<ReadonlyCommitIndexSegment>) -> IndexStats {
100        let changed_paths = CompositeChangedPathIndex::null();
101        let index = DefaultReadonlyIndex::from_segment(commits.clone(), changed_paths);
102        index.stats()
103    }
104
105    fn common_ancestors(
106        index: &DefaultMutableIndex,
107        set1: &[CommitId],
108        set2: &[CommitId],
109    ) -> Vec<CommitId> {
110        index.common_ancestors(set1, set2).unwrap()
111    }
112
113    fn is_ancestor(
114        index: &DefaultMutableIndex,
115        ancestor_id: &CommitId,
116        descendant_id: &CommitId,
117    ) -> bool {
118        index.is_ancestor(ancestor_id, descendant_id).unwrap()
119    }
120
121    #[test_case(false; "memory")]
122    #[test_case(true; "file")]
123    fn index_empty(on_disk: bool) {
124        let temp_dir = new_temp_dir();
125        let mutable_segment = MutableCommitIndexSegment::full(TEST_FIELD_LENGTHS);
126        let index_segment: Box<DynCommitIndexSegment> = if on_disk {
127            let saved_index = mutable_segment.save_in(temp_dir.path()).unwrap();
128            // Stats are as expected
129            let stats = get_commit_index_stats(&saved_index);
130            assert_eq!(stats.num_commits, 0);
131            assert_eq!(stats.num_heads, 0);
132            assert_eq!(stats.max_generation_number, 0);
133            assert_eq!(stats.num_merges, 0);
134            assert_eq!(stats.num_changes, 0);
135            Box::new(Arc::try_unwrap(saved_index).unwrap())
136        } else {
137            Box::new(mutable_segment)
138        };
139        let index = CompositeCommitIndex::new(index_segment.as_ref());
140        assert_eq!(index.num_commits(), 0);
141
142        // Cannot find any commits
143        assert!(index.entry_by_id(&CommitId::from_hex("000000")).is_none());
144        assert!(index.entry_by_id(&CommitId::from_hex("aaa111")).is_none());
145        assert!(index.entry_by_id(&CommitId::from_hex("ffffff")).is_none());
146    }
147
148    #[test_case(false; "memory")]
149    #[test_case(true; "file")]
150    fn index_root_commit(on_disk: bool) {
151        let temp_dir = new_temp_dir();
152        let mut new_change_id = change_id_generator();
153        let mut mutable_segment = MutableCommitIndexSegment::full(TEST_FIELD_LENGTHS);
154        let id_0 = CommitId::from_hex("000000");
155        let change_id0 = new_change_id();
156        mutable_segment.add_commit_data(id_0.clone(), change_id0.clone(), &[]);
157        let index_segment: Box<DynCommitIndexSegment> = if on_disk {
158            let saved_index = mutable_segment.save_in(temp_dir.path()).unwrap();
159            // Stats are as expected
160            let stats = get_commit_index_stats(&saved_index);
161            assert_eq!(stats.num_commits, 1);
162            assert_eq!(stats.num_heads, 1);
163            assert_eq!(stats.max_generation_number, 0);
164            assert_eq!(stats.num_merges, 0);
165            assert_eq!(stats.num_changes, 1);
166            Box::new(Arc::try_unwrap(saved_index).unwrap())
167        } else {
168            Box::new(mutable_segment)
169        };
170        let index = CompositeCommitIndex::new(index_segment.as_ref());
171        assert_eq!(index.num_commits(), 1);
172
173        // Can find only the root commit
174        assert_eq!(index.commit_id_to_pos(&id_0), Some(GlobalCommitPosition(0)));
175        assert_eq!(index.commit_id_to_pos(&CommitId::from_hex("aaaaaa")), None);
176        assert_eq!(index.commit_id_to_pos(&CommitId::from_hex("ffffff")), None);
177        // Check properties of root entry
178        let entry = index.entry_by_id(&id_0).unwrap();
179        assert_eq!(entry.position(), GlobalCommitPosition(0));
180        assert_eq!(entry.commit_id(), id_0);
181        assert_eq!(entry.change_id(), change_id0);
182        assert_eq!(entry.generation_number(), 0);
183        assert_eq!(entry.num_parents(), 0);
184        assert_eq!(
185            entry.parent_positions(),
186            SmallGlobalCommitPositionsVec::new()
187        );
188        assert_eq!(entry.parents().len(), 0);
189    }
190
191    #[test]
192    #[should_panic(expected = "parent commit is not indexed")]
193    fn index_missing_parent_commit() {
194        let mut new_change_id = change_id_generator();
195        let mut index = DefaultMutableIndex::full(TEST_FIELD_LENGTHS);
196        let id_0 = CommitId::from_hex("000000");
197        let id_1 = CommitId::from_hex("111111");
198        index.add_commit_data(id_1, new_change_id(), &[id_0]);
199    }
200
201    #[test_case(false, false; "full in memory")]
202    #[test_case(false, true; "full on disk")]
203    #[test_case(true, false; "incremental in memory")]
204    #[test_case(true, true; "incremental on disk")]
205    fn index_multiple_commits(incremental: bool, on_disk: bool) {
206        let temp_dir = new_temp_dir();
207        let mut new_change_id = change_id_generator();
208        let mut mutable_segment = MutableCommitIndexSegment::full(TEST_FIELD_LENGTHS);
209        // 5
210        // |\
211        // 4 | 3
212        // | |/
213        // 1 2
214        // |/
215        // 0
216        let id_0 = CommitId::from_hex("000000");
217        let change_id0 = new_change_id();
218        let id_1 = CommitId::from_hex("111111");
219        let change_id1 = new_change_id();
220        let id_2 = CommitId::from_hex("222222");
221        let change_id2 = change_id1.clone();
222        mutable_segment.add_commit_data(id_0.clone(), change_id0, &[]);
223        mutable_segment.add_commit_data(id_1.clone(), change_id1.clone(), &[id_0.clone()]);
224        mutable_segment.add_commit_data(id_2.clone(), change_id2.clone(), &[id_0.clone()]);
225
226        // If testing incremental indexing, write the first three commits to one file
227        // now and build the remainder as another segment on top.
228        if incremental {
229            let initial_file = mutable_segment.save_in(temp_dir.path()).unwrap();
230            mutable_segment = MutableCommitIndexSegment::incremental(initial_file);
231        }
232
233        let id_3 = CommitId::from_hex("333333");
234        let change_id3 = new_change_id();
235        let id_4 = CommitId::from_hex("444444");
236        let change_id4 = new_change_id();
237        let id_5 = CommitId::from_hex("555555");
238        let change_id5 = change_id3.clone();
239        mutable_segment.add_commit_data(id_3.clone(), change_id3.clone(), &[id_2.clone()]);
240        mutable_segment.add_commit_data(id_4.clone(), change_id4, &[id_1.clone()]);
241        mutable_segment.add_commit_data(id_5.clone(), change_id5, &[id_4.clone(), id_2.clone()]);
242        let index_segment: Box<DynCommitIndexSegment> = if on_disk {
243            let saved_index = mutable_segment.save_in(temp_dir.path()).unwrap();
244            // Stats are as expected
245            let stats = get_commit_index_stats(&saved_index);
246            assert_eq!(stats.num_commits, 6);
247            assert_eq!(stats.num_heads, 2);
248            assert_eq!(stats.max_generation_number, 3);
249            assert_eq!(stats.num_merges, 1);
250            assert_eq!(stats.num_changes, 4);
251            Box::new(Arc::try_unwrap(saved_index).unwrap())
252        } else {
253            Box::new(mutable_segment)
254        };
255        let index = CompositeCommitIndex::new(index_segment.as_ref());
256        assert_eq!(index.num_commits(), 6);
257
258        // Can find all the commits
259        let entry_0 = index.entry_by_id(&id_0).unwrap();
260        let entry_1 = index.entry_by_id(&id_1).unwrap();
261        let entry_2 = index.entry_by_id(&id_2).unwrap();
262        let entry_3 = index.entry_by_id(&id_3).unwrap();
263        let entry_4 = index.entry_by_id(&id_4).unwrap();
264        let entry_5 = index.entry_by_id(&id_5).unwrap();
265        // Check properties of some entries
266        assert_eq!(entry_0.position(), GlobalCommitPosition(0));
267        assert_eq!(entry_0.commit_id(), id_0);
268        assert_eq!(entry_1.position(), GlobalCommitPosition(1));
269        assert_eq!(entry_1.commit_id(), id_1);
270        assert_eq!(entry_1.change_id(), change_id1);
271        assert_eq!(entry_1.generation_number(), 1);
272        assert_eq!(entry_1.num_parents(), 1);
273        assert_eq!(
274            entry_1.parent_positions(),
275            smallvec_inline![GlobalCommitPosition(0)]
276        );
277        assert_eq!(entry_1.parents().len(), 1);
278        assert_eq!(
279            entry_1.parents().next().unwrap().position(),
280            GlobalCommitPosition(0)
281        );
282        assert_eq!(entry_2.position(), GlobalCommitPosition(2));
283        assert_eq!(entry_2.commit_id(), id_2);
284        assert_eq!(entry_2.change_id(), change_id2);
285        assert_eq!(entry_2.generation_number(), 1);
286        assert_eq!(entry_2.num_parents(), 1);
287        assert_eq!(
288            entry_2.parent_positions(),
289            smallvec_inline![GlobalCommitPosition(0)]
290        );
291        assert_eq!(entry_3.change_id(), change_id3);
292        assert_eq!(entry_3.generation_number(), 2);
293        assert_eq!(
294            entry_3.parent_positions(),
295            smallvec_inline![GlobalCommitPosition(2)]
296        );
297        assert_eq!(entry_4.position(), GlobalCommitPosition(4));
298        assert_eq!(entry_4.generation_number(), 2);
299        assert_eq!(entry_4.num_parents(), 1);
300        assert_eq!(
301            entry_4.parent_positions(),
302            smallvec_inline![GlobalCommitPosition(1)]
303        );
304        assert_eq!(entry_5.generation_number(), 3);
305        assert_eq!(entry_5.num_parents(), 2);
306        assert_eq!(
307            entry_5.parent_positions(),
308            smallvec_inline![GlobalCommitPosition(4), GlobalCommitPosition(2)]
309        );
310        assert_eq!(entry_5.parents().len(), 2);
311        assert_eq!(
312            entry_5.parents().next().unwrap().position(),
313            GlobalCommitPosition(4)
314        );
315        assert_eq!(
316            entry_5.parents().nth(1).unwrap().position(),
317            GlobalCommitPosition(2)
318        );
319    }
320
321    #[test_case(false; "in memory")]
322    #[test_case(true; "on disk")]
323    fn index_many_parents(on_disk: bool) {
324        let temp_dir = new_temp_dir();
325        let mut new_change_id = change_id_generator();
326        let mut mutable_segment = MutableCommitIndexSegment::full(TEST_FIELD_LENGTHS);
327        //     6
328        //    /|\
329        //   / | \
330        //  / /|\ \
331        // 1 2 3 4 5
332        //  \ \|/ /
333        //   \ | /
334        //    \|/
335        //     0
336        let id_0 = CommitId::from_hex("000000");
337        let id_1 = CommitId::from_hex("111111");
338        let id_2 = CommitId::from_hex("222222");
339        let id_3 = CommitId::from_hex("333333");
340        let id_4 = CommitId::from_hex("444444");
341        let id_5 = CommitId::from_hex("555555");
342        let id_6 = CommitId::from_hex("666666");
343        mutable_segment.add_commit_data(id_0.clone(), new_change_id(), &[]);
344        mutable_segment.add_commit_data(id_1.clone(), new_change_id(), &[id_0.clone()]);
345        mutable_segment.add_commit_data(id_2.clone(), new_change_id(), &[id_0.clone()]);
346        mutable_segment.add_commit_data(id_3.clone(), new_change_id(), &[id_0.clone()]);
347        mutable_segment.add_commit_data(id_4.clone(), new_change_id(), &[id_0.clone()]);
348        mutable_segment.add_commit_data(id_5.clone(), new_change_id(), &[id_0]);
349        mutable_segment.add_commit_data(
350            id_6.clone(),
351            new_change_id(),
352            &[id_1, id_2, id_3, id_4, id_5],
353        );
354        let index_segment: Box<DynCommitIndexSegment> = if on_disk {
355            let saved_index = mutable_segment.save_in(temp_dir.path()).unwrap();
356            // Stats are as expected
357            let stats = get_commit_index_stats(&saved_index);
358            assert_eq!(stats.num_commits, 7);
359            assert_eq!(stats.num_heads, 1);
360            assert_eq!(stats.max_generation_number, 2);
361            assert_eq!(stats.num_merges, 1);
362            Box::new(Arc::try_unwrap(saved_index).unwrap())
363        } else {
364            Box::new(mutable_segment)
365        };
366        let index = CompositeCommitIndex::new(index_segment.as_ref());
367        assert_eq!(index.num_commits(), 7);
368
369        // The octopus merge has the right parents
370        let entry_6 = index.entry_by_id(&id_6).unwrap();
371        assert_eq!(entry_6.commit_id(), id_6.clone());
372        assert_eq!(entry_6.num_parents(), 5);
373        assert_eq!(
374            entry_6.parent_positions(),
375            smallvec_inline![
376                GlobalCommitPosition(1),
377                GlobalCommitPosition(2),
378                GlobalCommitPosition(3),
379                GlobalCommitPosition(4),
380                GlobalCommitPosition(5),
381            ]
382        );
383        assert_eq!(entry_6.generation_number(), 2);
384    }
385
386    #[test]
387    fn resolve_commit_id_prefix() {
388        let temp_dir = new_temp_dir();
389        let mut new_change_id = change_id_generator();
390        let mut mutable_segment = MutableCommitIndexSegment::full(TEST_FIELD_LENGTHS);
391
392        // Create some commits with different various common prefixes.
393        let id_0 = CommitId::from_hex("000000");
394        let id_1 = CommitId::from_hex("009999");
395        let id_2 = CommitId::from_hex("055488");
396        mutable_segment.add_commit_data(id_0.clone(), new_change_id(), &[]);
397        mutable_segment.add_commit_data(id_1.clone(), new_change_id(), &[]);
398        mutable_segment.add_commit_data(id_2.clone(), new_change_id(), &[]);
399
400        // Write the first three commits to one file and build the remainder on top.
401        let initial_file = mutable_segment.save_in(temp_dir.path()).unwrap();
402        mutable_segment = MutableCommitIndexSegment::incremental(initial_file);
403
404        let id_3 = CommitId::from_hex("055444");
405        let id_4 = CommitId::from_hex("055555");
406        let id_5 = CommitId::from_hex("033333");
407        mutable_segment.add_commit_data(id_3, new_change_id(), &[]);
408        mutable_segment.add_commit_data(id_4, new_change_id(), &[]);
409        mutable_segment.add_commit_data(id_5, new_change_id(), &[]);
410
411        let index = mutable_segment.as_composite();
412
413        // Can find commits given the full hex number
414        assert_eq!(
415            index.resolve_commit_id_prefix(&HexPrefix::from_id(&id_0)),
416            PrefixResolution::SingleMatch(id_0)
417        );
418        assert_eq!(
419            index.resolve_commit_id_prefix(&HexPrefix::from_id(&id_1)),
420            PrefixResolution::SingleMatch(id_1)
421        );
422        assert_eq!(
423            index.resolve_commit_id_prefix(&HexPrefix::from_id(&id_2)),
424            PrefixResolution::SingleMatch(id_2)
425        );
426        // Test nonexistent commits
427        assert_eq!(
428            index.resolve_commit_id_prefix(&HexPrefix::try_from_hex("ffffff").unwrap()),
429            PrefixResolution::NoMatch
430        );
431        assert_eq!(
432            index.resolve_commit_id_prefix(&HexPrefix::try_from_hex("000001").unwrap()),
433            PrefixResolution::NoMatch
434        );
435        // Test ambiguous prefix
436        assert_eq!(
437            index.resolve_commit_id_prefix(&HexPrefix::try_from_hex("0").unwrap()),
438            PrefixResolution::AmbiguousMatch
439        );
440        // Test a globally unique prefix in initial part
441        assert_eq!(
442            index.resolve_commit_id_prefix(&HexPrefix::try_from_hex("009").unwrap()),
443            PrefixResolution::SingleMatch(CommitId::from_hex("009999"))
444        );
445        // Test a globally unique prefix in incremental part
446        assert_eq!(
447            index.resolve_commit_id_prefix(&HexPrefix::try_from_hex("03").unwrap()),
448            PrefixResolution::SingleMatch(CommitId::from_hex("033333"))
449        );
450        // Test a locally unique but globally ambiguous prefix
451        assert_eq!(
452            index.resolve_commit_id_prefix(&HexPrefix::try_from_hex("0554").unwrap()),
453            PrefixResolution::AmbiguousMatch
454        );
455    }
456
457    #[test]
458    #[expect(clippy::redundant_clone)] // allow id_n.clone()
459    fn neighbor_commit_ids() {
460        let temp_dir = new_temp_dir();
461        let mut new_change_id = change_id_generator();
462        let mut mutable_segment = MutableCommitIndexSegment::full(TEST_FIELD_LENGTHS);
463
464        // Create some commits with different various common prefixes.
465        let id_0 = CommitId::from_hex("000001");
466        let id_1 = CommitId::from_hex("009999");
467        let id_2 = CommitId::from_hex("055488");
468        mutable_segment.add_commit_data(id_0.clone(), new_change_id(), &[]);
469        mutable_segment.add_commit_data(id_1.clone(), new_change_id(), &[]);
470        mutable_segment.add_commit_data(id_2.clone(), new_change_id(), &[]);
471
472        // Write the first three commits to one file and build the remainder on top.
473        let initial_file = mutable_segment.save_in(temp_dir.path()).unwrap();
474        mutable_segment = MutableCommitIndexSegment::incremental(initial_file.clone());
475
476        let id_3 = CommitId::from_hex("055444");
477        let id_4 = CommitId::from_hex("055555");
478        let id_5 = CommitId::from_hex("033333");
479        mutable_segment.add_commit_data(id_3.clone(), new_change_id(), &[]);
480        mutable_segment.add_commit_data(id_4.clone(), new_change_id(), &[]);
481        mutable_segment.add_commit_data(id_5.clone(), new_change_id(), &[]);
482
483        // Local lookup in readonly index, commit_id exists.
484        assert_eq!(
485            initial_file.resolve_neighbor_commit_ids(&id_0),
486            (None, Some(id_1.clone())),
487        );
488        assert_eq!(
489            initial_file.resolve_neighbor_commit_ids(&id_1),
490            (Some(id_0.clone()), Some(id_2.clone())),
491        );
492        assert_eq!(
493            initial_file.resolve_neighbor_commit_ids(&id_2),
494            (Some(id_1.clone()), None),
495        );
496
497        // Local lookup in readonly index, commit_id does not exist.
498        assert_eq!(
499            initial_file.resolve_neighbor_commit_ids(&CommitId::from_hex("000000")),
500            (None, Some(id_0.clone())),
501        );
502        assert_eq!(
503            initial_file.resolve_neighbor_commit_ids(&CommitId::from_hex("000002")),
504            (Some(id_0.clone()), Some(id_1.clone())),
505        );
506        assert_eq!(
507            initial_file.resolve_neighbor_commit_ids(&CommitId::from_hex("ffffff")),
508            (Some(id_2.clone()), None),
509        );
510
511        // Local lookup in mutable index, commit_id exists. id_5 < id_3 < id_4
512        assert_eq!(
513            mutable_segment.resolve_neighbor_commit_ids(&id_5),
514            (None, Some(id_3.clone())),
515        );
516        assert_eq!(
517            mutable_segment.resolve_neighbor_commit_ids(&id_3),
518            (Some(id_5.clone()), Some(id_4.clone())),
519        );
520        assert_eq!(
521            mutable_segment.resolve_neighbor_commit_ids(&id_4),
522            (Some(id_3.clone()), None),
523        );
524
525        // Local lookup in mutable index, commit_id does not exist. id_5 < id_3 < id_4
526        assert_eq!(
527            mutable_segment.resolve_neighbor_commit_ids(&CommitId::from_hex("033332")),
528            (None, Some(id_5.clone())),
529        );
530        assert_eq!(
531            mutable_segment.resolve_neighbor_commit_ids(&CommitId::from_hex("033334")),
532            (Some(id_5.clone()), Some(id_3.clone())),
533        );
534        assert_eq!(
535            mutable_segment.resolve_neighbor_commit_ids(&CommitId::from_hex("ffffff")),
536            (Some(id_4.clone()), None),
537        );
538
539        // Global lookup, commit_id exists. id_0 < id_1 < id_5 < id_3 < id_2 < id_4
540        let composite_index = CompositeCommitIndex::new(&mutable_segment);
541        assert_eq!(
542            composite_index.resolve_neighbor_commit_ids(&id_0),
543            (None, Some(id_1.clone())),
544        );
545        assert_eq!(
546            composite_index.resolve_neighbor_commit_ids(&id_1),
547            (Some(id_0.clone()), Some(id_5.clone())),
548        );
549        assert_eq!(
550            composite_index.resolve_neighbor_commit_ids(&id_5),
551            (Some(id_1.clone()), Some(id_3.clone())),
552        );
553        assert_eq!(
554            composite_index.resolve_neighbor_commit_ids(&id_3),
555            (Some(id_5.clone()), Some(id_2.clone())),
556        );
557        assert_eq!(
558            composite_index.resolve_neighbor_commit_ids(&id_2),
559            (Some(id_3.clone()), Some(id_4.clone())),
560        );
561        assert_eq!(
562            composite_index.resolve_neighbor_commit_ids(&id_4),
563            (Some(id_2.clone()), None),
564        );
565
566        // Global lookup, commit_id doesn't exist. id_0 < id_1 < id_5 < id_3 < id_2 <
567        // id_4
568        assert_eq!(
569            composite_index.resolve_neighbor_commit_ids(&CommitId::from_hex("000000")),
570            (None, Some(id_0.clone())),
571        );
572        assert_eq!(
573            composite_index.resolve_neighbor_commit_ids(&CommitId::from_hex("010000")),
574            (Some(id_1.clone()), Some(id_5.clone())),
575        );
576        assert_eq!(
577            composite_index.resolve_neighbor_commit_ids(&CommitId::from_hex("033334")),
578            (Some(id_5.clone()), Some(id_3.clone())),
579        );
580        assert_eq!(
581            composite_index.resolve_neighbor_commit_ids(&CommitId::from_hex("ffffff")),
582            (Some(id_4.clone()), None),
583        );
584    }
585
586    #[test]
587    fn shortest_unique_commit_id_prefix() {
588        let temp_dir = new_temp_dir();
589        let mut new_change_id = change_id_generator();
590        let mut mutable_segment = MutableCommitIndexSegment::full(TEST_FIELD_LENGTHS);
591
592        // Create some commits with different various common prefixes.
593        let id_0 = CommitId::from_hex("000001");
594        let id_1 = CommitId::from_hex("009999");
595        let id_2 = CommitId::from_hex("055488");
596        mutable_segment.add_commit_data(id_0.clone(), new_change_id(), &[]);
597        mutable_segment.add_commit_data(id_1.clone(), new_change_id(), &[]);
598        mutable_segment.add_commit_data(id_2.clone(), new_change_id(), &[]);
599
600        // Write the first three commits to one file and build the remainder on top.
601        let initial_file = mutable_segment.save_in(temp_dir.path()).unwrap();
602        mutable_segment = MutableCommitIndexSegment::incremental(initial_file);
603
604        let id_3 = CommitId::from_hex("055444");
605        let id_4 = CommitId::from_hex("055555");
606        let id_5 = CommitId::from_hex("033333");
607        mutable_segment.add_commit_data(id_3.clone(), new_change_id(), &[]);
608        mutable_segment.add_commit_data(id_4.clone(), new_change_id(), &[]);
609        mutable_segment.add_commit_data(id_5.clone(), new_change_id(), &[]);
610
611        let index = mutable_segment.as_composite();
612
613        // Public API: calculate shortest unique prefix len with known commit_id
614        assert_eq!(index.shortest_unique_commit_id_prefix_len(&id_0), 3);
615        assert_eq!(index.shortest_unique_commit_id_prefix_len(&id_1), 3);
616        assert_eq!(index.shortest_unique_commit_id_prefix_len(&id_2), 5);
617        assert_eq!(index.shortest_unique_commit_id_prefix_len(&id_3), 5);
618        assert_eq!(index.shortest_unique_commit_id_prefix_len(&id_4), 4);
619        assert_eq!(index.shortest_unique_commit_id_prefix_len(&id_5), 2);
620
621        // Public API: calculate shortest unique prefix len with unknown commit_id
622        assert_eq!(
623            index.shortest_unique_commit_id_prefix_len(&CommitId::from_hex("000002")),
624            6
625        );
626        assert_eq!(
627            index.shortest_unique_commit_id_prefix_len(&CommitId::from_hex("010000")),
628            2
629        );
630        assert_eq!(
631            index.shortest_unique_commit_id_prefix_len(&CommitId::from_hex("033334")),
632            6
633        );
634        assert_eq!(
635            index.shortest_unique_commit_id_prefix_len(&CommitId::from_hex("ffffff")),
636            1
637        );
638    }
639
640    #[test]
641    fn resolve_change_id_prefix() {
642        let temp_dir = new_temp_dir();
643        let mut new_commit_id = commit_id_generator();
644        let local_positions_vec = |positions: &[u32]| -> SmallLocalCommitPositionsVec {
645            positions.iter().copied().map(LocalCommitPosition).collect()
646        };
647        let index_positions_vec = |positions: &[u32]| -> SmallGlobalCommitPositionsVec {
648            positions
649                .iter()
650                .copied()
651                .map(GlobalCommitPosition)
652                .collect()
653        };
654
655        let id_0 = ChangeId::from_hex("00000001");
656        let id_1 = ChangeId::from_hex("00999999");
657        let id_2 = ChangeId::from_hex("05548888");
658        let id_3 = ChangeId::from_hex("05544444");
659        let id_4 = ChangeId::from_hex("05555555");
660        let id_5 = ChangeId::from_hex("05555333");
661
662        // Create some commits with different various common prefixes.
663        let mut mutable_segment = MutableCommitIndexSegment::full(FieldLengths {
664            commit_id: 16,
665            change_id: 4,
666        });
667        mutable_segment.add_commit_data(new_commit_id(), id_0.clone(), &[]);
668        mutable_segment.add_commit_data(new_commit_id(), id_1.clone(), &[]);
669        mutable_segment.add_commit_data(new_commit_id(), id_2.clone(), &[]);
670        mutable_segment.add_commit_data(new_commit_id(), id_1.clone(), &[]);
671        mutable_segment.add_commit_data(new_commit_id(), id_2.clone(), &[]);
672        mutable_segment.add_commit_data(new_commit_id(), id_2.clone(), &[]);
673
674        // Write these commits to one file and build the remainder on top.
675        let initial_file = mutable_segment.save_in(temp_dir.path()).unwrap();
676        mutable_segment = MutableCommitIndexSegment::incremental(initial_file.clone());
677
678        mutable_segment.add_commit_data(new_commit_id(), id_3.clone(), &[]);
679        mutable_segment.add_commit_data(new_commit_id(), id_3.clone(), &[]);
680        mutable_segment.add_commit_data(new_commit_id(), id_4.clone(), &[]);
681        mutable_segment.add_commit_data(new_commit_id(), id_1.clone(), &[]);
682        mutable_segment.add_commit_data(new_commit_id(), id_5.clone(), &[]);
683
684        // Local lookup in readonly index with the full hex digits
685        assert_eq!(
686            initial_file.resolve_change_id_prefix(&HexPrefix::from_id(&id_0)),
687            PrefixResolution::SingleMatch((id_0.clone(), local_positions_vec(&[0])))
688        );
689        assert_eq!(
690            initial_file.resolve_change_id_prefix(&HexPrefix::from_id(&id_1)),
691            PrefixResolution::SingleMatch((id_1.clone(), local_positions_vec(&[1, 3])))
692        );
693        assert_eq!(
694            initial_file.resolve_change_id_prefix(&HexPrefix::from_id(&id_2)),
695            PrefixResolution::SingleMatch((id_2.clone(), local_positions_vec(&[2, 4, 5])))
696        );
697
698        // Local lookup in mutable index with the full hex digits
699        assert_eq!(
700            mutable_segment.resolve_change_id_prefix(&HexPrefix::from_id(&id_1)),
701            PrefixResolution::SingleMatch((id_1.clone(), local_positions_vec(&[3])))
702        );
703        assert_eq!(
704            mutable_segment.resolve_change_id_prefix(&HexPrefix::from_id(&id_3)),
705            PrefixResolution::SingleMatch((id_3.clone(), local_positions_vec(&[0, 1])))
706        );
707        assert_eq!(
708            mutable_segment.resolve_change_id_prefix(&HexPrefix::from_id(&id_4)),
709            PrefixResolution::SingleMatch((id_4.clone(), local_positions_vec(&[2])))
710        );
711        assert_eq!(
712            mutable_segment.resolve_change_id_prefix(&HexPrefix::from_id(&id_5)),
713            PrefixResolution::SingleMatch((id_5.clone(), local_positions_vec(&[4])))
714        );
715
716        // Local lookup with locally unknown prefix
717        assert_eq!(
718            initial_file.resolve_change_id_prefix(&HexPrefix::try_from_hex("0555").unwrap()),
719            PrefixResolution::NoMatch
720        );
721        assert_eq!(
722            mutable_segment.resolve_change_id_prefix(&HexPrefix::try_from_hex("000").unwrap()),
723            PrefixResolution::NoMatch
724        );
725
726        // Local lookup with locally unique prefix
727        assert_eq!(
728            initial_file.resolve_change_id_prefix(&HexPrefix::try_from_hex("0554").unwrap()),
729            PrefixResolution::SingleMatch((id_2.clone(), local_positions_vec(&[2, 4, 5])))
730        );
731        assert_eq!(
732            mutable_segment.resolve_change_id_prefix(&HexPrefix::try_from_hex("0554").unwrap()),
733            PrefixResolution::SingleMatch((id_3.clone(), local_positions_vec(&[0, 1])))
734        );
735
736        // Local lookup with locally ambiguous prefix
737        assert_eq!(
738            initial_file.resolve_change_id_prefix(&HexPrefix::try_from_hex("00").unwrap()),
739            PrefixResolution::AmbiguousMatch
740        );
741        assert_eq!(
742            mutable_segment.resolve_change_id_prefix(&HexPrefix::try_from_hex("05555").unwrap()),
743            PrefixResolution::AmbiguousMatch
744        );
745
746        let index = mutable_segment.as_composite();
747
748        // Global lookup with the full hex digits
749        assert_eq!(
750            index.resolve_change_id_prefix(&HexPrefix::from_id(&id_0)),
751            PrefixResolution::SingleMatch((id_0.clone(), index_positions_vec(&[0])))
752        );
753        assert_eq!(
754            index.resolve_change_id_prefix(&HexPrefix::from_id(&id_1)),
755            PrefixResolution::SingleMatch((id_1.clone(), index_positions_vec(&[1, 3, 9])))
756        );
757        assert_eq!(
758            index.resolve_change_id_prefix(&HexPrefix::from_id(&id_2)),
759            PrefixResolution::SingleMatch((id_2.clone(), index_positions_vec(&[2, 4, 5])))
760        );
761        assert_eq!(
762            index.resolve_change_id_prefix(&HexPrefix::from_id(&id_3)),
763            PrefixResolution::SingleMatch((id_3.clone(), index_positions_vec(&[6, 7])))
764        );
765        assert_eq!(
766            index.resolve_change_id_prefix(&HexPrefix::from_id(&id_4)),
767            PrefixResolution::SingleMatch((id_4.clone(), index_positions_vec(&[8])))
768        );
769        assert_eq!(
770            index.resolve_change_id_prefix(&HexPrefix::from_id(&id_5)),
771            PrefixResolution::SingleMatch((id_5.clone(), index_positions_vec(&[10])))
772        );
773
774        // Global lookup with unknown prefix
775        assert_eq!(
776            index.resolve_change_id_prefix(&HexPrefix::try_from_hex("ffffffff").unwrap()),
777            PrefixResolution::NoMatch
778        );
779        assert_eq!(
780            index.resolve_change_id_prefix(&HexPrefix::try_from_hex("00000002").unwrap()),
781            PrefixResolution::NoMatch
782        );
783
784        // Global lookup with globally unique prefix
785        assert_eq!(
786            index.resolve_change_id_prefix(&HexPrefix::try_from_hex("000").unwrap()),
787            PrefixResolution::SingleMatch((id_0.clone(), index_positions_vec(&[0])))
788        );
789        assert_eq!(
790            index.resolve_change_id_prefix(&HexPrefix::try_from_hex("055553").unwrap()),
791            PrefixResolution::SingleMatch((id_5.clone(), index_positions_vec(&[10])))
792        );
793
794        // Global lookup with globally unique prefix stored in both parts
795        assert_eq!(
796            index.resolve_change_id_prefix(&HexPrefix::try_from_hex("009").unwrap()),
797            PrefixResolution::SingleMatch((id_1.clone(), index_positions_vec(&[1, 3, 9])))
798        );
799
800        // Global lookup with locally ambiguous prefix
801        assert_eq!(
802            index.resolve_change_id_prefix(&HexPrefix::try_from_hex("00").unwrap()),
803            PrefixResolution::AmbiguousMatch
804        );
805        assert_eq!(
806            index.resolve_change_id_prefix(&HexPrefix::try_from_hex("05555").unwrap()),
807            PrefixResolution::AmbiguousMatch
808        );
809
810        // Global lookup with locally unique but globally ambiguous prefix
811        assert_eq!(
812            index.resolve_change_id_prefix(&HexPrefix::try_from_hex("0554").unwrap()),
813            PrefixResolution::AmbiguousMatch
814        );
815    }
816
817    #[test]
818    fn neighbor_change_ids() {
819        let temp_dir = new_temp_dir();
820        let mut new_commit_id = commit_id_generator();
821
822        let id_0 = ChangeId::from_hex("00000001");
823        let id_1 = ChangeId::from_hex("00999999");
824        let id_2 = ChangeId::from_hex("05548888");
825        let id_3 = ChangeId::from_hex("05544444");
826        let id_4 = ChangeId::from_hex("05555555");
827        let id_5 = ChangeId::from_hex("05555333");
828
829        // Create some commits with different various common prefixes.
830        let mut mutable_segment = MutableCommitIndexSegment::full(FieldLengths {
831            commit_id: 16,
832            change_id: 4,
833        });
834        mutable_segment.add_commit_data(new_commit_id(), id_0.clone(), &[]);
835        mutable_segment.add_commit_data(new_commit_id(), id_1.clone(), &[]);
836        mutable_segment.add_commit_data(new_commit_id(), id_2.clone(), &[]);
837        mutable_segment.add_commit_data(new_commit_id(), id_1.clone(), &[]);
838        mutable_segment.add_commit_data(new_commit_id(), id_2.clone(), &[]);
839        mutable_segment.add_commit_data(new_commit_id(), id_2.clone(), &[]);
840
841        // Write these commits to one file and build the remainder on top.
842        let initial_file = mutable_segment.save_in(temp_dir.path()).unwrap();
843        mutable_segment = MutableCommitIndexSegment::incremental(initial_file.clone());
844
845        mutable_segment.add_commit_data(new_commit_id(), id_3.clone(), &[]);
846        mutable_segment.add_commit_data(new_commit_id(), id_3.clone(), &[]);
847        mutable_segment.add_commit_data(new_commit_id(), id_4.clone(), &[]);
848        mutable_segment.add_commit_data(new_commit_id(), id_1.clone(), &[]);
849        mutable_segment.add_commit_data(new_commit_id(), id_5.clone(), &[]);
850
851        // Local lookup in readonly index, change_id exists.
852        assert_eq!(
853            initial_file.resolve_neighbor_change_ids(&id_0),
854            (None, Some(id_1.clone())),
855        );
856        assert_eq!(
857            initial_file.resolve_neighbor_change_ids(&id_1),
858            (Some(id_0.clone()), Some(id_2.clone())),
859        );
860        assert_eq!(
861            initial_file.resolve_neighbor_change_ids(&id_2),
862            (Some(id_1.clone()), None),
863        );
864
865        // Local lookup in readonly index, change_id does not exist.
866        assert_eq!(
867            initial_file.resolve_neighbor_change_ids(&ChangeId::from_hex("00000000")),
868            (None, Some(id_0.clone())),
869        );
870        assert_eq!(
871            initial_file.resolve_neighbor_change_ids(&ChangeId::from_hex("00000002")),
872            (Some(id_0.clone()), Some(id_1.clone())),
873        );
874        assert_eq!(
875            initial_file.resolve_neighbor_change_ids(&ChangeId::from_hex("ffffffff")),
876            (Some(id_2.clone()), None),
877        );
878
879        // Local lookup in mutable index, change_id exists.
880        // id_1 < id_3 < id_5 < id_4
881        assert_eq!(
882            mutable_segment.resolve_neighbor_change_ids(&id_1),
883            (None, Some(id_3.clone())),
884        );
885        assert_eq!(
886            mutable_segment.resolve_neighbor_change_ids(&id_3),
887            (Some(id_1.clone()), Some(id_5.clone())),
888        );
889        assert_eq!(
890            mutable_segment.resolve_neighbor_change_ids(&id_5),
891            (Some(id_3.clone()), Some(id_4.clone())),
892        );
893        assert_eq!(
894            mutable_segment.resolve_neighbor_change_ids(&id_4),
895            (Some(id_5.clone()), None),
896        );
897
898        // Local lookup in mutable index, change_id does not exist.
899        // id_1 < id_3 < id_5 < id_4
900        assert_eq!(
901            mutable_segment.resolve_neighbor_change_ids(&ChangeId::from_hex("00999998")),
902            (None, Some(id_1.clone())),
903        );
904        assert_eq!(
905            mutable_segment.resolve_neighbor_change_ids(&ChangeId::from_hex("01000000")),
906            (Some(id_1.clone()), Some(id_3.clone())),
907        );
908        assert_eq!(
909            mutable_segment.resolve_neighbor_change_ids(&ChangeId::from_hex("05555332")),
910            (Some(id_3.clone()), Some(id_5.clone())),
911        );
912        assert_eq!(
913            mutable_segment.resolve_neighbor_change_ids(&ChangeId::from_hex("ffffffff")),
914            (Some(id_4.clone()), None),
915        );
916
917        let index = mutable_segment.as_composite();
918
919        // Global lookup, change_id exists.
920        // id_0 < id_1 < id_3 < id_2 < id_5 < id_4
921        assert_eq!(
922            index.resolve_neighbor_change_ids(&id_0),
923            (None, Some(id_1.clone())),
924        );
925        assert_eq!(
926            index.resolve_neighbor_change_ids(&id_1),
927            (Some(id_0.clone()), Some(id_3.clone())),
928        );
929        assert_eq!(
930            index.resolve_neighbor_change_ids(&id_3),
931            (Some(id_1.clone()), Some(id_2.clone())),
932        );
933        assert_eq!(
934            index.resolve_neighbor_change_ids(&id_2),
935            (Some(id_3.clone()), Some(id_5.clone())),
936        );
937        assert_eq!(
938            index.resolve_neighbor_change_ids(&id_5),
939            (Some(id_2.clone()), Some(id_4.clone())),
940        );
941        assert_eq!(
942            index.resolve_neighbor_change_ids(&id_4),
943            (Some(id_5.clone()), None),
944        );
945
946        // Global lookup, change_id doesn't exist.
947        // id_0 < id_1 < id_3 < id_2 < id_5 < id_4
948        assert_eq!(
949            index.resolve_neighbor_change_ids(&ChangeId::from_hex("00000000")),
950            (None, Some(id_0.clone())),
951        );
952        assert_eq!(
953            index.resolve_neighbor_change_ids(&ChangeId::from_hex("01000000")),
954            (Some(id_1.clone()), Some(id_3.clone())),
955        );
956        assert_eq!(
957            index.resolve_neighbor_change_ids(&ChangeId::from_hex("05544555")),
958            (Some(id_3.clone()), Some(id_2.clone())),
959        );
960        assert_eq!(
961            index.resolve_neighbor_change_ids(&ChangeId::from_hex("ffffffff")),
962            (Some(id_4.clone()), None),
963        );
964    }
965
966    #[test]
967    fn shortest_unique_change_id_prefix() {
968        let temp_dir = new_temp_dir();
969        let mut new_commit_id = commit_id_generator();
970
971        let id_0 = ChangeId::from_hex("00000001");
972        let id_1 = ChangeId::from_hex("00999999");
973        let id_2 = ChangeId::from_hex("05548888");
974        let id_3 = ChangeId::from_hex("05544444");
975        let id_4 = ChangeId::from_hex("05555555");
976        let id_5 = ChangeId::from_hex("05555333");
977
978        // Create some commits with different various common prefixes.
979        let mut mutable_segment = MutableCommitIndexSegment::full(FieldLengths {
980            commit_id: 16,
981            change_id: 4,
982        });
983        mutable_segment.add_commit_data(new_commit_id(), id_0.clone(), &[]);
984        mutable_segment.add_commit_data(new_commit_id(), id_1.clone(), &[]);
985        mutable_segment.add_commit_data(new_commit_id(), id_2.clone(), &[]);
986        mutable_segment.add_commit_data(new_commit_id(), id_1.clone(), &[]);
987        mutable_segment.add_commit_data(new_commit_id(), id_2.clone(), &[]);
988        mutable_segment.add_commit_data(new_commit_id(), id_2.clone(), &[]);
989
990        // Write these commits to one file and build the remainder on top.
991        let initial_file = mutable_segment.save_in(temp_dir.path()).unwrap();
992        mutable_segment = MutableCommitIndexSegment::incremental(initial_file.clone());
993
994        mutable_segment.add_commit_data(new_commit_id(), id_3.clone(), &[]);
995        mutable_segment.add_commit_data(new_commit_id(), id_3.clone(), &[]);
996        mutable_segment.add_commit_data(new_commit_id(), id_4.clone(), &[]);
997        mutable_segment.add_commit_data(new_commit_id(), id_1.clone(), &[]);
998        mutable_segment.add_commit_data(new_commit_id(), id_5.clone(), &[]);
999
1000        let index = mutable_segment.as_composite();
1001
1002        // Calculate shortest unique prefix len with known change_id
1003        assert_eq!(index.shortest_unique_change_id_prefix_len(&id_0), 3);
1004        assert_eq!(index.shortest_unique_change_id_prefix_len(&id_1), 3);
1005        assert_eq!(index.shortest_unique_change_id_prefix_len(&id_2), 5);
1006        assert_eq!(index.shortest_unique_change_id_prefix_len(&id_3), 5);
1007        assert_eq!(index.shortest_unique_change_id_prefix_len(&id_4), 6);
1008        assert_eq!(index.shortest_unique_change_id_prefix_len(&id_5), 6);
1009
1010        // Calculate shortest unique prefix len with unknown change_id
1011        assert_eq!(
1012            index.shortest_unique_change_id_prefix_len(&ChangeId::from_hex("00000002")),
1013            8
1014        );
1015        assert_eq!(
1016            index.shortest_unique_change_id_prefix_len(&ChangeId::from_hex("01000000")),
1017            2
1018        );
1019        assert_eq!(
1020            index.shortest_unique_change_id_prefix_len(&ChangeId::from_hex("05555344")),
1021            7
1022        );
1023        assert_eq!(
1024            index.shortest_unique_change_id_prefix_len(&ChangeId::from_hex("ffffffff")),
1025            1
1026        );
1027    }
1028
1029    #[test]
1030    fn test_is_ancestor() {
1031        let mut new_change_id = change_id_generator();
1032        let mut index = DefaultMutableIndex::full(TEST_FIELD_LENGTHS);
1033        // 5
1034        // |\
1035        // 4 | 3
1036        // | |/
1037        // 1 2
1038        // |/
1039        // 0
1040        let id_0 = CommitId::from_hex("000000");
1041        let id_1 = CommitId::from_hex("111111");
1042        let id_2 = CommitId::from_hex("222222");
1043        let id_3 = CommitId::from_hex("333333");
1044        let id_4 = CommitId::from_hex("444444");
1045        let id_5 = CommitId::from_hex("555555");
1046        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
1047        index.add_commit_data(id_1.clone(), new_change_id(), &[id_0.clone()]);
1048        index.add_commit_data(id_2.clone(), new_change_id(), &[id_0.clone()]);
1049        index.add_commit_data(id_3.clone(), new_change_id(), &[id_2.clone()]);
1050        index.add_commit_data(id_4.clone(), new_change_id(), &[id_1.clone()]);
1051        index.add_commit_data(id_5.clone(), new_change_id(), &[id_4.clone(), id_2.clone()]);
1052
1053        assert!(is_ancestor(&index, &id_0, &id_0));
1054        assert!(is_ancestor(&index, &id_0, &id_1));
1055        assert!(is_ancestor(&index, &id_2, &id_3));
1056        assert!(is_ancestor(&index, &id_2, &id_5));
1057        assert!(is_ancestor(&index, &id_1, &id_5));
1058        assert!(is_ancestor(&index, &id_0, &id_5));
1059        assert!(!is_ancestor(&index, &id_1, &id_0));
1060        assert!(!is_ancestor(&index, &id_5, &id_3));
1061        assert!(!is_ancestor(&index, &id_3, &id_5));
1062        assert!(!is_ancestor(&index, &id_2, &id_4));
1063        assert!(!is_ancestor(&index, &id_4, &id_2));
1064    }
1065
1066    #[test]
1067    fn test_common_ancestors() {
1068        let mut new_change_id = change_id_generator();
1069        let mut index = DefaultMutableIndex::full(TEST_FIELD_LENGTHS);
1070        // 5
1071        // |\
1072        // 4 |
1073        // | |
1074        // 1 2 3
1075        // | |/
1076        // |/
1077        // 0
1078        let id_0 = CommitId::from_hex("000000");
1079        let id_1 = CommitId::from_hex("111111");
1080        let id_2 = CommitId::from_hex("222222");
1081        let id_3 = CommitId::from_hex("333333");
1082        let id_4 = CommitId::from_hex("444444");
1083        let id_5 = CommitId::from_hex("555555");
1084        let id_6 = CommitId::from_hex("666666");
1085        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
1086        index.add_commit_data(id_1.clone(), new_change_id(), &[id_0.clone()]);
1087        index.add_commit_data(id_2.clone(), new_change_id(), &[id_0.clone()]);
1088        index.add_commit_data(id_3.clone(), new_change_id(), &[id_0.clone()]);
1089        index.add_commit_data(id_4.clone(), new_change_id(), &[id_1.clone()]);
1090        index.add_commit_data(id_5.clone(), new_change_id(), &[id_4.clone(), id_2.clone()]);
1091        index.add_commit_data(id_6.clone(), new_change_id(), &[id_4.clone()]);
1092
1093        assert_eq!(
1094            common_ancestors(&index, &[id_0.clone()], &[id_0.clone()]),
1095            vec![id_0.clone()]
1096        );
1097        assert_eq!(
1098            common_ancestors(&index, &[id_5.clone()], &[id_5.clone()]),
1099            vec![id_5.clone()]
1100        );
1101        assert_eq!(
1102            common_ancestors(&index, &[id_1.clone()], &[id_2.clone()]),
1103            vec![id_0.clone()]
1104        );
1105        assert_eq!(
1106            common_ancestors(&index, &[id_2.clone()], &[id_1.clone()]),
1107            vec![id_0.clone()]
1108        );
1109        assert_eq!(
1110            common_ancestors(&index, &[id_1.clone()], &[id_4.clone()]),
1111            vec![id_1.clone()]
1112        );
1113        assert_eq!(
1114            common_ancestors(&index, &[id_4.clone()], &[id_1.clone()]),
1115            vec![id_1.clone()]
1116        );
1117        assert_eq!(
1118            common_ancestors(&index, &[id_3.clone()], &[id_5.clone()]),
1119            vec![id_0.clone()]
1120        );
1121        assert_eq!(
1122            common_ancestors(&index, &[id_5.clone()], &[id_3.clone()]),
1123            vec![id_0.clone()]
1124        );
1125        assert_eq!(
1126            common_ancestors(&index, &[id_2.clone()], &[id_6.clone()]),
1127            vec![id_0.clone()]
1128        );
1129
1130        // With multiple commits in an input set
1131        assert_eq!(
1132            common_ancestors(&index, &[id_0.clone(), id_1.clone()], &[id_0.clone()]),
1133            vec![id_0.clone()]
1134        );
1135        assert_eq!(
1136            common_ancestors(&index, &[id_0.clone(), id_1.clone()], &[id_1.clone()]),
1137            vec![id_1.clone()]
1138        );
1139        assert_eq!(
1140            common_ancestors(&index, &[id_1.clone(), id_2.clone()], &[id_1.clone()]),
1141            vec![id_1.clone()]
1142        );
1143        assert_eq!(
1144            common_ancestors(&index, &[id_1.clone(), id_2.clone()], &[id_4]),
1145            vec![id_1.clone()]
1146        );
1147        assert_eq!(
1148            common_ancestors(&index, &[id_5.clone(), id_6.clone()], &[id_2.clone()]),
1149            &[id_2.clone()]
1150        );
1151        // Both 1 and 2 are returned since (5) expands to (2, 4), which expands
1152        // to (1,2) and matches the (1,2) of the first input set.
1153        assert_eq!(
1154            common_ancestors(&index, &[id_1.clone(), id_2.clone()], &[id_5]),
1155            vec![id_2.clone(), id_1.clone()]
1156        );
1157        assert_eq!(common_ancestors(&index, &[id_1, id_2], &[id_3]), vec![id_0]);
1158    }
1159
1160    #[test]
1161    fn test_common_ancestors_criss_cross() {
1162        let mut new_change_id = change_id_generator();
1163        let mut index = DefaultMutableIndex::full(TEST_FIELD_LENGTHS);
1164        // 3 4
1165        // |X|
1166        // 1 2
1167        // |/
1168        // 0
1169        let id_0 = CommitId::from_hex("000000");
1170        let id_1 = CommitId::from_hex("111111");
1171        let id_2 = CommitId::from_hex("222222");
1172        let id_3 = CommitId::from_hex("333333");
1173        let id_4 = CommitId::from_hex("444444");
1174        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
1175        index.add_commit_data(id_1.clone(), new_change_id(), &[id_0.clone()]);
1176        index.add_commit_data(id_2.clone(), new_change_id(), &[id_0]);
1177        index.add_commit_data(id_3.clone(), new_change_id(), &[id_1.clone(), id_2.clone()]);
1178        index.add_commit_data(id_4.clone(), new_change_id(), &[id_1.clone(), id_2.clone()]);
1179
1180        let mut common_ancestors = common_ancestors(&index, &[id_3], &[id_4]);
1181        common_ancestors.sort();
1182        assert_eq!(common_ancestors, vec![id_1, id_2]);
1183    }
1184
1185    #[test]
1186    fn test_common_ancestors_merge_with_ancestor() {
1187        let mut new_change_id = change_id_generator();
1188        let mut index = DefaultMutableIndex::full(TEST_FIELD_LENGTHS);
1189        // 4   5
1190        // |\ /|
1191        // 1 2 3
1192        //  \|/
1193        //   0
1194        let id_0 = CommitId::from_hex("000000");
1195        let id_1 = CommitId::from_hex("111111");
1196        let id_2 = CommitId::from_hex("222222");
1197        let id_3 = CommitId::from_hex("333333");
1198        let id_4 = CommitId::from_hex("444444");
1199        let id_5 = CommitId::from_hex("555555");
1200        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
1201        index.add_commit_data(id_1, new_change_id(), &[id_0.clone()]);
1202        index.add_commit_data(id_2.clone(), new_change_id(), &[id_0.clone()]);
1203        index.add_commit_data(id_3, new_change_id(), &[id_0.clone()]);
1204        index.add_commit_data(id_4.clone(), new_change_id(), &[id_0.clone(), id_2.clone()]);
1205        index.add_commit_data(id_5.clone(), new_change_id(), &[id_0, id_2.clone()]);
1206
1207        let mut common_ancestors = common_ancestors(&index, &[id_4], &[id_5]);
1208        common_ancestors.sort();
1209        assert_eq!(common_ancestors, vec![id_2]);
1210    }
1211
1212    #[test]
1213    fn test_heads() {
1214        let mut new_change_id = change_id_generator();
1215        let mut index = DefaultMutableIndex::full(TEST_FIELD_LENGTHS);
1216        // 5
1217        // |\
1218        // 4 | 3
1219        // | |/
1220        // 1 2
1221        // |/
1222        // 0
1223        let id_0 = CommitId::from_hex("000000");
1224        let id_1 = CommitId::from_hex("111111");
1225        let id_2 = CommitId::from_hex("222222");
1226        let id_3 = CommitId::from_hex("333333");
1227        let id_4 = CommitId::from_hex("444444");
1228        let id_5 = CommitId::from_hex("555555");
1229        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
1230        index.add_commit_data(id_1.clone(), new_change_id(), &[id_0.clone()]);
1231        index.add_commit_data(id_2.clone(), new_change_id(), &[id_0.clone()]);
1232        index.add_commit_data(id_3.clone(), new_change_id(), &[id_2.clone()]);
1233        index.add_commit_data(id_4.clone(), new_change_id(), &[id_1.clone()]);
1234        index.add_commit_data(id_5.clone(), new_change_id(), &[id_4.clone(), id_2.clone()]);
1235
1236        // Empty input
1237        assert!(index.heads(&mut [].iter()).unwrap().is_empty());
1238        // Single head
1239        assert_eq!(
1240            index.heads(&mut [id_4.clone()].iter()).unwrap(),
1241            vec![id_4.clone()]
1242        );
1243        // Single head and parent
1244        assert_eq!(
1245            index.heads(&mut [id_4.clone(), id_1].iter()).unwrap(),
1246            vec![id_4.clone()]
1247        );
1248        // Single head and grand-parent
1249        assert_eq!(
1250            index.heads(&mut [id_4.clone(), id_0].iter()).unwrap(),
1251            vec![id_4.clone()]
1252        );
1253        // Multiple heads
1254        assert_eq!(
1255            index
1256                .heads(&mut [id_3.clone(), id_4.clone()].iter())
1257                .unwrap(),
1258            vec![id_4.clone(), id_3.clone()]
1259        );
1260        // Duplicated inputs
1261        assert_eq!(
1262            index
1263                .heads(&mut [id_4.clone(), id_3.clone(), id_4.clone()].iter())
1264                .unwrap(),
1265            vec![id_4.clone(), id_3.clone()]
1266        );
1267        // Merge commit and ancestors
1268        assert_eq!(
1269            index.heads(&mut [id_5.clone(), id_2].iter()).unwrap(),
1270            vec![id_5.clone()]
1271        );
1272        // Merge commit and other commit
1273        assert_eq!(
1274            index
1275                .heads(&mut [id_5.clone(), id_3.clone()].iter())
1276                .unwrap(),
1277            vec![id_5.clone(), id_3.clone()]
1278        );
1279
1280        assert_eq!(
1281            index.all_heads_for_gc().unwrap().collect_vec(),
1282            vec![id_3.clone(), id_5.clone()]
1283        );
1284    }
1285
1286    #[test]
1287    fn test_heads_range_with_filter() {
1288        let mut new_change_id = change_id_generator();
1289        let mut index = DefaultMutableIndex::full(TEST_FIELD_LENGTHS);
1290        // 5
1291        // |\
1292        // 4 | 3
1293        // | |/
1294        // 1 2
1295        // |/
1296        // 0
1297        let id_0 = CommitId::from_hex("000000");
1298        let id_1 = CommitId::from_hex("111111");
1299        let id_2 = CommitId::from_hex("222222");
1300        let id_3 = CommitId::from_hex("333333");
1301        let id_4 = CommitId::from_hex("444444");
1302        let id_5 = CommitId::from_hex("555555");
1303        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
1304        index.add_commit_data(id_1.clone(), new_change_id(), &[id_0.clone()]);
1305        index.add_commit_data(id_2.clone(), new_change_id(), &[id_0.clone()]);
1306        index.add_commit_data(id_3.clone(), new_change_id(), &[id_2.clone()]);
1307        index.add_commit_data(id_4.clone(), new_change_id(), &[id_1.clone()]);
1308        index.add_commit_data(id_5.clone(), new_change_id(), &[id_4.clone(), id_2.clone()]);
1309
1310        // Helper function to convert commit IDs to/from index positions and call
1311        // `heads_from_range_and_filter`.
1312        let heads_range = |roots: &[&CommitId],
1313                           heads: &[&CommitId],
1314                           parents_range: &Range<u32>,
1315                           filter: &dyn Fn(CommitId) -> bool|
1316         -> Vec<CommitId> {
1317            let roots = roots
1318                .iter()
1319                .map(|id| index.as_composite().commits().commit_id_to_pos(id).unwrap())
1320                .sorted_by_key(|&pos| Reverse(pos))
1321                .collect_vec();
1322            let heads = heads
1323                .iter()
1324                .map(|id| index.as_composite().commits().commit_id_to_pos(id).unwrap())
1325                .sorted_by_key(|&pos| Reverse(pos))
1326                .collect_vec();
1327            index
1328                .as_composite()
1329                .commits()
1330                .heads_from_range_and_filter::<Infallible>(roots, heads, parents_range, |pos| {
1331                    Ok(filter(
1332                        index.as_composite().commits().entry_by_pos(pos).commit_id(),
1333                    ))
1334                })
1335                .unwrap()
1336                .into_iter()
1337                .map(|pos| index.as_composite().commits().entry_by_pos(pos).commit_id())
1338                .collect_vec()
1339        };
1340
1341        // heads(::none())
1342        assert!(heads_range(&[], &[], &PARENTS_RANGE_FULL, &|_| true).is_empty());
1343        // heads(all())
1344        assert_eq!(
1345            heads_range(&[], &[&id_5, &id_3], &PARENTS_RANGE_FULL, &|_| true),
1346            vec![id_5.clone(), id_3.clone()]
1347        );
1348        // heads(~5)
1349        assert_eq!(
1350            heads_range(&[], &[&id_5, &id_3], &PARENTS_RANGE_FULL, &|id| id != id_5),
1351            vec![id_4.clone(), id_3.clone()]
1352        );
1353        // heads(5..)
1354        assert_eq!(
1355            heads_range(&[&id_5], &[&id_5, &id_3], &PARENTS_RANGE_FULL, &|_| true),
1356            vec![id_3.clone()]
1357        );
1358        // heads(5.. ~ 3)
1359        assert!(
1360            heads_range(&[&id_5], &[&id_5, &id_3], &PARENTS_RANGE_FULL, &|id| id
1361                != id_3)
1362            .is_empty()
1363        );
1364        // heads(2..4)
1365        assert_eq!(
1366            heads_range(&[&id_2], &[&id_4], &PARENTS_RANGE_FULL, &|_| true),
1367            vec![id_4.clone()]
1368        );
1369        // heads(2..4 ~ 4)
1370        assert_eq!(
1371            heads_range(&[&id_2], &[&id_4], &PARENTS_RANGE_FULL, &|id| id != id_4),
1372            vec![id_1.clone()]
1373        );
1374        // heads((3 | 1).. ~ 5)
1375        assert_eq!(
1376            heads_range(
1377                &[&id_3, &id_1],
1378                &[&id_5, &id_3],
1379                &PARENTS_RANGE_FULL,
1380                &|id| id != id_5
1381            ),
1382            vec![id_4.clone()]
1383        );
1384        // heads(::(5 | 4) ~ 5)
1385        assert_eq!(
1386            heads_range(&[], &[&id_5, &id_4], &PARENTS_RANGE_FULL, &|id| id != id_5),
1387            vec![id_4.clone(), id_2.clone()]
1388        );
1389        // heads(::(5 | 5))
1390        assert_eq!(
1391            heads_range(&[], &[&id_5, &id_5], &PARENTS_RANGE_FULL, &|_| true),
1392            vec![id_5.clone()]
1393        );
1394        // heads(::5 ~ 5)
1395        assert_eq!(
1396            heads_range(&[], &[&id_5], &PARENTS_RANGE_FULL, &|id| id != id_5),
1397            vec![id_4.clone(), id_2.clone()]
1398        );
1399        // heads(first_ancestors(5) ~ 5)
1400        assert_eq!(
1401            heads_range(&[], &[&id_5], &(0..1), &|id| id != id_5),
1402            vec![id_4.clone()]
1403        );
1404        // heads(ancestors(5, parents_range=1..2) ~ 5)
1405        assert_eq!(
1406            heads_range(&[], &[&id_5], &(1..2), &|id| id != id_5),
1407            vec![id_2.clone()]
1408        );
1409    }
1410}