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

  1. Target a commit on a mutable branch instead
  2. If the commit should be mutable, move/assign it into the workspace (mutable) branch first
  3. 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

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


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)