Skip to content

Commit 8132eb8

Browse files
committed
Rebase identity tests
1 parent 95cd28b commit 8132eb8

File tree

10 files changed

+296
-54
lines changed

10 files changed

+296
-54
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ impl GraphExt for Graph {
8787
}
8888
}
8989

90-
last_inserted = Some(ni.clone());
90+
last_inserted = Some(ni);
9191
steps_for_commits.insert(c.id, ni);
9292
}
9393

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//! Functions for materializing a rebase
2+
use crate::graph_rebase::rebase::SuccessfulRebase;
3+
use anyhow::Result;
4+
use but_core::worktree::{
5+
checkout::{Options, UncommitedWorktreeChanges},
6+
safe_checkout,
7+
};
8+
9+
impl SuccessfulRebase {
10+
/// Materializes a history rewrite
11+
pub fn materialize(&self, repo: &gix::Repository) -> Result<()> {
12+
for checkout in &self.checkouts {
13+
// TODO(CTO): improve safe_checkout to allow for speculation
14+
safe_checkout(
15+
checkout.old_head_id,
16+
checkout.head_id,
17+
repo,
18+
Options {
19+
uncommitted_changes: UncommitedWorktreeChanges::KeepAndAbortOnConflict,
20+
skip_head_update: true,
21+
},
22+
)?;
23+
}
24+
25+
repo.edit_references(self.ref_edits.clone())?;
26+
27+
Ok(())
28+
}
29+
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ mod creation;
1010
pub mod rebase;
1111
pub use creation::GraphExt;
1212
pub mod cherry_pick;
13+
pub mod materialize;
14+
pub mod mutate;
1315

1416
/// Utilities for testing
1517
pub mod testing;
@@ -49,7 +51,7 @@ type StepGraph = petgraph::stable_graph::StableDiGraph<Step, Edge>;
4951
/// Points to a step in the rebase editor.
5052
#[derive(Debug, Clone)]
5153
pub struct Selector {
52-
id: NodeIndex<StepGraphIndex>,
54+
id: StepGraphIndex,
5355
}
5456

5557
/// Used to manipulate a set of picks.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//! Operations for mutation the editor
2+
3+
use crate::graph_rebase::{Editor, Selector, Step};
4+
5+
/// Operations for mutating the commit graph
6+
impl Editor {
7+
/// Get a selector to a particular commit in the graph
8+
pub fn select_commit(&self, target: gix::ObjectId) -> Option<Selector> {
9+
for node_idx in self.graph.node_indices() {
10+
if let Step::Pick { id } = self.graph[node_idx]
11+
&& id == target
12+
{
13+
return Some(Selector { id: node_idx });
14+
}
15+
}
16+
17+
None
18+
}
19+
20+
/// Get a selector to a particular reference in the graph
21+
pub fn select_reference(&self, target: &gix::refs::FullNameRef) -> Option<Selector> {
22+
for node_idx in self.graph.node_indices() {
23+
if let Step::Reference { refname } = &self.graph[node_idx]
24+
&& target == refname.as_ref()
25+
{
26+
return Some(Selector { id: node_idx });
27+
}
28+
}
29+
30+
None
31+
}
32+
33+
/// Replaces the node that the function was pointing to.
34+
///
35+
/// Returns the replaced step.
36+
pub fn replace(&mut self, target: &Selector, step: Step) -> Step {
37+
let old = self.graph[target.id].clone();
38+
self.graph[target.id] = step;
39+
old
40+
}
41+
}

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

Lines changed: 83 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ use gix::refs::{
1313
};
1414
use petgraph::visit::EdgeRef;
1515

16+
/// Represents somethign to be checked out - I'm not quite sure how
17+
/// `safe_checkout` knows what is the main checkout & what is a worktree - but
18+
/// that is some voodoo I'm not touching right now
19+
#[derive(Debug, Clone)]
20+
pub struct CheckoutSpec {
21+
pub(crate) old_head_id: gix::ObjectId,
22+
pub(crate) head_id: gix::ObjectId,
23+
}
24+
1625
/// Represents a successful rebase, and any valid, but potentially conflicting scenarios it had.
1726
#[allow(unused)]
1827
#[derive(Debug, Clone)]
@@ -26,6 +35,10 @@ pub struct SuccessfulRebase {
2635
pub(crate) ref_edits: Vec<RefEdit>,
2736
/// The new step graph
2837
pub(crate) graph: StepGraph,
38+
/// Heads
39+
pub(crate) heads: Vec<StepGraphIndex>,
40+
/// To checkout
41+
pub(crate) checkouts: Vec<CheckoutSpec>,
2942
}
3043

3144
/// Represents the rebase output and the varying degrees of success it had.
@@ -61,11 +74,13 @@ impl Editor {
6174
// The step graph with updated commit oids
6275
let mut output_graph = StepGraph::new();
6376
let mut commit_mapping = HashMap::new();
77+
let mut checkouts = vec![];
78+
let mut unchanged_references = vec![];
6479

6580
for step_idx in steps_to_pick {
6681
// Do the frikkin rebase man!
6782
let step = self.graph[step_idx].clone();
68-
match step {
83+
let new_idx = match step {
6984
Step::Pick { id } => {
7085
let graph_parents = collect_ordered_parents(&self.graph, step_idx);
7186
let ontos = graph_parents
@@ -93,12 +108,14 @@ impl Editor {
93108
if id != new_id {
94109
commit_mapping.insert(id, new_id);
95110
}
111+
112+
new_idx
96113
}
97114
CherryPickOutcome::FailedToMergeBases => {
98115
// Exit early - the rebase failed because it encountered a commit it couldn't pick
99116
return Ok(RebaseOutcome::MergePickFailed(id));
100117
}
101-
};
118+
}
102119
}
103120
Step::Reference { refname } => {
104121
let graph_parents = collect_ordered_parents(&self.graph, step_idx);
@@ -120,7 +137,9 @@ impl Editor {
120137
let target = reference.target();
121138
match target {
122139
gix::refs::TargetRef::Object(id) => {
123-
if id != to_reference {
140+
if id == to_reference {
141+
unchanged_references.push(refname.clone());
142+
} else {
124143
ref_edits.push(RefEdit {
125144
name: refname.clone(),
126145
change: Change::Update {
@@ -132,6 +151,18 @@ impl Editor {
132151
},
133152
deref: false,
134153
});
154+
155+
// TODO(CTO): This is FAR from the full set
156+
// of capabilities needed here. This ommits
157+
// any consideration of the head commit
158+
// being replaced. This really needs to do
159+
// some other lookup of all the heads
160+
if self.heads.contains(&step_idx) {
161+
checkouts.push(CheckoutSpec {
162+
old_head_id: id.into(),
163+
head_id: to_reference,
164+
});
165+
}
135166
}
136167
}
137168
gix::refs::TargetRef::Symbolic(name) => {
@@ -150,38 +181,67 @@ impl Editor {
150181
});
151182
};
152183

153-
let new_idx = output_graph.add_node(Step::Reference { refname });
154-
graph_mapping.insert(step_idx, new_idx);
155-
}
156-
Step::None => {
157-
let new_idx = output_graph.add_node(Step::None);
158-
graph_mapping.insert(step_idx, new_idx);
184+
output_graph.add_node(Step::Reference { refname })
159185
}
186+
Step::None => output_graph.add_node(Step::None),
160187
};
161188

162-
// Find deleted references
163-
for reference in self.initial_references.iter() {
164-
if !ref_edits
189+
graph_mapping.insert(step_idx, new_idx);
190+
191+
let mut edges = self
192+
.graph
193+
.edges_directed(step_idx, petgraph::Direction::Outgoing)
194+
.collect::<Vec<_>>();
195+
edges.sort_by_key(|e| e.weight().order);
196+
edges.reverse();
197+
198+
for e in edges {
199+
let Some(new_parent) = graph_mapping.get(&e.target()) else {
200+
bail!("Failed to find cooresponding parent");
201+
};
202+
203+
output_graph.add_edge(new_idx, *new_parent, e.weight().clone());
204+
}
205+
}
206+
207+
// Find deleted references
208+
for reference in self.initial_references.iter() {
209+
if !ref_edits
210+
.iter()
211+
.any(|e| e.name.as_ref() == reference.as_ref())
212+
&& !unchanged_references
165213
.iter()
166-
.any(|e| e.name.as_ref() == reference.as_ref())
167-
{
168-
ref_edits.push(RefEdit {
169-
name: reference.clone(),
170-
change: Change::Delete {
171-
log: gix::refs::transaction::RefLog::AndReference,
172-
expected: PreviousValue::MustExist,
173-
},
174-
deref: false,
175-
});
176-
}
214+
.any(|e| e.as_ref() == reference.as_ref())
215+
{
216+
ref_edits.push(RefEdit {
217+
name: reference.clone(),
218+
change: Change::Delete {
219+
log: gix::refs::transaction::RefLog::AndReference,
220+
expected: PreviousValue::MustExist,
221+
},
222+
deref: false,
223+
});
177224
}
178225
}
179226

227+
let heads = self
228+
.heads
229+
.iter()
230+
.map(|h| {
231+
graph_mapping
232+
.get(h)
233+
.context("Failed to get new head")
234+
.cloned()
235+
})
236+
.collect::<Result<Vec<_>>>()?;
237+
180238
Ok(RebaseOutcome::Success(SuccessfulRebase {
181239
ref_edits,
182240
commit_mapping,
183241
graph_mapping,
184242
graph: output_graph,
243+
heads,
244+
checkouts,
185245
}))
186246
}
187247
}

0 commit comments

Comments
 (0)