Skip to content

Commit 7cee094

Browse files
committed
Rebase replace tests
1 parent 8132eb8 commit 7cee094

File tree

9 files changed

+189
-26
lines changed

9 files changed

+189
-26
lines changed

crates/but-rebase/src/graph_rebase/creation.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ use crate::graph_rebase::{Edge, Editor, Step, StepGraph, StepGraphIndex};
99
/// Provides an extension for creating an Editor out of the segment graph
1010
pub trait GraphExt {
1111
/// Creates an editor.
12-
fn create_editor(&self) -> Result<Editor>;
12+
fn to_editor(&self) -> Result<Editor>;
1313
}
1414

1515
impl GraphExt for Graph {
1616
/// Creates an editor out of the segment graph.
17-
fn create_editor(&self) -> Result<Editor> {
17+
fn to_editor(&self) -> Result<Editor> {
1818
// TODO(CTO): Look into traversing "in workspace" segments that are not reachable from the entrypoint
1919
// TODO(CTO): Look into stopping at the common base
2020
let entrypoint = self.lookup_entrypoint()?;

crates/but-rebase/src/graph_rebase/materialize.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use but_core::worktree::{
88

99
impl SuccessfulRebase {
1010
/// Materializes a history rewrite
11-
pub fn materialize(&self, repo: &gix::Repository) -> Result<()> {
12-
for checkout in &self.checkouts {
11+
pub fn materialize(self, repo: &gix::Repository) -> Result<()> {
12+
for checkout in self.checkouts {
1313
// TODO(CTO): improve safe_checkout to allow for speculation
1414
safe_checkout(
1515
checkout.old_head_id,

crates/but-rebase/src/graph_rebase/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
//! one mess of git2 code to bring them all,
55
//! and in the darknes bind them.
66
7-
use petgraph::graph::NodeIndex;
8-
97
mod creation;
108
pub mod rebase;
119
pub use creation::GraphExt;

crates/but-rebase/src/graph_rebase/rebase.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ pub struct SuccessfulRebase {
4343

4444
/// Represents the rebase output and the varying degrees of success it had.
4545
#[derive(Debug, Clone)]
46+
#[expect(clippy::large_enum_variant)]
47+
#[must_use = "Rebase does nothing to affect the commit graph unless materialized"]
4648
pub enum RebaseOutcome {
4749
/// The rebase
4850
Success(SuccessfulRebase),
@@ -51,15 +53,11 @@ pub enum RebaseOutcome {
5153
///
5254
/// Holds the gix::ObjectId of the commit it failed to pick
5355
MergePickFailed(gix::ObjectId),
54-
/// Symbolic reference was encountered. We should never be given these, and
55-
/// the sematics of how to work with them are currently unclear, so the
56-
/// rebase will be rejected if one is encountered.
57-
SymbolicRefEncountered(gix::refs::FullName),
5856
}
5957

6058
impl Editor {
6159
/// Perform the rebase
62-
pub fn rebase(&self, repo: &gix::Repository) -> Result<RebaseOutcome> {
60+
pub fn rebase(self, repo: &gix::Repository) -> Result<RebaseOutcome> {
6361
// First we want to get a list of nodes that can be reached by
6462
// traversing downwards from the heads that we care about.
6563
// Usually there would be just one "head" which is an index to access
@@ -166,7 +164,7 @@ impl Editor {
166164
}
167165
}
168166
gix::refs::TargetRef::Symbolic(name) => {
169-
return Ok(RebaseOutcome::SymbolicRefEncountered(name.to_owned()));
167+
bail!("Attempted to update the symbolic reference {}", name);
170168
}
171169
}
172170
} else {

crates/but-rebase/tests/rebase/graph_rebase/cherry_pick.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,7 @@ use but_rebase::graph_rebase::cherry_pick::{CherryPickOutcome, cherry_pick};
33
use but_testsupport::visualize_tree;
44
use gix::prelude::ObjectIdExt;
55

6-
use crate::utils::fixture_writable;
7-
8-
fn set_var(key: &str, value: &str) {
9-
unsafe {
10-
std::env::set_var(key, value);
11-
}
12-
}
6+
use crate::{graph_rebase::set_var, utils::fixture_writable};
137

148
fn get_parents(id: &gix::Id) -> Result<Vec<gix::ObjectId>> {
159
Ok(id

crates/but-rebase/tests/rebase/graph_rebase/editor_creation.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ fn four_commits() -> Result<()> {
1818

1919
let graph = Graph::from_head(&repo, &*meta, standard_options())?.validated()?;
2020

21-
let editor = graph.create_editor()?;
21+
let editor = graph.to_editor()?;
2222

2323
insta::assert_snapshot!(editor.steps_dot(), @r#"
2424
digraph {
@@ -53,7 +53,7 @@ fn merge_in_the_middle() -> Result<()> {
5353

5454
let graph = Graph::from_head(&repo, &*meta, standard_options())?.validated()?;
5555

56-
let editor = graph.create_editor()?;
56+
let editor = graph.to_editor()?;
5757

5858
insta::assert_snapshot!(editor.steps_dot(), @r#"
5959
digraph {
@@ -103,7 +103,7 @@ fn three_branches_merged() -> Result<()> {
103103

104104
let graph = Graph::from_head(&repo, &*meta, standard_options())?.validated()?;
105105

106-
let editor = graph.create_editor()?;
106+
let editor = graph.to_editor()?;
107107

108108
insta::assert_snapshot!(editor.steps_dot(), @r#"
109109
digraph {
@@ -161,7 +161,7 @@ fn many_references() -> Result<()> {
161161
└── ·35b8235 (⌂|1)
162162
");
163163

164-
let editor = graph.create_editor()?;
164+
let editor = graph.to_editor()?;
165165

166166
insta::assert_snapshot!(editor.steps_dot(), @r#"
167167
digraph {
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
mod cherry_pick;
22
mod editor_creation;
33
mod rebase_identities;
4+
mod replace;
5+
6+
pub fn set_var(key: &str, value: &str) {
7+
unsafe {
8+
std::env::set_var(key, value);
9+
}
10+
}

crates/but-rebase/tests/rebase/graph_rebase/rebase_identities.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ fn four_commits() -> Result<()> {
2121

2222
let graph = Graph::from_head(&repo, &*meta, standard_options())?.validated()?;
2323

24-
let editor = graph.create_editor()?;
24+
let editor = graph.to_editor()?;
2525
let outcome = editor.rebase(&repo)?;
2626
let RebaseOutcome::Success(outcome) = outcome else {
2727
bail!("Rebase failed");
@@ -50,7 +50,7 @@ fn merge_in_the_middle() -> Result<()> {
5050

5151
let graph = Graph::from_head(&repo, &*meta, standard_options())?.validated()?;
5252

53-
let editor = graph.create_editor()?;
53+
let editor = graph.to_editor()?;
5454
let outcome = editor.rebase(&repo)?;
5555
let RebaseOutcome::Success(outcome) = outcome else {
5656
bail!("Rebase failed");
@@ -83,7 +83,7 @@ fn three_branches_merged() -> Result<()> {
8383

8484
let graph = Graph::from_head(&repo, &*meta, standard_options())?.validated()?;
8585

86-
let editor = graph.create_editor()?;
86+
let editor = graph.to_editor()?;
8787
let outcome = editor.rebase(&repo)?;
8888
let RebaseOutcome::Success(outcome) = outcome else {
8989
bail!("Rebase failed");
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
//! These tests exercise the replace operation.
2+
use anyhow::{Context, Result, bail};
3+
use but_graph::Graph;
4+
use but_rebase::graph_rebase::{GraphExt, Step, rebase::RebaseOutcome};
5+
use but_testsupport::{git_status, visualize_commit_graph_all, visualize_tree};
6+
7+
use crate::{
8+
graph_rebase::set_var,
9+
utils::{fixture_writable, standard_options},
10+
};
11+
12+
#[test]
13+
fn reword_a_commit() -> Result<()> {
14+
set_var("GITBUTLER_CHANGE_ID", "1");
15+
let (repo, _tmpdir, meta) = fixture_writable("merge-in-the-middle")?;
16+
17+
insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r"
18+
* e8ee978 (HEAD -> with-inner-merge) on top of inner merge
19+
* 2fc288c Merge branch 'B' into with-inner-merge
20+
|\
21+
| * 984fd1c (B) C: new file with 10 lines
22+
* | add59d2 (A) A: 10 lines on top
23+
|/
24+
* 8f0d338 (tag: base, main) base
25+
");
26+
insta::assert_snapshot!(git_status(&repo)?, @r"
27+
");
28+
29+
let head_tree = repo.head_tree()?.id;
30+
31+
let graph = Graph::from_head(&repo, &*meta, standard_options())?.validated()?;
32+
33+
let mut editor = graph.to_editor()?;
34+
35+
// get the origional a
36+
let a = repo.rev_parse_single("A")?.detach();
37+
38+
// reword commit a
39+
let a_obj = repo.find_commit(a)?;
40+
let mut a_obj = a_obj.decode()?;
41+
a_obj.message = "A: a second coming".into();
42+
let a_new = repo.write_object(a_obj)?.detach();
43+
44+
// select the origional a out of the graph
45+
let a_selector = editor
46+
.select_commit(a)
47+
.context("Failed to find commit a in editor graph")?;
48+
// replace it with the new one
49+
editor.replace(&a_selector, Step::Pick { id: a_new });
50+
51+
let outcome = editor.rebase(&repo)?;
52+
let RebaseOutcome::Success(outcome) = outcome else {
53+
bail!("Rebase failed");
54+
};
55+
outcome.materialize(&repo)?;
56+
57+
assert_eq!(head_tree, repo.head_tree()?.id);
58+
59+
insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r"
60+
* 4894b95 (HEAD -> with-inner-merge) on top of inner merge
61+
* af38519 Merge branch 'B' into with-inner-merge
62+
|\
63+
| * 984fd1c (B) C: new file with 10 lines
64+
* | 6de6b92 (A) A: a second coming
65+
|/
66+
* 8f0d338 (tag: base, main) base
67+
");
68+
insta::assert_snapshot!(git_status(&repo)?, @r"
69+
");
70+
71+
Ok(())
72+
}
73+
74+
#[test]
75+
fn ammend_a_commit() -> Result<()> {
76+
set_var("GITBUTLER_CHANGE_ID", "1");
77+
let (repo, _tmpdir, meta) = fixture_writable("merge-in-the-middle")?;
78+
79+
insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r"
80+
* e8ee978 (HEAD -> with-inner-merge) on top of inner merge
81+
* 2fc288c Merge branch 'B' into with-inner-merge
82+
|\
83+
| * 984fd1c (B) C: new file with 10 lines
84+
* | add59d2 (A) A: 10 lines on top
85+
|/
86+
* 8f0d338 (tag: base, main) base
87+
");
88+
insta::assert_snapshot!(git_status(&repo)?, @r"
89+
");
90+
91+
let head_tree = repo.head_tree()?.id();
92+
insta::assert_snapshot!(visualize_tree(head_tree), @r#"
93+
f766d1f
94+
├── added-after-with-inner-merge:100644:861be1b "seq 10\n"
95+
├── file:100644:d78dd4f "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n50\n51\n52\n53\n54\n55\n56\n57\n58\n59\n60\n"
96+
└── new-file:100644:f00c965 "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n"
97+
"#);
98+
99+
let graph = Graph::from_head(&repo, &*meta, standard_options())?.validated()?;
100+
101+
let mut editor = graph.to_editor()?;
102+
103+
// get the origional a
104+
let a = repo.rev_parse_single("A")?;
105+
insta::assert_snapshot!(visualize_tree(a), @r#"
106+
0cc630c
107+
└── file:100644:d78dd4f "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n50\n51\n52\n53\n54\n55\n56\n57\n58\n59\n60\n"
108+
"#);
109+
110+
// reword commit a
111+
let mut a_obj = but_core::Commit::from_id(a)?;
112+
113+
let mut builder = repo.edit_tree(a_obj.tree)?;
114+
let new_blob = repo.write_blob("I'm a new file :D\n")?;
115+
builder.upsert("new-file.txt", gix::objs::tree::EntryKind::Blob, new_blob)?;
116+
let tree = builder.write()?;
117+
118+
a_obj.tree = tree.detach();
119+
a_obj.message = "A: a second coming".into();
120+
let a_new = repo.write_object(a_obj.inner)?.detach();
121+
122+
// select the origional a out of the graph
123+
let a_selector = editor
124+
.select_commit(a.detach())
125+
.context("Failed to find commit a in editor graph")?;
126+
// replace it with the new one
127+
editor.replace(&a_selector, Step::Pick { id: a_new });
128+
129+
let outcome = editor.rebase(&repo)?;
130+
let RebaseOutcome::Success(outcome) = outcome else {
131+
bail!("Rebase failed");
132+
};
133+
outcome.materialize(&repo)?;
134+
135+
insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r"
136+
* 4a8642c (HEAD -> with-inner-merge) on top of inner merge
137+
* fa3d6b8 Merge branch 'B' into with-inner-merge
138+
|\
139+
| * 984fd1c (B) C: new file with 10 lines
140+
* | f1905a8 (A) A: a second coming
141+
|/
142+
* 8f0d338 (tag: base, main) base
143+
");
144+
insta::assert_snapshot!(git_status(&repo)?, @r"
145+
");
146+
147+
// A should include our extra blob
148+
let a = repo.rev_parse_single("A")?;
149+
insta::assert_snapshot!(visualize_tree(a), @r#"
150+
0c482d4
151+
├── file:100644:d78dd4f "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n50\n51\n52\n53\n54\n55\n56\n57\n58\n59\n60\n"
152+
└── new-file.txt:100644:715faaf "I\'m a new file :D\n"
153+
"#);
154+
155+
// New head tree should also include our extra blob
156+
let new_head_tree = repo.head_tree()?.id();
157+
insta::assert_snapshot!(visualize_tree(new_head_tree), @r#"
158+
89042ca
159+
├── added-after-with-inner-merge:100644:861be1b "seq 10\n"
160+
├── file:100644:d78dd4f "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n50\n51\n52\n53\n54\n55\n56\n57\n58\n59\n60\n"
161+
├── new-file:100644:f00c965 "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n"
162+
└── new-file.txt:100644:715faaf "I\'m a new file :D\n"
163+
"#);
164+
165+
Ok(())
166+
}

0 commit comments

Comments
 (0)