GitoxideLabs/gitoxide · error · anyhow::Error

`--update-head` requires `--message`

Error message

`--update-head` requires `--message`

What it means

`--update-head` writes a merge commit to the current branch, and git requires commits to carry a message. The command validates up front that a `--message` was supplied whenever `--update-head` is set, and bails otherwise, since it would otherwise have to invent a commit message.

Solutions

  1. Add `--message "..."` to the command when using `--update-head`.
  2. Drop `--update-head` if you don't intend to create a commit on the current branch.
  3. In wrapper code, validate the flag pair before spawning the command to give users a clearer error.

Example fix

// before
gix repo merge tree --update-head <base> <ours> <theirs>
// after
gix repo merge tree --update-head --message "Merge branch 'feature'" <base> <ours> <theirs>
Defensive patterns

Strategy: validation

Validate before calling

if update_head && message.is_none() {
    return Err(anyhow::anyhow!("--update-head requires --message"));
}

Try / catch

match run_merge_tree(opts) {
    Err(e) if e.to_string().contains("requires `--message`") => eprintln!("supply a commit message when using --update-head"),
    r => r?,
}

Prevention

When it happens

Trigger: Calling `tree()` in gitoxide-core/src/repository/merge/tree.rs with `update_head == true` while `message` is `None`.

Common situations: Automating merges in CI where only the flag combination was copied from a non-committing invocation; forgetting that an in-memory merge and a head-updating merge have different flag requirements.

Understand the failure class

Background: "--flag is required" and "must specify" CLI errors: how missing-required-flag validation works and how to fix it — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at gitoxide-core/src/repository/merge/tree.rs:51

        theirs: BString,
        Options {
            format,
            file_favor,
            tree_favor,
            in_memory,
            debug,
            message,
            update_head,
        }: Options,
    ) -> anyhow::Result<()> {
        if format != OutputFormat::Human {
            bail!("JSON output isn't implemented yet");
        }
        if update_head && in_memory {
            bail!("`--update-head` cannot be used with `--in-memory` - cannot set head to nothing");
        }
        if update_head && message.is_none() {
            bail!("`--update-head` requires `--message`");
        }
        repo.object_cache_size_if_unset(repo.compute_object_cache_size_for_tree_diffs(&**repo.index_or_empty()?));
        if in_memory || message.is_some() {
            repo.objects.enable_object_memory();
        }
        let (base_ref, base_id) = refname_and_tree(&repo, base)?;
        let (ours_ref, ours_id) = refname_and_tree(&repo, ours)?;
        let (theirs_ref, theirs_id) = refname_and_tree(&repo, theirs)?;

        let options = repo
            .tree_merge_options()?
            .with_file_favor(file_favor)
            .with_tree_favor(tree_favor);
        let base_id_str = base_id.to_string();
        let ours_id_str = ours_id.to_string();
        let theirs_id_str = theirs_id.to_string();
        let labels = gix::merge::blob::builtin_driver::text::Labels {
            ancestor: base_ref

View on GitHub (pinned to e73179060b)