Skip to content

Commit f77adb8

Browse files
committed
refactor(graph): Delinearize by default
This will help us avoid biasing our algorithms based on implementation, like we could do with #6. This involved an audit of different graph ops to see if this would have an impact on them.
1 parent 36e7d00 commit f77adb8

File tree

4 files changed

+28
-29
lines changed

4 files changed

+28
-29
lines changed

src/bin/git-stack/stack.rs

-3
Original file line numberDiff line numberDiff line change
@@ -330,8 +330,6 @@ fn plan_rebase(state: &State, stack: &StackState) -> eyre::Result<git_stack::git
330330

331331
git_stack::graph::rebase_branches(&mut root, stack.onto.id)?;
332332

333-
git_stack::graph::delinearize(&mut root);
334-
335333
let script = git_stack::graph::to_script(&root);
336334

337335
Ok(script)
@@ -379,7 +377,6 @@ fn show(state: &State, colored_stdout: bool) -> eyre::Result<()> {
379377

380378
git_stack::graph::protect_branches(&mut root, &state.repo, &state.protected_branches)?;
381379
git_stack::graph::pushable(&mut root)?;
382-
git_stack::graph::delinearize(&mut root);
383380
if state.show_stacked {
384381
git_stack::graph::linearize_by_size(&mut root);
385382
}

src/graph/node.rs

+16-18
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ impl Node {
6262

6363
if merge_base_id != self_id {
6464
let mut prefix = Node::populate(repo, merge_base_id, self_id, possible_branches)?;
65-
prefix.push(self);
65+
let pushed = prefix.push(&self);
66+
assert!(pushed);
6667
self = prefix;
6768
self_id = merge_base_id;
6869
}
@@ -125,31 +126,28 @@ impl Node {
125126
.map(|commit| Node::new(commit, branches))
126127
.collect();
127128
stack.reverse();
129+
crate::graph::ops::delinearize_stack(&mut stack);
128130
if !stack.is_empty() {
129131
root.stacks.push(stack);
130132
}
131133

132134
Ok(root)
133135
}
134136

135-
fn push(&mut self, other: Self) {
136-
let other_oid = other.local_commit.id;
137-
if self.local_commit.id == other_oid {
138-
self.merge(other);
139-
} else if self.stacks.len() == 1 {
140-
let stack = &mut self.stacks[0];
141-
for node in stack.iter_mut() {
142-
if node.local_commit.id == other_oid {
143-
node.merge(other);
144-
if let Some(ext) = stack.last_mut().unwrap().stacks.pop() {
145-
stack.extend(ext);
137+
#[must_use]
138+
fn push(&mut self, other: &Self) -> bool {
139+
if self.local_commit.id == other.local_commit.id {
140+
self.merge(other.clone());
141+
true
142+
} else {
143+
for stack in self.stacks.iter_mut() {
144+
for node in stack.iter_mut() {
145+
if node.push(other) {
146+
return true;
146147
}
147-
return;
148148
}
149149
}
150-
unimplemented!("This case isn't needed yet");
151-
} else {
152-
unimplemented!("This case isn't needed yet");
150+
false
153151
}
154152
}
155153

@@ -227,9 +225,9 @@ fn merge_stack(lhs_nodes: &mut Vec<Node>, rhs_nodes: &mut Vec<Node>) {
227225
}
228226
}
229227
Some((index, itertools::EitherOrBoth::Right(_))) => {
230-
// rhs is a superset, so we can append it to lhs
228+
// rhs is a superset, so we can add it to lhs's stacks
231229
let remaining = rhs_nodes.split_off(index);
232-
lhs_nodes.extend(remaining);
230+
lhs_nodes.last_mut().unwrap().stacks.push(remaining);
233231
rhs_nodes.clear();
234232
}
235233
Some((_, itertools::EitherOrBoth::Left(_))) | None => {

src/graph/ops.rs

+12-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ pub fn protect_branches(
77
) -> Result<(), git2::Error> {
88
// Assuming the root is the base. The base is not guaranteed to be a protected branch but
99
// might be an ancestor of one.
10+
//
11+
// We can't use `descendant_protected` because a protect branch might not be in the
12+
// descendants, depending on what Graph the user selected.
1013
for protected_oid in protected_branches.oids() {
1114
if let Some(merge_base_oid) = repo.merge_base(root.local_commit.id, protected_oid) {
1215
if merge_base_oid == root.local_commit.id {
@@ -17,13 +20,13 @@ pub fn protect_branches(
1720
}
1821

1922
for stack in root.stacks.iter_mut() {
20-
protect_branches_internal(stack, repo, protected_branches)?;
23+
protect_branches_stack(stack, repo, protected_branches)?;
2124
}
2225

2326
Ok(())
2427
}
2528

26-
fn protect_branches_internal(
29+
fn protect_branches_stack(
2730
nodes: &mut Vec<Node>,
2831
repo: &dyn crate::git::Repo,
2932
protected_branches: &crate::git::Branches,
@@ -32,7 +35,7 @@ fn protect_branches_internal(
3235
for node in nodes.iter_mut().rev() {
3336
let mut stacks_protected = false;
3437
for stack in node.stacks.iter_mut() {
35-
let stack_protected = protect_branches_internal(stack, repo, protected_branches)?;
38+
let stack_protected = protect_branches_stack(stack, repo, protected_branches)?;
3639
stacks_protected |= stack_protected;
3740
}
3841
let self_protected = protected_branches.contains_oid(node.local_commit.id);
@@ -118,9 +121,12 @@ fn pushable_stack(nodes: &mut [Node]) -> Result<(), git2::Error> {
118121
log::debug!("{} is pushable", branch.name);
119122
node.pushable = true;
120123
}
124+
// Bail out, only the first branch of a stack is up for consideration
121125
return Ok(());
122-
} else if !node.stacks.is_empty() {
123-
cause = Some("ambiguous which branch owns some commits");
126+
} else {
127+
for stack in node.stacks.iter_mut() {
128+
pushable_stack(stack)?;
129+
}
124130
}
125131
}
126132

@@ -133,7 +139,7 @@ pub fn delinearize(node: &mut Node) {
133139
}
134140
}
135141

136-
fn delinearize_stack(nodes: &mut Vec<Node>) {
142+
pub(crate) fn delinearize_stack(nodes: &mut Vec<Node>) {
137143
for node in nodes.iter_mut() {
138144
for stack in node.stacks.iter_mut() {
139145
delinearize_stack(stack);

tests/graph.rs

-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ mod test_rebase {
2626
let mut root = Node::from_branches(&repo, graph_branches).unwrap();
2727
git_stack::graph::protect_branches(&mut root, &repo, &protected_branches).unwrap();
2828
git_stack::graph::rebase_branches(&mut root, master_commit.id).unwrap();
29-
git_stack::graph::delinearize(&mut root);
3029
let script = git_stack::graph::to_script(&root);
3130

3231
let mut executor = git_stack::git::Executor::new(&repo, false);
@@ -68,7 +67,6 @@ mod test_rebase {
6867
let mut root = Node::from_branches(&repo, graph_branches).unwrap();
6968
git_stack::graph::protect_branches(&mut root, &repo, &protected_branches).unwrap();
7069
git_stack::graph::rebase_branches(&mut root, master_commit.id).unwrap();
71-
git_stack::graph::delinearize(&mut root);
7270
let script = git_stack::graph::to_script(&root);
7371

7472
let mut executor = git_stack::git::Executor::new(&repo, false);

0 commit comments

Comments
 (0)