gitbutlerapp/gitbutler · error
BUG: Cannot possibly not have any changes here
Error message
BUG: Cannot possibly not have any changes here
What it means
Panic while resolving committed-changes sources for move: after pushing each selected committed file via push_changes_from_committed_file, the accumulated DiffSpec list is empty. Selection guarantees at least one committed file, so an empty result means the builder matched nothing - a selected path that is absent from the referenced commit (rename, case or unicode-normalization mismatch) silently appended no changes.
Source
Thrown at crates/but/src/command/legacy/move.rs:840
(None, Some(files), None) => {
let mut builder = DiffSpecBuilder::new(repo, context_lines);
let source_commit = files.head.0.clone();
for (commit, path) in files {
if commit.as_ref() != source_commit.as_ref() {
return Err(
bad_input("Cannot move changes from multiple commits")
.hint("Move changes from a single commit at first, then squash additional changes into the new commit")
.into()
);
}
builder.push_changes_from_committed_file(commit.commit_id, path.as_bstr())?;
}
// It doesn't appear as if we need to sort DiffSpecs when they're resolved on a file
// level. For the future hunk level DiffSpecs we may need to, however.
let changes = NonEmpty::from_vec(builder.into_diff_specs())
.expect("BUG: Cannot possibly not have any changes here");
Ok(ResolvedSources::CommittedChanges((source_commit, changes)))
}
(None, None, Some(branches)) => {
if !branches.tail.is_empty() {
Err(bad_input("Branches can only be moved one at a time")
.arg_name("<SOURCES>")
.into())
} else {
Ok(ResolvedSources::Branch(branches.head))
}
}
(None, None, None) => panic!("BUG: It should not be possible to omit sources"),
(_, _, _) => Err(bad_input("Mixing source types is not allowed")
.hint("You can only move one kind of source (e.g. commits) at a time")
.arg_name("<SOURCES>")
.into()),
}View on GitHub (pinned to 2497b8007a)
Solutions
- Re-derive the selection from the current commit state (diff view or 'but commit list') and retry
- Check exact path casing and unicode normalization against 'git ls-tree <commit>'
- Maintainer: make push_changes_from_committed_file report when a path matches nothing so the failure is explained instead of panicking later
Example fix
// before
builder.push_changes_from_committed_file(commit.commit_id, path.as_bstr())?;
// ...
let changes = NonEmpty::from_vec(builder.into_diff_specs())
.expect("BUG: Cannot possibly not have any changes here");
// after - fail per path so an empty result is explained where it happens
let specs = builder.push_changes_from_committed_file(commit.commit_id, path.as_bstr())?;
if specs == 0 {
anyhow::bail!("path {} not found in commit {}", path.as_bstr(), commit.commit_id);
} Defensive patterns
Strategy: validation
Validate before calling
// Before building the move operation, confirm each selected path exists in the commit
let tree = repo.find_tree(commit_id)?;
for path in paths.iter() {
if tree.lookup_entry_by_path(path.as_bstr() as &bstr::BStr).is_err() {
anyhow::bail!("path {} not present in commit {}", path.as_bstr(), commit_id);
}
} Prevention
- Derive file selections from the commit's actual tree (ls-tree/diff) instead of stale cached state
- Normalize path casing and unicode form (NFC) consistently across platforms
- Have builders report 'matched 0 entries' as an error at push time rather than an empty result
When it happens
Trigger: Selecting a committed file whose path no longer exists in that commit (renamed between commits); case-sensitivity differences on case-insensitive filesystems; NFC/NFD unicode normalization differences (typical on macOS); a file selection computed against a different commit than the one passed in.
Common situations: Moving changes right after a rename; paths copied from older tooling output with different normalization; cross-platform checkouts normalizing filenames.
Related errors
- changes being non-empty means paths are non-empty
- classified branches are guaranteed to be non-empty
- committed files being non-empty means paths are non-empty
- BUG: No CLI argument for resolved commit id
- source branches is already checked to be non-empty
AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17).
Data as JSON: /api/errors/93052e244ca06155.
Report an issue: GitHub.