gitbutlerapp/gitbutler · error

is a remote commit

Error message

{commit} is a remote commit

What it means

render_committed_file resolves a commit id through the IdMap and only supports local commits. Remote commits (e.g. commits known from origin that aren't in the local workspace) have no committed-file rendering implemented, so the function bails with '<commit> is a remote commit'. The comment notes this is a deliberate current limitation, not a bug.

Solutions

  1. Fetch the commit so it becomes local: `git fetch` / `but` fetch flow, then retry with the local commit.
  2. Use the corresponding local commit id instead of the remote one.
  3. As a fallback, view the remote commit with plain git: `git show <sha> -- <path>`.

Example fix

// before
but diff <remote-commit-id> -- src/main.rs
// after
git fetch origin && but diff <local-commit-id> -- src/main.rs
Defensive patterns

Strategy: validation

Validate before calling

# ensure the commit exists locally before rendering its diff
git cat-file -e "$SHA"^{commit} 2>/dev/null || { git fetch origin; }

Prevention

When it happens

Trigger: Calling render_committed_file (or a CLI command that renders a committed file diff) while passing a remote commit id — CommitWithId::Remote(_) from id_map.get_commit_by_id.

Common situations: Referencing a commit by an id that resolves to an upstream/remote-only commit; running file-diff commands against commits that exist only on the remote.

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/8dc27cc10d689693. Report an issue: GitHub.

Appendix: source

Thrown at crates/but/src/utils/diff_rendering.rs:818

pub fn render_committed_file(
    commit: ObjectId,
    path: BString,
    ctx: &Context,
    theme: &'static Theme,
    id_gen: &mut IdGen<'_>,
    options: Options,
    out: &mut dyn DiffLineWriter,
) -> anyhow::Result<()> {
    let mut id_gen = id_gen.scoped("committed_file");
    let mut id_gen = id_gen.scoped(commit);

    let id_map = IdMap::legacy_new_from_context(ctx)?;

    let workspace_commit = match id_map.get_commit_by_id(commit)? {
        CommitWithId::Local(commit) => commit,
        // We don't resolve committed files for remote commits at this time, so there is no current
        // use case for rendering a committed file from a remote commit.
        CommitWithId::Remote(_) => bail!("{commit} is a remote commit"),
    };
    let repo = ctx.repo.get()?;
    let tree_changes = workspace_commit
        .tree_changes_using_repo(&repo)?
        .into_iter()
        .filter(|tree_change| tree_change.inner.path == path)
        .filter_map(|tree_change| {
            let patch = tree_change
                .inner
                .unified_patch(&repo, ctx.settings.context_lines)
                .ok()
                .flatten()?;
            Some((tree_change, patch))
        })
        .collect::<Vec<_>>();

    if !options.skip_line_stats {
        let mut line_stats = LineStats::default();

View on GitHub (pinned to 58e5313667)