gitbutlerapp/gitbutler · error · anyhow::Error
Cannot uncommit commits that would result in merge conflicts
Error message
Cannot uncommit commits that would result in merge conflicts
What it means
Guard in the legacy squash flow's uncommit path: before performing any real mutation, the selected commits are simulated with `commit_uncommit_only_with_perm(..., DryRun::Yes)` and the resulting workspace is inspected. If `workspace.is_conflicted()` is true — i.e. moving those commits' changes back to the workspace would collide with other content — the operation is aborted before the `DryRun::No` transaction runs.
Source
Thrown at crates/but/src/command/legacy/squash.rs:1390
MoveCommittedFilesOperation { target, .. },
) => SquashOutcome::Hunks { target, new_commit },
};
Ok((outcome, Some(ws)))
}
ExecutableSquashOperation::Uncommit(op) => {
if op.has_commit_sources() {
let but_api::commit::types::UncommitResult {
workspace,
uncommitted_ids: _,
} = but_api::commit::uncommit::commit_uncommit_only_with_perm(
ctx,
op.commit_sources().collect(),
None,
DryRun::Yes,
perm,
)?;
anyhow::ensure!(
!workspace.is_conflicted(),
"Cannot uncommit commits that would result in merge conflicts"
);
}
let snapshot_details = SnapshotDetails::new(OperationKind::UndoCommit);
let ws = but_transaction::with_transaction_with_perm(
ctx,
meta,
perm,
snapshot_details,
DryRun::No,
|mut tx| {
match &op {
UncommitOperation::Commits { sources } => {
if !sources.is_empty() {
tx.uncommit_commits(sources.iter().map(|c| c.commit_id))?;
}View on GitHub (pinned to caf1f223d3)
Solutions
- Uncommit fewer commits — start with only the topmost commit of the stack and add more incrementally
- Uncommit the entire stack at once (the whole-range operation does not fight itself), or ship the later commits first and then uncommit
- Commit or stash overlapping workspace changes so the dry-run workspace is clean, then retry
- If the exact split is mandatory, do it with git-level tooling (revert/rebase) where you can resolve conflicts manually
Example fix
# before: uncommit an early commit whose lines later commits also touch but squash --uncommit c1 c2 c5 # after: take the tip only, then extend if the dry run stays clean but squash --uncommit c5
Defensive patterns
Strategy: validation
Validate before calling
let but_api::commit::types::UncommitResult { workspace, .. } =
but_api::commit::uncommit::commit_uncommit_only_with_perm(
ctx, ids.clone(), None, DryRun::Yes, perm,
)?;
if workspace.is_conflicted() {
// shrink the selection instead of letting the command fail
anyhow::bail!("selection would conflict; uncommit fewer commits (start at the tip)");
} Type guard
fn dry_run_is_conflicted(workspace: &but_graph::Workspace) -> bool {
workspace.is_conflicted()
} Try / catch
match run_uncommit(ctx, ids, perm) {
Ok(result) => { /* use result */ }
Err(err) if err.to_string().contains("would result in merge conflicts") => {
// retry with a smaller set: only the tip commit, then extend one at a time
}
Err(err) => return Err(err),
} Prevention
- Uncommit from the stack tip downward, one commit at a time, instead of arbitrary subsets
- Keep the workspace clean before uncommit operations that overlap its files
- When the dry run conflicts, ship or uncommit the dependent later commits first
When it happens
Trigger: Uncommitting a set of commits whose inverse changes overlap with lines modified by commits that stay in the stack, or with existing workspace changes — the dry-run workspace comes out conflicted, so the CLI refuses.
Common situations: Undoing an early commit in a stack whose later commits touch the same lines; uncommitting while the workspace already holds edits to the same files; selecting a non-contiguous commit range that interleaves with kept commits.
Related errors
- Cannot uncommit hunks that would result in merge conflicts
- When using OpenRouter, you must provide a valid API key
- HTTP Error ${response.statusText}: ${text}
- failed to discover GitButler branch with 'but --json status'
- Failed to determine target commit for hunk absorption due to
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/07cf01d7666b8412.
Report an issue: GitHub.