#15613

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

Openrednixopened 2mo ago

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 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

  1. 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.
  2. apply --ours/--theirs should handle whole-file add/add conflicts (or fail with a actionable message rather than BUG:).
  3. Zero-commit applied branches shouldn't panic graph init.
  4. Empty (fully-integrated) lanes shouldn't poison the pull's base merge — ideally they'd be detected as integrated and removed, per #15127.

Happy to provide more detail on any of these; the repo is private, but all four reproduce on state we can describe precisely.

Activity

2
krlvi2mo ago

hey @rednix thanks for reporting this and thanks for using the nightly to help spot this. I am having trouble creating repro for this - would you mind asking your agent to use the information that is on your machine already (app logs https://docs.gitbutler.com/development/debugging#logs , agent traces/ transcripts from when the issue occurred, data in /.git/gitbutler ) to try to create repro scenarios that can be shared here?

Thank you! 🙏

krlvi2mo ago

hey @rednix thanks for reporting this and thanks for using the nightly to help spot this. I am having trouble creating repro for this - would you mind asking your agent to use the information that is on your machine already (app logs https://docs.gitbutler.com/development/debugging#logs , agent traces/ transcripts from when the issue occurred, data in /.git/gitbutler ) to try to create repro scenarios that can be shared here?

Thank you! 🙏