Files

81 lines
3.4 KiB
Markdown

---
name: github-stack-rebase
description: 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
1. 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.
2. Check `git status --short --branch`. Stop if the worktree is dirty.
3. Fetch the remote: `git fetch <remote> --prune`.
4. 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.
5. 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:
```sh
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:
```sh
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:
```sh
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:
1. Announce the pause and leave the rebase in progress.
2. Inspect the conflicted files, the current base behavior, and the commit being replayed. Retain both behaviors unless the task intentionally replaces one.
3. Stage only the resolved files and run `git rebase --continue`.
4. 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:
```sh
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:
```sh
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.