gitbutlerapp/gitbutler · error · anyhow::Error

'{commit_id_str}' is not a commit

Error message

'{commit_id_str}' is not a commit

What it means

Thrown by `but show <id>` resolution: `rev_parse_single` did resolve the input to an object, but `try_into_commit()` failed — the object is a tree, blob, or annotated tag rather than a commit. The show command only renders commits, so non-commit objects are rejected here.

Source

Thrown at crates/but/src/command/legacy/show.rs:64

                // Found the branch in a stack
                return show_branch(ctx, out, &head_name, verbose, &id_map);
            }
        }
    }

    // Not a branch, resolve the commit ID through the IdMap
    let cli_ids = id_map.parse_using_context(commit_id_str, ctx)?;

    let commit_id = if cli_ids.is_empty() {
        // If not found in IdMap, try to parse as a git commit ID directly
        let repo = ctx.repo.get()?;
        let obj = repo
            .rev_parse_single(commit_id_str)
            .map_err(|_| anyhow::anyhow!("Commit '{commit_id_str}' not found"))?;
        let commit = obj
            .object()?
            .try_into_commit()
            .map_err(|_| anyhow::anyhow!("'{commit_id_str}' is not a commit"))?;
        commit.id
    } else if cli_ids.len() > 1 {
        bail!(
            "Commit ID '{}' is ambiguous. Found {} matches",
            commit_id_str,
            cli_ids.len()
        );
    } else {
        match &cli_ids[0] {
            CliId::Commit {
                commit: CommitId { commit_id, .. },
                id: _,
            } => *commit_id,
            CliId::Branch(branch) => {
                // This is a branch identified by CLI ID, show the branch
                return show_branch(ctx, out, &branch.name, verbose, &id_map);
            }
            _ => {

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Resolve to the commit explicitly: `git rev-parse <id>^{commit}` and pass that sha
  2. For tags, pass the tag name or use the peel syntax `<tag>^{}` instead of the tag object sha
  3. If you really wanted to inspect a blob or tree, use `git show <sha>` / `git cat-file -p <sha>` — the but command is commit-only

Example fix

# before: tree object sha
but show $(git rev-parse HEAD^{tree})

# after: the commit itself
but show $(git rev-parse HEAD^{commit})
Defensive patterns

Strategy: type-guard

Validate before calling

let obj = repo.rev_parse_single(id.as_bytes())?.object()?;
if obj.kind != gix::objs::ObjectType::Commit {
    anyhow::bail!("{id} is a {:?}; pass a commit-ish like '<rev>^{{commit}}'", obj.kind);
}

Type guard

fn as_commit_id(obj: gix::objs::ObjectRef<'_>) -> Option<gix::hash::ObjectId> {
    obj.try_into_commit().ok().map(|commit| commit.id)}
// or on the id: repo.find_object(id)?.try_into_commit().is_ok()

Try / catch

match repo.rev_parse_single(id.as_bytes()) {
    Ok(id) => match id.object().and_then(|o| Ok(o.try_into_commit()?)) {
        Ok(commit) => { /* show commit */ }
        Err(_) => { /* peel: rev_parse "id^{commit}" or reject with guidance */ }
    },
    Err(err) => return Err(err.into()),
}

Prevention

When it happens

Trigger: Passing a tree id (`git rev-parse HEAD^{tree}`), a blob sha from diff output, or an annotated tag object id where the command expects a commit-ish.

Common situations: Pasting the tree hash shown in `git write-tree` or cat-file output; confusion between `<tag>` (peels fine when passed by name) and the tag object's own sha; tooling that emits whatever oid it saw last.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/d2d64eb3aeacb969. Report an issue: GitHub.