Rewriting a local stack of branches in place: rebase --autosquash --update-refs
For an unpushed stack of branches (pr1 <- pr2 <- ... <- pr5) where a later fix belongs in an earlier PR: commit it on the tip with git commit --fixup=<target-sha>, then from the tip run GIT_SEQUENCE_EDITOR=true git rebase -i --autosquash --update-refs <base>. --update-refs (git >= 2.38) moves every intermediate branch ref along with its commits, so all five branches stay stacked and each line lands once in its final form. To insert a brand-new commit into an earlier PR (not a fixup), point GIT_SEQUENCE_EDITOR at a small script that moves its pick line to just after the last commit of that PR, before that branch's update-ref line. Untracked files in the worktree don't block the rebase.
Confirmed on git 2.43.0 with a three-branch stack (main <- pr1 <- pr2 <- pr3). After
git commit --fixup=<pr1 sha>on the tip andGIT_SEQUENCE_EDITOR=true git rebase -i --autosquash --update-refs main, git reported "Updated the following refs with --update-refs: refs/heads/pr1, refs/heads/pr2". pr1, pr2 and pr3 all showed the fixed line, each branch still had one commit on top of the previous, and an unrelated untracked file survived.For the insertion case, it helps to see the todo list that --update-refs generates (
GIT_SEQUENCE_EDITOR=cat):pick 3fdbf0d pr1 update-ref refs/heads/pr1 pick 4bf7fa8 pr2 update-ref refs/heads/pr2 pick bc13654 pr3
So "before that branch's update-ref line" means: move the new
pickaboveupdate-ref refs/heads/prN. The tip branch gets no update-ref line, because it is the branch being rebased.One caveat on the last sentence: untracked files don't block the rebase *unless a replayed commit creates a file with the same path*. With a stack containing "add f9" then "rm f9" and an untracked
f9in the worktree, the rebase stopped at that pick with "The following untracked working tree files would be overwritten by merge: f9".git rebase --abortrestored everything, with the untracked file intact.