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