diff --git a/crates/but-graph/src/init/mod.rs b/crates/but-graph/src/init/mod.rs index c6cb530febb..4bf20af2fc0 100644 --- a/crates/but-graph/src/init/mod.rs +++ b/crates/but-graph/src/init/mod.rs @@ -34,6 +34,14 @@ pub struct Overlay { entrypoint: Entrypoint, nonoverriding_references: Vec, overriding_references: Vec, + /// A list of references that should not be picked up anymore in the + /// re-traversal. + /// + /// For example, if the `but_rebase::graph_rebase::Editor` converts a + /// `Reference` step to a `None` step which is the equivalent of running + /// `git update-ref -d`, it should no longer be part of the [`Graph`], so we + /// would list the particular reference as a dropped reference. + dropped_references: Vec, meta_branches: Vec<(gix::refs::FullName, ref_metadata::Branch)>, workspace: Option<(gix::refs::FullName, ref_metadata::Workspace)>, } diff --git a/crates/but-graph/src/init/overlay.rs b/crates/but-graph/src/init/overlay.rs index 0bf8de2337f..14da90537ee 100644 --- a/crates/but-graph/src/init/overlay.rs +++ b/crates/but-graph/src/init/overlay.rs @@ -33,6 +33,22 @@ impl Overlay { self } + /// A list of references that should not be picked up anymore in the + /// re-traversal. + /// + /// For example, if the `but_rebase::graph_rebase::Editor` converts a + /// `Reference` step to a `None` step which is the equivalent of running + /// `git update-ref -d`, it should no longer be part of the + /// [`crate::Graph`], so we would list the particular reference as a dropped + /// reference. + pub fn with_dropped_references( + mut self, + refs: impl IntoIterator, + ) -> Self { + self.dropped_references.extend(refs); + self + } + /// Override the starting position of the traversal by setting it to `id`, /// and optionally, by providing the `ref_name` that points to `id`. pub fn with_entrypoint( @@ -40,6 +56,11 @@ impl Overlay { id: gix::ObjectId, ref_name: Option, ) -> Self { + if let Some((_id, ref_name)) = self.entrypoint { + self.overriding_references + .retain(|r| Some(&r.name) != ref_name.as_ref()) + } + if let Some(ref_name) = &ref_name { self.overriding_references.push(gix::refs::Reference { name: ref_name.to_owned(), @@ -84,20 +105,30 @@ impl Overlay { let Overlay { nonoverriding_references, overriding_references, + dropped_references, meta_branches, workspace, entrypoint, } = self; + // Construct BTreeMaps with a deterministic order from left to right. + let mut or = BTreeMap::new(); + for reference in overriding_references { + if !or.contains_key(&reference.name) { + or.insert(reference.name.clone(), reference); + } + } + let mut nor = BTreeMap::new(); + for reference in nonoverriding_references { + if !nor.contains_key(&reference.name) { + nor.insert(reference.name.clone(), reference); + } + } + ( OverlayRepo { - nonoverriding_references: nonoverriding_references - .into_iter() - .map(|r| (r.name.clone(), r)) - .collect(), - overriding_references: overriding_references - .into_iter() - .map(|r| (r.name.clone(), r)) - .collect(), + nonoverriding_references: nor, + overriding_references: or, + dropped_references: dropped_references.into_iter().collect(), inner: repo, }, OverlayMetadata { @@ -116,6 +147,7 @@ pub(crate) struct OverlayRepo<'repo> { inner: &'repo gix::Repository, nonoverriding_references: NameToReference, overriding_references: NameToReference, + dropped_references: BTreeSet, } /// Note that functions with `'repo` in their return value technically leak the bare repo, and it's @@ -129,7 +161,9 @@ impl<'repo> OverlayRepo<'repo> { &self, ref_name: &gix::refs::FullNameRef, ) -> anyhow::Result>> { - if let Some(r) = self.overriding_references.get(ref_name) { + if self.dropped_references.contains(ref_name) { + Ok(None) + } else if let Some(r) = self.overriding_references.get(ref_name) { Ok(Some(r.clone().attach(self.inner))) } else if let Some(rn) = self.inner.try_find_reference(ref_name)? { Ok(Some(rn)) @@ -144,6 +178,11 @@ impl<'repo> OverlayRepo<'repo> { &self, ref_name: &gix::refs::FullNameRef, ) -> anyhow::Result> { + if self.dropped_references.contains(ref_name) { + bail!( + "Failed to find reference {ref_name} due to it being dropped in the traversal overlay" + ); + } if let Some(r) = self.overriding_references.get(ref_name) { return Ok(r.clone().attach(self.inner)); } @@ -211,9 +250,13 @@ impl<'repo> OverlayRepo<'repo> { prefixes: impl Iterator, workspace_ref_names: &[&gix::refs::FullNameRef], ) -> anyhow::Result { - let mut seen = (!self.nonoverriding_references.is_empty()).then(BTreeSet::new); + let mut seen = BTreeSet::new(); let mut ref_filter = |r: gix::Reference<'_>| -> Option<(gix::ObjectId, gix::refs::FullName)> { + if self.dropped_references.contains(r.name()) { + return None; + } + if workspace_ref_names.contains(&r.name()) { return None; } @@ -226,11 +269,7 @@ impl<'repo> OverlayRepo<'repo> { (id.detach(), r.inner.name) }; // This is only for overrides. - if let Some(seen) = seen.as_mut() { - seen.insert(name.clone()).then_some((id, name)) - } else { - Some((id, name)) - } + seen.insert(name.clone()).then_some((id, name)) }; let mut all_refs_by_id = gix::hashtable::HashMap::<_, Vec<_>>::default(); for prefix in prefixes { diff --git a/crates/but-graph/tests/graph/init/mod.rs b/crates/but-graph/tests/graph/init/mod.rs index d926e35b9bb..24c6b7f8bb4 100644 --- a/crates/but-graph/tests/graph/init/mod.rs +++ b/crates/but-graph/tests/graph/init/mod.rs @@ -932,6 +932,7 @@ fn commit_with_two_parents() -> anyhow::Result<()> { Ok(()) } +mod overlay; mod with_workspace; pub(crate) mod utils; diff --git a/crates/but-graph/tests/graph/init/overlay.rs b/crates/but-graph/tests/graph/init/overlay.rs new file mode 100644 index 00000000000..29f6751ecd0 --- /dev/null +++ b/crates/but-graph/tests/graph/init/overlay.rs @@ -0,0 +1,325 @@ +//! Some tests that explicitly test the overlay functionality + +use but_graph::{Graph, init::Overlay}; +use but_testsupport::{graph_tree, visualize_commit_graph_all}; + +use crate::init::{read_only_in_memory_scenario, standard_options}; + +#[test] +fn drop_and_add_regular_refs() -> anyhow::Result<()> { + let (repo, meta) = read_only_in_memory_scenario("four-diamond")?; + insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r" + * 8a6c109 (HEAD -> merged) Merge branch 'C' into merged + |\ + | * 7ed512a (C) Merge branch 'D' into C + | |\ + | | * ecb1877 (D) D + | * | 35ee481 C + | |/ + * | 62b409a (A) Merge branch 'B' into A + |\ \ + | * | f16dddf (B) B + | |/ + * / 592abec A + |/ + * 965998b (main) base + "); + + let graph = Graph::from_head(&repo, &*meta, standard_options())?; + insta::assert_snapshot!(graph_tree(&graph), @r" + + └── πŸ‘‰β–Ί:0[0]:merged[🌳] + └── Β·8a6c109 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:A + β”‚ └── Β·62b409a (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:3[2]:anon: + β”‚ β”‚ └── Β·592abec (βŒ‚|1) + β”‚ β”‚ └── β–Ί:7[3]:main + β”‚ β”‚ └── Β·965998b (βŒ‚|1) + β”‚ └── β–Ί:4[2]:B + β”‚ └── Β·f16dddf (βŒ‚|1) + β”‚ └── β†’:7: (main) + └── β–Ί:2[1]:C + └── Β·7ed512a (βŒ‚|1) + β”œβ”€β”€ β–Ί:5[2]:anon: + β”‚ └── Β·35ee481 (βŒ‚|1) + β”‚ └── β†’:7: (main) + └── β–Ί:6[2]:D + └── Β·ecb1877 (βŒ‚|1) + └── β†’:7: (main) + "); + + let to_reference = repo.rev_parse_single("35ee481")?; + + let overlay = Overlay::default() + .with_references([gix::refs::Reference { + name: "refs/heads/new-reference".try_into()?, + target: gix::refs::Target::Object(to_reference.detach()), + peeled: Some(to_reference.detach()), + }]) + .with_dropped_references(["refs/heads/C".try_into()?]); + + let graph = graph.redo_traversal_with_overlay(&repo, &*meta, overlay)?; + + insta::assert_snapshot!(graph_tree(&graph), @" + + └── πŸ‘‰β–Ί:0[0]:merged[🌳] + └── Β·8a6c109 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:A + β”‚ └── Β·62b409a (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:3[2]:anon: + β”‚ β”‚ └── Β·592abec (βŒ‚|1) + β”‚ β”‚ └── β–Ί:7[3]:main + β”‚ β”‚ └── Β·965998b (βŒ‚|1) + β”‚ └── β–Ί:4[2]:B + β”‚ └── Β·f16dddf (βŒ‚|1) + β”‚ └── β†’:7: (main) + └── β–Ί:2[1]:anon: + └── Β·7ed512a (βŒ‚|1) + β”œβ”€β”€ β–Ί:5[2]:new-reference + β”‚ └── Β·35ee481 (βŒ‚|1) + β”‚ └── β†’:7: (main) + └── β–Ί:6[2]:D + └── Β·ecb1877 (βŒ‚|1) + └── β†’:7: (main) + "); + + Ok(()) +} + +#[test] +fn drop_head_ref() -> anyhow::Result<()> { + let (repo, meta) = read_only_in_memory_scenario("four-diamond")?; + insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r" + * 8a6c109 (HEAD -> merged) Merge branch 'C' into merged + |\ + | * 7ed512a (C) Merge branch 'D' into C + | |\ + | | * ecb1877 (D) D + | * | 35ee481 C + | |/ + * | 62b409a (A) Merge branch 'B' into A + |\ \ + | * | f16dddf (B) B + | |/ + * / 592abec A + |/ + * 965998b (main) base + "); + + let graph = Graph::from_head(&repo, &*meta, standard_options())?; + insta::assert_snapshot!(graph_tree(&graph), @r" + + └── πŸ‘‰β–Ί:0[0]:merged[🌳] + └── Β·8a6c109 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:A + β”‚ └── Β·62b409a (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:3[2]:anon: + β”‚ β”‚ └── Β·592abec (βŒ‚|1) + β”‚ β”‚ └── β–Ί:7[3]:main + β”‚ β”‚ └── Β·965998b (βŒ‚|1) + β”‚ └── β–Ί:4[2]:B + β”‚ └── Β·f16dddf (βŒ‚|1) + β”‚ └── β†’:7: (main) + └── β–Ί:2[1]:C + └── Β·7ed512a (βŒ‚|1) + β”œβ”€β”€ β–Ί:5[2]:anon: + β”‚ └── Β·35ee481 (βŒ‚|1) + β”‚ └── β†’:7: (main) + └── β–Ί:6[2]:D + └── Β·ecb1877 (βŒ‚|1) + └── β†’:7: (main) + "); + + let overlay = Overlay::default().with_dropped_references(["refs/heads/merged".try_into()?]); + + let graph = graph.redo_traversal_with_overlay(&repo, &*meta, overlay)?; + + insta::assert_snapshot!(graph_tree(&graph), @" + + └── β–Ί:0[0]:anon: + └── πŸ‘‰Β·8a6c109 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:A + β”‚ └── Β·62b409a (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:3[2]:anon: + β”‚ β”‚ └── Β·592abec (βŒ‚|1) + β”‚ β”‚ └── β–Ί:7[3]:main + β”‚ β”‚ └── Β·965998b (βŒ‚|1) + β”‚ └── β–Ί:4[2]:B + β”‚ └── Β·f16dddf (βŒ‚|1) + β”‚ └── β†’:7: (main) + └── β–Ί:2[1]:C + └── Β·7ed512a (βŒ‚|1) + β”œβ”€β”€ β–Ί:5[2]:anon: + β”‚ └── Β·35ee481 (βŒ‚|1) + β”‚ └── β†’:7: (main) + └── β–Ί:6[2]:D + └── Β·ecb1877 (βŒ‚|1) + └── β†’:7: (main) + "); + + Ok(()) +} + +#[test] +fn overriding_references() -> anyhow::Result<()> { + let (repo, meta) = read_only_in_memory_scenario("four-diamond")?; + insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r" + * 8a6c109 (HEAD -> merged) Merge branch 'C' into merged + |\ + | * 7ed512a (C) Merge branch 'D' into C + | |\ + | | * ecb1877 (D) D + | * | 35ee481 C + | |/ + * | 62b409a (A) Merge branch 'B' into A + |\ \ + | * | f16dddf (B) B + | |/ + * / 592abec A + |/ + * 965998b (main) base + "); + + let graph = Graph::from_head(&repo, &*meta, standard_options())?; + insta::assert_snapshot!(graph_tree(&graph), @r" + + └── πŸ‘‰β–Ί:0[0]:merged[🌳] + └── Β·8a6c109 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:A + β”‚ └── Β·62b409a (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:3[2]:anon: + β”‚ β”‚ └── Β·592abec (βŒ‚|1) + β”‚ β”‚ └── β–Ί:7[3]:main + β”‚ β”‚ └── Β·965998b (βŒ‚|1) + β”‚ └── β–Ί:4[2]:B + β”‚ └── Β·f16dddf (βŒ‚|1) + β”‚ └── β†’:7: (main) + └── β–Ί:2[1]:C + └── Β·7ed512a (βŒ‚|1) + β”œβ”€β”€ β–Ί:5[2]:anon: + β”‚ └── Β·35ee481 (βŒ‚|1) + β”‚ └── β†’:7: (main) + └── β–Ί:6[2]:D + └── Β·ecb1877 (βŒ‚|1) + └── β†’:7: (main) + "); + + let merged_a = repo.rev_parse_single("35ee481")?; + let merged_b = repo.rev_parse_single("592abec")?; + let merged: gix::refs::FullName = "refs/heads/merged".try_into()?; + + // The dropped takes precedence over git or overriding references. + let overlay = Overlay::default() + .with_dropped_references([merged.clone()]) + .with_references([ + gix::refs::Reference { + name: merged.clone(), + target: gix::refs::Target::Object(merged_a.detach()), + peeled: Some(merged_a.detach()), + }, + gix::refs::Reference { + name: merged.clone(), + target: gix::refs::Target::Object(merged_b.detach()), + peeled: Some(merged_b.detach()), + }, + ]); + + let graph = graph.redo_traversal_with_overlay(&repo, &*meta, overlay)?; + + insta::assert_snapshot!(graph_tree(&graph), @" + + └── β–Ί:0[0]:anon: + └── πŸ‘‰Β·8a6c109 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:A + β”‚ └── Β·62b409a (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:3[2]:anon: + β”‚ β”‚ └── Β·592abec (βŒ‚|1) + β”‚ β”‚ └── β–Ί:7[3]:main + β”‚ β”‚ └── Β·965998b (βŒ‚|1) + β”‚ └── β–Ί:4[2]:B + β”‚ └── Β·f16dddf (βŒ‚|1) + β”‚ └── β†’:7: (main) + └── β–Ί:2[1]:C + └── Β·7ed512a (βŒ‚|1) + β”œβ”€β”€ β–Ί:5[2]:anon: + β”‚ └── Β·35ee481 (βŒ‚|1) + β”‚ └── β†’:7: (main) + └── β–Ί:6[2]:D + └── Β·ecb1877 (βŒ‚|1) + └── β†’:7: (main) + "); + + // The first overriding reference precedence over git or other overriding references. + let overlay = Overlay::default().with_references([ + gix::refs::Reference { + name: merged.clone(), + target: gix::refs::Target::Object(merged_a.detach()), + peeled: Some(merged_a.detach()), + }, + gix::refs::Reference { + name: merged.clone(), + target: gix::refs::Target::Object(merged_b.detach()), + peeled: Some(merged_b.detach()), + }, + ]); + + let graph = graph.redo_traversal_with_overlay(&repo, &*meta, overlay)?; + + insta::assert_snapshot!(graph_tree(&graph), @" + + └── β–Ί:0[0]:anon: + └── πŸ‘‰Β·8a6c109 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:A + β”‚ └── Β·62b409a (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:3[2]:anon: + β”‚ β”‚ └── Β·592abec (βŒ‚|1) + β”‚ β”‚ └── β–Ί:7[3]:main + β”‚ β”‚ └── Β·965998b (βŒ‚|1) + β”‚ └── β–Ί:4[2]:B + β”‚ └── Β·f16dddf (βŒ‚|1) + β”‚ └── β†’:7: (main) + └── β–Ί:2[1]:C + └── Β·7ed512a (βŒ‚|1) + β”œβ”€β”€ β–Ί:5[2]:merged[🌳] + β”‚ └── Β·35ee481 (βŒ‚|1) + β”‚ └── β†’:7: (main) + └── β–Ί:6[2]:D + └── Β·ecb1877 (βŒ‚|1) + └── β†’:7: (main) + "); + + // overriding references take precedence over git. + let overlay = Overlay::default().with_references([gix::refs::Reference { + name: merged.clone(), + target: gix::refs::Target::Object(merged_b.detach()), + peeled: Some(merged_b.detach()), + }]); + + let graph = graph.redo_traversal_with_overlay(&repo, &*meta, overlay)?; + + insta::assert_snapshot!(graph_tree(&graph), @" + + └── β–Ί:0[0]:anon: + └── πŸ‘‰Β·8a6c109 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:A + β”‚ └── Β·62b409a (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:3[2]:merged[🌳] + β”‚ β”‚ └── Β·592abec (βŒ‚|1) + β”‚ β”‚ └── β–Ί:7[3]:main + β”‚ β”‚ └── Β·965998b (βŒ‚|1) + β”‚ └── β–Ί:4[2]:B + β”‚ └── Β·f16dddf (βŒ‚|1) + β”‚ └── β†’:7: (main) + └── β–Ί:2[1]:C + └── Β·7ed512a (βŒ‚|1) + β”œβ”€β”€ β–Ί:5[2]:anon: + β”‚ └── Β·35ee481 (βŒ‚|1) + β”‚ └── β†’:7: (main) + └── β–Ί:6[2]:D + └── Β·ecb1877 (βŒ‚|1) + └── β†’:7: (main) + "); + + Ok(()) +} diff --git a/crates/but-rebase/src/graph_rebase/mod.rs b/crates/but-rebase/src/graph_rebase/mod.rs index b0cbde30d41..7438e4f12fe 100644 --- a/crates/but-rebase/src/graph_rebase/mod.rs +++ b/crates/but-rebase/src/graph_rebase/mod.rs @@ -10,7 +10,10 @@ use std::collections::{BTreeMap, HashMap}; use anyhow::{Context, Result, bail}; use but_core::RefMetadata; +use but_graph::init::Overlay; use gix::refs::transaction::RefEdit; + +use crate::graph_rebase::util::collect_ordered_parents; pub mod cherry_pick; pub mod commit; pub mod materialize; @@ -211,7 +214,7 @@ pub struct Editor<'ws, 'meta, M: RefMetadata> { /// Provides data about how the editor instance was transformed. history: RevisionHistory, /// A reference to the workspace that the editor was created for. - pub workspace: &'ws mut but_graph::projection::Workspace, + workspace: &'ws mut but_graph::projection::Workspace, /// A reference to the metadata that the editor was created for. meta: &'meta mut M, } @@ -235,6 +238,68 @@ pub struct SuccessfulRebase<'ws, 'meta, M: RefMetadata> { meta: &'meta mut M, } +impl<'ws, 'meta, M: RefMetadata> SuccessfulRebase<'ws, 'meta, M> { + /// Returns a preview of what the but-graph will look like after + /// materialization. + /// + /// Any objects referenced in the resulting graph must be accessed via the + /// in-memory repository owned by this [`SuccessfulRebase`] (`self.repo`), + /// since they might exist only in memory. + pub fn overlayed_graph(&self) -> Result { + let dropped_refs = self.ref_edits.iter().filter_map(|edit| match &edit.change { + gix::refs::transaction::Change::Delete { .. } => Some(edit.name.clone()), + _ => None, + }); + let updated_refs = self.ref_edits.iter().filter_map(|edit| match &edit.change { + gix::refs::transaction::Change::Update { new, .. } => Some(gix::refs::Reference { + name: edit.name.clone(), + target: new.clone(), + // TODO(CTO): Peeled is only relevant for symbolic refs? + peeled: None, + }), + _ => None, + }); + + let Some((entrypoint_id, entrypoint_refname)) = self + .checkouts + .iter() + .filter_map(|checkout| match checkout { + Checkout::Head(selector) => { + let selector = self.history.normalize_selector(*selector).ok()?; + let step = &self.graph[selector.id]; + + match step { + Step::None => None, + Step::Pick(Pick { id, .. }) => Some((*id, None)), + Step::Reference { refname } => { + let parents = collect_ordered_parents(&self.graph, selector.id); + + if let Some(to_reference) = parents.first() + && let Step::Pick(Pick { id, .. }) = self.graph[*to_reference] + { + Some((id, Some(refname.clone()))) + } else { + None + } + } + } + } + }) + .next() + else { + bail!("BUG: Tried to construct rebase engine graph overlay with no entrypoints"); + }; + + let overlay = Overlay::default() + .with_references(updated_refs) + .with_dropped_references(dropped_refs) + .with_entrypoint(entrypoint_id, entrypoint_refname); + self.workspace + .graph + .redo_traversal_with_overlay(&self.repo, self.meta, overlay) + } +} + /// The outcome of a materialize #[derive(Debug)] pub struct MaterializeOutcome<'ws, 'meta, M: RefMetadata> { diff --git a/crates/but-rebase/tests/rebase/graph_rebase/conflictable_restriction.rs b/crates/but-rebase/tests/rebase/graph_rebase/conflictable_restriction.rs index 3d574f7a98c..e5e383d85e1 100644 --- a/crates/but-rebase/tests/rebase/graph_rebase/conflictable_restriction.rs +++ b/crates/but-rebase/tests/rebase/graph_rebase/conflictable_restriction.rs @@ -6,7 +6,7 @@ use but_rebase::{ commit::DateMode, graph_rebase::{Editor, LookupStep, Step, mutate::InsertSide}, }; -use but_testsupport::{cat_commit, visualize_commit_graph_all}; +use but_testsupport::{cat_commit, graph_tree, visualize_commit_graph_all}; use crate::utils::{fixture_writable, standard_options}; @@ -32,7 +32,17 @@ fn by_default_conflicts_are_allowed() -> Result<()> { editor.replace(b_sel, Step::None)?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + β”œβ”€β”€ Β·3411540 (βŒ‚|1) β–Ίc + └── Β·5e0ba46 (βŒ‚|1) β–Ίa, β–Ίb + └── β–Ί:1[1]:base + └── Β·6155f21 (βŒ‚|1) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); // We expect to see conflicted headers insta::assert_snapshot!(cat_commit(&repo, "c")?, @" @@ -126,7 +136,21 @@ fn if_a_commit_has_been_configured_not_to_conflict_and_doesnt_end_up_conflicted_ editor.replace(c_sel, Step::Pick(c_pick))?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + └── Β·00c31ec (βŒ‚|1) β–Ίc + └── β–Ί:1[1]:b + β”œβ”€β”€ Β·7762cf9 (βŒ‚|1) + └── Β·3b3bd41 (βŒ‚|1) + └── β–Ί:2[2]:a + └── Β·5e0ba46 (βŒ‚|1) + └── β–Ί:3[3]:base + └── Β·6155f21 (βŒ‚|1) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); // The rebase is successful because `c` remained unconflicted insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @" diff --git a/crates/but-rebase/tests/rebase/graph_rebase/disconnect.rs b/crates/but-rebase/tests/rebase/graph_rebase/disconnect.rs index 263e63a3160..14716fb1d1b 100644 --- a/crates/but-rebase/tests/rebase/graph_rebase/disconnect.rs +++ b/crates/but-rebase/tests/rebase/graph_rebase/disconnect.rs @@ -4,7 +4,7 @@ use std::collections::HashSet; use anyhow::{Context, Result}; use but_graph::Graph; use but_rebase::graph_rebase::{Editor, Step, mutate}; -use but_testsupport::{git_status, visualize_commit_graph_all}; +use but_testsupport::{git_status, graph_tree, visualize_commit_graph_all}; use gix::prelude::ObjectIdExt; use crate::utils::{fixture_writable, standard_options}; @@ -44,7 +44,16 @@ fn disconnect_and_remove_middle_commit_in_linear_history() -> Result<()> { editor.replace(b_selector, Step::None)?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + β”œβ”€β”€ Β·4de0144 (βŒ‚|1) + β”œβ”€β”€ Β·d591dfe (βŒ‚|1) + └── Β·35b8235 (βŒ‚|1) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r" * 4de0144 (HEAD -> main) c @@ -96,7 +105,15 @@ fn disconnect_and_remove_two_middle_commits_in_linear_history() -> Result<()> { editor.replace(a_selector, Step::None)?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + β”œβ”€β”€ Β·f55e07c (βŒ‚|1) + └── Β·35b8235 (βŒ‚|1) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @" * f55e07c (HEAD -> main) c @@ -145,7 +162,21 @@ fn disconnect_and_remove_commit_in_merge_history_rewires_children() -> Result<() editor.replace(a_selector, Step::None)?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:with-inner-merge[🌳] + └── Β·dde6cc8 (βŒ‚|1) + └── β–Ί:1[1]:anon: + └── Β·5f962e2 (βŒ‚|1) + β”œβ”€β”€ β–Ί:2[3]:anon: + β”‚ └── Β·8f0d338 (βŒ‚|1) β–ΊA, β–Ίmain, β–Ίtags/base + └── β–Ί:3[2]:B + └── Β·984fd1c (βŒ‚|1) + └── β†’:2: + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); let a_now = repo.rev_parse_single("A")?.detach(); let base = repo.rev_parse_single("base")?.detach(); @@ -206,7 +237,27 @@ fn disconnect_and_remove_merge_with_two_parents_and_two_children() -> Result<()> editor.replace(merge_selector, Step::None)?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:with-two-children[🌳] + └── Β·f914957 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:C1 + β”‚ └── Β·d8cc9ec (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:3[2]:anon: + β”‚ β”‚ └── Β·bc0e772 (βŒ‚|1) β–ΊM, β–ΊP1 + β”‚ β”‚ └── β–Ί:5[3]:main + β”‚ β”‚ └── Β·7674a5e (βŒ‚|1) β–Ίtags/base + β”‚ └── β–Ί:4[2]:P2 + β”‚ └── Β·392a8f8 (βŒ‚|1) + β”‚ └── β†’:5: (main) + └── β–Ί:2[1]:C2 + └── Β·72b8072 (βŒ‚|1) + β”œβ”€β”€ β†’:3: + └── β†’:4: (P2) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); let p1 = repo.rev_parse_single("P1")?.detach(); let p2 = repo.rev_parse_single("P2")?.detach(); @@ -311,7 +362,27 @@ fn disconnect_and_remove_merge_with_two_parents_and_two_children_from_one_side() )?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:with-two-children[🌳] + └── Β·3305e26 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:C1 + β”‚ └── Β·f928700 (βŒ‚|1) + β”‚ └── β–Ί:3[2]:P1 + β”‚ └── Β·bc0e772 (βŒ‚|1) + β”‚ └── β–Ί:5[4]:main + β”‚ └── Β·7674a5e (βŒ‚|1) β–Ίtags/base + └── β–Ί:2[1]:C2 + └── Β·0e87cd3 (βŒ‚|1) + └── β–Ί:4[2]:M + └── Β·3089592 (βŒ‚|1) + └── β–Ί:6[3]:P2 + └── Β·392a8f8 (βŒ‚|1) + └── β†’:5: (main) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); let p1 = repo.rev_parse_single("P1")?.detach(); let m = repo.rev_parse_single("M")?.detach(); @@ -408,7 +479,25 @@ fn disconnect_remove_merge_with_two_parents_and_two_children_children_only() -> )?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:with-two-children[🌳] + └── Β·2eac185 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:C1 + β”‚ └── Β·76e6d3c (βŒ‚|1) + β”‚ └── β–Ί:3[2]:M + β”‚ └── Β·3089592 (βŒ‚|1) + β”‚ └── β–Ί:4[3]:P2 + β”‚ └── Β·392a8f8 (βŒ‚|1) + β”‚ └── β–Ί:5[4]:main + β”‚ └── Β·7674a5e (βŒ‚|1) β–Ίtags/base + └── β–Ί:2[1]:C2 + └── Β·0e87cd3 (βŒ‚|1) + └── β†’:3: (M) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); let p1 = repo.rev_parse_single("P1")?.detach(); let p2 = repo.rev_parse_single("P2")?.detach(); @@ -525,7 +614,28 @@ fn disconnect_fails_when_parents_to_disconnect_is_none() -> Result<()> { ); let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:with-two-children[🌳] + └── Β·d1cc4c7 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:C1 + β”‚ └── Β·f94f259 (βŒ‚|1) + β”‚ └── β–Ί:3[2]:M + β”‚ └── Β·c5d1178 (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:4[3]:P1 + β”‚ β”‚ └── Β·bc0e772 (βŒ‚|1) + β”‚ β”‚ └── β–Ί:6[4]:main + β”‚ β”‚ └── Β·7674a5e (βŒ‚|1) β–Ίtags/base + β”‚ └── β–Ί:5[3]:P2 + β”‚ └── Β·392a8f8 (βŒ‚|1) + β”‚ └── β†’:6: (main) + └── β–Ί:2[1]:C2 + └── Β·ce6aca9 (βŒ‚|1) + └── β†’:3: (M) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); let after = visualize_commit_graph_all(&repo)?; assert_eq!(before, after, "graph should remain unchanged on failure"); @@ -576,7 +686,28 @@ fn disconnect_fails_fast_if_parent_to_disconnect_is_not_direct_parent() -> Resul ); let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:with-two-children[🌳] + └── Β·d1cc4c7 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:C1 + β”‚ └── Β·f94f259 (βŒ‚|1) + β”‚ └── β–Ί:3[2]:M + β”‚ └── Β·c5d1178 (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:4[3]:P1 + β”‚ β”‚ └── Β·bc0e772 (βŒ‚|1) + β”‚ β”‚ └── β–Ί:6[4]:main + β”‚ β”‚ └── Β·7674a5e (βŒ‚|1) β–Ίtags/base + β”‚ └── β–Ί:5[3]:P2 + β”‚ └── Β·392a8f8 (βŒ‚|1) + β”‚ └── β†’:6: (main) + └── β–Ί:2[1]:C2 + └── Β·ce6aca9 (βŒ‚|1) + └── β†’:3: (M) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); let after = visualize_commit_graph_all(&repo)?; assert_eq!(before, after, "graph should remain unchanged on failure"); @@ -627,7 +758,28 @@ fn disconnect_fails_fast_if_child_to_disconnect_is_not_direct_child() -> Result< ); let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:with-two-children[🌳] + └── Β·d1cc4c7 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:C1 + β”‚ └── Β·f94f259 (βŒ‚|1) + β”‚ └── β–Ί:3[2]:M + β”‚ └── Β·c5d1178 (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:4[3]:P1 + β”‚ β”‚ └── Β·bc0e772 (βŒ‚|1) + β”‚ β”‚ └── β–Ί:6[4]:main + β”‚ β”‚ └── Β·7674a5e (βŒ‚|1) β–Ίtags/base + β”‚ └── β–Ί:5[3]:P2 + β”‚ └── Β·392a8f8 (βŒ‚|1) + β”‚ └── β†’:6: (main) + └── β–Ί:2[1]:C2 + └── Β·ce6aca9 (βŒ‚|1) + └── β†’:3: (M) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); let after = visualize_commit_graph_all(&repo)?; assert_eq!(before, after, "graph should remain unchanged on failure"); diff --git a/crates/but-rebase/tests/rebase/graph_rebase/insert.rs b/crates/but-rebase/tests/rebase/graph_rebase/insert.rs index 17f8698b0ff..0d3e5707575 100644 --- a/crates/but-rebase/tests/rebase/graph_rebase/insert.rs +++ b/crates/but-rebase/tests/rebase/graph_rebase/insert.rs @@ -2,7 +2,7 @@ use anyhow::{Context, Result}; use but_graph::Graph; use but_rebase::graph_rebase::{Editor, Step, mutate::InsertSide}; -use but_testsupport::{git_status, visualize_commit_graph_all}; +use but_testsupport::{git_status, graph_tree, visualize_commit_graph_all}; use crate::utils::{fixture_writable, standard_options}; @@ -43,7 +43,24 @@ fn insert_below_merge_commit() -> Result<()> { editor.insert(selector, Step::new_pick(new_commit), InsertSide::Below)?; let outcome = editor.rebase()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:with-inner-merge[🌳] + β”œβ”€β”€ Β·ceb4158 (βŒ‚|1) + └── Β·ea55b6e (βŒ‚|1) + └── β–Ί:1[1]:anon: + └── Β·ec48031 (βŒ‚|1) + β”œβ”€β”€ β–Ί:2[2]:A + β”‚ └── Β·add59d2 (βŒ‚|1) + β”‚ └── β–Ί:4[3]:main + β”‚ └── Β·8f0d338 (βŒ‚|1) β–Ίtags/base + └── β–Ί:3[2]:B + └── Β·984fd1c (βŒ‚|1) + └── β†’:4: (main) + "); let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r" * ceb4158 (HEAD -> with-inner-merge) on top of inner merge @@ -108,7 +125,24 @@ fn insert_below_merge_commit_excluded_mappings() -> Result<()> { )?; let outcome = editor.rebase()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:with-inner-merge[🌳] + β”œβ”€β”€ Β·ceb4158 (βŒ‚|1) + └── Β·ea55b6e (βŒ‚|1) + └── β–Ί:1[1]:anon: + └── Β·ec48031 (βŒ‚|1) + β”œβ”€β”€ β–Ί:2[2]:A + β”‚ └── Β·add59d2 (βŒ‚|1) + β”‚ └── β–Ί:4[3]:main + β”‚ └── Β·8f0d338 (βŒ‚|1) β–Ίtags/base + └── β–Ί:3[2]:B + └── Β·984fd1c (βŒ‚|1) + └── β†’:4: (main) + "); let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r" * ceb4158 (HEAD -> with-inner-merge) on top of inner merge @@ -168,7 +202,24 @@ fn insert_above_commit_with_two_children() -> Result<()> { editor.insert(selector, Step::new_pick(new_commit), InsertSide::Above)?; let outcome = editor.rebase()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:with-inner-merge[🌳] + └── Β·9d2b9d9 (βŒ‚|1) + └── β–Ί:1[1]:anon: + └── Β·8502201 (βŒ‚|1) + β”œβ”€β”€ β–Ί:2[2]:A + β”‚ └── Β·0379d6c (βŒ‚|1) + β”‚ └── β–Ί:4[3]:main + β”‚ β”œβ”€β”€ Β·055ead5 (βŒ‚|1) β–Ίtags/base + β”‚ └── Β·8f0d338 (βŒ‚|1) + └── β–Ί:3[2]:B + └── Β·97c7cc6 (βŒ‚|1) + └── β†’:4: (main) + "); let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r" * 9d2b9d9 (HEAD -> with-inner-merge) on top of inner merge diff --git a/crates/but-rebase/tests/rebase/graph_rebase/insert_segment.rs b/crates/but-rebase/tests/rebase/graph_rebase/insert_segment.rs index 556f27a8bbe..2bb4eca960a 100644 --- a/crates/but-rebase/tests/rebase/graph_rebase/insert_segment.rs +++ b/crates/but-rebase/tests/rebase/graph_rebase/insert_segment.rs @@ -2,7 +2,7 @@ use anyhow::{Context, Result}; use but_graph::Graph; use but_rebase::graph_rebase::{Editor, mutate}; -use but_testsupport::{git_status, visualize_commit_graph, visualize_commit_graph_all}; +use but_testsupport::{git_status, graph_tree, visualize_commit_graph, visualize_commit_graph_all}; use crate::utils::{fixture_writable, standard_options}; @@ -45,7 +45,27 @@ fn insert_single_node_segment_above() -> Result<()> { editor.insert_segment(b_selector, delimiter, mutate::InsertSide::Above)?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + └── Β·5ae394a (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:A + β”‚ └── Β·77b07be (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:3[3]:anon: + β”‚ β”‚ └── Β·8f0d338 (βŒ‚|1) β–Ίtags/base + β”‚ └── β–Ί:4[2]:B + β”‚ β”œβ”€β”€ Β·a748762 (βŒ‚|1) + β”‚ └── Β·62e05ba (βŒ‚|1) + β”‚ └── β†’:3: + └── β–Ί:2[1]:C + β”œβ”€β”€ Β·930563a (βŒ‚|1) + β”œβ”€β”€ Β·68a2fc3 (βŒ‚|1) + └── Β·984fd1c (βŒ‚|1) + └── β†’:3: + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r" * 5ae394a (HEAD -> main) Merge branches 'A', 'B' and 'C' @@ -106,7 +126,29 @@ fn insert_single_node_segment_below() -> Result<()> { editor.insert_segment(b_selector, delimiter, mutate::InsertSide::Below)?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + └── Β·e22f751 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[2]:A + β”‚ └── Β·507ce96 (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:4[4]:anon: + β”‚ β”‚ └── Β·8f0d338 (βŒ‚|1) β–Ίtags/base + β”‚ └── β–Ί:5[3]:anon: + β”‚ └── Β·62e05ba (βŒ‚|1) + β”‚ └── β†’:4: + β”œβ”€β”€ β–Ί:2[1]:B + β”‚ └── Β·743ea2e (βŒ‚|1) + β”‚ └── β†’:1: (A) + └── β–Ί:3[1]:C + β”œβ”€β”€ Β·930563a (βŒ‚|1) + β”œβ”€β”€ Β·68a2fc3 (βŒ‚|1) + └── Β·984fd1c (βŒ‚|1) + └── β†’:4: + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r" *-. e22f751 (HEAD -> main) Merge branches 'A', 'B' and 'C' @@ -172,7 +214,28 @@ fn insert_multi_node_segment_above() -> Result<()> { editor.insert_segment(a_selector, delimiter, mutate::InsertSide::Above)?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + └── Β·b7346f3 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:anon: + β”‚ └── Β·4670c6d (βŒ‚|1) β–ΊA, β–ΊB + β”‚ └── β–Ί:3[2]:anon: + β”‚ └── Β·1470cfe (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:4[4]:anon: + β”‚ β”‚ └── Β·8f0d338 (βŒ‚|1) β–Ίtags/base + β”‚ └── β–Ί:5[3]:anon: + β”‚ └── Β·add59d2 (βŒ‚|1) + β”‚ └── β†’:4: + └── β–Ί:2[1]:C + β”œβ”€β”€ Β·930563a (βŒ‚|1) + β”œβ”€β”€ Β·68a2fc3 (βŒ‚|1) + └── Β·984fd1c (βŒ‚|1) + └── β†’:4: + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r" * b7346f3 (HEAD -> main) Merge branches 'A', 'B' and 'C' @@ -237,7 +300,27 @@ fn insert_multi_node_segment_below() -> Result<()> { editor.insert_segment(a_selector, delimiter, mutate::InsertSide::Below)?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + └── Β·78624ea (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:A + β”‚ └── Β·e4c78ba (βŒ‚|1) + β”‚ └── β–Ί:2[2]:B + β”‚ β”œβ”€β”€ Β·a748762 (βŒ‚|1) + β”‚ └── Β·62e05ba (βŒ‚|1) + β”‚ └── β–Ί:4[3]:anon: + β”‚ └── Β·8f0d338 (βŒ‚|1) β–Ίtags/base + β”œβ”€β”€ β†’:2: (B) + └── β–Ί:3[1]:C + β”œβ”€β”€ Β·930563a (βŒ‚|1) + β”œβ”€β”€ Β·68a2fc3 (βŒ‚|1) + └── Β·984fd1c (βŒ‚|1) + └── β†’:4: + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r" *-. 78624ea (HEAD -> main) Merge branches 'A', 'B' and 'C' @@ -305,7 +388,30 @@ fn insert_single_node_segment_above_with_explicit_children() -> Result<()> { )?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + └── Β·a14ecd6 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[2]:A + β”‚ └── Β·77b07be (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:4[4]:anon: + β”‚ β”‚ └── Β·8f0d338 (βŒ‚|1) β–Ίtags/base + β”‚ └── β–Ί:2[3]:B + β”‚ β”œβ”€β”€ Β·a748762 (βŒ‚|1) + β”‚ └── Β·62e05ba (βŒ‚|1) + β”‚ └── β†’:4: + β”œβ”€β”€ β†’:2: (B) + └── β–Ί:3[1]:C + └── Β·53c45c8 (βŒ‚|1) + β”œβ”€β”€ β–Ί:5[2]:anon: + β”‚ β”œβ”€β”€ Β·68a2fc3 (βŒ‚|1) + β”‚ └── Β·984fd1c (βŒ‚|1) + β”‚ └── β†’:4: + └── β†’:1: (A) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r" *-. a14ecd6 (HEAD -> main) Merge branches 'A', 'B' and 'C' @@ -377,7 +483,30 @@ fn insert_single_node_segment_below_with_explicit_parents() -> Result<()> { )?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + └── Β·9cf36b2 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:A + β”‚ └── Β·37fb54d (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:4[4]:anon: + β”‚ β”‚ └── Β·8f0d338 (βŒ‚|1) β–Ίtags/base + β”‚ └── β–Ί:2[2]:B + β”‚ └── Β·d202f84 (βŒ‚|1) + β”‚ β”œβ”€β”€ β–Ί:5[3]:anon: + β”‚ β”‚ └── Β·62e05ba (βŒ‚|1) + β”‚ β”‚ └── β†’:4: + β”‚ └── β–Ί:3[3]:C + β”‚ β”œβ”€β”€ Β·930563a (βŒ‚|1) + β”‚ β”œβ”€β”€ Β·68a2fc3 (βŒ‚|1) + β”‚ └── Β·984fd1c (βŒ‚|1) + β”‚ └── β†’:4: + β”œβ”€β”€ β†’:2: (B) + └── β†’:3: (C) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r" *-. 9cf36b2 (HEAD -> main) Merge branches 'A', 'B' and 'C' diff --git a/crates/but-rebase/tests/rebase/graph_rebase/materialize.rs b/crates/but-rebase/tests/rebase/graph_rebase/materialize.rs index 731e290e162..94216cdc998 100644 --- a/crates/but-rebase/tests/rebase/graph_rebase/materialize.rs +++ b/crates/but-rebase/tests/rebase/graph_rebase/materialize.rs @@ -2,7 +2,7 @@ use anyhow::Result; use but_graph::Graph; use but_rebase::graph_rebase::{Editor, Step}; -use but_testsupport::{visualize_commit_graph_all, visualize_disk_tree_skip_dot_git}; +use but_testsupport::{graph_tree, visualize_commit_graph_all, visualize_disk_tree_skip_dot_git}; use crate::utils::{fixture_writable, standard_options}; @@ -37,7 +37,16 @@ fn materialize_removes_dropped_commit_changes_from_worktree() -> Result<()> { editor.replace(c_sel, Step::None)?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + β”œβ”€β”€ Β·a96434e (βŒ‚|1) + β”œβ”€β”€ Β·d591dfe (βŒ‚|1) + └── Β·35b8235 (βŒ‚|1) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); // After materialize, file 'c' should be GONE from worktree insta::assert_snapshot!(visualize_disk_tree_skip_dot_git(worktree)?, @" @@ -88,7 +97,16 @@ fn materialize_without_checkout_preserves_dropped_commit_changes_in_worktree() - editor.replace(c_sel, Step::None)?; let outcome = editor.rebase()?; - outcome.materialize_without_checkout()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + β”œβ”€β”€ Β·a96434e (βŒ‚|1) + β”œβ”€β”€ Β·d591dfe (βŒ‚|1) + └── Β·35b8235 (βŒ‚|1) + "); + let outcome = outcome.materialize_without_checkout()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); // After materialize_without_checkout, file 'c' should STILL exist in worktree insta::assert_snapshot!(visualize_disk_tree_skip_dot_git(worktree)?, @" @@ -113,7 +131,7 @@ fn materialize_without_checkout_preserves_dropped_commit_changes_in_worktree() - #[test] fn both_methods_update_references_identically() -> Result<()> { // Test with materialize - let ref_after_materialize = { + let (ref_after_materialize, overlayed_materialize) = { let (repo, _tmpdir, mut meta) = fixture_writable("four-commits")?; let graph = Graph::from_head(&repo, &*meta, standard_options())?.validated()?; @@ -125,13 +143,18 @@ fn both_methods_update_references_identically() -> Result<()> { editor.replace(c_sel, Step::None)?; let outcome = editor.rebase()?; - outcome.materialize()?; - - repo.rev_parse_single("main")?.detach().to_string() + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); + + ( + repo.rev_parse_single("main")?.detach().to_string(), + overlayed, + ) }; // Test with materialize_without_checkout - let ref_after_materialize_without_checkout = { + let (ref_after_materialize_without_checkout, overlayed_without_checkout) = { let (repo, _tmpdir, mut meta) = fixture_writable("four-commits")?; let graph = Graph::from_head(&repo, &*meta, standard_options())?.validated()?; @@ -143,11 +166,25 @@ fn both_methods_update_references_identically() -> Result<()> { editor.replace(c_sel, Step::None)?; let outcome = editor.rebase()?; - outcome.materialize_without_checkout()?; - - repo.rev_parse_single("main")?.detach().to_string() + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + let outcome = outcome.materialize_without_checkout()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); + + ( + repo.rev_parse_single("main")?.detach().to_string(), + overlayed, + ) }; + insta::assert_snapshot!(overlayed_materialize, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + β”œβ”€β”€ Β·a96434e (βŒ‚|1) + β”œβ”€β”€ Β·d591dfe (βŒ‚|1) + └── Β·35b8235 (βŒ‚|1) + "); + assert_eq!(overlayed_materialize, overlayed_without_checkout); + // Both should update 'main' to the same commit assert_eq!( ref_after_materialize, ref_after_materialize_without_checkout, diff --git a/crates/but-rebase/tests/rebase/graph_rebase/parents_must_be_references_restriction.rs b/crates/but-rebase/tests/rebase/graph_rebase/parents_must_be_references_restriction.rs index 8e4e9b2c1b6..8f3c94aad0e 100644 --- a/crates/but-rebase/tests/rebase/graph_rebase/parents_must_be_references_restriction.rs +++ b/crates/but-rebase/tests/rebase/graph_rebase/parents_must_be_references_restriction.rs @@ -3,7 +3,7 @@ use anyhow::{Result, bail}; use but_graph::Graph; use but_rebase::graph_rebase::{Editor, LookupStep, Step}; -use but_testsupport::visualize_commit_graph_all; +use but_testsupport::{graph_tree, visualize_commit_graph_all}; use crate::utils::{fixture_writable, standard_options}; @@ -25,7 +25,17 @@ fn by_default_parents_can_be_picks() -> Result<()> { // By default, picks can have other picks as parents let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + β”œβ”€β”€ Β·120e3a9 (βŒ‚|1) + β”œβ”€β”€ Β·a96434e (βŒ‚|1) + β”œβ”€β”€ Β·d591dfe (βŒ‚|1) + └── Β·35b8235 (βŒ‚|1) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); // The graph should remain unchanged since we made no modifications insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @" @@ -106,7 +116,20 @@ fn if_a_commit_requires_reference_parents_and_has_reference_parent_result_is_ok( // The rebase should succeed because "a"'s parent is "base" which has a reference let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + └── Β·f37690f (βŒ‚|1) β–Ίc + └── β–Ί:1[1]:b + └── Β·3b3bd41 (βŒ‚|1) + └── β–Ί:2[2]:a + └── Β·5e0ba46 (βŒ‚|1) + └── β–Ί:3[3]:base + └── Β·6155f21 (βŒ‚|1) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); // The graph should remain unchanged since we made no content modifications insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @" diff --git a/crates/but-rebase/tests/rebase/graph_rebase/rebase_identities.rs b/crates/but-rebase/tests/rebase/graph_rebase/rebase_identities.rs index d3769825c6d..f235be8784b 100644 --- a/crates/but-rebase/tests/rebase/graph_rebase/rebase_identities.rs +++ b/crates/but-rebase/tests/rebase/graph_rebase/rebase_identities.rs @@ -3,7 +3,7 @@ use anyhow::Result; use but_graph::Graph; use but_rebase::graph_rebase::Editor; -use but_testsupport::{graph_workspace, visualize_commit_graph_all}; +use but_testsupport::{graph_tree, graph_workspace, visualize_commit_graph_all}; use crate::utils::{fixture_writable, standard_options}; @@ -21,10 +21,20 @@ fn four_commits() -> Result<()> { let graph = Graph::from_head(&repo, &*meta, standard_options())?.validated()?; - let mut ws = graph.into_workspace()?; + let mut ws = graph.clone().into_workspace()?; let editor = Editor::create(&mut ws, &mut *meta, &repo)?; let outcome = editor.rebase()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + β”œβ”€β”€ Β·120e3a9 (βŒ‚|1) + β”œβ”€β”€ Β·a96434e (βŒ‚|1) + β”œβ”€β”€ Β·d591dfe (βŒ‚|1) + └── Β·35b8235 (βŒ‚|1) + "); let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); assert_eq!(visualize_commit_graph_all(&repo)?, before); insta::assert_debug_snapshot!(outcome.history.commit_mappings(), @"{}"); @@ -46,7 +56,7 @@ fn four_commits_with_short_traversal() -> Result<()> { let options = standard_options().with_hard_limit(4); let graph = Graph::from_head(&repo, &*meta, options)?.validated()?; - let mut ws = graph.into_workspace()?; + let mut ws = graph.clone().into_workspace()?; insta::assert_snapshot!(graph_workspace(&ws), @" βŒ‚:0:main[🌳] <> βœ“! @@ -58,7 +68,15 @@ fn four_commits_with_short_traversal() -> Result<()> { let editor = Editor::create(&mut ws, &mut *meta, &repo)?; let outcome = editor.rebase()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + β”œβ”€β”€ Β·120e3a9 (βŒ‚|1) + └── ❌·a96434e (βŒ‚|1) + "); let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); assert_eq!(visualize_commit_graph_all(&repo)?, before); insta::assert_debug_snapshot!(outcome.history.commit_mappings(), @"{}"); @@ -83,10 +101,26 @@ fn merge_in_the_middle() -> Result<()> { let graph = Graph::from_head(&repo, &*meta, standard_options())?.validated()?; - let mut ws = graph.into_workspace()?; + let mut ws = graph.clone().into_workspace()?; let editor = Editor::create(&mut ws, &mut *meta, &repo)?; let outcome = editor.rebase()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:with-inner-merge[🌳] + └── Β·e8ee978 (βŒ‚|1) + └── β–Ί:1[1]:anon: + └── Β·2fc288c (βŒ‚|1) + β”œβ”€β”€ β–Ί:2[2]:A + β”‚ └── Β·add59d2 (βŒ‚|1) + β”‚ └── β–Ί:4[3]:main + β”‚ └── Β·8f0d338 (βŒ‚|1) β–Ίtags/base + └── β–Ί:3[2]:B + └── Β·984fd1c (βŒ‚|1) + └── β†’:4: (main) + "); let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); assert_eq!(visualize_commit_graph_all(&repo)?, before); insta::assert_debug_snapshot!(outcome.history.commit_mappings(), @"{}"); @@ -115,10 +149,30 @@ fn three_branches_merged() -> Result<()> { let graph = Graph::from_head(&repo, &*meta, standard_options())?.validated()?; - let mut ws = graph.into_workspace()?; + let mut ws = graph.clone().into_workspace()?; let editor = Editor::create(&mut ws, &mut *meta, &repo)?; let outcome = editor.rebase()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + └── Β·1348870 (βŒ‚|1) + β”œβ”€β”€ β–Ί:1[1]:A + β”‚ └── Β·add59d2 (βŒ‚|1) + β”‚ └── β–Ί:4[2]:anon: + β”‚ └── Β·8f0d338 (βŒ‚|1) β–Ίtags/base + β”œβ”€β”€ β–Ί:2[1]:B + β”‚ β”œβ”€β”€ Β·a748762 (βŒ‚|1) + β”‚ └── Β·62e05ba (βŒ‚|1) + β”‚ └── β†’:4: + └── β–Ί:3[1]:C + β”œβ”€β”€ Β·930563a (βŒ‚|1) + β”œβ”€β”€ Β·68a2fc3 (βŒ‚|1) + └── Β·984fd1c (βŒ‚|1) + └── β†’:4: + "); let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); assert_eq!(visualize_commit_graph_all(&repo)?, before); insta::assert_debug_snapshot!(outcome.history.commit_mappings(), @"{}"); diff --git a/crates/but-rebase/tests/rebase/graph_rebase/replace.rs b/crates/but-rebase/tests/rebase/graph_rebase/replace.rs index 8ef7360213c..901dc3b2e80 100644 --- a/crates/but-rebase/tests/rebase/graph_rebase/replace.rs +++ b/crates/but-rebase/tests/rebase/graph_rebase/replace.rs @@ -2,7 +2,7 @@ use anyhow::{Context, Result}; use but_graph::Graph; use but_rebase::graph_rebase::{Editor, Step}; -use but_testsupport::{git_status, visualize_commit_graph_all, visualize_tree}; +use but_testsupport::{git_status, graph_tree, visualize_commit_graph_all, visualize_tree}; use crate::utils::{fixture_writable, standard_options}; @@ -45,7 +45,23 @@ fn reword_a_commit() -> Result<()> { editor.replace(a_selector, Step::new_pick(a_new))?; let outcome = editor.rebase()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:with-inner-merge[🌳] + └── Β·b475cbc (βŒ‚|1) + └── β–Ί:1[1]:anon: + └── Β·3d1e2c5 (βŒ‚|1) + β”œβ”€β”€ β–Ί:2[2]:A + β”‚ └── Β·6de6b92 (βŒ‚|1) + β”‚ └── β–Ί:4[3]:main + β”‚ └── Β·8f0d338 (βŒ‚|1) β–Ίtags/base + └── β–Ί:3[2]:B + └── Β·984fd1c (βŒ‚|1) + └── β†’:4: (main) + "); let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); assert_eq!(head_tree, repo.head_tree()?.id); @@ -124,7 +140,23 @@ fn amend_a_commit() -> Result<()> { editor.replace(a_selector, Step::new_pick(a_new))?; let outcome = editor.rebase()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:with-inner-merge[🌳] + └── Β·4772ab7 (βŒ‚|1) + └── β–Ί:1[1]:anon: + └── Β·e7d8400 (βŒ‚|1) + β”œβ”€β”€ β–Ί:2[2]:A + β”‚ └── Β·f1905a8 (βŒ‚|1) + β”‚ └── β–Ί:4[3]:main + β”‚ └── Β·8f0d338 (βŒ‚|1) β–Ίtags/base + └── β–Ί:3[2]:B + └── Β·984fd1c (βŒ‚|1) + └── β†’:4: (main) + "); let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r" * 4772ab7 (HEAD -> with-inner-merge) on top of inner merge diff --git a/crates/but-rebase/tests/rebase/graph_rebase/signing_preferences.rs b/crates/but-rebase/tests/rebase/graph_rebase/signing_preferences.rs index d9a7c24f06f..37e774498f6 100644 --- a/crates/but-rebase/tests/rebase/graph_rebase/signing_preferences.rs +++ b/crates/but-rebase/tests/rebase/graph_rebase/signing_preferences.rs @@ -2,7 +2,7 @@ use anyhow::Result; use but_graph::Graph; use but_rebase::graph_rebase::{Editor, Pick, Step}; -use but_testsupport::{cat_commit, visualize_commit_graph_all}; +use but_testsupport::{cat_commit, graph_tree, visualize_commit_graph_all}; use crate::utils::{fixture_writable_with_signing, standard_options}; @@ -30,7 +30,20 @@ fn commits_maintain_state_if_not_cherry_picked() -> Result<()> { editor.replace(c_sel, Step::Pick(pick))?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + └── Β·dd72792 (βŒ‚|1) β–Ίc + └── β–Ί:1[1]:b + └── Β·e5aa7b5 (βŒ‚|1) + └── β–Ί:2[2]:a + └── Β·3bfeb52 (βŒ‚|1) + └── β–Ί:3[3]:base + └── Β·b6e2f57 (βŒ‚|1) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); assert_eq!(visualize_commit_graph_all(&repo)?, before); @@ -59,7 +72,17 @@ fn commits_are_signed_by_default() -> Result<()> { editor.replace(b_sel, Step::None)?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + β”œβ”€β”€ Β·de980c3 (βŒ‚|1) β–Ίc + └── Β·3bfeb52 (βŒ‚|1) β–Ίa, β–Ίb + └── β–Ί:1[1]:base + └── Β·b6e2f57 (βŒ‚|1) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @" * de980c3 (HEAD -> main, c) c @@ -124,7 +147,17 @@ fn when_cherry_picking_dont_resign_if_not_set() -> Result<()> { editor.replace(b_sel, Step::None)?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + β”œβ”€β”€ Β·06fee46 (βŒ‚|1) β–Ίc + └── Β·3bfeb52 (βŒ‚|1) β–Ίa, β–Ίb + └── β–Ί:1[1]:base + └── Β·b6e2f57 (βŒ‚|1) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @" * 06fee46 (HEAD -> main, c) c diff --git a/crates/but-rebase/tests/rebase/graph_rebase/workspace_commit_behaviour.rs b/crates/but-rebase/tests/rebase/graph_rebase/workspace_commit_behaviour.rs index 5838a2d6975..91f55b1e5bb 100644 --- a/crates/but-rebase/tests/rebase/graph_rebase/workspace_commit_behaviour.rs +++ b/crates/but-rebase/tests/rebase/graph_rebase/workspace_commit_behaviour.rs @@ -3,7 +3,7 @@ use anyhow::Result; use but_graph::Graph; use but_rebase::graph_rebase::{Editor, LookupStep, Pick, Step}; -use but_testsupport::{cat_commit, visualize_commit_graph_all}; +use but_testsupport::{cat_commit, graph_tree, visualize_commit_graph_all}; use crate::utils::{fixture_writable, fixture_writable_with_signing, standard_options}; @@ -36,6 +36,19 @@ fn workspace_remains_unchanged_with_no_operations() -> Result<()> { ); let outcome = editor.rebase()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:gitbutler/workspace[🌳] + β”œβ”€β”€ Β·8795f47 (βŒ‚|1) + └── Β·dd72792 (βŒ‚|1) β–Ίc, β–Ίmain + └── β–Ί:1[1]:b + └── Β·e5aa7b5 (βŒ‚|1) + └── β–Ί:2[2]:a + └── Β·3bfeb52 (βŒ‚|1) + └── β–Ί:3[3]:base + └── Β·b6e2f57 (βŒ‚|1) + "); let step = outcome.lookup_step(selector)?; assert_eq!( @@ -45,6 +58,10 @@ fn workspace_remains_unchanged_with_no_operations() -> Result<()> { ); let mat_outcome = outcome.materialize()?; + assert_eq!( + overlayed, + graph_tree(&mat_outcome.workspace.graph).to_string() + ); let step = mat_outcome.lookup_step(selector)?; assert_eq!( @@ -81,7 +98,18 @@ fn workspace_commit_is_not_signed_after_cherry_pick() -> Result<()> { editor.replace(b_sel, Step::None)?; let outcome = editor.rebase()?; - outcome.materialize()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:gitbutler/workspace[🌳] + β”œβ”€β”€ Β·31c75e2 (βŒ‚|1) + β”œβ”€β”€ Β·de980c3 (βŒ‚|1) β–Ίc, β–Ίmain + └── Β·3bfeb52 (βŒ‚|1) β–Ίa, β–Ίb + └── β–Ί:1[1]:base + └── Β·b6e2f57 (βŒ‚|1) + "); + let outcome = outcome.materialize()?; + assert_eq!(overlayed, graph_tree(&outcome.workspace.graph).to_string()); insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @" * 31c75e2 (HEAD -> gitbutler/workspace) GitButler Workspace Commit @@ -158,6 +186,15 @@ fn ad_hoc_workspace_keeps_regular_defaults() -> Result<()> { ); let outcome = editor.rebase()?; + let overlayed = graph_tree(&outcome.overlayed_graph()?).to_string(); + insta::assert_snapshot!(overlayed, @" + + └── πŸ‘‰β–Ί:0[0]:main[🌳] + β”œβ”€β”€ Β·120e3a9 (βŒ‚|1) + β”œβ”€β”€ Β·a96434e (βŒ‚|1) + β”œβ”€β”€ Β·d591dfe (βŒ‚|1) + └── Β·35b8235 (βŒ‚|1) + "); let step = outcome.lookup_step(selector)?; assert_eq!( @@ -167,6 +204,10 @@ fn ad_hoc_workspace_keeps_regular_defaults() -> Result<()> { ); let mat_outcome = outcome.materialize()?; + assert_eq!( + overlayed, + graph_tree(&mat_outcome.workspace.graph).to_string() + ); let step = mat_outcome.lookup_step(selector)?; assert_eq!( diff --git a/crates/but-workspace/src/branch/move_branch.rs b/crates/but-workspace/src/branch/move_branch.rs index 3817687295b..b45d2534aeb 100644 --- a/crates/but-workspace/src/branch/move_branch.rs +++ b/crates/but-workspace/src/branch/move_branch.rs @@ -48,14 +48,14 @@ pub(super) mod function { /// /// Returns the in memory update [outcome](Outcome) that can then used for materialisation. pub fn tear_off_branch<'ws, 'meta, M: RefMetadata>( - mut editor: Editor<'ws, 'meta, M>, + editor: Editor<'ws, 'meta, M>, subject_branch_name: &FullNameRef, stack_id_override: Option, ) -> anyhow::Result> { - let Some(source) = editor - .workspace - .find_segment_and_stack_by_refname(subject_branch_name) - else { + let successful_rebase = editor.rebase()?; + let workspace = successful_rebase.overlayed_graph()?.into_workspace()?; + let mut editor = successful_rebase.into_editor(); + let Some(source) = workspace.find_segment_and_stack_by_refname(subject_branch_name) else { bail!( "Couldn't find branch to move in workspace with reference name: {subject_branch_name}" ); @@ -64,7 +64,7 @@ pub(super) mod function { // We're currently stopping the move branch operations imperatively at this stage, in order to // reduce the scope of this first iteration of moving the branches. // TODO: Enable and test that we can move branches in any kind of workspace. - match &editor.workspace.kind { + match &workspace.kind { WorkspaceKind::Managed { .. } => {} WorkspaceKind::ManagedMissingWorkspaceCommit { .. } => { bail!("Moving branches currently need a workspace commit") @@ -74,7 +74,7 @@ pub(super) mod function { } }; - let mut ws_meta = editor.workspace.metadata.clone(); + let mut ws_meta = workspace.metadata.clone(); let (source_stack, subject_segment) = source; @@ -86,17 +86,16 @@ pub(super) mod function { }); } - let Some(workspace_head) = editor.workspace.tip_commit().map(|commit| commit.id) else { + let Some(workspace_head) = workspace.tip_commit().map(|commit| commit.id) else { bail!("Couldn't find workspace head.") }; let head_selector = editor .select_commit(workspace_head) .context("Failed to find the workspace head in the graph.")?; - let Some(lower_bound_ref) = editor - .workspace + let Some(lower_bound_ref) = workspace .lower_bound_segment_id - .map(|segment_id| &editor.workspace.graph[segment_id]) + .map(|segment_id| &workspace.graph[segment_id]) .and_then(|segment| segment.ref_name()) else { bail!("Tearing off a branch requires a workspace common base"); @@ -107,7 +106,13 @@ pub(super) mod function { .context("Failed to find target reference in graph.")?; let (subject_delimiter, children_to_disconnect, parents_to_disconnect) = - get_disconnect_parameters(&editor, source_stack, subject_segment, workspace_head)?; + get_disconnect_parameters( + &editor, + &workspace, + source_stack, + subject_segment, + workspace_head, + )?; editor.disconnect_segment_from( subject_delimiter.clone(), @@ -158,24 +163,25 @@ pub(super) mod function { /// /// Returns an [outcome](Outcome) for potential materialisation. pub fn move_branch<'ws, 'meta, M: RefMetadata>( - mut editor: Editor<'ws, 'meta, M>, + editor: Editor<'ws, 'meta, M>, subject_branch_name: &FullNameRef, target_branch_name: &FullNameRef, ) -> anyhow::Result> { - let (source, destination) = retrieve_branches_and_containers( - editor.workspace, - subject_branch_name, - target_branch_name, - )?; + let successful_rebase = editor.rebase()?; + let workspace = successful_rebase.overlayed_graph()?.into_workspace()?; + let mut editor = successful_rebase.into_editor(); + + let (source, destination) = + retrieve_branches_and_containers(&workspace, subject_branch_name, target_branch_name)?; - let Some(workspace_head) = editor.workspace.tip_commit().map(|commit| commit.id) else { + let Some(workspace_head) = workspace.tip_commit().map(|commit| commit.id) else { bail!("Couldn't find workspace head.") }; // We're currently stopping the move branch operations imperatively at this stage, in order to // reduce the scope of this first iteration of moving the branches. // TODO: Enable and test that we can move branches in any kind of workspace. - match &editor.workspace.kind { + match &workspace.kind { WorkspaceKind::Managed { .. } => {} WorkspaceKind::ManagedMissingWorkspaceCommit { .. } => { bail!("Moving branches currently need a workspace commit") @@ -185,7 +191,7 @@ pub(super) mod function { } }; - let mut ws_meta = editor.workspace.metadata.clone(); + let mut ws_meta = workspace.metadata.clone(); let (source_stack, subject_segment) = source; let (_, target_segment) = destination; @@ -197,7 +203,13 @@ pub(super) mod function { .context("Failed to find target reference in graph.")?; let (subject_delimiter, children_to_disconnect, parents_to_disconnect) = - get_disconnect_parameters(&editor, &source_stack, &subject_segment, workspace_head)?; + get_disconnect_parameters( + &editor, + &workspace, + &source_stack, + &subject_segment, + workspace_head, + )?; let skip_reconnect_step = source_stack.segments.len() == 1; editor.disconnect_segment_from( @@ -306,6 +318,7 @@ pub(super) mod function { /// as well as the right segment delimiter to move. fn get_disconnect_parameters<'ws, 'meta, M: RefMetadata>( editor: &Editor<'ws, 'meta, M>, + workspace: &but_graph::projection::Workspace, source_stack: &Stack, subject_segment: &StackSegment, workspace_head: gix::ObjectId, @@ -358,7 +371,7 @@ fn get_disconnect_parameters<'ws, 'meta, M: RefMetadata>( // graph data, and it's probably the target branch, which is not included in the workspace. let graph_base_segment = subject_segment .base_segment_id - .map(|segment_idx| &editor.workspace.graph[segment_idx]); + .map(|segment_idx| &workspace.graph[segment_idx]); let parents_to_disconnect = if let Some(stack_base_segment) = stack_base_segment { // Base segment is part of the source stack. diff --git a/crates/but-workspace/src/commit/move_commit.rs b/crates/but-workspace/src/commit/move_commit.rs index c65bc47ed0f..4b96a91084f 100644 --- a/crates/but-workspace/src/commit/move_commit.rs +++ b/crates/but-workspace/src/commit/move_commit.rs @@ -25,15 +25,18 @@ pub(crate) mod function { /// The subject commit will be detached from the source segment, and inserted relative /// to a given anchor (branch or commit). pub fn move_commit<'ws, 'meta, M: RefMetadata>( - mut editor: Editor<'ws, 'meta, M>, + editor: Editor<'ws, 'meta, M>, subject_commit: impl ToCommitSelector, anchor: impl ToSelector, side: InsertSide, ) -> anyhow::Result> { + let successful_rebase = editor.rebase()?; + let workspace = successful_rebase.overlayed_graph()?.into_workspace()?; + let mut editor = successful_rebase.into_editor(); let (subject_commit_selector, subject_commit) = editor.find_selectable_commit(subject_commit)?; - let subject = retrieve_commit_and_containers(editor.workspace, &subject_commit)?; + let subject = retrieve_commit_and_containers(&workspace, &subject_commit)?; let (source_stack, source_segment, _) = subject; @@ -54,6 +57,7 @@ pub(crate) mod function { let parent_to_disconnect = determine_parent_selector( &editor, + &workspace, source_stack, source_segment, index_of_subject_commit, @@ -139,6 +143,7 @@ pub(crate) mod function { /// and the position of the source segment in the source stack. fn determine_parent_selector<'ws, 'meta, M: RefMetadata>( editor: &Editor<'ws, 'meta, M>, + workspace: &but_graph::projection::Workspace, source_stack: &but_graph::projection::Stack, source_segment: &but_graph::projection::StackSegment, index_of_subject_commit: usize, @@ -161,7 +166,7 @@ pub(crate) mod function { // Look for the base segment in the graph data, as a fallback if there's no stack segment found. let graph_base_segment_ref_name = source_segment .base_segment_id - .map(|base_segment_id| &editor.workspace.graph[base_segment_id]) + .map(|base_segment_id| &workspace.graph[base_segment_id]) .and_then(|segment| segment.ref_name()); match stack_base_segment_ref_name.or(graph_base_segment_ref_name) { diff --git a/crates/but-workspace/src/commit/squash_commits.rs b/crates/but-workspace/src/commit/squash_commits.rs index b37ed71ecbf..6132d4ea5df 100644 --- a/crates/but-workspace/src/commit/squash_commits.rs +++ b/crates/but-workspace/src/commit/squash_commits.rs @@ -115,6 +115,11 @@ pub(crate) mod function { subject_commit: impl ToCommitSelector, target_commit: impl ToCommitSelector, ) -> Result> { + let repo = editor.repo().clone(); + let successful_rebase = editor.rebase()?; + let workspace = successful_rebase.overlayed_graph()?.into_workspace()?; + let mut editor = successful_rebase.into_editor(); + let (subject_selector, subject) = editor.find_selectable_commit(subject_commit)?; let (target_selector, target) = editor.find_selectable_commit(target_commit)?; @@ -130,9 +135,7 @@ pub(crate) mod function { bail!("Target commit must not be conflicted") } - let direction = - determine_reorder_direction(editor.workspace, editor.repo(), &subject, &target)?; - let mut editor = editor; + let direction = determine_reorder_direction(&workspace, &repo, &subject, &target)?; let mut combined_message = Vec::new(); combined_message.extend_from_slice(subject.message.as_ref()); diff --git a/crates/but-workspace/tests/workspace/commit/move_commit.rs b/crates/but-workspace/tests/workspace/commit/move_commit.rs index 5309c589a14..652d589e7f8 100644 --- a/crates/but-workspace/tests/workspace/commit/move_commit.rs +++ b/crates/but-workspace/tests/workspace/commit/move_commit.rs @@ -613,7 +613,7 @@ fn move_commit_to_empty_branch() -> anyhow::Result<()> { ); insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r" - * 6d5c23e (HEAD -> gitbutler/workspace) GitButler Workspace Commit + * de62bba (HEAD -> gitbutler/workspace) GitButler Workspace Commit |\ | * 09d8e52 (B) A |/