You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
CLI: edit-mode resolve finish rebuilds commits against their pre-pull base (silent upstream-file deletions); resolve apply BUG on add/add; graph panic on empty applied branch #15613
GitButler CLI 0.5.2168 through 0.5.2173 (all reproduced; updated mid-incident), macOS arm64 (Darwin 25.3).
Workspace: large private monorepo (~80 cargo workspace crates), 40–70 applied virtual branches, but pull spanning ~300 upstream commits after a stale base.
Summary
After a large but pull left ~86 conflicted commits across ~25 stacks, we hit an interlocking set of CLI failures. The two headline items (1, 2) look unreported; (3) and (4) are included because they forced the paths that trigger (1) and (2), and may share a root with #15112 / #15127 / #14497.
1. but resolve finish rebuilds the commit against its PRE-pull base — the rewritten commit silently deletes everything main added since (data loss shaped)
For commits whose conflicts can only be resolved in edit mode (but resolve <sha> … but resolve finish), the checkout that edit mode materializes is based on the commit's original, pre-pull base — files that upstream added since are missing from the worktree, and finish records that as the commit's new tree. Every edit-mode-finished commit silently became a commit that deletes upstream content:
measured across ~15 commits resolved this way: the workspace tree afterwards lacked 129 files that exist on origin/main (whole applications, crates, CI scripts);
everything stays green — no error, no warning; we only caught it via git diff --diff-filter=D $(git merge-base <branch> origin/main) <branch> per branch;
finish additionally absorbs unrelated uncommitted workspace changes into the commit being resolved (an untouched file from the uncommitted area appeared as M in the finished commit's tree).
The pattern is easy to observe live: enter edit mode on a conflicted commit after a big pull and run git diff --name-status HEAD~1 — the D entries are upstream files about to be recorded as deletions.
Related but distinct: #15499 (deletions via upstream rename; ours needs no rename) and #15112 (finish erroring, not corrupting).
Recovery that worked: but oplog restore to a pre-edit-mode snapshot, then a repaired flow — inside edit mode, snapshot git status --porcelain first, write HEAD~1's blob over every path in git diff --name-only HEAD~1 (delete paths absent there), fix the marker files, finish, then restore only the files that were uncommitted before entry. That reliably produces a clean collapsed commit with zero deletions vs merge-base.
2. but resolve apply fails with BUG: all conflicts of commit … were resolved, yet re-merging the narrowed trees still conflicts on whole-file add/add conflicts
but resolve apply <file> --commit <branch> --ours (also --theirs, and piping content into apply <file>:1) consistently fails with the BUG: error whenever the conflict spans an entire file that both sides added — typical when a squash-merged branch's original commits collide with the landed final files. Region-shaped conflicts apply fine. This is what forces users into edit mode and therefore into bug (1).
3. Panic crates/but-graph/src/init/post.rs:1113 — called Option::unwrap() on a None value — when a zero-commit branch is applied
With an applied branch containing no commits (created via but branch new <name> --above <commit> to name an anonymous segment, or an integrated branch that came back empty), but branch new and but commit panic at but-graph/src/init/post.rs:1113 every time. but status / but diff still work, which makes it look like corruption rather than a graph-init issue. Unapplying the empty branch immediately unblocks everything. Reproduced on 0.5.2168, 0.5.2171, 0.5.2172.
4. but pull hard-fails Failed to merge bases while cherry picking <workspace commit> when applied lanes are empty landed relics — refs-level analysis shows nothing wrong
After several branches' content had landed upstream (squash merges) and their local commits had been collapsed to empty, but pull failed repeatedly with the merge-bases error naming only in-memory ids — while every applied branch ref merged cleanly against origin/main both individually and pairwise (verified exhaustively with git merge-tree --write-tree across all ~1,300 pairs). The fix: audit each applied lane with git diff --shortstat $(git merge-base <lane> origin/main) <lane> and but unapply every lane whose tree equals its merge-base (25 of 52). The very next but pull succeeded with all remaining lanes rebased clean. Suggests the base-merge is choking on conflict-era metadata attached to empty lanes — possibly the same stale/phantom-parent shape as #15112 / #14497, and these relic lanes are the "(no changes)" commits of #15127.
Expected
Edit-mode finish should rebuild the commit against its current (rebased) parent, never the pre-pull base, and should not fold unrelated uncommitted changes into the commit.
apply --ours/--theirs should handle whole-file add/add conflicts (or fail with a actionable message rather than BUG:).
Version
GitButler CLI 0.5.2168 through 0.5.2173 (all reproduced; updated mid-incident), macOS arm64 (Darwin 25.3).
Workspace: large private monorepo (~80 cargo workspace crates), 40–70 applied virtual branches,
but pullspanning ~300 upstream commits after a stale base.Summary
After a large
but pullleft ~86 conflicted commits across ~25 stacks, we hit an interlocking set of CLI failures. The two headline items (1, 2) look unreported; (3) and (4) are included because they forced the paths that trigger (1) and (2), and may share a root with #15112 / #15127 / #14497.1.
but resolve finishrebuilds the commit against its PRE-pull base — the rewritten commit silently deletes everything main added since (data loss shaped)For commits whose conflicts can only be resolved in edit mode (
but resolve <sha>…but resolve finish), the checkout that edit mode materializes is based on the commit's original, pre-pull base — files that upstream added since are missing from the worktree, andfinishrecords that as the commit's new tree. Every edit-mode-finished commit silently became a commit that deletes upstream content:git diff --diff-filter=D $(git merge-base <branch> origin/main) <branch>per branch;finishadditionally absorbs unrelated uncommitted workspace changes into the commit being resolved (an untouched file from the uncommitted area appeared asMin the finished commit's tree).The pattern is easy to observe live: enter edit mode on a conflicted commit after a big pull and run
git diff --name-status HEAD~1— theDentries are upstream files about to be recorded as deletions.Related but distinct: #15499 (deletions via upstream rename; ours needs no rename) and #15112 (finish erroring, not corrupting).
Recovery that worked:
but oplog restoreto a pre-edit-mode snapshot, then a repaired flow — inside edit mode, snapshotgit status --porcelainfirst, writeHEAD~1's blob over every path ingit diff --name-only HEAD~1(delete paths absent there), fix the marker files,finish, then restore only the files that were uncommitted before entry. That reliably produces a clean collapsed commit with zero deletions vs merge-base.2.
but resolve applyfails withBUG: all conflicts of commit … were resolved, yet re-merging the narrowed trees still conflictson whole-file add/add conflictsbut resolve apply <file> --commit <branch> --ours(also--theirs, and piping content intoapply <file>:1) consistently fails with theBUG:error whenever the conflict spans an entire file that both sides added — typical when a squash-merged branch's original commits collide with the landed final files. Region-shaped conflicts apply fine. This is what forces users into edit mode and therefore into bug (1).3. Panic
crates/but-graph/src/init/post.rs:1113—called Option::unwrap() on a None value— when a zero-commit branch is appliedWith an applied branch containing no commits (created via
but branch new <name> --above <commit>to name an anonymous segment, or an integrated branch that came back empty),but branch newandbut commitpanic atbut-graph/src/init/post.rs:1113every time.but status/but diffstill work, which makes it look like corruption rather than a graph-init issue. Unapplying the empty branch immediately unblocks everything. Reproduced on 0.5.2168, 0.5.2171, 0.5.2172.4.
but pullhard-failsFailed to merge bases while cherry picking <workspace commit>when applied lanes are empty landed relics — refs-level analysis shows nothing wrongAfter several branches' content had landed upstream (squash merges) and their local commits had been collapsed to empty,
but pullfailed repeatedly with the merge-bases error naming only in-memory ids — while every applied branch ref merged cleanly against origin/main both individually and pairwise (verified exhaustively withgit merge-tree --write-treeacross all ~1,300 pairs). The fix: audit each applied lane withgit diff --shortstat $(git merge-base <lane> origin/main) <lane>andbut unapplyevery lane whose tree equals its merge-base (25 of 52). The very nextbut pullsucceeded with all remaining lanes rebased clean. Suggests the base-merge is choking on conflict-era metadata attached to empty lanes — possibly the same stale/phantom-parent shape as #15112 / #14497, and these relic lanes are the "(no changes)" commits of #15127.Expected
finishshould rebuild the commit against its current (rebased) parent, never the pre-pull base, and should not fold unrelated uncommitted changes into the commit.apply --ours/--theirsshould handle whole-file add/add conflicts (or fail with a actionable message rather thanBUG:).Happy to provide more detail on any of these; the repo is private, but all four reproduce on state we can describe precisely.