Version
0.22
Operating System
macOS
Distribution Method
dmg (Mac OS - Apple Silicon)
Describe the issue
Summary
When but pull rebases a branch across an upstream commit that renames files, the
rebased commits end up containing neither the old nor the new path — the files are
silently deleted from every rebased branch and from the working tree. No conflict is
raised, no warning printed, and but status reports a clean workspace.
A plain git rebase / git cherry-pick of the same commits onto the same base keeps
all the files.
Environment
but 0.22.0 (latest at time of report)
- macOS (darwin 25.6.0), APFS
- Large monorepo, ~14 applied stacks
Reproduction
- A branch is based on
main@A. None of its commits touch directory D.
- Upstream advances
main@A → main@B. Among those commits, one renames
src/test/snapshots/** → src/androidHostTest/snapshots/** (232 files, R100).
The same push contains ~1889 renames in total.
but pull.
Result — for every rebased stack:
$ git ls-tree -r --name-only origin/main -- .../src/androidHostTest/snapshots | wc -l
232
$ git ls-tree -r --name-only ^ -- .../src/androidHostTest/snapshots | wc -l # parent == origin/main
232
$ git ls-tree -r --name-only -- .../src/androidHostTest/snapshots | wc -l
0
$ git ls-tree -r --name-only -- .../src/test/snapshots | wc -l
0
The commit's parent is origin/main and contains the files, yet the commit's own
tree does not — so each rebased commit carries 232 spurious deletions it never made.
They are gone from the working tree too, and but diff shows "No diffs to show".
Plain git on the identical inputs is correct:
$ T=$(git merge-tree --write-tree --merge-base=^ origin/main )
$ git ls-tree -r --name-only $T -- .../src/androidHostTest/snapshots | wc -l
232
Knock-on effect: false "conflicts with existing stack"
Once a stack carries these phantom deletions, but apply starts rejecting unrelated
branches:
Failed to apply branch. 'X' conflicts with existing stack in the workspace:
This happened for a stack that additionally deleted files of its own (a removed Gradle
module plus two .java files). It reported a conflict against all 13 other stacks —
including ones touching entirely disjoint files — while git merge-tree --merge-base=origin/main X Y
exited 0 for every pair. but pull --check failed the same way:
Error: Failed to merge bases while cherry picking commit .
Encountered a conflict while merging the commit's new bases: <18 in-memory oids>.
Rebuilding all branches with plain-git cherry-picks onto the same base (same messages,
same change-id headers, correct trees) made the false conflicts disappear entirely:
all stacks applied, but pull --check clean.
Second false-conflict case: base skew
Two branches that add new files in disjoint paths, both rebased onto a base one commit
ahead of the workspace base, could not coexist:
Failed to apply branch. 'A' conflicts with existing stack in the workspace: B
Each applied fine alongside the other 14 stacks; only the pair collided, and
git merge-tree --merge-base=origin/main A B exited 0. Running but pull to align every
stack on the same base made both apply cleanly.
Impact
Silent data loss. Any rebase across a rename-heavy upstream range can drop the renamed
files from every applied branch with no conflict, no warning, and a clean-looking
but status. If those commits are pushed, the deletions land in the PR.
Expected
The rebased commit's tree should differ from its parent only by what the original commit
changed — matching git cherry-pick. If the rename cannot be tracked, it should surface
as a conflict, not a silent delete.
How to reproduce (Optional)
No response
Expected behavior (Optional)
No response
Relevant log output (Optional)
Version
0.22
Operating System
macOS
Distribution Method
dmg (Mac OS - Apple Silicon)
Describe the issue
Summary
When
but pullrebases a branch across an upstream commit that renames files, therebased commits end up containing neither the old nor the new path — the files are
silently deleted from every rebased branch and from the working tree. No conflict is
raised, no warning printed, and
but statusreports a clean workspace.A plain
git rebase/git cherry-pickof the same commits onto the same base keepsall the files.
Environment
but0.22.0 (latest at time of report)Reproduction
main@A. None of its commits touch directoryD.main@A → main@B. Among those commits, one renamessrc/test/snapshots/**→src/androidHostTest/snapshots/**(232 files,R100).The same push contains ~1889 renames in total.
but pull.Result — for every rebased stack:
$ git ls-tree -r --name-only origin/main -- .../src/androidHostTest/snapshots | wc -l
232
$ git ls-tree -r --name-only ^ -- .../src/androidHostTest/snapshots | wc -l # parent == origin/main
232
$ git ls-tree -r --name-only -- .../src/androidHostTest/snapshots | wc -l
0
$ git ls-tree -r --name-only -- .../src/test/snapshots | wc -l
0
The commit's parent is
origin/mainand contains the files, yet the commit's owntree does not — so each rebased commit carries 232 spurious deletions it never made.
They are gone from the working tree too, and
but diffshows "No diffs to show".Plain git on the identical inputs is correct:
$ T=$(git merge-tree --write-tree --merge-base=^ origin/main )
$ git ls-tree -r --name-only $T -- .../src/androidHostTest/snapshots | wc -l
232
Knock-on effect: false "conflicts with existing stack"
Once a stack carries these phantom deletions,
but applystarts rejecting unrelatedbranches:
Failed to apply branch. 'X' conflicts with existing stack in the workspace:
This happened for a stack that additionally deleted files of its own (a removed Gradle
module plus two
.javafiles). It reported a conflict against all 13 other stacks —including ones touching entirely disjoint files — while
git merge-tree --merge-base=origin/main X Yexited 0 for every pair.
but pull --checkfailed the same way:Error: Failed to merge bases while cherry picking commit .
Encountered a conflict while merging the commit's new bases: <18 in-memory oids>.
Rebuilding all branches with plain-git cherry-picks onto the same base (same messages,
same
change-idheaders, correct trees) made the false conflicts disappear entirely:all stacks applied,
but pull --checkclean.Second false-conflict case: base skew
Two branches that add new files in disjoint paths, both rebased onto a base one commit
ahead of the workspace base, could not coexist:
Failed to apply branch. 'A' conflicts with existing stack in the workspace: B
Each applied fine alongside the other 14 stacks; only the pair collided, and
git merge-tree --merge-base=origin/main A Bexited 0. Runningbut pullto align everystack on the same base made both apply cleanly.
Impact
Silent data loss. Any rebase across a rename-heavy upstream range can drop the renamed
files from every applied branch with no conflict, no warning, and a clean-looking
but status. If those commits are pushed, the deletions land in the PR.Expected
The rebased commit's tree should differ from its parent only by what the original commit
changed — matching
git cherry-pick. If the rename cannot be tracked, it should surfaceas a conflict, not a silent delete.
How to reproduce (Optional)
No response
Expected behavior (Optional)
No response
Relevant log output (Optional)