name: rebasing-with-git description: Guides git rebase workflows including simple rebases and complex fork rebases with drop-and-repick for personal integration branches. Use when the user asks to rebase, mentions rebasing, needs help with rebase conflicts, or asks to pull feature branch changes into a personal branch. Supports branch-based repos, worktrees, and wt-managed bare repos. license: GPL-3.0-or-later metadata: author: Amolith amolith@secluded.site
General principles
- Non-interactive rebases: prefix with
GIT_EDITOR=true. For interactive rebases that edit the todo, also setGIT_SEQUENCE_EDITOR(see drop-and-repick). - Never push unless the user explicitly says to. If they say "force push", use
--force-with-lease. Do not suggest pushing. When work is complete, stop. - Dirty worktree: if uncommitted changes exist when a rebase is requested, ask the user how to proceed before doing anything.
- In-progress rebase: if
.git/rebase-mergeor.git/rebase-applyexists (or the worktree-local equivalents under.git/worktrees/<name>/), read the state—conflicts, staged changes, progress through the todo—and offer to continue or abort based on what you find. - rerere: the user has
rerereenabled. If a conflict is auto-resolved by rerere, note this to the user and verify the auto-resolution looks correct before continuing. - Checks: read the project's
agents.mdfor build/lint/format/test commands. Run format, lint, and typecheck after each logical step. Run the full test suite only at the end of the entire operation.
Repo layout detection
Before starting, determine the layout:
-
wt-managed bare repo:
../.bareis a directory. Worktrees are siblings of.bare/. Usewt lto list worktrees,cd ../branch-nameto navigate. Add:wt a name -b. Remove:wt r name -b. Ifwtis not installed, fall back togit worktreecommands. -
Legacy worktree layout:
git worktree listshows multiple entries but../.baredoes not exist. Worktrees are sibling directories. Navigate withcd ../name. Usegit worktree add/remove. -
Branch-based: a normal clone with a single worktree. Use
git switchto change branches.
Upstream remote and canonical branch
For contributed-to projects, the upstream remote is named upstream. For the user's own projects, inspect git remote -v to identify the hosting remote.
Determine the canonical branch:
git symbolic-ref refs/remotes/<remote>/HEAD | sed 's|refs/remotes/<remote>/||'
Fallback if the ref is not set:
git remote show <remote> | grep 'HEAD branch' | sed 's/.*: //'
Simple rebase
git fetch <remote>GIT_EDITOR=true git rebase <remote>/<canonical>- Handle conflicts if they arise (see Handling conflicts).
- Run checks. Stop.
Handling conflicts
- Stop immediately. Do not resolve without user confirmation. Never skip a commit. If a commit seems too complex to resolve, present what you see and ask the user—they may know the exact resolution.
- Run
git statusand examine every conflicting file thoroughly. - Present to the user:
- What the local changes are doing and why they were made
- What the incoming changes are doing and why
- Why the two sides conflict
- Possible resolution strategies, with a light recommendation and brief reasoning for the recommended approach
- Wait for the user to confirm or direct a resolution.
- After resolving:
git addthe resolved files, thenGIT_EDITOR=true git rebase --continue.
Bulk mechanical changes (import renames, formatting, etc.)
Large-scale rename or formatting commits often produce massive conflict diffs that obscure the actual change. The typical resolution pattern is:
- Check out HEAD's version of the affected files:
git checkout HEAD -- <paths> - Re-apply the mechanical transformation (e.g.
fd <pattern> -x sd '<old>' '<new>') - Run the project's formatter
git addand continue
Do not attempt to hand-resolve hundreds of conflicting hunks. Recognize when a commit is mechanical in nature, describe it to the user, and ask how they want to re-apply the transformation.
Fork rebase: feature branches
The user will already be in the correct worktree or have the branch checked out. Rebase only the current branch.
git fetch upstream- Check divergence:
git log --oneline upstream/<canonical>..HEAD GIT_EDITOR=true git rebase upstream/<canonical>- Resolve conflicts per Handling conflicts.
- Run format/lint/typecheck.
- Stop and wait. The user may want to test manually, push, or move on.
Fork rebase: drop-and-repick (personal branch)
This workflow is always explicitly requested by the user (e.g. "rebase on main, then pull changes back from feature branches"). The current branch/worktree is the personal branch.
Do not start the rebase immediately. Analyze and confirm first.
Step 1: Verify feature branches are rebased
For each local feature branch (exclude the canonical branch and the current personal branch), check whether it has already been rebased onto upstream:
git log --oneline upstream/<canonical>..<branch>
If any feature branch appears to need rebasing, warn the user or remind them to do it first.
To enumerate branches: use wt l or git worktree list in worktree layouts, git branch in branch-based layouts.
Step 2: Examine the personal branch
git log --oneline upstream/<canonical>..HEAD
Step 3: Correlate and categorize personal commits
Each commit in the personal branch falls into one of three categories:
- Feature branch representation: matches a feature branch, drop and re-pick fresh
- Already merged upstream: exists in upstream's history, drop without re-picking
- Personal-only: matches nothing, stays as-is
3a: Match commits to feature branches
For each feature branch, find its commits beyond upstream:
git log --oneline --reverse upstream/<canonical>..<branch>
The oldest commit's subject line identifies the feature branch in the personal branch. Search the personal branch log for a commit with the same subject. If found, that personal commit is a squashed (fixup'd) representation of the entire feature branch and should be dropped during rebase, then the feature branch re-picked fresh.
Only branches with at least one commit ahead of upstream whose subject matches a commit in the personal branch should be included. Branches with no matching commit in personal are not represented there and should be ignored unless the user explicitly says to include new ones.
3b: Detect commits merged upstream
For each unmatched commit in the personal branch, check if it was merged upstream:
git log --oneline --grep="<subject>" upstream/<canonical>
If the subject appears in upstream's history, the commit is likely merged and can be dropped without re-picking. Mark it as "merged upstream."
3c: Request user categorization for ambiguous commits
After automatic correlation, some commits may remain ambiguous:
- Subject matches multiple feature branches
- Subject partially matches but isn't exact
- No match found, but commit doesn't look like a personal customization
Do not guess. Present all unmatched/ambiguous commits to the user and ask them to categorize each as:
- (a) Merged upstream — drop without re-picking
- (b) Feature branch — specify which branch; drop and re-pick
- (c) Personal-only — keep as-is
Wait for the user to respond before proceeding to Step 4.
Step 4: Present the plan
Show the user:
- Commits to drop: hash, subject, which feature branch they correspond to
- Commits to keep: hash, subject (personal-only customizations)
- Feature branches to re-pick: branch name, commit count, planned re-pick order
Preserve the order the feature-branch commits originally appeared in the personal branch. Consistent ordering lets rerere help with any previously-resolved conflicts.
Wait for explicit confirmation before proceeding.
Step 5: Rebase with drops and break
The rebase todo must be structured as: drop → break → pick. The drops remove stale feature-branch commits, the break pauses the rebase so fresh cherry-picks can be inserted, and the remaining picks replay personal-only commits on top.
Build a sed command that:
- Changes
picktodropfor each identified commit - Inserts a
breakline after the lastdrop, before the first remainingpick
Use short hashes from git log --oneline; these match the abbreviation the rebase todo uses.
GIT_EDITOR=true \
GIT_SEQUENCE_EDITOR="sed -i \
-e 's/^pick <hash1>/drop <hash1>/' \
-e 's/^pick <hash2>/drop <hash2>/' \
-e '/^drop <hash2>/a break'" \
git rebase -i upstream/<canonical>
Where <hash2> is the last commit being dropped (use /a break to append after it). If drops are non-contiguous in the todo, insert the break after the last one.
The rebase will process the drops, then pause at the break. If personal-only commits conflict during the rebase (before or after the break), handle per Handling conflicts.
Step 6: Re-pick from feature branches (during break)
The rebase is paused. Cherry-pick feature branches back in now, before the personal-only commits are replayed.
For each feature branch, in the order confirmed in step 4:
-
Get commits oldest-first:
git log --oneline --reverse upstream/<canonical>..<branch> -
If the branch has a single commit, cherry-pick it:
git cherry-pick <hash> -
If the branch has multiple commits, cherry-pick the first, then fixup the rest:
git cherry-pick <first-hash> git cherry-pick --no-commit <second-hash> git commit --amend --no-edit git cherry-pick --no-commit <third-hash> git commit --amend --no-editContinue for all remaining commits.
-
If
cherry-pick --no-commithits a conflict: resolve per Handling conflicts,git addresolved files, thengit cherry-pick --quitto clear the cherry-pick state while preserving staged changes, thengit commit --amend --no-edit. -
Run format/lint/typecheck after completing each feature branch's re-pick.
Step 7: Continue the rebase
After all feature branches are re-picked:
GIT_EDITOR=true git rebase --continue
This replays the personal-only commits on top of the freshly cherry-picked feature work. Handle conflicts per Handling conflicts.
Step 8: Final verification
-
Review the log:
git log --oneline upstream/<canonical>..HEADConfirm the expected structure: personal-only commits and one squashed commit per re-picked feature branch, in the expected order.
-
Run the full test suite.
-
Stop and wait.