Infer the default hidden base for relative travel targets and exclude its boundary ancestry from navigation. This makes --to first select the oldest displayed commit above the base.
Unified · Split
| @@ -58,8 +58,10 @@ pub(super) fn run(repository: gix::Repository, args: Args) -> Result<()> { · 57 unmodified lines | ||
| 58 | 58 | let (selected, resolved_graph) = match (&args.revision, args.to) { |
| 59 | 59 | (Some(revision), None) => super::resolve_commit(&repository, revision, "time-travel destination")?, |
| 60 | 60 | (None, Some(to)) => { |
| 61 | − let graph = crate::edit::loaded_view_graph(&repository)?; | |
| 62 | − let selected = relative_destination(&repository, &graph, head_id, to)?; | |
| 61 | + let hidden = crate::history::available_hidden_revisions(&repository, &[], true)?.0; | |
| 62 | + let hidden_tips = crate::history::snapshot(&repository, &[], &hidden, false)?.hidden_tips; | |
| 63 | + let graph = crate::edit::loaded_view_graph_with_hidden(&repository, &[], &hidden)?; | |
| 64 | + let selected = relative_destination(&repository, &graph, &hidden_tips, head_id, to)?; | |
| 63 | 65 | (selected, Some(graph)) |
| 64 | 66 | } |
| 65 | 67 | _ => anyhow::bail!("exactly one time-travel destination is required"), |
| @@ -131,10 +133,14 @@ pub(super) fn run(repository: gix::Repository, args: Args) -> Result<()> { · 65 unmodified lines | ||
| 131 | 133 | fn relative_destination( |
| 132 | 134 | repository: &gix::Repository, |
| 133 | 135 | graph: &crate::history::HistoryGraph, |
| 136 | + hidden_tips: &[gix::ObjectId], | |
| 134 | 137 | head: gix::ObjectId, |
| 135 | 138 | to: To, |
| 136 | 139 | ) -> Result<gix::ObjectId> { |
| 137 | − let order = graph.stored_commit_ids().collect::<Vec<_>>(); | |
| 140 | + let order = graph | |
| 141 | + .stored_commit_ids() | |
| 142 | + .filter(|id| !hidden_tips.iter().any(|hidden| graph.is_ancestor(*id, *hidden))) | |
| 143 | + .collect::<Vec<_>>(); | |
| 138 | 144 | let stored = order.iter().copied().collect::<HashSet<_>>(); |
| 139 | 145 | if !stored.contains(&head) { |
| 140 | 146 | anyhow::bail!("HEAD is not present in the default Tix view"); |
| @@ -487,6 +493,35 @@ mod tests { · 346 unmodified lines | ||
| 487 | 493 | Ok(()) |
| 488 | 494 | } |
| 489 | 495 | |
| 496 | + #[test] | |
| 497 | + fn first_stops_above_the_inferred_hidden_base() -> gix_testtools::Result { | |
| 498 | + let fixture = gix_testtools::scripted_fixture_writable("rebase_edit.sh")?; | |
| 499 | + let path = fixture.path(); | |
| 500 | + git(path, &["config", "remote.origin.url", "."])?; | |
| 501 | + git( | |
| 502 | + path, | |
| 503 | + &["config", "remote.origin.fetch", "+refs/heads/*:refs/remotes/origin/*"], | |
| 504 | + )?; | |
| 505 | + git(path, &["update-ref", "refs/remotes/origin/main", "main"])?; | |
| 506 | + git( | |
| 507 | + path, | |
| 508 | + &["symbolic-ref", "refs/remotes/origin/HEAD", "refs/remotes/origin/main"], | |
| 509 | + )?; | |
| 510 | + git(path, &["checkout", "-q", "-b", "topic"])?; | |
| 511 | + git(path, &["commit", "-q", "--allow-empty", "-m", "first topic commit"])?; | |
| 512 | + let first = crate::test_repository::open(path)?.head_id()?.detach(); | |
| 513 | + git(path, &["commit", "-q", "--allow-empty", "-m", "topic tip"])?; | |
| 514 | + | |
| 515 | + run(crate::test_repository::open(path)?, relative_args(To::First))?; | |
| 516 | + | |
| 517 | + assert_eq!( | |
| 518 | + crate::test_repository::open(path)?.head_id()?.detach(), | |
| 519 | + first, | |
| 520 | + "first selects the oldest commit displayed above the inferred base" | |
| 521 | + ); | |
| 522 | + Ok(()) | |
| 523 | + } | |
| 524 | + | |
| 490 | 525 | #[test] |
| 491 | 526 | fn relative_target_ambiguity_lists_every_candidate() -> gix_testtools::Result { |
| 492 | 527 | let fixture = gix_testtools::scripted_fixture_writable("rebase_edit.sh")?; |