gitbutlerapp/gitbutler · error

Cannot compute diff specs for branch `{}`

Error message

Cannot compute diff specs for branch `{}`

What it means

DiffSpecBuilder::push_changes_id turns a resolved change selection into diff specs, but only handles hunks, path prefixes, committed files, commits, and uncommitted changes. Branch-level selections (CliId::Branch) are supposed to be expanded into files during resolution; if one reaches the builder unexpanded there is no single diff spec for a whole branch, so it bails with the branch name.

Source

Thrown at crates/but/src/utils/diff_specs.rs:71

            CliId::UncommittedHunkOrFile(uncommitted) => {
                self.push_changes_from_uncommitted(uncommitted)
            }
            CliId::PathPrefix {
                id: _,
                hunks,
                source: _,
            } => self.push_changes_from_path_prefix(hunks),
            CliId::CommittedFile {
                committed_file:
                    CommittedFileId {
                        commit_id,
                        path,
                        change_id: _,
                    },
                id: _,
            } => self.push_changes_from_committed_file(*commit_id, path.as_ref()),
            CliId::Branch(branch) => {
                anyhow::bail!("Cannot compute diff specs for branch `{}`", branch.name)
            }
            CliId::Commit {
                commit:
                    CommitId {
                        commit_id,
                        change_id: _,
                    },
                id: _,
            } => self.push_changes_from_commit(*commit_id),
            CliId::Uncommitted { id: _ } => self.push_changes_from_uncommitted_area(),
            // A worktree is expanded into its files during resolution, so the
            // builder only ever sees hunks that already come from its own repo.
            CliId::Worktree { name, .. } => {
                anyhow::bail!("Cannot compute diff specs for worktree `{name}`")
            }
            CliId::Stack { .. } => {
                anyhow::bail!("Cannot compute diff specs for stacks")
            }

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Expand the branch into its files/commits during resolution before computing diff specs
  2. Use a branch-diff API instead of the hunk/file diff-spec builder
  3. If you maintain the caller, match on CliId and reject Branch early with a clearer message

Example fix

// before
for id in ids {
    builder.push_changes_from_id(id)?; // panics-bails on CliId::Branch
}
// after
for id in ids {
    if let CliId::Branch(b) = id { return Err(anyhow::anyhow!("expand branch `{}` first", b.name)); }
    builder.push_changes_from_id(id)?;
}
Defensive patterns

Strategy: type-guard

Type guard

fn supports_diff_specs(id: &CliId) -> bool {
    !matches!(id, CliId::Branch { .. })
}
// guard every id before building specs:
assert!(ids.iter().all(|id| supports_diff_specs(id)), "branch ids must be expanded first");

Prevention

When it happens

Trigger: Calling the diff-spec computation with a CliId::Branch that skipped the expansion step - a new caller feeding unresolved ids, or a resolution-order change that lets branch ids through to the builder.

Common situations: Programmatic users of the change-selection APIs, refactors that bypass the resolver, new command implementations reusing DiffSpecBuilder directly.

Related errors


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