gitbutlerapp/gitbutler · error
cannot amend into : the commit is immutable (not part of a…
Error message
cannot amend into {target_id}: the commit is immutable (not part of a mutable branch) What it means
Amending is only allowed into commits on a mutable branch. If the target commit is an immutable pick (e.g. part of reviewed/landed history that GitButler treats as read-only), amending it would create an unreachable commit, so the library refuses with this error.
Solutions
- Target a commit on a mutable branch instead
- If the commit should be mutable, move/assign it into the workspace (mutable) branch first
- If the change is needed on top of immutable history, create a new commit rather than amending
Example fix
// before editor.commit_amend(landed_commit_id, msg, source)?; // immutable // after let (sel, _) = editor.find_selectable_commit(landed_commit_id)?; // pick a workspace commit instead of landed_commit_id editor.commit_amend(workspace_commit_id, msg, source)?;
Defensive patterns
Strategy: validation
Validate before calling
fn is_mutable(editor: &WorkspaceEditor, commit: impl Into<CommitSelector>) -> anyhow::Result<bool> {
let (sel, _) = editor.find_selectable_commit(commit)?;
Ok(matches!(editor.lookup_step(sel)?, Step::Pick(p) if p.mutable))
} Type guard
fn is_mutable_pick(step: &Step) -> bool { matches!(step, Step::Pick(p) if p.mutable) } Try / catch
match editor.commit_amend(target, msg, source) {
Err(e) if e.to_string().contains("immutable") => create_new_commit_instead(),
other => other,
} Prevention
- Only amend commits on mutable workspace branches
- Treat landed/integrated commits as read-only
- Verify the Pick step's mutable flag before amend operations
When it happens
Trigger: Calling commit_amend with a commit selector whose Step::Pick has mutable == false — i.e. the commit lives on an immutable base branch rather than a mutable workspace branch.
Common situations: Trying to amend a commit that has already been pushed/landed to an integrated base branch; scripts that blindly take the first commit in a list which happens to be an upstream commit.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Cannot amend a conflicted commit
- Cannot uncommit changes from a conflicted commit
- Couldn't find workspace head.
- Destination commit must not be conflicted
- Expected to be in open workspace mode
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/b1c4fe89f7da19b8.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-workspace/src/commit/commit_amend.rs:76
changes: Vec<DiffSpec>,
context_lines: u32,
source: ChangeSource<'_>,
) -> Result<CommitAmendOutcome<'ws, 'meta, M>> {
let (target_selector, target) = editor.find_selectable_commit(commit)?;
let target_id = target.id;
if target.attach(editor.repo()).is_conflicted() {
bail!("Cannot amend a conflicted commit")
}
// An immutable pick would be replaced in the step graph while the rebase copies
// its descendants verbatim and never moves the (immutable) refs pointing at it -
// the amended commit would be written but stay unreachable, with this function
// still reporting success. Fail fast instead.
let Step::Pick(target_pick) = editor.lookup_step(target_selector)? else {
bail!("BUG: Expected pick step from commit selector. This should never happen");
};
if !target_pick.mutable {
bail!(
"cannot amend into {target_id}: the commit is immutable (not part of a mutable branch)"
);
}
// Clone before `create_commit` consumes the vec — needed afterwards
// to determine which changes were consumed (not rejected).
let all_changes = changes.clone();
let create_out = create_commit(
source.repo(&editor),
Destination::AmendCommit {
commit_id: target_id,
new_message: None,
},
changes,
context_lines,
)?;
let Some(new_commit_id) = create_out.new_commit else {View on GitHub (pinned to 58e5313667)