3.4 KiB
name, description
| name | description |
|---|---|
| github-stack-rebase | Safely rebase and update a dependent GitHub pull-request stack. Use when asked to refresh, repair conflicts in, rebase, or force-push a stacked set of Git branches/PRs onto a newer base branch, especially when descendants must be rewritten in order. |
GitHub Stack Rebase
Use this workflow for a stack that has already been identified as ordered from a base branch through one or more dependent PR branches. Never infer a stack solely from ticket keys or branch names.
Confirm the rewrite boundary
- Obtain explicit authorization to rewrite and force-push the named branches. Treat an explicit request to rebase the stack as authorization only for those branches.
- Check
git status --short --branch. Stop if the worktree is dirty. - Fetch the remote:
git fetch <remote> --prune. - Identify each PR's actual head branch and verify every parent relation with
git merge-base --is-ancestor <parent> <child>. A ticket key may have multiple, unrelated branches. - Report the complete, bottom-to-top chain and its base. Stop if a head branch is missing, the graph is not linear, or the requested base is ambiguous.
Preserve recovery points
Before changing a branch, record its fetched remote SHA and create a unique local backup ref for every branch, for example:
backup_ref="codex/rebase-backup/$(date +%Y%m%d-%H%M%S)/<branch>"
git branch "$backup_ref" "origin/<branch>"
Do not overwrite an existing backup ref. Keep the original remote SHA for the push lease.
Rebase bottom-to-top
Rebase the first stack branch onto the fetched base:
git switch <first-branch>
git rebase origin/<base-branch>
For every child, save the parent's pre-rebase SHA before rewriting it, then replay only the child commits:
old_parent=<saved-pre-rebase-parent-sha>
git switch <child-branch>
git rebase --onto <rewritten-parent-branch> "$old_parent"
Use --rebase-merges only after confirming that the stack intentionally contains merge commits that must remain. Do not use --skip merely to get through a conflict: it can silently remove feature work.
Resolve conflicts deliberately
When a rebase stops:
- Announce the pause and leave the rebase in progress.
- Inspect the conflicted files, the current base behavior, and the commit being replayed. Retain both behaviors unless the task intentionally replaces one.
- Stage only the resolved files and run
git rebase --continue. - Run the smallest relevant test or build after resolving non-trivial product conflicts.
Use git rebase --abort if the intended resolution cannot be determined. Do not force-push a branch whose rebase is incomplete.
Push each rewritten branch safely
After a branch has rebased successfully, push it with the recorded SHA as an explicit lease:
git push --force-with-lease=refs/heads/<branch>:<original-remote-sha> \
<remote> HEAD:refs/heads/<branch>
Push in the same bottom-to-top order. If a lease fails, stop: fetch, inspect the new remote commit, and ask for direction before overwriting anyone else's update.
Verify the result
Fetch again, then verify:
git merge-base --is-ancestor origin/<base-branch> origin/<first-branch>
git merge-base --is-ancestor origin/<parent-branch> origin/<child-branch>
git status --short --branch
Report the refreshed chain, rewritten branches, conflict resolutions, tests run, and backup-ref namespace. Create any new feature branch only from the verified top branch.