gitbutlerapp/gitbutler · error
Source commit must not be conflicted
Error message
Source commit must not be conflicted
What it means
move_changes_between_commits requires the source commit to be conflict-free, because it computes a new tree by removing the described diffs from the source tree — impossible to do reliably on a conflicted tree. It bails before any tree rewrite.
Solutions
- Resolve the source commit's conflicts first, then retry the move
- Pick a different source commit that is not conflicted
- Abort the conflicted operation and redo it before moving changes
Example fix
// before
editor.move_changes_between_commits(conflicted_src, dst, changes, ctx)?;
// after
if conflicted_src.attach(repo).is_conflicted() { resolve_first()?; }
editor.move_changes_between_commits(conflicted_src, dst, changes, ctx)?; Defensive patterns
Strategy: validation
Validate before calling
if source.attach(editor.repo()).is_conflicted() {
bail!("resolve source conflicts before moving changes");
}
editor.move_changes_between_commits(source_selector, dest_selector, changes, ctx)?; Type guard
fn moveable(commit: &gix::Commit) -> bool { !commit.clone().attach(commit.repo).is_conflicted() } Try / catch
match editor.move_changes_between_commits(src, dst, changes, ctx) {
Err(e) if e.to_string().contains("Source commit must not be conflicted") => resolve_then_retry(src),
other => other,
} Prevention
- Check conflict state on both source and destination before moves
- Resolve rebase/merge conflicts before reassigning changes
- Avoid moving changes out of commits created during conflicted operations
When it happens
Trigger: Calling move_changes_between_commits (or any wrapper like move_file_between_commits) where source_commit.clone().attach(repo).is_conflicted() is true.
Common situations: Moving file changes out of a commit that is mid-conflict after a rebase/merge; automated reassignment flows that don't check commit conflict state first.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Destination commit must not be conflicted
- Cannot amend a conflicted commit
- Branch ' ' already exists
- Branch ' ' cannot be created: the target commit ( ) already…
- cannot amend into : the commit is immutable (not part of a…
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/efa9d02f71638519.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-workspace/src/commit/move_changes.rs:67
let (destination_selector, destination_commit) =
editor.find_selectable_commit(destination_commit)?;
// Early return if source and destination are the same
if source_commit.id == destination_commit.id {
// Select the commit to get a valid selector, then just rebase (no-op)
let outcome = editor.rebase()?;
return Ok(MoveChangesOutcome {
rebase: outcome,
source_selector,
destination_selector,
});
}
// Step 1: Get the source commit and its tree
let source_tree_id = {
let source_commit = source_commit.clone().attach(editor.repo());
if source_commit.is_conflicted() {
bail!("Source commit must not be conflicted")
}
source_commit.tree
};
let (source_tree_without_changes_id, dropped_diffs) = create_tree_without_diff(
editor.repo(),
ChangesSource::Commit {
id: source_commit.id,
},
changes_to_move,
context_lines,
)?;
if !dropped_diffs.is_empty() {
bail!("Failed to extract described changes from source commit");
}
let new_source_commit_id = {View on GitHub (pinned to 58e5313667)