The history cache already recorded whether a commit had been loaded, but `parents_of()` returned an empty parent list for an interned frontier. This made an external AutoMerge input look like a root and required an AutoMerge- specific workaround that equated edit scope with cache completeness. Honor the existing loaded bit and move read-through ancestry lookup into `HistoryGraph`. Name traversal-only cached edges `known_parents` and limit descendant rewrites to the explicit edit scope. Looking up missing ancestry neither populates the cache nor expands permission to edit it. Extend the exact-scope regression to distinguish unloaded frontiers, loaded roots, and ancestry reads. It failed with `Some([])` before the fix and now passes, together with the external-input replay regressions.
Unified · Split
| @@ -1176,6 +1176,9 @@ views. · 1175 unmodified lines | |||
| 1176 | results by immutable commit ID. Idle application state retains detached recipes | 1176 | results by immutable commit ID. Idle application state retains detached recipes |
| 1177 | and picker data only. Pin-consuming checkouts may reload the current projection | 1177 | and picker data only. Pin-consuming checkouts may reload the current projection |
| 1178 | to determine which pins must be retained. | 1178 | to determine which pins must be retained. |
| 1179 | The graph distinguishes an unloaded frontier from a loaded root or shallow | ||
| 1180 | boundary. Reading an external input's ancestry does not expand the editable | ||
| 1181 | scope; descendant rewrites remain confined to that scope. | ||
| 1179 | 1182 | ||
| 1180 | ### Reviews | 1183 | ### Reviews |
| 1181 | 1184 | ||
| @@ -309,20 +309,6 @@ pub(crate) struct Preparation { · 308 unmodified lines | |||
| 309 | pub eager: HashSet<ObjectId>, | 309 | pub eager: HashSet<ObjectId>, |
| 310 | } | 310 | } |
| 311 | 311 | ||
| 312 | pub(crate) fn parents( | ||
| 313 | repo: &gix::Repository, | ||
| 314 | graph: &crate::history::HistoryGraph, | ||
| 315 | commit_id: ObjectId, | ||
| 316 | ) -> Result<Vec<ObjectId>> { | ||
| 317 | match graph | ||
| 318 | .parents_of(commit_id) | ||
| 319 | .filter(|_| graph.is_in_edit_scope(commit_id)) | ||
| 320 | { | ||
| 321 | Some(parents) => Ok(parents), | ||
| 322 | None => Ok(repo.find_commit(commit_id)?.parent_ids().map(gix::Id::detach).collect()), | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 326 | /// Only the checkout's ordinary first-parent path requires conflict materialization. | 312 | /// Only the checkout's ordinary first-parent path requires conflict materialization. |
| 327 | /// Inputs beneath an AutoMerge can instead remain pending and be muted. | 313 | /// Inputs beneath an AutoMerge can instead remain pending and be muted. |
| 328 | pub(crate) fn checkout_path( | 314 | pub(crate) fn checkout_path( |
| @@ -900,7 +886,8 @@ pub(crate) fn expand_plan( · 571 unmodified lines | |||
| 900 | } | 886 | } |
| 901 | let extra: Vec<_> = affected.into_iter().filter(|id| !original.contains(id)).collect(); | 887 | let extra: Vec<_> = affected.into_iter().filter(|id| !original.contains(id)).collect(); |
| 902 | for &commit_id in &extra { | 888 | for &commit_id in &extra { |
| 903 | let parent_commit_id = parents(repo, graph, commit_id)? | 889 | let parent_commit_id = graph |
| 890 | .parents_or_load(repo, commit_id)? | ||
| 904 | .first() | 891 | .first() |
| 905 | .copied() | 892 | .copied() |
| 906 | .context("a pending input has no parent")?; | 893 | .context("a pending input has no parent")?; |
| @@ -1267,7 +1267,7 @@ fn perform_inner( · 1266 unmodified lines | |||
| 1267 | pending.retain(|id| Some(*id) != root); | 1267 | pending.retain(|id| Some(*id) != root); |
| 1268 | } | 1268 | } |
| 1269 | for old_id in pending { | 1269 | for old_id in pending { |
| 1270 | let old_parents = auto_merge::parents(&repo, graph, old_id)?; | 1270 | let old_parents = graph.parents_or_load(&repo, old_id)?; |
| 1271 | let mut commit = match replacement.as_ref().filter(|_| Some(old_id) == root) { | 1271 | let mut commit = match replacement.as_ref().filter(|_| Some(old_id) == root) { |
| 1272 | Some(commit) => commit.clone(), | 1272 | Some(commit) => commit.clone(), |
| 1273 | None => repo | 1273 | None => repo |
| @@ -1546,7 +1546,7 @@ pub(super) fn finish_review_with_progress( · 272 unmodified lines | |||
| 1546 | )?; | 1546 | )?; |
| 1547 | } | 1547 | } |
| 1548 | for id in review_ids.iter().chain(&natural_ids) { | 1548 | for id in review_ids.iter().chain(&natural_ids) { |
| 1549 | if auto_merge::parents(&repo, graph, *id)?.len() > 1 | 1549 | if graph.parents_or_load(&repo, *id)?.len() > 1 |
| 1550 | && !auto_merge::is_auto_merge(&repo.find_commit(*id)?.decode()?.into_owned()?) | 1550 | && !auto_merge::is_auto_merge(&repo.find_commit(*id)?.decode()?.into_owned()?) |
| 1551 | { | 1551 | { |
| 1552 | anyhow::bail!("review finish cannot rewrite merge descendants"); | 1552 | anyhow::bail!("review finish cannot rewrite merge descendants"); |
| @@ -1607,7 +1607,7 @@ pub(super) fn finish_review_with_progress( · 54 unmodified lines | |||
| 1607 | 1607 | ||
| 1608 | rewritten.insert(tip, Some(finished_review)); | 1608 | rewritten.insert(tip, Some(finished_review)); |
| 1609 | for old in natural_ids { | 1609 | for old in natural_ids { |
| 1610 | let old_parents = auto_merge::parents(&repo, graph, old)?; | 1610 | let old_parents = graph.parents_or_load(&repo, old)?; |
| 1611 | let mut commit = repo.find_commit(old)?.decode()?.into_owned()?; | 1611 | let mut commit = repo.find_commit(old)?.decode()?.into_owned()?; |
| 1612 | if auto_merge::is_auto_merge(&commit) { | 1612 | if auto_merge::is_auto_merge(&commit) { |
| 1613 | let eager = conflict.is_none() && auto.eager.contains(&old); | 1613 | let eager = conflict.is_none() && auto.eager.contains(&old); |
| @@ -1772,7 +1772,7 @@ pub(crate) fn perform_plan_with_progress( · 158 unmodified lines | |||
| 1772 | if automatic && step.squash.contains(&id) { | 1772 | if automatic && step.squash.contains(&id) { |
| 1773 | anyhow::bail!("an AutoMerge cannot be squashed"); | 1773 | anyhow::bail!("an AutoMerge cannot be squashed"); |
| 1774 | } | 1774 | } |
| 1775 | if auto_merge::parents(&repo, graph, id)?.len() > 1 && !automatic { | 1775 | if graph.parents_or_load(&repo, id)?.len() > 1 && !automatic { |
| 1776 | anyhow::bail!("merge commits cannot be picked by the rebase editor"); | 1776 | anyhow::bail!("merge commits cannot be picked by the rebase editor"); |
| 1777 | } | 1777 | } |
| 1778 | } | 1778 | } |
| @@ -1944,7 +1944,7 @@ pub(crate) fn perform_plan_with_progress( · 165 unmodified lines | |||
| 1944 | } | 1944 | } |
| 1945 | let graph_parents = match step.commit { | 1945 | let graph_parents = match step.commit { |
| 1946 | PlanCommit::Pick(id) | PlanCommit::Copy(id) | PlanCommit::Resolved(id) => { | 1946 | PlanCommit::Pick(id) | PlanCommit::Copy(id) | PlanCommit::Resolved(id) => { |
| 1947 | auto_merge::parents(&repo, graph, id)? | 1947 | graph.parents_or_load(&repo, id)? |
| 1948 | } | 1948 | } |
| 1949 | PlanCommit::Empty(_) => vec![parent], | 1949 | PlanCommit::Empty(_) => vec![parent], |
| 1950 | }; | 1950 | }; |
| @@ -2655,7 +2655,7 @@ fn validate( · 704 unmodified lines | |||
| 2655 | tree: Tree, | 2655 | tree: Tree, |
| 2656 | ) -> Result<()> { | 2656 | ) -> Result<()> { |
| 2657 | for (position, id) in affected.iter().enumerate() { | 2657 | for (position, id) in affected.iter().enumerate() { |
| 2658 | let parents = auto_merge::parents(repo, graph, *id)?; | 2658 | let parents = graph.parents_or_load(repo, *id)?; |
| 2659 | if parents.len() > 1 | 2659 | if parents.len() > 1 |
| 2660 | && (position > 0 || removed || tree == Tree::CherryPick) | 2660 | && (position > 0 || removed || tree == Tree::CherryPick) |
| 2661 | && !auto_merge::is_auto_merge(&repo.find_commit(*id)?.decode()?.into_owned()?) | 2661 | && !auto_merge::is_auto_merge(&repo.find_commit(*id)?.decode()?.into_owned()?) |
| @@ -268,7 +268,8 @@ impl HistoryGraph { · 267 unmodified lines | |||
| 268 | self.commits[index.as_usize()].id | 268 | self.commits[index.as_usize()].id |
| 269 | } | 269 | } |
| 270 | 270 | ||
| 271 | pub(crate) fn parents(&self, index: CommitIndex) -> &[CommitIndex] { | 271 | /// Cached edges for traversal; an unloaded frontier has no known edges. |
| 272 | pub(crate) fn known_parents(&self, index: CommitIndex) -> &[CommitIndex] { | ||
| 272 | let range = self.commits[index.as_usize()].parents.clone(); | 273 | let range = self.commits[index.as_usize()].parents.clone(); |
| 273 | &self.parents[range.start as usize..range.end as usize] | 274 | &self.parents[range.start as usize..range.end as usize] |
| 274 | } | 275 | } |
| @@ -326,7 +327,7 @@ impl HistoryGraph { · 51 unmodified lines | |||
| 326 | if std::mem::replace(&mut seen[index.as_usize()], true) { | 327 | if std::mem::replace(&mut seen[index.as_usize()], true) { |
| 327 | continue; | 328 | continue; |
| 328 | } | 329 | } |
| 329 | pending.extend_from_slice(self.parents(index)); | 330 | pending.extend_from_slice(self.known_parents(index)); |
| 330 | } | 331 | } |
| 331 | false | 332 | false |
| 332 | } | 333 | } |
| @@ -344,9 +345,23 @@ impl HistoryGraph { · 11 unmodified lines | |||
| 344 | .collect() | 345 | .collect() |
| 345 | } | 346 | } |
| 346 | 347 | ||
| 348 | /// `None` means unloaded or unknown; an empty list means a loaded root or shallow boundary. | ||
| 347 | pub(crate) fn parents_of(&self, id: ObjectId) -> Option<Vec<ObjectId>> { | 349 | pub(crate) fn parents_of(&self, id: ObjectId) -> Option<Vec<ObjectId>> { |
| 348 | let index = self.index(id)?; | 350 | let index = self.index(id)?; |
| 349 | Some(self.parents(index).iter().map(|parent| self.id(*parent)).collect()) | 351 | (self.commits[index.as_usize()].state & NODE_LOADED != 0).then(|| { |
| 352 | self.known_parents(index) | ||
| 353 | .iter() | ||
| 354 | .map(|parent| self.id(*parent)) | ||
| 355 | .collect() | ||
| 356 | }) | ||
| 357 | } | ||
| 358 | |||
| 359 | /// Read missing ancestry without changing the cached graph or its editable scope. | ||
| 360 | pub(crate) fn parents_or_load(&self, repo: &gix::Repository, commit_id: ObjectId) -> Result<Vec<ObjectId>> { | ||
| 361 | match self.parents_of(commit_id) { | ||
| 362 | Some(parents) => Ok(parents), | ||
| 363 | None => Ok(repo.find_commit(commit_id)?.parent_ids().map(gix::Id::detach).collect()), | ||
| 364 | } | ||
| 350 | } | 365 | } |
| 351 | 366 | ||
| 352 | pub(crate) fn commits_with_merge_descendants(&self) -> HashSet<ObjectId> { | 367 | pub(crate) fn commits_with_merge_descendants(&self) -> HashSet<ObjectId> { |
| @@ -367,7 +382,7 @@ impl HistoryGraph { · 14 unmodified lines | |||
| 367 | while let Some(index) = pending.pop() { | 382 | while let Some(index) = pending.pop() { |
| 368 | if ancestors.insert(index) { | 383 | if ancestors.insert(index) { |
| 369 | pending.extend( | 384 | pending.extend( |
| 370 | self.parents(index) | 385 | self.known_parents(index) |
| 371 | .iter() | 386 | .iter() |
| 372 | .copied() | 387 | .copied() |
| 373 | .filter(|parent| self.commits[parent.as_usize()].state & NODE_IN_VIEW != 0), | 388 | .filter(|parent| self.commits[parent.as_usize()].state & NODE_IN_VIEW != 0), |
| @@ -378,18 +393,18 @@ impl HistoryGraph { · 4 unmodified lines | |||
| 378 | } | 393 | } |
| 379 | 394 | ||
| 380 | pub(crate) fn descendants_in_parent_order(&self, root: ObjectId) -> Option<Vec<ObjectId>> { | 395 | pub(crate) fn descendants_in_parent_order(&self, root: ObjectId) -> Option<Vec<ObjectId>> { |
| 381 | let root = self.index(root)?; | 396 | if !self.is_in_edit_scope(root) { |
| 382 | if self.commits[root.as_usize()].state & NODE_IN_VIEW == 0 { | ||
| 383 | return None; | 397 | return None; |
| 384 | } | 398 | } |
| 399 | let root = self.index(root)?; | ||
| 385 | let mut included = HashSet::from([root]); | 400 | let mut included = HashSet::from([root]); |
| 386 | loop { | 401 | loop { |
| 387 | let mut changed = false; | 402 | let mut changed = false; |
| 388 | for index in 0..self.commits.len() { | 403 | for index in 0..self.commits.len() { |
| 389 | let index = CommitIndex::new(index).expect("an existing graph index fits into u32"); | 404 | let index = CommitIndex::new(index).expect("an existing graph index fits into u32"); |
| 390 | if self.commits[index.as_usize()].state & NODE_IN_VIEW == 0 | 405 | if !self.is_in_edit_scope(self.id(index)) |
| 391 | || included.contains(&index) | 406 | || included.contains(&index) |
| 392 | || !self.parents(index).iter().any(|parent| included.contains(parent)) | 407 | || !self.known_parents(index).iter().any(|parent| included.contains(parent)) |
| 393 | { | 408 | { |
| 394 | continue; | 409 | continue; |
| 395 | } | 410 | } |
| @@ -406,7 +421,7 @@ impl HistoryGraph { · 10 unmodified lines | |||
| 406 | for index in &included { | 421 | for index in &included { |
| 407 | if out.contains(index) | 422 | if out.contains(index) |
| 408 | || self | 423 | || self |
| 409 | .parents(*index) | 424 | .known_parents(*index) |
| 410 | .iter() | 425 | .iter() |
| 411 | .any(|parent| included.contains(parent) && !out.contains(parent)) | 426 | .any(|parent| included.contains(parent) && !out.contains(parent)) |
| 412 | { | 427 | { |
| @@ -431,7 +446,7 @@ impl HistoryGraph { · 18 unmodified lines | |||
| 431 | continue; | 446 | continue; |
| 432 | } | 447 | } |
| 433 | self.commits[index.as_usize()].state |= NODE_IN_VIEW; | 448 | self.commits[index.as_usize()].state |= NODE_IN_VIEW; |
| 434 | pending.extend_from_slice(self.parents(index)); | 449 | pending.extend_from_slice(self.known_parents(index)); |
| 435 | } | 450 | } |
| 436 | } | 451 | } |
| 437 | 452 | ||
| @@ -447,7 +462,7 @@ impl HistoryGraph { · 9 unmodified lines | |||
| 447 | fn set_edit_scope(&mut self, view_tips: &[ObjectId], hidden_tips: &[ObjectId]) { | 462 | fn set_edit_scope(&mut self, view_tips: &[ObjectId], hidden_tips: &[ObjectId]) { |
| 448 | let (visible, boundary) = view_scope(view_tips, hidden_tips, |id, out| { | 463 | let (visible, boundary) = view_scope(view_tips, hidden_tips, |id, out| { |
| 449 | if let Some(index) = self.index(id) { | 464 | if let Some(index) = self.index(id) { |
| 450 | out.extend(self.parents(index).iter().map(|parent| self.id(*parent))); | 465 | out.extend(self.known_parents(index).iter().map(|parent| self.id(*parent))); |
| 451 | } | 466 | } |
| 452 | }); | 467 | }); |
| 453 | self.edit_scope = visible; | 468 | self.edit_scope = visible; |
| @@ -455,7 +470,10 @@ impl HistoryGraph { · 1 unmodified line | |||
| 455 | } | 470 | } |
| 456 | 471 | ||
| 457 | fn parent_ids(&self, index: CommitIndex) -> gix::traverse::commit::ParentIds { | 472 | fn parent_ids(&self, index: CommitIndex) -> gix::traverse::commit::ParentIds { |
| 458 | self.parents(index).iter().map(|parent| self.id(*parent)).collect() | 473 | self.known_parents(index) |
| 474 | .iter() | ||
| 475 | .map(|parent| self.id(*parent)) | ||
| 476 | .collect() | ||
| 459 | } | 477 | } |
| 460 | 478 | ||
| 461 | fn ensure_commit( | 479 | fn ensure_commit( |
| @@ -748,7 +766,7 @@ impl HistoryGraph { · 286 unmodified lines | |||
| 748 | propagated |= STALE; | 766 | propagated |= STALE; |
| 749 | flags[index.as_usize()] = propagated; | 767 | flags[index.as_usize()] = propagated; |
| 750 | } | 768 | } |
| 751 | for &parent in self.parents(index) { | 769 | for &parent in self.known_parents(index) { |
| 752 | let parent_flags = &mut flags[parent.as_usize()]; | 770 | let parent_flags = &mut flags[parent.as_usize()]; |
| 753 | let previous = *parent_flags; | 771 | let previous = *parent_flags; |
| 754 | if previous & propagated != propagated { | 772 | if previous & propagated != propagated { |
| @@ -924,7 +942,7 @@ impl HistoryGraph { · 169 unmodified lines | |||
| 924 | let stop = !should_store | 942 | let stop = !should_store |
| 925 | && self.commits[index.as_usize()].state & NODE_COMPLETE != 0 | 943 | && self.commits[index.as_usize()].state & NODE_COMPLETE != 0 |
| 926 | && (delta & EXPAND == 0 || was_stored && !expand.contains(&id)); | 944 | && (delta & EXPAND == 0 || was_stored && !expand.contains(&id)); |
| 927 | let parent_indices = self.parents(index).to_vec(); | 945 | let parent_indices = self.known_parents(index).to_vec(); |
| 928 | if should_store { | 946 | if should_store { |
| 929 | if let Some(names) = local_refs.get(&id) { | 947 | if let Some(names) = local_refs.get(&id) { |
| 930 | let tracked = resolve_tracking(repo, names)?; | 948 | let tracked = resolve_tracking(repo, names)?; |
| @@ -1180,7 +1198,7 @@ fn hidden_frontier( · 249 unmodified lines | |||
| 1180 | propagated |= STALE; | 1198 | propagated |= STALE; |
| 1181 | flags[index.as_usize()] = propagated; | 1199 | flags[index.as_usize()] = propagated; |
| 1182 | } | 1200 | } |
| 1183 | let parents = graph.parents(index).to_vec(); | 1201 | let parents = graph.known_parents(index).to_vec(); |
| 1184 | for parent in parents { | 1202 | for parent in parents { |
| 1185 | let parent_id = graph.id(parent); | 1203 | let parent_id = graph.id(parent); |
| 1186 | let parent = graph.ensure_commit(repo, cache, shallow, parent_id, &mut buf)?; | 1204 | let parent = graph.ensure_commit(repo, cache, shallow, parent_id, &mut buf)?; |
| @@ -1381,7 +1399,7 @@ pub(crate) fn load( · 194 unmodified lines | |||
| 1381 | if hidden.is_empty() { | 1399 | if hidden.is_empty() { |
| 1382 | graph.commits[index.as_usize()].state |= NODE_COMPLETE; | 1400 | graph.commits[index.as_usize()].state |= NODE_COMPLETE; |
| 1383 | } | 1401 | } |
| 1384 | let parent_indices = graph.parents(index).to_vec(); | 1402 | let parent_indices = graph.known_parents(index).to_vec(); |
| 1385 | let parent_ids = graph.parent_ids(index); | 1403 | let parent_ids = graph.parent_ids(index); |
| 1386 | let generation = graph.commits[index.as_usize()].generation(); | 1404 | let generation = graph.commits[index.as_usize()].generation(); |
| 1387 | if should_emit && let Some(names) = local_refs.get(&id) { | 1405 | if should_emit && let Some(names) = local_refs.get(&id) { |
| @@ -3355,6 +3373,33 @@ mod tests { · 1967 unmodified lines | |||
| 3355 | 3373 | ||
| 3356 | assert!(graph.is_in_edit_scope(tip), "the requested commit is in the edit scope"); | 3374 | assert!(graph.is_in_edit_scope(tip), "the requested commit is in the edit scope"); |
| 3357 | assert!(graph.index(parent).is_some(), "the requested commit's parent is known"); | 3375 | assert!(graph.index(parent).is_some(), "the requested commit's parent is known"); |
| 3376 | assert_eq!(graph.parents_of(tip), Some(vec![parent]), "loaded edges are available"); | ||
| 3377 | assert_eq!( | ||
| 3378 | graph.parents_of(parent), | ||
| 3379 | None, | ||
| 3380 | "an unloaded frontier is not a root commit" | ||
| 3381 | ); | ||
| 3382 | let root = repo.rev_parse_single("topic^^")?.detach(); | ||
| 3383 | assert_eq!( | ||
| 3384 | graph.parents_or_load(&repo, parent)?, | ||
| 3385 | [root], | ||
| 3386 | "a frontier can be read on demand" | ||
| 3387 | ); | ||
| 3388 | assert_eq!( | ||
| 3389 | graph.parents_of(parent), | ||
| 3390 | None, | ||
| 3391 | "an ancestry read does not expand the cache" | ||
| 3392 | ); | ||
| 3393 | assert_eq!( | ||
| 3394 | HistoryGraph::for_commits(&repo, &[root])?.parents_of(root), | ||
| 3395 | Some(Vec::new()), | ||
| 3396 | "a loaded root has a known empty parent list" | ||
| 3397 | ); | ||
| 3398 | assert_eq!( | ||
| 3399 | graph.descendants_in_parent_order(parent), | ||
| 3400 | None, | ||
| 3401 | "interning an ancestor does not make it an editable rewrite root" | ||
| 3402 | ); | ||
| 3358 | assert!( | 3403 | assert!( |
| 3359 | !graph.is_in_edit_scope(parent), | 3404 | !graph.is_in_edit_scope(parent), |
| 3360 | "an interned parent stays outside the explicit edit scope" | 3405 | "an interned parent stays outside the explicit edit scope" |
| @@ -3658,7 +3703,7 @@ mod tests { · 297 unmodified lines | |||
| 3658 | let repo = crate::test_repository::open(fixture.path())?; | 3703 | let repo = crate::test_repository::open(fixture.path())?; |
| 3659 | let index = graph.index(main).expect("the tracking tip was scheduled"); | 3704 | let index = graph.index(main).expect("the tracking tip was scheduled"); |
| 3660 | let fake_parent = graph.intern(id(255)).expect("the small test graph fits in u32"); | 3705 | let fake_parent = graph.intern(id(255)).expect("the small test graph fits in u32"); |
| 3661 | let mut parents = graph.parents(index).to_vec(); | 3706 | let mut parents = graph.known_parents(index).to_vec(); |
| 3662 | parents.push(fake_parent); | 3707 | parents.push(fake_parent); |
| 3663 | let start = graph.parents.len() as u32; | 3708 | let start = graph.parents.len() as u32; |
| 3664 | graph.parents.extend(parents); | 3709 | graph.parents.extend(parents); |
| @@ -998,13 +998,13 @@ impl Overview { · 997 unmodified lines | |||
| 998 | if !included.insert(index) { | 998 | if !included.insert(index) { |
| 999 | break; | 999 | break; |
| 1000 | } | 1000 | } |
| 1001 | current = graph.parents(index).first().copied(); | 1001 | current = graph.known_parents(index).first().copied(); |
| 1002 | } | 1002 | } |
| 1003 | } | 1003 | } |
| 1004 | let mut children = vec![Vec::new(); graph.commit_count()]; | 1004 | let mut children = vec![Vec::new(); graph.commit_count()]; |
| 1005 | for child in included.iter().copied() { | 1005 | for child in included.iter().copied() { |
| 1006 | if let Some(parent) = graph | 1006 | if let Some(parent) = graph |
| 1007 | .parents(child) | 1007 | .known_parents(child) |
| 1008 | .first() | 1008 | .first() |
| 1009 | .copied() | 1009 | .copied() |
| 1010 | .filter(|parent| included.contains(parent)) | 1010 | .filter(|parent| included.contains(parent)) |
| @@ -1019,7 +1019,7 @@ impl Overview { · 8 unmodified lines | |||
| 1019 | anchors.contains(index) | 1019 | anchors.contains(index) |
| 1020 | || children[index.as_usize()].len() != 1 | 1020 | || children[index.as_usize()].len() != 1 |
| 1021 | || graph | 1021 | || graph |
| 1022 | .parents(*index) | 1022 | .known_parents(*index) |
| 1023 | .first() | 1023 | .first() |
| 1024 | .is_none_or(|parent| !included.contains(parent)) | 1024 | .is_none_or(|parent| !included.contains(parent)) |
| 1025 | }) | 1025 | }) |
| @@ -1058,7 +1058,7 @@ impl Overview { · 32 unmodified lines | |||
| 1058 | let mut roots = Vec::new(); | 1058 | let mut roots = Vec::new(); |
| 1059 | for child in 0..nodes.len() { | 1059 | for child in 0..nodes.len() { |
| 1060 | let mut hidden = Vec::new(); | 1060 | let mut hidden = Vec::new(); |
| 1061 | let mut parent = graph.parents(nodes[child].commit).first().copied(); | 1061 | let mut parent = graph.known_parents(nodes[child].commit).first().copied(); |
| 1062 | while let Some(index) = parent.filter(|index| included.contains(index)) { | 1062 | while let Some(index) = parent.filter(|index| included.contains(index)) { |
| 1063 | if let Some(parent_node) = by_commit.get(&index).copied() { | 1063 | if let Some(parent_node) = by_commit.get(&index).copied() { |
| 1064 | nodes[child].parent = Some(parent_node); | 1064 | nodes[child].parent = Some(parent_node); |
| @@ -1071,7 +1071,7 @@ impl Overview { · 6 unmodified lines | |||
| 1071 | break; | 1071 | break; |
| 1072 | } | 1072 | } |
| 1073 | hidden.push(index); | 1073 | hidden.push(index); |
| 1074 | parent = graph.parents(index).first().copied(); | 1074 | parent = graph.known_parents(index).first().copied(); |
| 1075 | } | 1075 | } |
| 1076 | if nodes[child].parent.is_none() { | 1076 | if nodes[child].parent.is_none() { |
| 1077 | roots.push(child); | 1077 | roots.push(child); |
| @@ -1128,13 +1128,13 @@ impl Overlay { · 50 unmodified lines | |||
| 1128 | continue; | 1128 | continue; |
| 1129 | } | 1129 | } |
| 1130 | total += 1; | 1130 | total += 1; |
| 1131 | pending.extend_from_slice(graph.parents(index)); | 1131 | pending.extend_from_slice(graph.known_parents(index)); |
| 1132 | } | 1132 | } |
| 1133 | let mut first_parent = vec![false; graph.commit_count()]; | 1133 | let mut first_parent = vec![false; graph.commit_count()]; |
| 1134 | let mut current = Some(selected_commit); | 1134 | let mut current = Some(selected_commit); |
| 1135 | while let Some(index) = current { | 1135 | while let Some(index) = current { |
| 1136 | first_parent[index.as_usize()] = true; | 1136 | first_parent[index.as_usize()] = true; |
| 1137 | current = graph.parents(index).first().copied(); | 1137 | current = graph.known_parents(index).first().copied(); |
| 1138 | } | 1138 | } |
| 1139 | let mut counts = vec![None; overview.nodes.len()]; | 1139 | let mut counts = vec![None; overview.nodes.len()]; |
| 1140 | counts[selected] = Some(total); | 1140 | counts[selected] = Some(total); |
| @@ -1187,7 +1187,7 @@ impl Overlay { · 46 unmodified lines | |||
| 1187 | continue; | 1187 | continue; |
| 1188 | } | 1188 | } |
| 1189 | count += 1; | 1189 | count += 1; |
| 1190 | pending.extend_from_slice(graph.parents(index)); | 1190 | pending.extend_from_slice(graph.known_parents(index)); |
| 1191 | } | 1191 | } |
| 1192 | self.counts[node] = Some(count); | 1192 | self.counts[node] = Some(count); |
| 1193 | } | 1193 | } |