1use 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#[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
81impl 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 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 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
158pub 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 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 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 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 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 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 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 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 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 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 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 } 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 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 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 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 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 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 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 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 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 return None;
1552 }
1553 let mut low = 0;
1554 let mut high = self.num_local_commits - 1;
1555
1556 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 assert_eq!(
1925 index.resolve_prefix(&HexPrefix::new("0").unwrap()),
1926 PrefixResolution::AmbiguousMatch
1927 );
1928 assert_eq!(
1930 index.resolve_prefix(&HexPrefix::new("009").unwrap()),
1931 PrefixResolution::SingleMatch(CommitId::from_hex("009999"))
1932 );
1933 assert_eq!(
1935 index.resolve_prefix(&HexPrefix::new("03").unwrap()),
1936 PrefixResolution::SingleMatch(CommitId::from_hex("033333"))
1937 );
1938 assert_eq!(
1940 index.resolve_prefix(&HexPrefix::new("0554").unwrap()),
1941 PrefixResolution::AmbiguousMatch
1942 );
1943 }
1944
1945 #[test]
1946 #[allow(clippy::redundant_clone)] 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 assert!(walk_commit_ids(&[], &[]).is_empty());
2330 assert_eq!(
2332 walk_commit_ids(&[id_4.clone()], &[]),
2333 vec![id_4.clone(), id_1.clone(), id_0.clone()]
2334 );
2335 assert_eq!(walk_commit_ids(&[id_0.clone()], &[id_0.clone()]), vec![]);
2337 assert_eq!(
2339 walk_commit_ids(&[id_0.clone(), id_0.clone()], &[]),
2340 vec![id_0.clone()]
2341 );
2342 assert_eq!(
2345 walk_commit_ids(&[id_0.clone(), id_1.clone()], &[]),
2346 vec![id_1.clone(), id_0.clone()]
2347 );
2348 assert_eq!(
2350 walk_commit_ids(&[id_2.clone()], &[id_1.clone()]),
2351 vec![id_2.clone()]
2352 );
2353 assert_eq!(
2356 walk_commit_ids(&[id_1.clone()], &[id_2.clone()]),
2357 vec![id_1.clone()]
2358 );
2359 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 assert_eq!(
2366 walk_commit_ids(&[id_2.clone(), id_1.clone()], &[]),
2367 vec![id_2.clone(), id_1.clone(), id_0]
2368 );
2369 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 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 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 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 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 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 assert!(index.heads(&mut [].iter()).is_empty());
2484 assert_eq!(index.heads(&mut [id_4.clone()].iter()), vec![id_4.clone()]);
2486 assert_eq!(
2488 index.heads(&mut [id_4.clone(), id_1].iter()),
2489 vec![id_4.clone()]
2490 );
2491 assert_eq!(
2493 index.heads(&mut [id_4.clone(), id_0].iter()),
2494 vec![id_4.clone()]
2495 );
2496 assert_eq!(
2498 index.heads(&mut [id_4.clone(), id_3.clone()].iter()),
2499 vec![id_3.clone(), id_4]
2500 );
2501 assert_eq!(
2503 index.heads(&mut [id_5.clone(), id_2].iter()),
2504 vec![id_5.clone()]
2505 );
2506 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}