Skip to content

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

Description

@rednix

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:1113called 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 Commits already contained in the new base stay applied as "(no changes)", and can still be handed to me as conflicts #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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    feedback requestedFeedback was requested to help resolve the issue

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions