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