From adbc3bd206a6b9ad89f29d6a679166ed9b6add27 Mon Sep 17 00:00:00 2001 From: Sameen Karim Date: Sun, 26 Jul 2026 14:30:42 -0400 Subject: [PATCH] Stop reporting rebases that git refused to run as successes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `gh stack rebase` and `gh stack sync` could print a full success report while leaving the stack exactly where it was: ✓ Trunk main fast-forwarded to 84bacb3 ✓ Rebased phil/trd-mark-edge-drills onto main ✓ Rebased phil/implement-in-place-apis onto phil/trd-mark-edge-drills All branches in stack rebased locally with main …after which `git merge-base --is-ancestor main HEAD` still reported that main was not an ancestor. The cause is in `tryAutoResolveRebase`, the shared error path for every rebase: it returned nil whenever no rebase was in progress. That is only a valid success signal *after* an auto-`--continue`. On the first check it means `git rebase` exited non-zero without ever starting — a dirty working tree, a branch checked out in another worktree, a missing local trunk, or a stale rebase still in progress. All of those were silently swallowed and the cascade carried on reporting success. - Return a typed `*git.RebaseStartError` for a rebase that never started, and detect an already-running rebase up front so its leftover conflicts are not mistaken for this rebase's. - Treat that error as fatal in `cascadeRebase` (and in `modify`) rather than a conflict, so no bogus rebase state is written and git's own message shows. - Add the preflight checks `modify` already had — no rebase in progress, clean working tree — to `rebase` and `sync`, with `--autostash` to opt out of the clean-tree requirement by passing `--autostash` through to git. - Verify after every cascade that each branch really does sit on top of its parent, and fail instead of printing the success summary. `sync` checks before pushing so an unrebased stack is never force-pushed. Also fixes duplicate commits: the cascade passed the parent's current tip as the `--onto` upstream with no staleness guard, so a parent amended, reordered, or squash-merged out of band had its old commits replayed onto the child. The merged-PR path guarded this but fell back to `merge-base(newBase, branch)`, which replays squashed commits. Both paths now use the latest commit the child genuinely contains. Smaller fixes found alongside: - `git rebase