GitoxideLabs/gitoxide · error

rewording the commit would cause a merge conflict

Error message

rewording the commit would cause a merge conflict

What it means

`Perform::complete` unwraps the result of a reword operation: a `Perform::Complete` yields the outcome, but a `Perform::Conflict` means the rebase machinery hit a merge conflict while rewording, which this API does not support interactively. The library converts that into an error stating that rewording would cause a merge conflict.

Solutions

  1. Abort the reword and resolve conflicts manually with an interactive rebase (`git rebase -i <base>`), editing the message there and resolving conflicts as they appear.
  2. Reword only the tip commit (no descendants to replay) to avoid the conflict path.
  3. Use `git commit --amend` after checking out the commit, then rebase descendants with conflict resolution.

Example fix

// before: non-interactive reword that hits conflict
let outcome = perform.complete()?; // bails with conflict message
// after: shell-based resolution
// git rebase -i <base>   # mark the commit as `reword`, resolve conflicts as prompted
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check that rewording the tip only (no descendants) avoids replay conflicts
let head = repo.head_id()?.detach();
if commit_id != head {
    eprintln!("rewording non-tip commits may conflict; prefer git rebase -i");
}

Try / catch

match reword_perform.complete() {
    Err(e) if e.to_string().contains("merge conflict") => {
        eprintln!("fall back to interactive rebase: git rebase -i <base>, mark the commit as reword");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling reword (via `complete`) on a commit whose changes conflict with the surrounding history — typically rewording a commit while `relocate_after_editor` must replay descendants, and those replays clash with the reworded content.

Common situations: Rewording a commit whose message is referenced by patches of later commits is usually fine, but content-level conflicts arise when descendants touch the same lines and the reword path also rewrites content; rebased histories with overlapping edits; interactive edits of deep history.

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


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/241adaec65217ab2. Report an issue: GitHub.

Appendix: source

Thrown at gix-tix/src/edit/reword.rs:43

pub(crate) struct Outcome {
    pub target: gix::ObjectId,
    pub commit: Option<gix::ObjectId>,
    pub enrichment: Option<crate::enrich::Enrichment>,
    pub ref_rewrites: Vec<rebase::RefRewrite>,
    pub ref_changes: Vec<super::undo::RefChange>,
}

pub(crate) enum Perform {
    Complete(Outcome),
    Conflict(rebase::Conflict),
}

impl Perform {
    fn complete(self) -> Result<Outcome> {
        match self {
            Perform::Complete(outcome) => Ok(outcome),
            Perform::Conflict(_) => anyhow::bail!("rewording the commit would cause a merge conflict"),
        }
    }
}

pub(crate) fn relocate_after_editor(
    repo: &gix::Repository,
    revisions: &[std::ffi::OsString],
    hidden_revisions: &[std::ffi::OsString],
    change_id: gix::hash::ChangeId,
) -> Result<(crate::history::HistoryGraph, gix::ObjectId)> {
    let graph = super::loaded_view_graph_with_hidden(repo, revisions, hidden_revisions)?;
    let mut matches = Vec::new();
    for id in graph.stored_commit_ids() {
        if crate::change_id::for_commit(repo, id)? == change_id {
            matches.push(id);
        }
    }
    match matches.as_slice() {

View on GitHub (pinned to e73179060b)