jujutsu_lib/
index.rs

1// Copyright 2020 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
15use std::cmp::{max, min, Ordering};
16use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashSet};
17use std::fmt::{Debug, Formatter};
18use std::fs::File;
19use std::hash::{Hash, Hasher};
20use std::io::{Cursor, Read, Write};
21use std::ops::{Bound, Range};
22use std::path::PathBuf;
23use std::sync::Arc;
24use std::{io, iter};
25
26use blake2::{Blake2b512, Digest};
27use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
28use itertools::Itertools;
29use tempfile::NamedTempFile;
30use thiserror::Error;
31
32use crate::backend::{self, ChangeId, CommitId, ObjectId};
33use crate::commit::Commit;
34use crate::file_util::persist_content_addressed_temp_file;
35#[cfg(not(feature = "map_first_last"))]
36// This import is used on Rust 1.61, but not on recent version.
37// TODO: Remove it when our MSRV becomes recent enough.
38#[allow(unused_imports)]
39use crate::nightly_shims::BTreeSetExt;
40
41#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
42pub struct IndexPosition(u32);
43
44impl IndexPosition {
45    pub const MAX: Self = IndexPosition(u32::MAX);
46}
47pub trait Index {
48    fn num_commits(&self) -> u32;
49
50    fn stats(&self) -> IndexStats;
51
52    fn commit_id_to_pos(&self, commit_id: &CommitId) -> Option<IndexPosition>;
53
54    fn shortest_unique_commit_id_prefix_len(&self, commit_id: &CommitId) -> usize;
55
56    fn resolve_prefix(&self, prefix: &HexPrefix) -> PrefixResolution<CommitId>;
57
58    fn entry_by_id(&self, commit_id: &CommitId) -> Option<IndexEntry>;
59
60    fn entry_by_pos(&self, pos: IndexPosition) -> IndexEntry;
61
62    fn has_id(&self, commit_id: &CommitId) -> bool;
63
64    fn is_ancestor(&self, ancestor_id: &CommitId, descendant_id: &CommitId) -> bool;
65
66    fn common_ancestors(&self, set1: &[CommitId], set2: &[CommitId]) -> Vec<CommitId>;
67
68    fn walk_revs(&self, wanted: &[CommitId], unwanted: &[CommitId]) -> RevWalk;
69
70    fn heads(&self, candidates: &mut dyn Iterator<Item = &CommitId>) -> Vec<CommitId>;
71
72    fn topo_order(&self, input: &mut dyn Iterator<Item = &CommitId>) -> Vec<IndexEntry>;
73}
74
75struct CommitGraphEntry<'a> {
76    data: &'a [u8],
77    commit_id_length: usize,
78    change_id_length: usize,
79}
80
81// TODO: Add pointers to ancestors further back, like a skip list. Clear the
82// lowest set bit to determine which generation number the pointers point to.
83impl CommitGraphEntry<'_> {
84    fn size(commit_id_length: usize, change_id_length: usize) -> usize {
85        20 + commit_id_length + change_id_length
86    }
87
88    fn generation_number(&self) -> u32 {
89        (&self.data[4..]).read_u32::<LittleEndian>().unwrap()
90    }
91
92    fn num_parents(&self) -> u32 {
93        (&self.data[8..]).read_u32::<LittleEndian>().unwrap()
94    }
95
96    fn parent1_pos(&self) -> IndexPosition {
97        IndexPosition((&self.data[12..]).read_u32::<LittleEndian>().unwrap())
98    }
99
100    fn parent2_overflow_pos(&self) -> u32 {
101        (&self.data[16..]).read_u32::<LittleEndian>().unwrap()
102    }
103
104    // TODO: Consider storing the change ids in a separate table. That table could
105    // be sorted by change id and have the end index into a list as value. That list
106    // would be the concatenation of all index positions associated with the change.
107    // Possible advantages: avoids duplicating change ids; smaller main graph leads
108    // to better cache locality when walking it; ability to quickly find all
109    // commits associated with a change id.
110    fn change_id(&self) -> ChangeId {
111        ChangeId::new(self.data[20..20 + self.change_id_length].to_vec())
112    }
113
114    fn commit_id(&self) -> CommitId {
115        CommitId::from_bytes(
116            &self.data
117                [20 + self.change_id_length..20 + self.change_id_length + self.commit_id_length],
118        )
119    }
120}
121
122struct CommitLookupEntry<'a> {
123    data: &'a [u8],
124    commit_id_length: usize,
125}
126
127impl CommitLookupEntry<'_> {
128    fn size(commit_id_length: usize) -> usize {
129        commit_id_length + 4
130    }
131
132    fn commit_id(&self) -> CommitId {
133        CommitId::from_bytes(self.commit_id_bytes())
134    }
135
136    // might be better to add borrowed version of CommitId
137    fn commit_id_bytes(&self) -> &[u8] {
138        &self.data[0..self.commit_id_length]
139    }
140
141    fn pos(&self) -> IndexPosition {
142        IndexPosition(
143            (&self.data[self.commit_id_length..self.commit_id_length + 4])
144                .read_u32::<LittleEndian>()
145                .unwrap(),
146        )
147    }
148}
149
150#[derive(Error, Debug)]
151pub enum IndexLoadError {
152    #[error("Index file '{0}' is corrupt.")]
153    IndexCorrupt(String),
154    #[error("I/O error while loading index file: {0}")]
155    IoError(#[from] io::Error),
156}
157
158// File format:
159// u32: number of entries
160// u32: number of parent overflow entries
161// for each entry, in some topological order with parents first:
162//   u32: generation number
163//   u32: number of parents
164//   u32: position in this table for parent 1
165//   u32: position in the overflow table of parent 2
166//   <hash length number of bytes>: commit id
167// for each entry, sorted by commit id:
168//   <hash length number of bytes>: commit id
169//    u32: position in the entry table above
170// TODO: add a version number
171// TODO: replace the table by a trie so we don't have to repeat the full commit
172//       ids
173// TODO: add a fanout table like git's commit graph has?
174pub struct ReadonlyIndex {
175    parent_file: Option<Arc<ReadonlyIndex>>,
176    num_parent_commits: u32,
177    name: String,
178    commit_id_length: usize,
179    change_id_length: usize,
180    commit_graph_entry_size: usize,
181    commit_lookup_entry_size: usize,
182    // Number of commits not counting the parent file
183    num_local_commits: u32,
184    graph: Vec<u8>,
185    lookup: Vec<u8>,
186    overflow_parent: Vec<u8>,
187}
188
189impl Debug for ReadonlyIndex {
190    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
191        f.debug_struct("ReadonlyIndex")
192            .field("name", &self.name)
193            .field("parent_file", &self.parent_file)
194            .finish()
195    }
196}
197
198#[derive(Debug, Clone, PartialEq, Eq)]
199pub struct HexPrefix {
200    // For odd-length prefix, lower 4 bits of the last byte is padded with 0
201    min_prefix_bytes: Vec<u8>,
202    has_odd_byte: bool,
203}
204
205impl HexPrefix {
206    pub fn new(prefix: &str) -> Option<HexPrefix> {
207        let has_odd_byte = prefix.len() & 1 != 0;
208        let min_prefix_bytes = if has_odd_byte {
209            hex::decode(prefix.to_owned() + "0").ok()?
210        } else {
211            hex::decode(prefix).ok()?
212        };
213        Some(HexPrefix {
214            min_prefix_bytes,
215            has_odd_byte,
216        })
217    }
218
219    pub fn from_bytes(bytes: &[u8]) -> Self {
220        HexPrefix {
221            min_prefix_bytes: bytes.to_owned(),
222            has_odd_byte: false,
223        }
224    }
225
226    pub fn hex(&self) -> String {
227        let mut hex_string = hex::encode(&self.min_prefix_bytes);
228        if self.has_odd_byte {
229            hex_string.pop().unwrap();
230        }
231        hex_string
232    }
233
234    /// Minimum bytes that would match this prefix. (e.g. "abc0" for "abc")
235    ///
236    /// Use this to partition a sorted slice, and test `matches(id)` from there.
237    pub fn min_prefix_bytes(&self) -> &[u8] {
238        &self.min_prefix_bytes
239    }
240
241    fn split_odd_byte(&self) -> (Option<u8>, &[u8]) {
242        if self.has_odd_byte {
243            let (&odd, prefix) = self.min_prefix_bytes.split_last().unwrap();
244            (Some(odd), prefix)
245        } else {
246            (None, &self.min_prefix_bytes)
247        }
248    }
249
250    pub fn matches<Q: ObjectId>(&self, id: &Q) -> bool {
251        let id_bytes = id.as_bytes();
252        let (maybe_odd, prefix) = self.split_odd_byte();
253        if id_bytes.starts_with(prefix) {
254            if let Some(odd) = maybe_odd {
255                matches!(id_bytes.get(prefix.len()), Some(v) if v & 0xf0 == odd)
256            } else {
257                true
258            }
259        } else {
260            false
261        }
262    }
263}
264
265#[derive(Debug, Clone, PartialEq, Eq)]
266pub enum PrefixResolution<T> {
267    NoMatch,
268    SingleMatch(T),
269    AmbiguousMatch,
270}
271
272impl<T: Clone> PrefixResolution<T> {
273    fn plus(&self, other: &PrefixResolution<T>) -> PrefixResolution<T> {
274        match (self, other) {
275            (PrefixResolution::NoMatch, other) => other.clone(),
276            (local, PrefixResolution::NoMatch) => local.clone(),
277            (PrefixResolution::AmbiguousMatch, _) => PrefixResolution::AmbiguousMatch,
278            (_, PrefixResolution::AmbiguousMatch) => PrefixResolution::AmbiguousMatch,
279            (PrefixResolution::SingleMatch(_), PrefixResolution::SingleMatch(_)) => {
280                PrefixResolution::AmbiguousMatch
281            }
282        }
283    }
284}
285
286#[derive(Debug)]
287struct MutableGraphEntry {
288    commit_id: CommitId,
289    change_id: ChangeId,
290    generation_number: u32,
291    parent_positions: Vec<IndexPosition>,
292}
293
294pub struct MutableIndex {
295    parent_file: Option<Arc<ReadonlyIndex>>,
296    num_parent_commits: u32,
297    commit_id_length: usize,
298    change_id_length: usize,
299    graph: Vec<MutableGraphEntry>,
300    lookup: BTreeMap<CommitId, IndexPosition>,
301}
302
303impl MutableIndex {
304    pub(crate) fn full(commit_id_length: usize, change_id_length: usize) -> Self {
305        Self {
306            parent_file: None,
307            num_parent_commits: 0,
308            commit_id_length,
309            change_id_length,
310            graph: vec![],
311            lookup: BTreeMap::new(),
312        }
313    }
314
315    pub fn incremental(parent_file: Arc<ReadonlyIndex>) -> Self {
316        let num_parent_commits = parent_file.num_parent_commits + parent_file.num_local_commits;
317        let commit_id_length = parent_file.commit_id_length;
318        let change_id_length = parent_file.change_id_length;
319        Self {
320            parent_file: Some(parent_file),
321            num_parent_commits,
322            commit_id_length,
323            change_id_length,
324            graph: vec![],
325            lookup: BTreeMap::new(),
326        }
327    }
328
329    pub fn add_commit(&mut self, commit: &Commit) {
330        self.add_commit_data(
331            commit.id().clone(),
332            commit.change_id().clone(),
333            commit.parent_ids(),
334        );
335    }
336
337    pub(crate) fn add_commit_data(
338        &mut self,
339        commit_id: CommitId,
340        change_id: ChangeId,
341        parent_ids: &[CommitId],
342    ) {
343        if self.has_id(&commit_id) {
344            return;
345        }
346        let mut entry = MutableGraphEntry {
347            commit_id,
348            change_id,
349            generation_number: 0,
350            parent_positions: vec![],
351        };
352        for parent_id in parent_ids {
353            let parent_entry = self
354                .entry_by_id(parent_id)
355                .expect("parent commit is not indexed");
356            entry.generation_number = max(
357                entry.generation_number,
358                parent_entry.generation_number() + 1,
359            );
360            entry.parent_positions.push(parent_entry.pos);
361        }
362        self.lookup.insert(
363            entry.commit_id.clone(),
364            IndexPosition(self.graph.len() as u32 + self.num_parent_commits),
365        );
366        self.graph.push(entry);
367    }
368
369    fn add_commits_from(&mut self, other_segment: &dyn IndexSegment) {
370        let other = CompositeIndex(other_segment);
371        for pos in other_segment.segment_num_parent_commits()..other.num_commits() {
372            let entry = other.entry_by_pos(IndexPosition(pos));
373            let parent_ids = entry
374                .parents()
375                .iter()
376                .map(|entry| entry.commit_id())
377                .collect_vec();
378            self.add_commit_data(entry.commit_id(), entry.change_id(), &parent_ids);
379        }
380    }
381
382    pub fn merge_in(&mut self, other: &Arc<ReadonlyIndex>) {
383        let mut maybe_own_ancestor = self.parent_file.clone();
384        let mut maybe_other_ancestor = Some(other.clone());
385        let mut files_to_add = vec![];
386        loop {
387            if maybe_other_ancestor.is_none() {
388                break;
389            }
390            let other_ancestor = maybe_other_ancestor.as_ref().unwrap();
391            if maybe_own_ancestor.is_none() {
392                files_to_add.push(other_ancestor.clone());
393                maybe_other_ancestor = other_ancestor.parent_file.clone();
394                continue;
395            }
396            let own_ancestor = maybe_own_ancestor.as_ref().unwrap();
397            if own_ancestor.name == other_ancestor.name {
398                break;
399            }
400            if own_ancestor.num_commits() < other_ancestor.num_commits() {
401                files_to_add.push(other_ancestor.clone());
402                maybe_other_ancestor = other_ancestor.parent_file.clone();
403            } else {
404                maybe_own_ancestor = own_ancestor.parent_file.clone();
405            }
406        }
407
408        for file in files_to_add.iter().rev() {
409            self.add_commits_from(file.as_ref());
410        }
411    }
412
413    fn serialize(self) -> Vec<u8> {
414        assert_eq!(self.graph.len(), self.lookup.len());
415
416        let num_commits = self.graph.len() as u32;
417
418        let mut buf = vec![];
419
420        if let Some(parent_file) = &self.parent_file {
421            buf.write_u32::<LittleEndian>(parent_file.name.len() as u32)
422                .unwrap();
423            buf.write_all(parent_file.name.as_bytes()).unwrap();
424        } else {
425            buf.write_u32::<LittleEndian>(0).unwrap();
426        }
427
428        buf.write_u32::<LittleEndian>(num_commits).unwrap();
429        // We'll write the actual value later
430        let parent_overflow_offset = buf.len();
431        buf.write_u32::<LittleEndian>(0_u32).unwrap();
432
433        let mut parent_overflow = vec![];
434        for entry in self.graph {
435            let flags = 0;
436            buf.write_u32::<LittleEndian>(flags).unwrap();
437
438            buf.write_u32::<LittleEndian>(entry.generation_number)
439                .unwrap();
440
441            buf.write_u32::<LittleEndian>(entry.parent_positions.len() as u32)
442                .unwrap();
443            let mut parent1_pos = IndexPosition(0);
444            let parent_overflow_pos = parent_overflow.len() as u32;
445            for (i, parent_pos) in entry.parent_positions.iter().enumerate() {
446                if i == 0 {
447                    parent1_pos = *parent_pos;
448                } else {
449                    parent_overflow.push(*parent_pos);
450                }
451            }
452            buf.write_u32::<LittleEndian>(parent1_pos.0).unwrap();
453            buf.write_u32::<LittleEndian>(parent_overflow_pos).unwrap();
454
455            assert_eq!(entry.change_id.as_bytes().len(), self.change_id_length);
456            buf.write_all(entry.change_id.as_bytes()).unwrap();
457
458            assert_eq!(entry.commit_id.as_bytes().len(), self.commit_id_length);
459            buf.write_all(entry.commit_id.as_bytes()).unwrap();
460        }
461
462        for (commit_id, pos) in self.lookup {
463            buf.write_all(commit_id.as_bytes()).unwrap();
464            buf.write_u32::<LittleEndian>(pos.0).unwrap();
465        }
466
467        buf[parent_overflow_offset..parent_overflow_offset + 4]
468            .as_mut()
469            .write_u32::<LittleEndian>(parent_overflow.len() as u32)
470            .unwrap();
471        for parent_pos in parent_overflow {
472            buf.write_u32::<LittleEndian>(parent_pos.0).unwrap();
473        }
474
475        buf
476    }
477
478    /// If the MutableIndex has more than half the commits of its parent
479    /// ReadonlyIndex, return MutableIndex with the commits from both. This
480    /// is done recursively, so the stack of index files has O(log n) files.
481    fn maybe_squash_with_ancestors(self) -> MutableIndex {
482        let mut num_new_commits = self.segment_num_commits();
483        let mut files_to_squash = vec![];
484        let mut maybe_parent_file = self.parent_file.clone();
485        let mut squashed;
486        loop {
487            match maybe_parent_file {
488                Some(parent_file) => {
489                    // TODO: We should probably also squash if the parent file has less than N
490                    // commits, regardless of how many (few) are in `self`.
491                    if 2 * num_new_commits < parent_file.segment_num_commits() {
492                        squashed = MutableIndex::incremental(parent_file);
493                        break;
494                    }
495                    num_new_commits += parent_file.segment_num_commits();
496                    files_to_squash.push(parent_file.clone());
497                    maybe_parent_file = parent_file.parent_file.clone();
498                }
499                None => {
500                    squashed = MutableIndex::full(self.commit_id_length, self.change_id_length);
501                    break;
502                }
503            }
504        }
505
506        if files_to_squash.is_empty() {
507            return self;
508        }
509
510        for parent_file in files_to_squash.iter().rev() {
511            squashed.add_commits_from(parent_file.as_ref());
512        }
513        squashed.add_commits_from(&self);
514        squashed
515    }
516
517    pub fn save_in(self, dir: PathBuf) -> io::Result<Arc<ReadonlyIndex>> {
518        if self.segment_num_commits() == 0 && self.parent_file.is_some() {
519            return Ok(self.parent_file.unwrap());
520        }
521
522        let commit_id_length = self.commit_id_length;
523        let change_id_length = self.change_id_length;
524
525        let buf = self.maybe_squash_with_ancestors().serialize();
526        let mut hasher = Blake2b512::new();
527        hasher.update(&buf);
528        let index_file_id_hex = hex::encode(hasher.finalize());
529        let index_file_path = dir.join(&index_file_id_hex);
530
531        let mut temp_file = NamedTempFile::new_in(&dir)?;
532        let file = temp_file.as_file_mut();
533        file.write_all(&buf)?;
534        persist_content_addressed_temp_file(temp_file, index_file_path)?;
535
536        let mut cursor = Cursor::new(&buf);
537        ReadonlyIndex::load_from(
538            &mut cursor,
539            dir,
540            index_file_id_hex,
541            commit_id_length,
542            change_id_length,
543        )
544        .map_err(|err| match err {
545            IndexLoadError::IndexCorrupt(err) => {
546                panic!("Just-created index file is corrupt: {err}")
547            }
548            IndexLoadError::IoError(err) => err,
549        })
550    }
551}
552
553impl Index for MutableIndex {
554    fn num_commits(&self) -> u32 {
555        CompositeIndex(self).num_commits()
556    }
557
558    fn stats(&self) -> IndexStats {
559        CompositeIndex(self).stats()
560    }
561
562    fn commit_id_to_pos(&self, commit_id: &CommitId) -> Option<IndexPosition> {
563        CompositeIndex(self).commit_id_to_pos(commit_id)
564    }
565
566    fn shortest_unique_commit_id_prefix_len(&self, commit_id: &CommitId) -> usize {
567        CompositeIndex(self).shortest_unique_commit_id_prefix_len(commit_id)
568    }
569
570    fn resolve_prefix(&self, prefix: &HexPrefix) -> PrefixResolution<CommitId> {
571        CompositeIndex(self).resolve_prefix(prefix)
572    }
573
574    fn entry_by_id(&self, commit_id: &CommitId) -> Option<IndexEntry> {
575        CompositeIndex(self).entry_by_id(commit_id)
576    }
577
578    fn entry_by_pos(&self, pos: IndexPosition) -> IndexEntry {
579        CompositeIndex(self).entry_by_pos(pos)
580    }
581
582    fn has_id(&self, commit_id: &CommitId) -> bool {
583        CompositeIndex(self).has_id(commit_id)
584    }
585
586    fn is_ancestor(&self, ancestor_id: &CommitId, descendant_id: &CommitId) -> bool {
587        CompositeIndex(self).is_ancestor(ancestor_id, descendant_id)
588    }
589
590    fn common_ancestors(&self, set1: &[CommitId], set2: &[CommitId]) -> Vec<CommitId> {
591        CompositeIndex(self).common_ancestors(set1, set2)
592    }
593
594    fn walk_revs(&self, wanted: &[CommitId], unwanted: &[CommitId]) -> RevWalk {
595        CompositeIndex(self).walk_revs(wanted, unwanted)
596    }
597
598    fn heads(&self, candidates: &mut dyn Iterator<Item = &CommitId>) -> Vec<CommitId> {
599        CompositeIndex(self).heads(candidates)
600    }
601
602    fn topo_order(&self, input: &mut dyn Iterator<Item = &CommitId>) -> Vec<IndexEntry> {
603        CompositeIndex(self).topo_order(input)
604    }
605}
606
607trait IndexSegment {
608    fn segment_num_parent_commits(&self) -> u32;
609
610    fn segment_num_commits(&self) -> u32;
611
612    fn segment_parent_file(&self) -> Option<&Arc<ReadonlyIndex>>;
613
614    fn segment_name(&self) -> Option<String>;
615
616    fn segment_commit_id_to_pos(&self, commit_id: &CommitId) -> Option<IndexPosition>;
617
618    /// Suppose the given `commit_id` exists, returns the positions of the
619    /// previous and next commit ids in lexicographical order.
620    fn segment_commit_id_to_neighbor_positions(
621        &self,
622        commit_id: &CommitId,
623    ) -> (Option<IndexPosition>, Option<IndexPosition>);
624
625    fn segment_resolve_prefix(&self, prefix: &HexPrefix) -> PrefixResolution<CommitId>;
626
627    fn segment_generation_number(&self, local_pos: u32) -> u32;
628
629    fn segment_commit_id(&self, local_pos: u32) -> CommitId;
630
631    fn segment_change_id(&self, local_pos: u32) -> ChangeId;
632
633    fn segment_num_parents(&self, local_pos: u32) -> u32;
634
635    fn segment_parent_positions(&self, local_pos: u32) -> Vec<IndexPosition>;
636
637    fn segment_entry_by_pos(&self, pos: IndexPosition, local_pos: u32) -> IndexEntry;
638}
639
640#[derive(Clone)]
641struct CompositeIndex<'a>(&'a dyn IndexSegment);
642
643impl<'a> CompositeIndex<'a> {
644    fn ancestor_files_without_local(&self) -> impl Iterator<Item = &Arc<ReadonlyIndex>> {
645        let parent_file = self.0.segment_parent_file();
646        iter::successors(parent_file, |file| file.segment_parent_file())
647    }
648
649    fn ancestor_index_segments(&self) -> impl Iterator<Item = &dyn IndexSegment> {
650        iter::once(self.0).chain(
651            self.ancestor_files_without_local()
652                .map(|file| file.as_ref() as &dyn IndexSegment),
653        )
654    }
655
656    pub fn num_commits(&self) -> u32 {
657        self.0.segment_num_parent_commits() + self.0.segment_num_commits()
658    }
659
660    pub fn stats(&self) -> IndexStats {
661        let num_commits = self.num_commits();
662        let mut num_merges = 0;
663        let mut max_generation_number = 0;
664        let mut is_head = vec![true; num_commits as usize];
665        let mut change_ids = HashSet::new();
666        for pos in 0..num_commits {
667            let entry = self.entry_by_pos(IndexPosition(pos));
668            max_generation_number = max(max_generation_number, entry.generation_number());
669            if entry.num_parents() > 1 {
670                num_merges += 1;
671            }
672            for parent_pos in entry.parent_positions() {
673                is_head[parent_pos.0 as usize] = false;
674            }
675            change_ids.insert(entry.change_id());
676        }
677        let num_heads = is_head.iter().filter(|is_head| **is_head).count() as u32;
678
679        let mut levels = self
680            .ancestor_index_segments()
681            .map(|segment| IndexLevelStats {
682                num_commits: segment.segment_num_commits(),
683                name: segment.segment_name(),
684            })
685            .collect_vec();
686        levels.reverse();
687
688        IndexStats {
689            num_commits,
690            num_merges,
691            max_generation_number,
692            num_heads,
693            num_changes: change_ids.len() as u32,
694            levels,
695        }
696    }
697
698    fn entry_by_pos(&self, pos: IndexPosition) -> IndexEntry<'a> {
699        let num_parent_commits = self.0.segment_num_parent_commits();
700        if pos.0 >= num_parent_commits {
701            self.0.segment_entry_by_pos(pos, pos.0 - num_parent_commits)
702        } else {
703            let parent_file: &ReadonlyIndex = self.0.segment_parent_file().unwrap().as_ref();
704            // The parent ReadonlyIndex outlives the child
705            let parent_file: &'a ReadonlyIndex = unsafe { std::mem::transmute(parent_file) };
706
707            CompositeIndex(parent_file).entry_by_pos(pos)
708        }
709    }
710
711    pub fn commit_id_to_pos(&self, commit_id: &CommitId) -> Option<IndexPosition> {
712        self.ancestor_index_segments()
713            .find_map(|segment| segment.segment_commit_id_to_pos(commit_id))
714    }
715
716    /// Suppose the given `commit_id` exists, returns the minimum prefix length
717    /// to disambiguate it. The length to be returned is a number of hexadecimal
718    /// digits.
719    ///
720    /// If the given `commit_id` doesn't exist, this will return the prefix
721    /// length that never matches with any commit ids.
722    pub fn shortest_unique_commit_id_prefix_len(&self, commit_id: &CommitId) -> usize {
723        let (prev_id, next_id) = self.resolve_neighbor_commit_ids(commit_id);
724        itertools::chain(prev_id, next_id)
725            .map(|id| backend::common_hex_len(commit_id.as_bytes(), id.as_bytes()) + 1)
726            .max()
727            .unwrap_or(0)
728    }
729
730    /// Suppose the given `commit_id` exists, returns the previous and next
731    /// commit ids in lexicographical order.
732    fn resolve_neighbor_commit_ids(
733        &self,
734        commit_id: &CommitId,
735    ) -> (Option<CommitId>, Option<CommitId>) {
736        self.ancestor_index_segments()
737            .map(|segment| {
738                let num_parent_commits = segment.segment_num_parent_commits();
739                let to_local_pos = |pos: IndexPosition| pos.0 - num_parent_commits;
740                let (prev_pos, next_pos) =
741                    segment.segment_commit_id_to_neighbor_positions(commit_id);
742                (
743                    prev_pos.map(|p| segment.segment_commit_id(to_local_pos(p))),
744                    next_pos.map(|p| segment.segment_commit_id(to_local_pos(p))),
745                )
746            })
747            .reduce(|(acc_prev_id, acc_next_id), (prev_id, next_id)| {
748                (
749                    acc_prev_id.into_iter().chain(prev_id).max(),
750                    acc_next_id.into_iter().chain(next_id).min(),
751                )
752            })
753            .unwrap()
754    }
755
756    pub fn resolve_prefix(&self, prefix: &HexPrefix) -> PrefixResolution<CommitId> {
757        self.ancestor_index_segments()
758            .fold(PrefixResolution::NoMatch, |acc_match, segment| {
759                if acc_match == PrefixResolution::AmbiguousMatch {
760                    acc_match // avoid checking the parent file(s)
761                } else {
762                    let local_match = segment.segment_resolve_prefix(prefix);
763                    acc_match.plus(&local_match)
764                }
765            })
766    }
767
768    pub fn entry_by_id(&self, commit_id: &CommitId) -> Option<IndexEntry<'a>> {
769        self.commit_id_to_pos(commit_id)
770            .map(|pos| self.entry_by_pos(pos))
771    }
772
773    pub fn has_id(&self, commit_id: &CommitId) -> bool {
774        self.commit_id_to_pos(commit_id).is_some()
775    }
776
777    pub fn is_ancestor(&self, ancestor_id: &CommitId, descendant_id: &CommitId) -> bool {
778        let ancestor_pos = self.commit_id_to_pos(ancestor_id).unwrap();
779        let descendant_pos = self.commit_id_to_pos(descendant_id).unwrap();
780        self.is_ancestor_pos(ancestor_pos, descendant_pos)
781    }
782
783    fn is_ancestor_pos(&self, ancestor_pos: IndexPosition, descendant_pos: IndexPosition) -> bool {
784        let ancestor_generation = self.entry_by_pos(ancestor_pos).generation_number();
785        let mut work = vec![descendant_pos];
786        let mut visited = HashSet::new();
787        while let Some(descendant_pos) = work.pop() {
788            let descendant_entry = self.entry_by_pos(descendant_pos);
789            if descendant_pos == ancestor_pos {
790                return true;
791            }
792            if !visited.insert(descendant_entry.pos) {
793                continue;
794            }
795            if descendant_entry.generation_number() <= ancestor_generation {
796                continue;
797            }
798            work.extend(descendant_entry.parent_positions());
799        }
800        false
801    }
802
803    pub fn common_ancestors(&self, set1: &[CommitId], set2: &[CommitId]) -> Vec<CommitId> {
804        let pos1 = set1
805            .iter()
806            .map(|id| self.commit_id_to_pos(id).unwrap())
807            .collect_vec();
808        let pos2 = set2
809            .iter()
810            .map(|id| self.commit_id_to_pos(id).unwrap())
811            .collect_vec();
812        self.common_ancestors_pos(&pos1, &pos2)
813            .iter()
814            .map(|pos| self.entry_by_pos(*pos).commit_id())
815            .collect()
816    }
817
818    fn common_ancestors_pos(
819        &self,
820        set1: &[IndexPosition],
821        set2: &[IndexPosition],
822    ) -> BTreeSet<IndexPosition> {
823        let mut items1: BTreeSet<_> = set1
824            .iter()
825            .map(|pos| IndexEntryByGeneration(self.entry_by_pos(*pos)))
826            .collect();
827        let mut items2: BTreeSet<_> = set2
828            .iter()
829            .map(|pos| IndexEntryByGeneration(self.entry_by_pos(*pos)))
830            .collect();
831
832        let mut result = BTreeSet::new();
833        while !(items1.is_empty() || items2.is_empty()) {
834            #[allow(unstable_name_collisions)]
835            let entry1 = items1.last().unwrap();
836            #[allow(unstable_name_collisions)]
837            let entry2 = items2.last().unwrap();
838            match entry1.cmp(entry2) {
839                Ordering::Greater => {
840                    #[allow(unstable_name_collisions)]
841                    let entry1 = items1.pop_last().unwrap();
842                    for parent_entry in entry1.0.parents() {
843                        items1.insert(IndexEntryByGeneration(parent_entry));
844                    }
845                }
846                Ordering::Less => {
847                    #[allow(unstable_name_collisions)]
848                    let entry2 = items2.pop_last().unwrap();
849                    for parent_entry in entry2.0.parents() {
850                        items2.insert(IndexEntryByGeneration(parent_entry));
851                    }
852                }
853                Ordering::Equal => {
854                    result.insert(entry1.0.pos);
855                    #[allow(unstable_name_collisions)]
856                    items1.pop_last();
857                    #[allow(unstable_name_collisions)]
858                    items2.pop_last();
859                }
860            }
861        }
862        self.heads_pos(result)
863    }
864
865    pub fn walk_revs(&self, wanted: &[CommitId], unwanted: &[CommitId]) -> RevWalk<'a> {
866        let mut rev_walk = RevWalk::new(self.clone());
867        for pos in wanted.iter().map(|id| self.commit_id_to_pos(id).unwrap()) {
868            rev_walk.add_wanted(pos);
869        }
870        for pos in unwanted.iter().map(|id| self.commit_id_to_pos(id).unwrap()) {
871            rev_walk.add_unwanted(pos);
872        }
873        rev_walk
874    }
875
876    pub fn heads(&self, candidate_ids: &mut dyn Iterator<Item = &CommitId>) -> Vec<CommitId> {
877        let candidate_positions: BTreeSet<_> = candidate_ids
878            .map(|id| self.commit_id_to_pos(id).unwrap())
879            .collect();
880
881        self.heads_pos(candidate_positions)
882            .iter()
883            .map(|pos| self.entry_by_pos(*pos).commit_id())
884            .collect()
885    }
886
887    fn heads_pos(
888        &self,
889        mut candidate_positions: BTreeSet<IndexPosition>,
890    ) -> BTreeSet<IndexPosition> {
891        // Add all parents of the candidates to the work queue. The parents and their
892        // ancestors are not heads.
893        // Also find the smallest generation number among the candidates.
894        let mut work = BinaryHeap::new();
895        let mut min_generation = u32::MAX;
896        for pos in &candidate_positions {
897            let entry = self.entry_by_pos(*pos);
898            min_generation = min(min_generation, entry.generation_number());
899            for parent_entry in entry.parents() {
900                work.push(IndexEntryByGeneration(parent_entry));
901            }
902        }
903
904        // Walk ancestors of the parents of the candidates. Remove visited commits from
905        // set of candidates. Stop walking when we have gone past the minimum
906        // candidate generation.
907        let mut visited = HashSet::new();
908        while let Some(IndexEntryByGeneration(item)) = work.pop() {
909            if !visited.insert(item.pos) {
910                continue;
911            }
912            if item.generation_number() < min_generation {
913                break;
914            }
915            candidate_positions.remove(&item.pos);
916            for parent_entry in item.parents() {
917                work.push(IndexEntryByGeneration(parent_entry));
918            }
919        }
920        candidate_positions
921    }
922
923    pub fn topo_order(&self, input: &mut dyn Iterator<Item = &CommitId>) -> Vec<IndexEntry<'a>> {
924        let mut entries_by_generation = input
925            .into_iter()
926            .map(|id| IndexEntryByPosition(self.entry_by_id(id).unwrap()))
927            .collect_vec();
928        entries_by_generation.sort();
929        entries_by_generation
930            .into_iter()
931            .map(|key| key.0)
932            .collect_vec()
933    }
934}
935
936pub struct IndexLevelStats {
937    pub num_commits: u32,
938    pub name: Option<String>,
939}
940
941pub struct IndexStats {
942    pub num_commits: u32,
943    pub num_merges: u32,
944    pub max_generation_number: u32,
945    pub num_heads: u32,
946    pub num_changes: u32,
947    pub levels: Vec<IndexLevelStats>,
948}
949
950#[derive(Clone, Eq, PartialEq)]
951pub struct IndexEntryByPosition<'a>(IndexEntry<'a>);
952
953impl Ord for IndexEntryByPosition<'_> {
954    fn cmp(&self, other: &Self) -> Ordering {
955        self.0.pos.cmp(&other.0.pos)
956    }
957}
958
959impl PartialOrd for IndexEntryByPosition<'_> {
960    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
961        Some(self.cmp(other))
962    }
963}
964
965#[derive(Clone, Eq, PartialEq)]
966struct IndexEntryByGeneration<'a>(IndexEntry<'a>);
967
968impl Ord for IndexEntryByGeneration<'_> {
969    fn cmp(&self, other: &Self) -> Ordering {
970        self.0
971            .generation_number()
972            .cmp(&other.0.generation_number())
973            .then(self.0.pos.cmp(&other.0.pos))
974    }
975}
976
977impl PartialOrd for IndexEntryByGeneration<'_> {
978    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
979        Some(self.cmp(other))
980    }
981}
982
983#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
984struct RevWalkWorkItem<'a, T> {
985    entry: IndexEntryByPosition<'a>,
986    state: RevWalkWorkItemState<T>,
987}
988
989#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
990enum RevWalkWorkItemState<T> {
991    // Order matters: Unwanted should appear earlier in the max-heap.
992    Wanted(T),
993    Unwanted,
994}
995
996impl<'a, T> RevWalkWorkItem<'a, T> {
997    fn is_wanted(&self) -> bool {
998        matches!(self.state, RevWalkWorkItemState::Wanted(_))
999    }
1000
1001    fn map_wanted<U>(self, f: impl FnOnce(T) -> U) -> RevWalkWorkItem<'a, U> {
1002        RevWalkWorkItem {
1003            entry: self.entry,
1004            state: match self.state {
1005                RevWalkWorkItemState::Wanted(t) => RevWalkWorkItemState::Wanted(f(t)),
1006                RevWalkWorkItemState::Unwanted => RevWalkWorkItemState::Unwanted,
1007            },
1008        }
1009    }
1010}
1011
1012#[derive(Clone)]
1013struct RevWalkQueue<'a, T> {
1014    index: CompositeIndex<'a>,
1015    items: BinaryHeap<RevWalkWorkItem<'a, T>>,
1016    unwanted_count: usize,
1017}
1018
1019impl<'a, T: Ord> RevWalkQueue<'a, T> {
1020    fn new(index: CompositeIndex<'a>) -> Self {
1021        Self {
1022            index,
1023            items: BinaryHeap::new(),
1024            unwanted_count: 0,
1025        }
1026    }
1027
1028    fn map_wanted<U: Ord>(self, mut f: impl FnMut(T) -> U) -> RevWalkQueue<'a, U> {
1029        RevWalkQueue {
1030            index: self.index,
1031            items: self
1032                .items
1033                .into_iter()
1034                .map(|x| x.map_wanted(&mut f))
1035                .collect(),
1036            unwanted_count: self.unwanted_count,
1037        }
1038    }
1039
1040    fn push_wanted(&mut self, pos: IndexPosition, t: T) {
1041        self.items.push(RevWalkWorkItem {
1042            entry: IndexEntryByPosition(self.index.entry_by_pos(pos)),
1043            state: RevWalkWorkItemState::Wanted(t),
1044        });
1045    }
1046
1047    fn push_unwanted(&mut self, pos: IndexPosition) {
1048        self.items.push(RevWalkWorkItem {
1049            entry: IndexEntryByPosition(self.index.entry_by_pos(pos)),
1050            state: RevWalkWorkItemState::Unwanted,
1051        });
1052        self.unwanted_count += 1;
1053    }
1054
1055    fn push_wanted_parents(&mut self, entry: &IndexEntry<'_>, t: T)
1056    where
1057        T: Clone,
1058    {
1059        for pos in entry.parent_positions() {
1060            self.push_wanted(pos, t.clone());
1061        }
1062    }
1063
1064    fn push_unwanted_parents(&mut self, entry: &IndexEntry<'_>) {
1065        for pos in entry.parent_positions() {
1066            self.push_unwanted(pos);
1067        }
1068    }
1069
1070    fn pop(&mut self) -> Option<RevWalkWorkItem<'a, T>> {
1071        if let Some(x) = self.items.pop() {
1072            self.unwanted_count -= !x.is_wanted() as usize;
1073            Some(x)
1074        } else {
1075            None
1076        }
1077    }
1078
1079    fn pop_eq(&mut self, entry: &IndexEntry<'_>) -> Option<RevWalkWorkItem<'a, T>> {
1080        if let Some(x) = self.items.peek() {
1081            (&x.entry.0 == entry).then(|| self.pop().unwrap())
1082        } else {
1083            None
1084        }
1085    }
1086
1087    fn skip_while_eq(&mut self, entry: &IndexEntry<'_>) {
1088        while self.pop_eq(entry).is_some() {
1089            continue;
1090        }
1091    }
1092}
1093
1094#[derive(Clone)]
1095pub struct RevWalk<'a> {
1096    queue: RevWalkQueue<'a, ()>,
1097}
1098
1099impl<'a> RevWalk<'a> {
1100    fn new(index: CompositeIndex<'a>) -> Self {
1101        let queue = RevWalkQueue::new(index);
1102        Self { queue }
1103    }
1104
1105    fn add_wanted(&mut self, pos: IndexPosition) {
1106        self.queue.push_wanted(pos, ());
1107    }
1108
1109    fn add_unwanted(&mut self, pos: IndexPosition) {
1110        self.queue.push_unwanted(pos);
1111    }
1112
1113    /// Filters entries by generation (or depth from the current wanted set.)
1114    ///
1115    /// The generation of the current wanted entries starts from 0.
1116    pub fn filter_by_generation(self, generation_range: Range<u32>) -> RevWalkGenerationRange<'a> {
1117        RevWalkGenerationRange {
1118            queue: self.queue.map_wanted(|()| 0),
1119            generation_range,
1120        }
1121    }
1122}
1123
1124impl<'a> Iterator for RevWalk<'a> {
1125    type Item = IndexEntry<'a>;
1126
1127    fn next(&mut self) -> Option<Self::Item> {
1128        while let Some(item) = self.queue.pop() {
1129            self.queue.skip_while_eq(&item.entry.0);
1130            if item.is_wanted() {
1131                self.queue.push_wanted_parents(&item.entry.0, ());
1132                return Some(item.entry.0);
1133            } else if self.queue.items.len() == self.queue.unwanted_count {
1134                // No more wanted entries to walk
1135                debug_assert!(!self.queue.items.iter().any(|x| x.is_wanted()));
1136                return None;
1137            } else {
1138                self.queue.push_unwanted_parents(&item.entry.0);
1139            }
1140        }
1141
1142        debug_assert_eq!(
1143            self.queue.items.iter().filter(|x| !x.is_wanted()).count(),
1144            self.queue.unwanted_count
1145        );
1146        None
1147    }
1148}
1149
1150#[derive(Clone)]
1151pub struct RevWalkGenerationRange<'a> {
1152    queue: RevWalkQueue<'a, u32>,
1153    generation_range: Range<u32>,
1154}
1155
1156impl<'a> Iterator for RevWalkGenerationRange<'a> {
1157    type Item = IndexEntry<'a>;
1158
1159    fn next(&mut self) -> Option<Self::Item> {
1160        while let Some(item) = self.queue.pop() {
1161            if let RevWalkWorkItemState::Wanted(mut known_gen) = item.state {
1162                let mut some_in_range = self.generation_range.contains(&known_gen);
1163                if known_gen + 1 < self.generation_range.end {
1164                    self.queue.push_wanted_parents(&item.entry.0, known_gen + 1);
1165                }
1166                while let Some(x) = self.queue.pop_eq(&item.entry.0) {
1167                    // For wanted item, simply track all generation chains. This can
1168                    // be optimized if the wanted range is just upper/lower bounded.
1169                    // If the range is fully bounded and if the range is wide, we
1170                    // can instead extend 'gen' to a range of the same width, and
1171                    // merge overlapping generation ranges.
1172                    match x.state {
1173                        RevWalkWorkItemState::Wanted(gen) if known_gen != gen => {
1174                            some_in_range |= self.generation_range.contains(&gen);
1175                            if gen + 1 < self.generation_range.end {
1176                                self.queue.push_wanted_parents(&item.entry.0, gen + 1);
1177                            }
1178                            known_gen = gen;
1179                        }
1180                        RevWalkWorkItemState::Wanted(_) => {}
1181                        RevWalkWorkItemState::Unwanted => unreachable!(),
1182                    }
1183                }
1184                if some_in_range {
1185                    return Some(item.entry.0);
1186                }
1187            } else if self.queue.items.len() == self.queue.unwanted_count {
1188                // No more wanted entries to walk
1189                debug_assert!(!self.queue.items.iter().any(|x| x.is_wanted()));
1190                return None;
1191            } else {
1192                self.queue.skip_while_eq(&item.entry.0);
1193                self.queue.push_unwanted_parents(&item.entry.0);
1194            }
1195        }
1196
1197        debug_assert_eq!(
1198            self.queue.items.iter().filter(|x| !x.is_wanted()).count(),
1199            self.queue.unwanted_count
1200        );
1201        None
1202    }
1203}
1204
1205impl IndexSegment for ReadonlyIndex {
1206    fn segment_num_parent_commits(&self) -> u32 {
1207        self.num_parent_commits
1208    }
1209
1210    fn segment_num_commits(&self) -> u32 {
1211        self.num_local_commits
1212    }
1213
1214    fn segment_parent_file(&self) -> Option<&Arc<ReadonlyIndex>> {
1215        self.parent_file.as_ref()
1216    }
1217
1218    fn segment_name(&self) -> Option<String> {
1219        Some(self.name.clone())
1220    }
1221
1222    fn segment_commit_id_to_pos(&self, commit_id: &CommitId) -> Option<IndexPosition> {
1223        let lookup_pos = self.commit_id_byte_prefix_to_lookup_pos(commit_id)?;
1224        let entry = self.lookup_entry(lookup_pos);
1225        (&entry.commit_id() == commit_id).then(|| entry.pos())
1226    }
1227
1228    fn segment_commit_id_to_neighbor_positions(
1229        &self,
1230        commit_id: &CommitId,
1231    ) -> (Option<IndexPosition>, Option<IndexPosition>) {
1232        if let Some(lookup_pos) = self.commit_id_byte_prefix_to_lookup_pos(commit_id) {
1233            let entry_commit_id = self.lookup_entry(lookup_pos).commit_id();
1234            let (prev_lookup_pos, next_lookup_pos) = match entry_commit_id.cmp(commit_id) {
1235                Ordering::Less => {
1236                    assert_eq!(lookup_pos + 1, self.num_local_commits);
1237                    (Some(lookup_pos), None)
1238                }
1239                Ordering::Equal => {
1240                    let succ = ((lookup_pos + 1)..self.num_local_commits).next();
1241                    (lookup_pos.checked_sub(1), succ)
1242                }
1243                Ordering::Greater => (lookup_pos.checked_sub(1), Some(lookup_pos)),
1244            };
1245            let prev_pos = prev_lookup_pos.map(|p| self.lookup_entry(p).pos());
1246            let next_pos = next_lookup_pos.map(|p| self.lookup_entry(p).pos());
1247            (prev_pos, next_pos)
1248        } else {
1249            (None, None)
1250        }
1251    }
1252
1253    fn segment_resolve_prefix(&self, prefix: &HexPrefix) -> PrefixResolution<CommitId> {
1254        let min_bytes_prefix = CommitId::from_bytes(prefix.min_prefix_bytes());
1255        let lookup_pos = self
1256            .commit_id_byte_prefix_to_lookup_pos(&min_bytes_prefix)
1257            .unwrap_or(self.num_local_commits);
1258        let mut matches = (lookup_pos..self.num_local_commits)
1259            .map(|pos| self.lookup_entry(pos).commit_id())
1260            .take_while(|id| prefix.matches(id))
1261            .fuse();
1262        match (matches.next(), matches.next()) {
1263            (Some(id), None) => PrefixResolution::SingleMatch(id),
1264            (Some(_), Some(_)) => PrefixResolution::AmbiguousMatch,
1265            (None, _) => PrefixResolution::NoMatch,
1266        }
1267    }
1268
1269    fn segment_generation_number(&self, local_pos: u32) -> u32 {
1270        self.graph_entry(local_pos).generation_number()
1271    }
1272
1273    fn segment_commit_id(&self, local_pos: u32) -> CommitId {
1274        self.graph_entry(local_pos).commit_id()
1275    }
1276
1277    fn segment_change_id(&self, local_pos: u32) -> ChangeId {
1278        self.graph_entry(local_pos).change_id()
1279    }
1280
1281    fn segment_num_parents(&self, local_pos: u32) -> u32 {
1282        self.graph_entry(local_pos).num_parents()
1283    }
1284
1285    fn segment_parent_positions(&self, local_pos: u32) -> Vec<IndexPosition> {
1286        let graph_entry = self.graph_entry(local_pos);
1287        let mut parent_entries = vec![];
1288        if graph_entry.num_parents() >= 1 {
1289            parent_entries.push(graph_entry.parent1_pos());
1290        }
1291        if graph_entry.num_parents() >= 2 {
1292            let mut parent_overflow_pos = graph_entry.parent2_overflow_pos();
1293            for _ in 1..graph_entry.num_parents() {
1294                parent_entries.push(self.overflow_parent(parent_overflow_pos));
1295                parent_overflow_pos += 1;
1296            }
1297        }
1298        parent_entries
1299    }
1300
1301    fn segment_entry_by_pos(&self, pos: IndexPosition, local_pos: u32) -> IndexEntry {
1302        IndexEntry {
1303            source: self,
1304            local_pos,
1305            pos,
1306        }
1307    }
1308}
1309
1310impl IndexSegment for MutableIndex {
1311    fn segment_num_parent_commits(&self) -> u32 {
1312        self.num_parent_commits
1313    }
1314
1315    fn segment_num_commits(&self) -> u32 {
1316        self.graph.len() as u32
1317    }
1318
1319    fn segment_parent_file(&self) -> Option<&Arc<ReadonlyIndex>> {
1320        self.parent_file.as_ref()
1321    }
1322
1323    fn segment_name(&self) -> Option<String> {
1324        None
1325    }
1326
1327    fn segment_commit_id_to_pos(&self, commit_id: &CommitId) -> Option<IndexPosition> {
1328        self.lookup.get(commit_id).cloned()
1329    }
1330
1331    fn segment_commit_id_to_neighbor_positions(
1332        &self,
1333        commit_id: &CommitId,
1334    ) -> (Option<IndexPosition>, Option<IndexPosition>) {
1335        let prev_pos = self
1336            .lookup
1337            .range((Bound::Unbounded, Bound::Excluded(commit_id)))
1338            .next_back()
1339            .map(|(_, &pos)| pos);
1340        let next_pos = self
1341            .lookup
1342            .range((Bound::Excluded(commit_id), Bound::Unbounded))
1343            .next()
1344            .map(|(_, &pos)| pos);
1345        (prev_pos, next_pos)
1346    }
1347
1348    fn segment_resolve_prefix(&self, prefix: &HexPrefix) -> PrefixResolution<CommitId> {
1349        let min_bytes_prefix = CommitId::from_bytes(prefix.min_prefix_bytes());
1350        let mut matches = self
1351            .lookup
1352            .range((Bound::Included(&min_bytes_prefix), Bound::Unbounded))
1353            .map(|(id, _pos)| id)
1354            .take_while(|&id| prefix.matches(id))
1355            .fuse();
1356        match (matches.next(), matches.next()) {
1357            (Some(id), None) => PrefixResolution::SingleMatch(id.clone()),
1358            (Some(_), Some(_)) => PrefixResolution::AmbiguousMatch,
1359            (None, _) => PrefixResolution::NoMatch,
1360        }
1361    }
1362
1363    fn segment_generation_number(&self, local_pos: u32) -> u32 {
1364        self.graph[local_pos as usize].generation_number
1365    }
1366
1367    fn segment_commit_id(&self, local_pos: u32) -> CommitId {
1368        self.graph[local_pos as usize].commit_id.clone()
1369    }
1370
1371    fn segment_change_id(&self, local_pos: u32) -> ChangeId {
1372        self.graph[local_pos as usize].change_id.clone()
1373    }
1374
1375    fn segment_num_parents(&self, local_pos: u32) -> u32 {
1376        self.graph[local_pos as usize].parent_positions.len() as u32
1377    }
1378
1379    fn segment_parent_positions(&self, local_pos: u32) -> Vec<IndexPosition> {
1380        self.graph[local_pos as usize].parent_positions.clone()
1381    }
1382
1383    fn segment_entry_by_pos(&self, pos: IndexPosition, local_pos: u32) -> IndexEntry {
1384        IndexEntry {
1385            source: self,
1386            local_pos,
1387            pos,
1388        }
1389    }
1390}
1391
1392#[derive(Clone)]
1393pub struct IndexEntry<'a> {
1394    source: &'a dyn IndexSegment,
1395    pos: IndexPosition,
1396    // Position within the source segment
1397    local_pos: u32,
1398}
1399
1400impl Debug for IndexEntry<'_> {
1401    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1402        f.debug_struct("IndexEntry")
1403            .field("pos", &self.pos)
1404            .field("local_pos", &self.local_pos)
1405            .field("commit_id", &self.commit_id().hex())
1406            .finish()
1407    }
1408}
1409
1410impl PartialEq for IndexEntry<'_> {
1411    fn eq(&self, other: &Self) -> bool {
1412        self.pos == other.pos
1413    }
1414}
1415impl Eq for IndexEntry<'_> {}
1416
1417impl Hash for IndexEntry<'_> {
1418    fn hash<H: Hasher>(&self, state: &mut H) {
1419        self.pos.hash(state)
1420    }
1421}
1422
1423impl<'a> IndexEntry<'a> {
1424    pub fn position(&self) -> IndexPosition {
1425        self.pos
1426    }
1427
1428    pub fn generation_number(&self) -> u32 {
1429        self.source.segment_generation_number(self.local_pos)
1430    }
1431
1432    pub fn commit_id(&self) -> CommitId {
1433        self.source.segment_commit_id(self.local_pos)
1434    }
1435
1436    pub fn change_id(&self) -> ChangeId {
1437        self.source.segment_change_id(self.local_pos)
1438    }
1439
1440    pub fn num_parents(&self) -> u32 {
1441        self.source.segment_num_parents(self.local_pos)
1442    }
1443
1444    pub fn parent_positions(&self) -> Vec<IndexPosition> {
1445        self.source.segment_parent_positions(self.local_pos)
1446    }
1447
1448    pub fn parents(&self) -> Vec<IndexEntry<'a>> {
1449        let composite = CompositeIndex(self.source);
1450        self.parent_positions()
1451            .into_iter()
1452            .map(|pos| composite.entry_by_pos(pos))
1453            .collect()
1454    }
1455}
1456
1457impl ReadonlyIndex {
1458    pub(crate) fn load_from(
1459        file: &mut dyn Read,
1460        dir: PathBuf,
1461        name: String,
1462        commit_id_length: usize,
1463        change_id_length: usize,
1464    ) -> Result<Arc<ReadonlyIndex>, IndexLoadError> {
1465        let parent_filename_len = file.read_u32::<LittleEndian>()?;
1466        let num_parent_commits;
1467        let maybe_parent_file;
1468        if parent_filename_len > 0 {
1469            let mut parent_filename_bytes = vec![0; parent_filename_len as usize];
1470            file.read_exact(&mut parent_filename_bytes)?;
1471            let parent_filename = String::from_utf8(parent_filename_bytes).unwrap();
1472            let parent_file_path = dir.join(&parent_filename);
1473            let mut index_file = File::open(parent_file_path).unwrap();
1474            let parent_file = ReadonlyIndex::load_from(
1475                &mut index_file,
1476                dir,
1477                parent_filename,
1478                commit_id_length,
1479                change_id_length,
1480            )?;
1481            num_parent_commits = parent_file.num_parent_commits + parent_file.num_local_commits;
1482            maybe_parent_file = Some(parent_file);
1483        } else {
1484            num_parent_commits = 0;
1485            maybe_parent_file = None;
1486        };
1487        let num_commits = file.read_u32::<LittleEndian>()?;
1488        let num_parent_overflow_entries = file.read_u32::<LittleEndian>()?;
1489        let mut data = vec![];
1490        file.read_to_end(&mut data)?;
1491        let commit_graph_entry_size = CommitGraphEntry::size(commit_id_length, change_id_length);
1492        let graph_size = (num_commits as usize) * commit_graph_entry_size;
1493        let commit_lookup_entry_size = CommitLookupEntry::size(commit_id_length);
1494        let lookup_size = (num_commits as usize) * commit_lookup_entry_size;
1495        let parent_overflow_size = (num_parent_overflow_entries as usize) * 4;
1496        let expected_size = graph_size + lookup_size + parent_overflow_size;
1497        if data.len() != expected_size {
1498            return Err(IndexLoadError::IndexCorrupt(name));
1499        }
1500        let overflow_parent = data.split_off(graph_size + lookup_size);
1501        let lookup = data.split_off(graph_size);
1502        let graph = data;
1503        Ok(Arc::new(ReadonlyIndex {
1504            parent_file: maybe_parent_file,
1505            num_parent_commits,
1506            name,
1507            commit_id_length,
1508            change_id_length,
1509            commit_graph_entry_size,
1510            commit_lookup_entry_size,
1511            num_local_commits: num_commits,
1512            graph,
1513            lookup,
1514            overflow_parent,
1515        }))
1516    }
1517
1518    pub fn name(&self) -> &str {
1519        &self.name
1520    }
1521
1522    fn graph_entry(&self, local_pos: u32) -> CommitGraphEntry {
1523        let offset = (local_pos as usize) * self.commit_graph_entry_size;
1524        CommitGraphEntry {
1525            data: &self.graph[offset..offset + self.commit_graph_entry_size],
1526            commit_id_length: self.commit_id_length,
1527            change_id_length: self.change_id_length,
1528        }
1529    }
1530
1531    fn lookup_entry(&self, lookup_pos: u32) -> CommitLookupEntry {
1532        let offset = (lookup_pos as usize) * self.commit_lookup_entry_size;
1533        CommitLookupEntry {
1534            data: &self.lookup[offset..offset + self.commit_lookup_entry_size],
1535            commit_id_length: self.commit_id_length,
1536        }
1537    }
1538
1539    fn overflow_parent(&self, overflow_pos: u32) -> IndexPosition {
1540        let offset = (overflow_pos as usize) * 4;
1541        IndexPosition(
1542            (&self.overflow_parent[offset..offset + 4])
1543                .read_u32::<LittleEndian>()
1544                .unwrap(),
1545        )
1546    }
1547
1548    fn commit_id_byte_prefix_to_lookup_pos(&self, prefix: &CommitId) -> Option<u32> {
1549        if self.num_local_commits == 0 {
1550            // Avoid overflow when subtracting 1 below
1551            return None;
1552        }
1553        let mut low = 0;
1554        let mut high = self.num_local_commits - 1;
1555
1556        // binary search for the commit id
1557        loop {
1558            let mid = (low + high) / 2;
1559            if high == low {
1560                return Some(mid);
1561            }
1562            let entry = self.lookup_entry(mid);
1563            if entry.commit_id_bytes() < prefix.as_bytes() {
1564                low = mid + 1;
1565            } else {
1566                high = mid;
1567            }
1568        }
1569    }
1570}
1571
1572impl Index for ReadonlyIndex {
1573    fn num_commits(&self) -> u32 {
1574        CompositeIndex(self).num_commits()
1575    }
1576
1577    fn stats(&self) -> IndexStats {
1578        CompositeIndex(self).stats()
1579    }
1580
1581    fn commit_id_to_pos(&self, commit_id: &CommitId) -> Option<IndexPosition> {
1582        CompositeIndex(self).commit_id_to_pos(commit_id)
1583    }
1584
1585    fn shortest_unique_commit_id_prefix_len(&self, commit_id: &CommitId) -> usize {
1586        CompositeIndex(self).shortest_unique_commit_id_prefix_len(commit_id)
1587    }
1588
1589    fn resolve_prefix(&self, prefix: &HexPrefix) -> PrefixResolution<CommitId> {
1590        CompositeIndex(self).resolve_prefix(prefix)
1591    }
1592
1593    fn entry_by_id(&self, commit_id: &CommitId) -> Option<IndexEntry> {
1594        CompositeIndex(self).entry_by_id(commit_id)
1595    }
1596
1597    fn entry_by_pos(&self, pos: IndexPosition) -> IndexEntry {
1598        CompositeIndex(self).entry_by_pos(pos)
1599    }
1600
1601    fn has_id(&self, commit_id: &CommitId) -> bool {
1602        CompositeIndex(self).has_id(commit_id)
1603    }
1604
1605    fn is_ancestor(&self, ancestor_id: &CommitId, descendant_id: &CommitId) -> bool {
1606        CompositeIndex(self).is_ancestor(ancestor_id, descendant_id)
1607    }
1608
1609    fn common_ancestors(&self, set1: &[CommitId], set2: &[CommitId]) -> Vec<CommitId> {
1610        CompositeIndex(self).common_ancestors(set1, set2)
1611    }
1612
1613    fn walk_revs(&self, wanted: &[CommitId], unwanted: &[CommitId]) -> RevWalk {
1614        CompositeIndex(self).walk_revs(wanted, unwanted)
1615    }
1616
1617    fn heads(&self, candidates: &mut dyn Iterator<Item = &CommitId>) -> Vec<CommitId> {
1618        CompositeIndex(self).heads(candidates)
1619    }
1620
1621    fn topo_order(&self, input: &mut dyn Iterator<Item = &CommitId>) -> Vec<IndexEntry> {
1622        CompositeIndex(self).topo_order(input)
1623    }
1624}
1625
1626#[cfg(test)]
1627mod tests {
1628    use test_case::test_case;
1629
1630    use super::*;
1631    use crate::index::Index;
1632
1633    /// Generator of unique 16-byte ChangeId excluding root id
1634    fn change_id_generator() -> impl FnMut() -> ChangeId {
1635        let mut iter = (1_u128..).map(|n| ChangeId::new(n.to_le_bytes().into()));
1636        move || iter.next().unwrap()
1637    }
1638
1639    #[test_case(false; "memory")]
1640    #[test_case(true; "file")]
1641    fn index_empty(on_disk: bool) {
1642        let temp_dir = testutils::new_temp_dir();
1643        let index = MutableIndex::full(3, 16);
1644        let index: Box<dyn Index> = if on_disk {
1645            let saved_index = index.save_in(temp_dir.path().to_owned()).unwrap();
1646            Box::new(Arc::try_unwrap(saved_index).unwrap())
1647        } else {
1648            Box::new(index)
1649        };
1650
1651        // Stats are as expected
1652        let stats = index.stats();
1653        assert_eq!(stats.num_commits, 0);
1654        assert_eq!(stats.num_heads, 0);
1655        assert_eq!(stats.max_generation_number, 0);
1656        assert_eq!(stats.num_merges, 0);
1657        assert_eq!(stats.num_changes, 0);
1658        assert_eq!(index.num_commits(), 0);
1659        // Cannot find any commits
1660        assert!(index.entry_by_id(&CommitId::from_hex("000000")).is_none());
1661        assert!(index.entry_by_id(&CommitId::from_hex("aaa111")).is_none());
1662        assert!(index.entry_by_id(&CommitId::from_hex("ffffff")).is_none());
1663    }
1664
1665    #[test_case(false; "memory")]
1666    #[test_case(true; "file")]
1667    fn index_root_commit(on_disk: bool) {
1668        let temp_dir = testutils::new_temp_dir();
1669        let mut new_change_id = change_id_generator();
1670        let mut index = MutableIndex::full(3, 16);
1671        let id_0 = CommitId::from_hex("000000");
1672        let change_id0 = new_change_id();
1673        index.add_commit_data(id_0.clone(), change_id0.clone(), &[]);
1674        let index: Box<dyn Index> = if on_disk {
1675            let saved_index = index.save_in(temp_dir.path().to_owned()).unwrap();
1676            Box::new(Arc::try_unwrap(saved_index).unwrap())
1677        } else {
1678            Box::new(index)
1679        };
1680
1681        // Stats are as expected
1682        let stats = index.stats();
1683        assert_eq!(stats.num_commits, 1);
1684        assert_eq!(stats.num_heads, 1);
1685        assert_eq!(stats.max_generation_number, 0);
1686        assert_eq!(stats.num_merges, 0);
1687        assert_eq!(stats.num_changes, 1);
1688        assert_eq!(index.num_commits(), 1);
1689        // Can find only the root commit
1690        assert_eq!(index.commit_id_to_pos(&id_0), Some(IndexPosition(0)));
1691        assert_eq!(index.commit_id_to_pos(&CommitId::from_hex("aaaaaa")), None);
1692        assert_eq!(index.commit_id_to_pos(&CommitId::from_hex("ffffff")), None);
1693        // Check properties of root entry
1694        let entry = index.entry_by_id(&id_0).unwrap();
1695        assert_eq!(entry.pos, IndexPosition(0));
1696        assert_eq!(entry.commit_id(), id_0);
1697        assert_eq!(entry.change_id(), change_id0);
1698        assert_eq!(entry.generation_number(), 0);
1699        assert_eq!(entry.num_parents(), 0);
1700        assert_eq!(entry.parent_positions(), Vec::<IndexPosition>::new());
1701        assert_eq!(entry.parents(), Vec::<IndexEntry>::new());
1702    }
1703
1704    #[test]
1705    #[should_panic(expected = "parent commit is not indexed")]
1706    fn index_missing_parent_commit() {
1707        let mut new_change_id = change_id_generator();
1708        let mut index = MutableIndex::full(3, 16);
1709        let id_0 = CommitId::from_hex("000000");
1710        let id_1 = CommitId::from_hex("111111");
1711        index.add_commit_data(id_1, new_change_id(), &[id_0]);
1712    }
1713
1714    #[test_case(false, false; "full in memory")]
1715    #[test_case(false, true; "full on disk")]
1716    #[test_case(true, false; "incremental in memory")]
1717    #[test_case(true, true; "incremental on disk")]
1718    fn index_multiple_commits(incremental: bool, on_disk: bool) {
1719        let temp_dir = testutils::new_temp_dir();
1720        let mut new_change_id = change_id_generator();
1721        let mut index = MutableIndex::full(3, 16);
1722        // 5
1723        // |\
1724        // 4 | 3
1725        // | |/
1726        // 1 2
1727        // |/
1728        // 0
1729        let id_0 = CommitId::from_hex("000000");
1730        let change_id0 = new_change_id();
1731        let id_1 = CommitId::from_hex("111111");
1732        let change_id1 = new_change_id();
1733        let id_2 = CommitId::from_hex("222222");
1734        let change_id2 = change_id1.clone();
1735        index.add_commit_data(id_0.clone(), change_id0, &[]);
1736        index.add_commit_data(id_1.clone(), change_id1.clone(), &[id_0.clone()]);
1737        index.add_commit_data(id_2.clone(), change_id2.clone(), &[id_0.clone()]);
1738
1739        // If testing incremental indexing, write the first three commits to one file
1740        // now and build the remainder as another segment on top.
1741        if incremental {
1742            let initial_file = index.save_in(temp_dir.path().to_owned()).unwrap();
1743            index = MutableIndex::incremental(initial_file);
1744        }
1745
1746        let id_3 = CommitId::from_hex("333333");
1747        let change_id3 = new_change_id();
1748        let id_4 = CommitId::from_hex("444444");
1749        let change_id4 = new_change_id();
1750        let id_5 = CommitId::from_hex("555555");
1751        let change_id5 = change_id3.clone();
1752        index.add_commit_data(id_3.clone(), change_id3.clone(), &[id_2.clone()]);
1753        index.add_commit_data(id_4.clone(), change_id4, &[id_1.clone()]);
1754        index.add_commit_data(id_5.clone(), change_id5, &[id_4.clone(), id_2.clone()]);
1755        let index: Box<dyn Index> = if on_disk {
1756            let saved_index = index.save_in(temp_dir.path().to_owned()).unwrap();
1757            Box::new(Arc::try_unwrap(saved_index).unwrap())
1758        } else {
1759            Box::new(index)
1760        };
1761
1762        // Stats are as expected
1763        let stats = index.stats();
1764        assert_eq!(stats.num_commits, 6);
1765        assert_eq!(stats.num_heads, 2);
1766        assert_eq!(stats.max_generation_number, 3);
1767        assert_eq!(stats.num_merges, 1);
1768        assert_eq!(stats.num_changes, 4);
1769        assert_eq!(index.num_commits(), 6);
1770        // Can find all the commits
1771        let entry_0 = index.entry_by_id(&id_0).unwrap();
1772        let entry_1 = index.entry_by_id(&id_1).unwrap();
1773        let entry_2 = index.entry_by_id(&id_2).unwrap();
1774        let entry_3 = index.entry_by_id(&id_3).unwrap();
1775        let entry_4 = index.entry_by_id(&id_4).unwrap();
1776        let entry_5 = index.entry_by_id(&id_5).unwrap();
1777        // Check properties of some entries
1778        assert_eq!(entry_0.pos, IndexPosition(0));
1779        assert_eq!(entry_0.commit_id(), id_0);
1780        assert_eq!(entry_1.pos, IndexPosition(1));
1781        assert_eq!(entry_1.commit_id(), id_1);
1782        assert_eq!(entry_1.change_id(), change_id1);
1783        assert_eq!(entry_1.generation_number(), 1);
1784        assert_eq!(entry_1.num_parents(), 1);
1785        assert_eq!(entry_1.parent_positions(), vec![IndexPosition(0)]);
1786        assert_eq!(entry_1.parents().len(), 1);
1787        assert_eq!(entry_1.parents()[0].pos, IndexPosition(0));
1788        assert_eq!(entry_2.pos, IndexPosition(2));
1789        assert_eq!(entry_2.commit_id(), id_2);
1790        assert_eq!(entry_2.change_id(), change_id2);
1791        assert_eq!(entry_2.generation_number(), 1);
1792        assert_eq!(entry_2.num_parents(), 1);
1793        assert_eq!(entry_2.parent_positions(), vec![IndexPosition(0)]);
1794        assert_eq!(entry_3.change_id(), change_id3);
1795        assert_eq!(entry_3.generation_number(), 2);
1796        assert_eq!(entry_3.parent_positions(), vec![IndexPosition(2)]);
1797        assert_eq!(entry_4.pos, IndexPosition(4));
1798        assert_eq!(entry_4.generation_number(), 2);
1799        assert_eq!(entry_4.num_parents(), 1);
1800        assert_eq!(entry_4.parent_positions(), vec![IndexPosition(1)]);
1801        assert_eq!(entry_5.generation_number(), 3);
1802        assert_eq!(entry_5.num_parents(), 2);
1803        assert_eq!(
1804            entry_5.parent_positions(),
1805            vec![IndexPosition(4), IndexPosition(2)]
1806        );
1807        assert_eq!(entry_5.parents().len(), 2);
1808        assert_eq!(entry_5.parents()[0].pos, IndexPosition(4));
1809        assert_eq!(entry_5.parents()[1].pos, IndexPosition(2));
1810    }
1811
1812    #[test_case(false; "in memory")]
1813    #[test_case(true; "on disk")]
1814    fn index_many_parents(on_disk: bool) {
1815        let temp_dir = testutils::new_temp_dir();
1816        let mut new_change_id = change_id_generator();
1817        let mut index = MutableIndex::full(3, 16);
1818        //     6
1819        //    /|\
1820        //   / | \
1821        //  / /|\ \
1822        // 1 2 3 4 5
1823        //  \ \|/ /
1824        //   \ | /
1825        //    \|/
1826        //     0
1827        let id_0 = CommitId::from_hex("000000");
1828        let id_1 = CommitId::from_hex("111111");
1829        let id_2 = CommitId::from_hex("222222");
1830        let id_3 = CommitId::from_hex("333333");
1831        let id_4 = CommitId::from_hex("444444");
1832        let id_5 = CommitId::from_hex("555555");
1833        let id_6 = CommitId::from_hex("666666");
1834        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
1835        index.add_commit_data(id_1.clone(), new_change_id(), &[id_0.clone()]);
1836        index.add_commit_data(id_2.clone(), new_change_id(), &[id_0.clone()]);
1837        index.add_commit_data(id_3.clone(), new_change_id(), &[id_0.clone()]);
1838        index.add_commit_data(id_4.clone(), new_change_id(), &[id_0.clone()]);
1839        index.add_commit_data(id_5.clone(), new_change_id(), &[id_0]);
1840        index.add_commit_data(
1841            id_6.clone(),
1842            new_change_id(),
1843            &[id_1, id_2, id_3, id_4, id_5],
1844        );
1845        let index: Box<dyn Index> = if on_disk {
1846            let saved_index = index.save_in(temp_dir.path().to_owned()).unwrap();
1847            Box::new(Arc::try_unwrap(saved_index).unwrap())
1848        } else {
1849            Box::new(index)
1850        };
1851
1852        // Stats are as expected
1853        let stats = index.stats();
1854        assert_eq!(stats.num_commits, 7);
1855        assert_eq!(stats.num_heads, 1);
1856        assert_eq!(stats.max_generation_number, 2);
1857        assert_eq!(stats.num_merges, 1);
1858
1859        // The octopus merge has the right parents
1860        let entry_6 = index.entry_by_id(&id_6).unwrap();
1861        assert_eq!(entry_6.commit_id(), id_6.clone());
1862        assert_eq!(entry_6.num_parents(), 5);
1863        assert_eq!(
1864            entry_6.parent_positions(),
1865            vec![
1866                IndexPosition(1),
1867                IndexPosition(2),
1868                IndexPosition(3),
1869                IndexPosition(4),
1870                IndexPosition(5)
1871            ]
1872        );
1873        assert_eq!(entry_6.generation_number(), 2);
1874    }
1875
1876    #[test]
1877    fn resolve_prefix() {
1878        let temp_dir = testutils::new_temp_dir();
1879        let mut new_change_id = change_id_generator();
1880        let mut index = MutableIndex::full(3, 16);
1881
1882        // Create some commits with different various common prefixes.
1883        let id_0 = CommitId::from_hex("000000");
1884        let id_1 = CommitId::from_hex("009999");
1885        let id_2 = CommitId::from_hex("055488");
1886        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
1887        index.add_commit_data(id_1.clone(), new_change_id(), &[]);
1888        index.add_commit_data(id_2.clone(), new_change_id(), &[]);
1889
1890        // Write the first three commits to one file and build the remainder on top.
1891        let initial_file = index.save_in(temp_dir.path().to_owned()).unwrap();
1892        index = MutableIndex::incremental(initial_file);
1893
1894        let id_3 = CommitId::from_hex("055444");
1895        let id_4 = CommitId::from_hex("055555");
1896        let id_5 = CommitId::from_hex("033333");
1897        index.add_commit_data(id_3, new_change_id(), &[]);
1898        index.add_commit_data(id_4, new_change_id(), &[]);
1899        index.add_commit_data(id_5, new_change_id(), &[]);
1900
1901        // Can find commits given the full hex number
1902        assert_eq!(
1903            index.resolve_prefix(&HexPrefix::new(&id_0.hex()).unwrap()),
1904            PrefixResolution::SingleMatch(id_0)
1905        );
1906        assert_eq!(
1907            index.resolve_prefix(&HexPrefix::new(&id_1.hex()).unwrap()),
1908            PrefixResolution::SingleMatch(id_1)
1909        );
1910        assert_eq!(
1911            index.resolve_prefix(&HexPrefix::new(&id_2.hex()).unwrap()),
1912            PrefixResolution::SingleMatch(id_2)
1913        );
1914        // Test nonexistent commits
1915        assert_eq!(
1916            index.resolve_prefix(&HexPrefix::new("ffffff").unwrap()),
1917            PrefixResolution::NoMatch
1918        );
1919        assert_eq!(
1920            index.resolve_prefix(&HexPrefix::new("000001").unwrap()),
1921            PrefixResolution::NoMatch
1922        );
1923        // Test ambiguous prefix
1924        assert_eq!(
1925            index.resolve_prefix(&HexPrefix::new("0").unwrap()),
1926            PrefixResolution::AmbiguousMatch
1927        );
1928        // Test a globally unique prefix in initial part
1929        assert_eq!(
1930            index.resolve_prefix(&HexPrefix::new("009").unwrap()),
1931            PrefixResolution::SingleMatch(CommitId::from_hex("009999"))
1932        );
1933        // Test a globally unique prefix in incremental part
1934        assert_eq!(
1935            index.resolve_prefix(&HexPrefix::new("03").unwrap()),
1936            PrefixResolution::SingleMatch(CommitId::from_hex("033333"))
1937        );
1938        // Test a locally unique but globally ambiguous prefix
1939        assert_eq!(
1940            index.resolve_prefix(&HexPrefix::new("0554").unwrap()),
1941            PrefixResolution::AmbiguousMatch
1942        );
1943    }
1944
1945    #[test]
1946    #[allow(clippy::redundant_clone)] // allow id_n.clone()
1947    fn neighbor_commit_ids() {
1948        let temp_dir = testutils::new_temp_dir();
1949        let mut new_change_id = change_id_generator();
1950        let mut index = MutableIndex::full(3, 16);
1951
1952        // Create some commits with different various common prefixes.
1953        let id_0 = CommitId::from_hex("000001");
1954        let id_1 = CommitId::from_hex("009999");
1955        let id_2 = CommitId::from_hex("055488");
1956        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
1957        index.add_commit_data(id_1.clone(), new_change_id(), &[]);
1958        index.add_commit_data(id_2.clone(), new_change_id(), &[]);
1959
1960        // Write the first three commits to one file and build the remainder on top.
1961        let initial_file = index.save_in(temp_dir.path().to_owned()).unwrap();
1962        index = MutableIndex::incremental(initial_file.clone());
1963
1964        let id_3 = CommitId::from_hex("055444");
1965        let id_4 = CommitId::from_hex("055555");
1966        let id_5 = CommitId::from_hex("033333");
1967        index.add_commit_data(id_3.clone(), new_change_id(), &[]);
1968        index.add_commit_data(id_4.clone(), new_change_id(), &[]);
1969        index.add_commit_data(id_5.clone(), new_change_id(), &[]);
1970
1971        // Local lookup in readonly index, commit_id exists.
1972        assert_eq!(
1973            initial_file.segment_commit_id_to_neighbor_positions(&id_0),
1974            (None, Some(IndexPosition(1))),
1975        );
1976        assert_eq!(
1977            initial_file.segment_commit_id_to_neighbor_positions(&id_1),
1978            (Some(IndexPosition(0)), Some(IndexPosition(2))),
1979        );
1980        assert_eq!(
1981            initial_file.segment_commit_id_to_neighbor_positions(&id_2),
1982            (Some(IndexPosition(1)), None),
1983        );
1984
1985        // Local lookup in readonly index, commit_id does not exist.
1986        assert_eq!(
1987            initial_file.segment_commit_id_to_neighbor_positions(&CommitId::from_hex("000000")),
1988            (None, Some(IndexPosition(0))),
1989        );
1990        assert_eq!(
1991            initial_file.segment_commit_id_to_neighbor_positions(&CommitId::from_hex("000002")),
1992            (Some(IndexPosition(0)), Some(IndexPosition(1))),
1993        );
1994        assert_eq!(
1995            initial_file.segment_commit_id_to_neighbor_positions(&CommitId::from_hex("ffffff")),
1996            (Some(IndexPosition(2)), None),
1997        );
1998
1999        // Local lookup in mutable index, commit_id exists. id_5 < id_3 < id_4
2000        assert_eq!(
2001            index.segment_commit_id_to_neighbor_positions(&id_5),
2002            (None, Some(IndexPosition(3))),
2003        );
2004        assert_eq!(
2005            index.segment_commit_id_to_neighbor_positions(&id_3),
2006            (Some(IndexPosition(5)), Some(IndexPosition(4))),
2007        );
2008        assert_eq!(
2009            index.segment_commit_id_to_neighbor_positions(&id_4),
2010            (Some(IndexPosition(3)), None),
2011        );
2012
2013        // Local lookup in mutable index, commit_id does not exist. id_5 < id_3 < id_4
2014        assert_eq!(
2015            index.segment_commit_id_to_neighbor_positions(&CommitId::from_hex("033332")),
2016            (None, Some(IndexPosition(5))),
2017        );
2018        assert_eq!(
2019            index.segment_commit_id_to_neighbor_positions(&CommitId::from_hex("033334")),
2020            (Some(IndexPosition(5)), Some(IndexPosition(3))),
2021        );
2022        assert_eq!(
2023            index.segment_commit_id_to_neighbor_positions(&CommitId::from_hex("ffffff")),
2024            (Some(IndexPosition(4)), None),
2025        );
2026
2027        // Global lookup, commit_id exists. id_0 < id_1 < id_5 < id_3 < id_2 < id_4
2028        let composite_index = CompositeIndex(&index);
2029        assert_eq!(
2030            composite_index.resolve_neighbor_commit_ids(&id_0),
2031            (None, Some(id_1.clone())),
2032        );
2033        assert_eq!(
2034            composite_index.resolve_neighbor_commit_ids(&id_1),
2035            (Some(id_0.clone()), Some(id_5.clone())),
2036        );
2037        assert_eq!(
2038            composite_index.resolve_neighbor_commit_ids(&id_5),
2039            (Some(id_1.clone()), Some(id_3.clone())),
2040        );
2041        assert_eq!(
2042            composite_index.resolve_neighbor_commit_ids(&id_3),
2043            (Some(id_5.clone()), Some(id_2.clone())),
2044        );
2045        assert_eq!(
2046            composite_index.resolve_neighbor_commit_ids(&id_2),
2047            (Some(id_3.clone()), Some(id_4.clone())),
2048        );
2049        assert_eq!(
2050            composite_index.resolve_neighbor_commit_ids(&id_4),
2051            (Some(id_2.clone()), None),
2052        );
2053
2054        // Global lookup, commit_id doesn't exist. id_0 < id_1 < id_5 < id_3 < id_2 <
2055        // id_4
2056        assert_eq!(
2057            composite_index.resolve_neighbor_commit_ids(&CommitId::from_hex("000000")),
2058            (None, Some(id_0.clone())),
2059        );
2060        assert_eq!(
2061            composite_index.resolve_neighbor_commit_ids(&CommitId::from_hex("010000")),
2062            (Some(id_1.clone()), Some(id_5.clone())),
2063        );
2064        assert_eq!(
2065            composite_index.resolve_neighbor_commit_ids(&CommitId::from_hex("033334")),
2066            (Some(id_5.clone()), Some(id_3.clone())),
2067        );
2068        assert_eq!(
2069            composite_index.resolve_neighbor_commit_ids(&CommitId::from_hex("ffffff")),
2070            (Some(id_4.clone()), None),
2071        );
2072    }
2073
2074    #[test]
2075    fn shortest_unique_commit_id_prefix() {
2076        let temp_dir = testutils::new_temp_dir();
2077        let mut new_change_id = change_id_generator();
2078        let mut index = MutableIndex::full(3, 16);
2079
2080        // Create some commits with different various common prefixes.
2081        let id_0 = CommitId::from_hex("000001");
2082        let id_1 = CommitId::from_hex("009999");
2083        let id_2 = CommitId::from_hex("055488");
2084        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
2085        index.add_commit_data(id_1.clone(), new_change_id(), &[]);
2086        index.add_commit_data(id_2.clone(), new_change_id(), &[]);
2087
2088        // Write the first three commits to one file and build the remainder on top.
2089        let initial_file = index.save_in(temp_dir.path().to_owned()).unwrap();
2090        index = MutableIndex::incremental(initial_file);
2091
2092        let id_3 = CommitId::from_hex("055444");
2093        let id_4 = CommitId::from_hex("055555");
2094        let id_5 = CommitId::from_hex("033333");
2095        index.add_commit_data(id_3.clone(), new_change_id(), &[]);
2096        index.add_commit_data(id_4.clone(), new_change_id(), &[]);
2097        index.add_commit_data(id_5.clone(), new_change_id(), &[]);
2098
2099        // Public API: calculate shortest unique prefix len with known commit_id
2100        assert_eq!(index.shortest_unique_commit_id_prefix_len(&id_0), 3);
2101        assert_eq!(index.shortest_unique_commit_id_prefix_len(&id_1), 3);
2102        assert_eq!(index.shortest_unique_commit_id_prefix_len(&id_2), 5);
2103        assert_eq!(index.shortest_unique_commit_id_prefix_len(&id_3), 5);
2104        assert_eq!(index.shortest_unique_commit_id_prefix_len(&id_4), 4);
2105        assert_eq!(index.shortest_unique_commit_id_prefix_len(&id_5), 2);
2106
2107        // Public API: calculate shortest unique prefix len with unknown commit_id
2108        assert_eq!(
2109            index.shortest_unique_commit_id_prefix_len(&CommitId::from_hex("000002")),
2110            6
2111        );
2112        assert_eq!(
2113            index.shortest_unique_commit_id_prefix_len(&CommitId::from_hex("010000")),
2114            2
2115        );
2116        assert_eq!(
2117            index.shortest_unique_commit_id_prefix_len(&CommitId::from_hex("033334")),
2118            6
2119        );
2120        assert_eq!(
2121            index.shortest_unique_commit_id_prefix_len(&CommitId::from_hex("ffffff")),
2122            1
2123        );
2124    }
2125
2126    #[test]
2127    fn test_is_ancestor() {
2128        let mut new_change_id = change_id_generator();
2129        let mut index = MutableIndex::full(3, 16);
2130        // 5
2131        // |\
2132        // 4 | 3
2133        // | |/
2134        // 1 2
2135        // |/
2136        // 0
2137        let id_0 = CommitId::from_hex("000000");
2138        let id_1 = CommitId::from_hex("111111");
2139        let id_2 = CommitId::from_hex("222222");
2140        let id_3 = CommitId::from_hex("333333");
2141        let id_4 = CommitId::from_hex("444444");
2142        let id_5 = CommitId::from_hex("555555");
2143        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
2144        index.add_commit_data(id_1.clone(), new_change_id(), &[id_0.clone()]);
2145        index.add_commit_data(id_2.clone(), new_change_id(), &[id_0.clone()]);
2146        index.add_commit_data(id_3.clone(), new_change_id(), &[id_2.clone()]);
2147        index.add_commit_data(id_4.clone(), new_change_id(), &[id_1.clone()]);
2148        index.add_commit_data(id_5.clone(), new_change_id(), &[id_4.clone(), id_2.clone()]);
2149
2150        assert!(index.is_ancestor(&id_0, &id_0));
2151        assert!(index.is_ancestor(&id_0, &id_1));
2152        assert!(index.is_ancestor(&id_2, &id_3));
2153        assert!(index.is_ancestor(&id_2, &id_5));
2154        assert!(index.is_ancestor(&id_1, &id_5));
2155        assert!(index.is_ancestor(&id_0, &id_5));
2156        assert!(!index.is_ancestor(&id_1, &id_0));
2157        assert!(!index.is_ancestor(&id_5, &id_3));
2158        assert!(!index.is_ancestor(&id_3, &id_5));
2159        assert!(!index.is_ancestor(&id_2, &id_4));
2160        assert!(!index.is_ancestor(&id_4, &id_2));
2161    }
2162
2163    #[test]
2164    fn test_common_ancestors() {
2165        let mut new_change_id = change_id_generator();
2166        let mut index = MutableIndex::full(3, 16);
2167        // 5
2168        // |\
2169        // 4 |
2170        // | |
2171        // 1 2 3
2172        // | |/
2173        // |/
2174        // 0
2175        let id_0 = CommitId::from_hex("000000");
2176        let id_1 = CommitId::from_hex("111111");
2177        let id_2 = CommitId::from_hex("222222");
2178        let id_3 = CommitId::from_hex("333333");
2179        let id_4 = CommitId::from_hex("444444");
2180        let id_5 = CommitId::from_hex("555555");
2181        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
2182        index.add_commit_data(id_1.clone(), new_change_id(), &[id_0.clone()]);
2183        index.add_commit_data(id_2.clone(), new_change_id(), &[id_0.clone()]);
2184        index.add_commit_data(id_3.clone(), new_change_id(), &[id_0.clone()]);
2185        index.add_commit_data(id_4.clone(), new_change_id(), &[id_1.clone()]);
2186        index.add_commit_data(id_5.clone(), new_change_id(), &[id_4.clone(), id_2.clone()]);
2187
2188        assert_eq!(
2189            index.common_ancestors(&[id_0.clone()], &[id_0.clone()]),
2190            vec![id_0.clone()]
2191        );
2192        assert_eq!(
2193            index.common_ancestors(&[id_5.clone()], &[id_5.clone()]),
2194            vec![id_5.clone()]
2195        );
2196        assert_eq!(
2197            index.common_ancestors(&[id_1.clone()], &[id_2.clone()]),
2198            vec![id_0.clone()]
2199        );
2200        assert_eq!(
2201            index.common_ancestors(&[id_2.clone()], &[id_1.clone()]),
2202            vec![id_0.clone()]
2203        );
2204        assert_eq!(
2205            index.common_ancestors(&[id_1.clone()], &[id_4.clone()]),
2206            vec![id_1.clone()]
2207        );
2208        assert_eq!(
2209            index.common_ancestors(&[id_4.clone()], &[id_1.clone()]),
2210            vec![id_1.clone()]
2211        );
2212        assert_eq!(
2213            index.common_ancestors(&[id_3.clone()], &[id_5.clone()]),
2214            vec![id_0.clone()]
2215        );
2216        assert_eq!(
2217            index.common_ancestors(&[id_5.clone()], &[id_3.clone()]),
2218            vec![id_0.clone()]
2219        );
2220
2221        // With multiple commits in an input set
2222        assert_eq!(
2223            index.common_ancestors(&[id_0.clone(), id_1.clone()], &[id_0.clone()]),
2224            vec![id_0.clone()]
2225        );
2226        assert_eq!(
2227            index.common_ancestors(&[id_0.clone(), id_1.clone()], &[id_1.clone()]),
2228            vec![id_1.clone()]
2229        );
2230        assert_eq!(
2231            index.common_ancestors(&[id_1.clone(), id_2.clone()], &[id_1.clone()]),
2232            vec![id_1.clone()]
2233        );
2234        assert_eq!(
2235            index.common_ancestors(&[id_1.clone(), id_2.clone()], &[id_4]),
2236            vec![id_1.clone()]
2237        );
2238        assert_eq!(
2239            index.common_ancestors(&[id_1.clone(), id_2.clone()], &[id_5]),
2240            vec![id_1.clone(), id_2.clone()]
2241        );
2242        assert_eq!(index.common_ancestors(&[id_1, id_2], &[id_3]), vec![id_0]);
2243    }
2244
2245    #[test]
2246    fn test_common_ancestors_criss_cross() {
2247        let mut new_change_id = change_id_generator();
2248        let mut index = MutableIndex::full(3, 16);
2249        // 3 4
2250        // |X|
2251        // 1 2
2252        // |/
2253        // 0
2254        let id_0 = CommitId::from_hex("000000");
2255        let id_1 = CommitId::from_hex("111111");
2256        let id_2 = CommitId::from_hex("222222");
2257        let id_3 = CommitId::from_hex("333333");
2258        let id_4 = CommitId::from_hex("444444");
2259        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
2260        index.add_commit_data(id_1.clone(), new_change_id(), &[id_0.clone()]);
2261        index.add_commit_data(id_2.clone(), new_change_id(), &[id_0]);
2262        index.add_commit_data(id_3.clone(), new_change_id(), &[id_1.clone(), id_2.clone()]);
2263        index.add_commit_data(id_4.clone(), new_change_id(), &[id_1.clone(), id_2.clone()]);
2264
2265        let mut common_ancestors = index.common_ancestors(&[id_3], &[id_4]);
2266        common_ancestors.sort();
2267        assert_eq!(common_ancestors, vec![id_1, id_2]);
2268    }
2269
2270    #[test]
2271    fn test_common_ancestors_merge_with_ancestor() {
2272        let mut new_change_id = change_id_generator();
2273        let mut index = MutableIndex::full(3, 16);
2274        // 4   5
2275        // |\ /|
2276        // 1 2 3
2277        //  \|/
2278        //   0
2279        let id_0 = CommitId::from_hex("000000");
2280        let id_1 = CommitId::from_hex("111111");
2281        let id_2 = CommitId::from_hex("222222");
2282        let id_3 = CommitId::from_hex("333333");
2283        let id_4 = CommitId::from_hex("444444");
2284        let id_5 = CommitId::from_hex("555555");
2285        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
2286        index.add_commit_data(id_1, new_change_id(), &[id_0.clone()]);
2287        index.add_commit_data(id_2.clone(), new_change_id(), &[id_0.clone()]);
2288        index.add_commit_data(id_3, new_change_id(), &[id_0.clone()]);
2289        index.add_commit_data(id_4.clone(), new_change_id(), &[id_0.clone(), id_2.clone()]);
2290        index.add_commit_data(id_5.clone(), new_change_id(), &[id_0, id_2.clone()]);
2291
2292        let mut common_ancestors = index.common_ancestors(&[id_4], &[id_5]);
2293        common_ancestors.sort();
2294        assert_eq!(common_ancestors, vec![id_2]);
2295    }
2296
2297    #[test]
2298    fn test_walk_revs() {
2299        let mut new_change_id = change_id_generator();
2300        let mut index = MutableIndex::full(3, 16);
2301        // 5
2302        // |\
2303        // 4 | 3
2304        // | |/
2305        // 1 2
2306        // |/
2307        // 0
2308        let id_0 = CommitId::from_hex("000000");
2309        let id_1 = CommitId::from_hex("111111");
2310        let id_2 = CommitId::from_hex("222222");
2311        let id_3 = CommitId::from_hex("333333");
2312        let id_4 = CommitId::from_hex("444444");
2313        let id_5 = CommitId::from_hex("555555");
2314        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
2315        index.add_commit_data(id_1.clone(), new_change_id(), &[id_0.clone()]);
2316        index.add_commit_data(id_2.clone(), new_change_id(), &[id_0.clone()]);
2317        index.add_commit_data(id_3.clone(), new_change_id(), &[id_2.clone()]);
2318        index.add_commit_data(id_4.clone(), new_change_id(), &[id_1.clone()]);
2319        index.add_commit_data(id_5.clone(), new_change_id(), &[id_4.clone(), id_2.clone()]);
2320
2321        let walk_commit_ids = |wanted: &[CommitId], unwanted: &[CommitId]| {
2322            index
2323                .walk_revs(wanted, unwanted)
2324                .map(|entry| entry.commit_id())
2325                .collect_vec()
2326        };
2327
2328        // No wanted commits
2329        assert!(walk_commit_ids(&[], &[]).is_empty());
2330        // Simple linear walk to roo
2331        assert_eq!(
2332            walk_commit_ids(&[id_4.clone()], &[]),
2333            vec![id_4.clone(), id_1.clone(), id_0.clone()]
2334        );
2335        // Commits that are both wanted and unwanted are not walked
2336        assert_eq!(walk_commit_ids(&[id_0.clone()], &[id_0.clone()]), vec![]);
2337        // Commits that are listed twice are only walked once
2338        assert_eq!(
2339            walk_commit_ids(&[id_0.clone(), id_0.clone()], &[]),
2340            vec![id_0.clone()]
2341        );
2342        // If a commit and its ancestor are both wanted, the ancestor still gets walked
2343        // only once
2344        assert_eq!(
2345            walk_commit_ids(&[id_0.clone(), id_1.clone()], &[]),
2346            vec![id_1.clone(), id_0.clone()]
2347        );
2348        // Ancestors of both wanted and unwanted commits are not walked
2349        assert_eq!(
2350            walk_commit_ids(&[id_2.clone()], &[id_1.clone()]),
2351            vec![id_2.clone()]
2352        );
2353        // Same as above, but the opposite order, to make sure that order in index
2354        // doesn't matter
2355        assert_eq!(
2356            walk_commit_ids(&[id_1.clone()], &[id_2.clone()]),
2357            vec![id_1.clone()]
2358        );
2359        // Two wanted nodes
2360        assert_eq!(
2361            walk_commit_ids(&[id_1.clone(), id_2.clone()], &[]),
2362            vec![id_2.clone(), id_1.clone(), id_0.clone()]
2363        );
2364        // Order of output doesn't depend on order of input
2365        assert_eq!(
2366            walk_commit_ids(&[id_2.clone(), id_1.clone()], &[]),
2367            vec![id_2.clone(), id_1.clone(), id_0]
2368        );
2369        // Two wanted nodes that share an unwanted ancestor
2370        assert_eq!(
2371            walk_commit_ids(&[id_5.clone(), id_3.clone()], &[id_2]),
2372            vec![id_5, id_4, id_3, id_1]
2373        );
2374    }
2375
2376    #[test]
2377    fn test_walk_revs_filter_by_generation() {
2378        let mut new_change_id = change_id_generator();
2379        let mut index = MutableIndex::full(3, 16);
2380        // 8 6
2381        // | |
2382        // 7 5
2383        // |/|
2384        // 4 |
2385        // | 3
2386        // 2 |
2387        // |/
2388        // 1
2389        // |
2390        // 0
2391        let id_0 = CommitId::from_hex("000000");
2392        let id_1 = CommitId::from_hex("111111");
2393        let id_2 = CommitId::from_hex("222222");
2394        let id_3 = CommitId::from_hex("333333");
2395        let id_4 = CommitId::from_hex("444444");
2396        let id_5 = CommitId::from_hex("555555");
2397        let id_6 = CommitId::from_hex("666666");
2398        let id_7 = CommitId::from_hex("777777");
2399        let id_8 = CommitId::from_hex("888888");
2400        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
2401        index.add_commit_data(id_1.clone(), new_change_id(), &[id_0.clone()]);
2402        index.add_commit_data(id_2.clone(), new_change_id(), &[id_1.clone()]);
2403        index.add_commit_data(id_3.clone(), new_change_id(), &[id_1.clone()]);
2404        index.add_commit_data(id_4.clone(), new_change_id(), &[id_2.clone()]);
2405        index.add_commit_data(id_5.clone(), new_change_id(), &[id_4.clone(), id_3.clone()]);
2406        index.add_commit_data(id_6.clone(), new_change_id(), &[id_5.clone()]);
2407        index.add_commit_data(id_7.clone(), new_change_id(), &[id_4.clone()]);
2408        index.add_commit_data(id_8.clone(), new_change_id(), &[id_7.clone()]);
2409
2410        let walk_commit_ids = |wanted: &[CommitId], unwanted: &[CommitId], range: Range<u32>| {
2411            index
2412                .walk_revs(wanted, unwanted)
2413                .filter_by_generation(range)
2414                .map(|entry| entry.commit_id())
2415                .collect_vec()
2416        };
2417
2418        // Simple generation bounds
2419        assert_eq!(walk_commit_ids(&[&id_8].map(Clone::clone), &[], 0..0), []);
2420        assert_eq!(
2421            walk_commit_ids(&[&id_2].map(Clone::clone), &[], 0..3),
2422            [&id_2, &id_1, &id_0].map(Clone::clone)
2423        );
2424
2425        // Ancestors may be walked with different generations
2426        assert_eq!(
2427            walk_commit_ids(&[&id_6].map(Clone::clone), &[], 2..4),
2428            [&id_4, &id_3, &id_2, &id_1].map(Clone::clone)
2429        );
2430        assert_eq!(
2431            walk_commit_ids(&[&id_5].map(Clone::clone), &[], 2..3),
2432            [&id_2, &id_1].map(Clone::clone)
2433        );
2434        assert_eq!(
2435            walk_commit_ids(&[&id_5, &id_7].map(Clone::clone), &[], 2..3),
2436            [&id_2, &id_1].map(Clone::clone)
2437        );
2438        assert_eq!(
2439            walk_commit_ids(&[&id_7, &id_8].map(Clone::clone), &[], 0..2),
2440            [&id_8, &id_7, &id_4].map(Clone::clone)
2441        );
2442        assert_eq!(
2443            walk_commit_ids(&[&id_6, &id_7].map(Clone::clone), &[], 0..3),
2444            [&id_7, &id_6, &id_5, &id_4, &id_3, &id_2].map(Clone::clone)
2445        );
2446        assert_eq!(
2447            walk_commit_ids(&[&id_6, &id_7].map(Clone::clone), &[], 2..3),
2448            [&id_4, &id_3, &id_2].map(Clone::clone)
2449        );
2450
2451        // Ancestors of both wanted and unwanted commits are not walked
2452        assert_eq!(
2453            walk_commit_ids(&[&id_5].map(Clone::clone), &[&id_2].map(Clone::clone), 1..5),
2454            [&id_4, &id_3].map(Clone::clone)
2455        );
2456    }
2457
2458    #[test]
2459    fn test_heads() {
2460        let mut new_change_id = change_id_generator();
2461        let mut index = MutableIndex::full(3, 16);
2462        // 5
2463        // |\
2464        // 4 | 3
2465        // | |/
2466        // 1 2
2467        // |/
2468        // 0
2469        let id_0 = CommitId::from_hex("000000");
2470        let id_1 = CommitId::from_hex("111111");
2471        let id_2 = CommitId::from_hex("222222");
2472        let id_3 = CommitId::from_hex("333333");
2473        let id_4 = CommitId::from_hex("444444");
2474        let id_5 = CommitId::from_hex("555555");
2475        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
2476        index.add_commit_data(id_1.clone(), new_change_id(), &[id_0.clone()]);
2477        index.add_commit_data(id_2.clone(), new_change_id(), &[id_0.clone()]);
2478        index.add_commit_data(id_3.clone(), new_change_id(), &[id_2.clone()]);
2479        index.add_commit_data(id_4.clone(), new_change_id(), &[id_1.clone()]);
2480        index.add_commit_data(id_5.clone(), new_change_id(), &[id_4.clone(), id_2.clone()]);
2481
2482        // Empty input
2483        assert!(index.heads(&mut [].iter()).is_empty());
2484        // Single head
2485        assert_eq!(index.heads(&mut [id_4.clone()].iter()), vec![id_4.clone()]);
2486        // Single head and parent
2487        assert_eq!(
2488            index.heads(&mut [id_4.clone(), id_1].iter()),
2489            vec![id_4.clone()]
2490        );
2491        // Single head and grand-parent
2492        assert_eq!(
2493            index.heads(&mut [id_4.clone(), id_0].iter()),
2494            vec![id_4.clone()]
2495        );
2496        // Multiple heads
2497        assert_eq!(
2498            index.heads(&mut [id_4.clone(), id_3.clone()].iter()),
2499            vec![id_3.clone(), id_4]
2500        );
2501        // Merge commit and ancestors
2502        assert_eq!(
2503            index.heads(&mut [id_5.clone(), id_2].iter()),
2504            vec![id_5.clone()]
2505        );
2506        // Merge commit and other commit
2507        assert_eq!(
2508            index.heads(&mut [id_5.clone(), id_3.clone()].iter()),
2509            vec![id_3, id_5]
2510        );
2511    }
2512
2513    #[test]
2514    fn test_hex_prefix_prefixes() {
2515        let prefix = HexPrefix::new("").unwrap();
2516        assert_eq!(prefix.min_prefix_bytes(), b"");
2517
2518        let prefix = HexPrefix::new("1").unwrap();
2519        assert_eq!(prefix.min_prefix_bytes(), b"\x10");
2520
2521        let prefix = HexPrefix::new("12").unwrap();
2522        assert_eq!(prefix.min_prefix_bytes(), b"\x12");
2523
2524        let prefix = HexPrefix::new("123").unwrap();
2525        assert_eq!(prefix.min_prefix_bytes(), b"\x12\x30");
2526    }
2527
2528    #[test]
2529    fn test_hex_prefix_matches() {
2530        let id = CommitId::from_hex("1234");
2531
2532        assert!(HexPrefix::new("").unwrap().matches(&id));
2533        assert!(HexPrefix::new("1").unwrap().matches(&id));
2534        assert!(HexPrefix::new("12").unwrap().matches(&id));
2535        assert!(HexPrefix::new("123").unwrap().matches(&id));
2536        assert!(HexPrefix::new("1234").unwrap().matches(&id));
2537        assert!(!HexPrefix::new("12345").unwrap().matches(&id));
2538
2539        assert!(!HexPrefix::new("a").unwrap().matches(&id));
2540        assert!(!HexPrefix::new("1a").unwrap().matches(&id));
2541        assert!(!HexPrefix::new("12a").unwrap().matches(&id));
2542        assert!(!HexPrefix::new("123a").unwrap().matches(&id));
2543    }
2544}