81 lines
3.4 KiB
Markdown
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.
|