Skip to content

Commit 7e3a824

Browse files
committed
Preserve stack when committing by using explicit parent
Committing to a stacked branch was unstacking it because the commit routine let the parent be auto-detected, which could detach the stack. This change finds the intended target branch (honoring an optional branch hint via exact name or CLI id parsing), uses that branch's tip as the explicit parent for create_commit_simple, and thus preserves the stack relationship. - Resolve target stack and branch selection: try branch hint first, fall back to stack HEAD. - Parse CLI branch hints to match branch names when needed. - Pass branch tip as parent_id to the simple commit engine to avoid unstacking.
1 parent 90f4eab commit 7e3a824

File tree

1 file changed

+39
-7
lines changed

1 file changed

+39
-7
lines changed

crates/but/src/commit/mod.rs

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,16 +109,45 @@ pub(crate) fn commit(
109109
anyhow::bail!("Aborting commit due to empty commit message.");
110110
}
111111

112-
// Find the target branch (first head of the target stack)
112+
// Find the target stack and determine the target branch
113113
let target_stack = &stacks
114114
.iter()
115115
.find(|(id, _)| *id == target_stack_id)
116116
.unwrap()
117117
.1;
118-
let target_branch = target_stack
119-
.branch_details
120-
.first()
121-
.ok_or_else(|| anyhow::anyhow!("No branches found in target stack"))?;
118+
119+
// If a branch hint was provided, find that specific branch; otherwise use first branch
120+
let target_branch = if let Some(hint) = branch_hint {
121+
// First try exact name match
122+
target_stack
123+
.branch_details
124+
.iter()
125+
.find(|branch| branch.name.to_string() == hint)
126+
.or_else(|| {
127+
// If no exact match, try to parse as CLI ID and match
128+
if let Ok(cli_ids) = crate::id::CliId::from_str(&mut ctx, hint) {
129+
for cli_id in cli_ids {
130+
if let crate::id::CliId::Branch { name } = cli_id {
131+
if let Some(branch) = target_stack
132+
.branch_details
133+
.iter()
134+
.find(|b| b.name.to_string() == name)
135+
{
136+
return Some(branch);
137+
}
138+
}
139+
}
140+
}
141+
None
142+
})
143+
.ok_or_else(|| anyhow::anyhow!("Branch '{}' not found in target stack", hint))?
144+
} else {
145+
// No branch hint, use first branch (HEAD of stack)
146+
target_stack
147+
.branch_details
148+
.first()
149+
.ok_or_else(|| anyhow::anyhow!("No branches found in target stack"))?
150+
};
122151

123152
// Convert files to DiffSpec
124153
let diff_specs: Vec<DiffSpec> = files_to_commit
@@ -139,6 +168,9 @@ pub(crate) fn commit(
139168
})
140169
.collect();
141170

171+
// Get the HEAD commit of the target branch to use as parent (preserves stacking)
172+
let parent_commit_id = target_branch.tip;
173+
142174
// Create a snapshot before committing
143175
let mut guard = project.exclusive_worktree_access();
144176
let _snapshot = ctx
@@ -148,11 +180,11 @@ pub(crate) fn commit(
148180
)
149181
.ok(); // Ignore errors for snapshot creation
150182

151-
// Commit using the simpler commit engine
183+
// Commit using the simpler commit engine with explicit parent to preserve stacking
152184
let outcome = but_workspace::commit_engine::create_commit_simple(
153185
&ctx,
154186
target_stack_id,
155-
None, // parent_id - let it auto-detect from branch head
187+
Some(parent_commit_id), // Use the branch HEAD as parent to preserve stacking
156188
diff_specs,
157189
commit_message,
158190
target_branch.name.to_string(),

0 commit comments

Comments
 (0)