|
15 | 15 | #![allow(missing_docs)] |
16 | 16 |
|
17 | 17 | use std::cmp::max; |
18 | | -use std::cmp::min; |
19 | 18 | use std::cmp::Ordering; |
20 | 19 | use std::collections::binary_heap; |
21 | 20 | use std::collections::BTreeSet; |
@@ -318,32 +317,13 @@ impl CompositeIndex { |
318 | 317 | set1: &[IndexPosition], |
319 | 318 | set2: &[IndexPosition], |
320 | 319 | ) -> BTreeSet<IndexPosition> { |
321 | | - fn shift_to_parents( |
322 | | - index: &CompositeIndex, |
323 | | - items: &mut BinaryHeap<IndexPosition>, |
324 | | - cur_pos: IndexPosition, |
325 | | - ) { |
326 | | - let mut parent_positions = index.entry_by_pos(cur_pos).parent_positions().into_iter(); |
327 | | - if let Some(parent_pos) = parent_positions.next() { |
328 | | - assert!(parent_pos < cur_pos); |
329 | | - dedup_replace(items, parent_pos).unwrap(); |
330 | | - } else { |
331 | | - dedup_pop(items).unwrap(); |
332 | | - return; |
333 | | - } |
334 | | - for parent_pos in parent_positions { |
335 | | - assert!(parent_pos < cur_pos); |
336 | | - items.push(parent_pos); |
337 | | - } |
338 | | - } |
339 | | - |
340 | 320 | let mut items1: BinaryHeap<_> = set1.iter().copied().collect(); |
341 | 321 | let mut items2: BinaryHeap<_> = set2.iter().copied().collect(); |
342 | 322 | let mut result = BTreeSet::new(); |
343 | 323 | while let (Some(&pos1), Some(&pos2)) = (items1.peek(), items2.peek()) { |
344 | 324 | match pos1.cmp(&pos2) { |
345 | | - Ordering::Greater => shift_to_parents(self, &mut items1, pos1), |
346 | | - Ordering::Less => shift_to_parents(self, &mut items2, pos2), |
| 325 | + Ordering::Greater => shift_to_parents(&mut items1, &self.entry_by_pos(pos1)), |
| 326 | + Ordering::Less => shift_to_parents(&mut items2, &self.entry_by_pos(pos2)), |
347 | 327 | Ordering::Equal => { |
348 | 328 | result.insert(pos1); |
349 | 329 | dedup_pop(&mut items1).unwrap(); |
@@ -380,44 +360,40 @@ impl CompositeIndex { |
380 | 360 | /// entries that are heads in the repository. |
381 | 361 | pub fn heads_pos( |
382 | 362 | &self, |
383 | | - mut candidate_positions: BTreeSet<IndexPosition>, |
| 363 | + candidate_positions: BTreeSet<IndexPosition>, |
384 | 364 | ) -> BTreeSet<IndexPosition> { |
385 | | - // Add all parents of the candidates to the work queue. The parents and their |
386 | | - // ancestors are not heads. |
387 | | - // Also find the smallest generation number among the candidates. |
388 | | - let mut work = Vec::new(); |
389 | | - let mut min_generation = u32::MAX; |
390 | | - for pos in &candidate_positions { |
391 | | - let entry = self.entry_by_pos(*pos); |
392 | | - min_generation = min(min_generation, entry.generation_number()); |
393 | | - work.extend(entry.parent_positions()); |
394 | | - } |
395 | | - let mut work = BinaryHeap::from(work); |
396 | | - |
397 | | - // Walk ancestors of the parents of the candidates. Remove visited commits from |
398 | | - // set of candidates. Stop walking when we have gone past the minimum |
399 | | - // candidate generation. |
400 | | - while let Some(&cur_pos) = work.peek() { |
401 | | - candidate_positions.remove(&cur_pos); |
402 | | - let entry = self.entry_by_pos(cur_pos); |
403 | | - if entry.generation_number() <= min_generation { |
404 | | - dedup_pop(&mut work).unwrap(); |
405 | | - continue; |
406 | | - } |
407 | | - let mut parent_positions = entry.parent_positions().into_iter(); |
408 | | - if let Some(parent_pos) = parent_positions.next() { |
409 | | - assert!(parent_pos < cur_pos); |
410 | | - dedup_replace(&mut work, parent_pos).unwrap(); |
411 | | - } else { |
412 | | - dedup_pop(&mut work).unwrap(); |
413 | | - continue; |
414 | | - } |
415 | | - for parent_pos in parent_positions { |
416 | | - assert!(parent_pos < cur_pos); |
417 | | - work.push(parent_pos); |
| 365 | + let Some(min_generation) = candidate_positions |
| 366 | + .iter() |
| 367 | + .map(|&pos| self.entry_by_pos(pos).generation_number()) |
| 368 | + .min() |
| 369 | + else { |
| 370 | + return BTreeSet::new(); |
| 371 | + }; |
| 372 | + |
| 373 | + // Iterate though the candidates by reverse index position, keeping track of the |
| 374 | + // ancestors of already-found heads. If a candidate is an ancestor of an |
| 375 | + // already-found head, then it can be removed. |
| 376 | + let mut parents = BinaryHeap::new(); |
| 377 | + let mut heads = BTreeSet::new(); |
| 378 | + 'outer: for candidate in candidate_positions.into_iter().rev() { |
| 379 | + while let Some(&parent) = parents.peek().filter(|&&parent| parent >= candidate) { |
| 380 | + let entry = self.entry_by_pos(parent); |
| 381 | + if entry.generation_number() <= min_generation { |
| 382 | + dedup_pop(&mut parents).unwrap(); |
| 383 | + } else { |
| 384 | + shift_to_parents(&mut parents, &entry); |
| 385 | + } |
| 386 | + if parent == candidate { |
| 387 | + // The candidate is an ancestor of an existing head, so we can skip it. |
| 388 | + continue 'outer; |
| 389 | + } |
418 | 390 | } |
| 391 | + // No parents matched, so this commit is a head. |
| 392 | + let entry = self.entry_by_pos(candidate); |
| 393 | + parents.extend(entry.parent_positions()); |
| 394 | + heads.insert(candidate); |
419 | 395 | } |
420 | | - candidate_positions |
| 396 | + heads |
421 | 397 | } |
422 | 398 |
|
423 | 399 | pub(super) fn evaluate_revset( |
@@ -595,6 +571,22 @@ pub struct IndexStats { |
595 | 571 | pub levels: Vec<IndexLevelStats>, |
596 | 572 | } |
597 | 573 |
|
| 574 | +/// Removes an entry from the queue and replace it with its parents. |
| 575 | +fn shift_to_parents(items: &mut BinaryHeap<IndexPosition>, entry: &IndexEntry) { |
| 576 | + let mut parent_positions = entry.parent_positions().into_iter(); |
| 577 | + if let Some(parent_pos) = parent_positions.next() { |
| 578 | + assert!(parent_pos < entry.position()); |
| 579 | + dedup_replace(items, parent_pos).unwrap(); |
| 580 | + } else { |
| 581 | + dedup_pop(items).unwrap(); |
| 582 | + return; |
| 583 | + } |
| 584 | + for parent_pos in parent_positions { |
| 585 | + assert!(parent_pos < entry.position()); |
| 586 | + items.push(parent_pos); |
| 587 | + } |
| 588 | +} |
| 589 | + |
598 | 590 | /// Removes the greatest items (including duplicates) from the heap, returns |
599 | 591 | /// one. |
600 | 592 | fn dedup_pop<T: Ord>(heap: &mut BinaryHeap<T>) -> Option<T> { |
|
0 commit comments