GitoxideLabs/gitoxide · error · anyhow::Error

the new commit would be empty; use new-empty instead

Error message

the new commit would be empty; use new-empty instead

What it means

When creating a commit in Insert mode, the prepared commit content was empty (no changes relative to the parent), which the tool treats as a user mistake. It directs the user to the explicit `new-empty` mode, which allows empty commits intentionally.

Solutions

  1. Use the `new-empty` command/mode instead of `new`/insert to intentionally create an empty commit.
  2. Verify there are actually staged/selected changes producing a diff against the parent.
  3. Check the chosen parent commit is the one you intend to diff against.

Example fix

// before
app.run(CreateMode::Insert)?; // fails when nothing changed
// after
if prepared_changes.is_empty() {
    app.run(CreateMode::InsertEmpty)?;
} else {
    app.run(CreateMode::Insert)?;
}
Defensive patterns

Strategy: validation

Validate before calling

let has_changes = !status_diff_against_parent(repo)?.is_empty();
if !has_changes { /* use new-empty or abort */ }

Try / catch

match create_commit(terminal, repo, CreateMode::Insert) {
    Err(err) if err.to_string().contains("would be empty") => {
        create_commit(terminal, repo, CreateMode::InsertEmpty)?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: Invoking commit creation with CreateMode::Insert when the working tree/selected changes produce no diff against the parent commit, so prepare() reports prepared.is_empty == true.

Common situations: Trying to insert a commit after all changes were already committed or reverted; editing produced no modifications; wrong parent/base selected so the diff is empty.

Related errors


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

Appendix: source

Thrown at gix-tix/src/lib.rs:5903

fn create_commit(
    terminal: &mut ratatui::DefaultTerminal,
    repository_path: &Path,
    bare: bool,
    graph: &HistoryGraph,
    parent: Option<gix::ObjectId>,
    mode: CreateMode,
    enhanced_keyboard: bool,
) -> Result<Option<edit::rebase::Perform>> {
    let mut repository =
        open_repository(repository_path, bare, false).context("could not open repository before creating commit")?;
    repository.object_cache_size(None);
    let mut prepared = if matches!(mode, CreateMode::InsertEmpty) {
        edit::create::prepare_empty(repository, parent)?
    } else {
        edit::create::prepare(repository, parent)?
    };
    if matches!(mode, CreateMode::Insert) && prepared.is_empty {
        anyhow::bail!("the new commit would be empty; use new-empty instead");
    }
    let editor = prepared.editor.take().expect("prepared commits have an editor");
    let Some(edited) = edit::edit_document(
        terminal,
        editor,
        &prepared.document,
        &format!("tix-commit-{}.md", std::process::id()),
        enhanced_keyboard,
    )?
    else {
        return Ok(None);
    };
    let outcome = match mode {
        CreateMode::Insert | CreateMode::InsertEmpty => run_with_todo_progress(terminal, move |report| {
            let mut repository = open_repository(repository_path, bare, false)
                .context("could not reopen repository after editing commit")?;
            repository.object_cache_size(None);
            edit::create::apply_conflict_reporting(repository, graph, prepared, &edited, report)

View on GitHub (pinned to e73179060b)