1---
2name: rebasing-with-git
3description: 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.
4license: GPL-3.0-or-later
5metadata:
6 author: Amolith <amolith@secluded.site>
7---
8
9## General principles
10
11- **Non-interactive rebases**: prefix with `GIT_EDITOR=true`. For interactive rebases that edit the todo, also set `GIT_SEQUENCE_EDITOR` (see [drop-and-repick](#step-5-rebase-with-drops-and-break)).
12- **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.
13- **Dirty worktree**: if uncommitted changes exist when a rebase is requested, ask the user how to proceed before doing anything.
14- **In-progress rebase**: if `.git/rebase-merge` or `.git/rebase-apply` exists (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.
15- **rerere**: the user has `rerere` enabled. If a conflict is auto-resolved by rerere, note this to the user and verify the auto-resolution looks correct before continuing.
16- **Checks**: read the project's `agents.md` for 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.
17
18## Repo layout detection
19
20Before starting, determine the layout:
21
221. **wt-managed bare repo**: `../.bare` is a directory. Worktrees are siblings of `.bare/`. Use `wt l` to list worktrees, `cd ../branch-name` to navigate. Add: `wt a name -b`. Remove: `wt r name -b`. If `wt` is not installed, fall back to `git worktree` commands.
23
242. **Legacy worktree layout**: `git worktree list` shows multiple entries but `../.bare` does not exist. Worktrees are sibling directories. Navigate with `cd ../name`. Use `git worktree add`/`remove`.
25
263. **Branch-based**: a normal clone with a single worktree. Use `git switch` to change branches.
27
28## Upstream remote and canonical branch
29
30For contributed-to projects, the upstream remote is named `upstream`. For the user's own projects, inspect `git remote -v` to identify the hosting remote.
31
32Determine the canonical branch:
33
34```sh
35git symbolic-ref refs/remotes/<remote>/HEAD | sed 's|refs/remotes/<remote>/||'
36```
37
38Fallback if the ref is not set:
39
40```sh
41git remote show <remote> | grep 'HEAD branch' | sed 's/.*: //'
42```
43
44## Simple rebase
45
461. `git fetch <remote>`
472. `GIT_EDITOR=true git rebase <remote>/<canonical>`
483. Handle conflicts if they arise (see [Handling conflicts](#handling-conflicts)).
494. Run checks. Stop.
50
51## Handling conflicts
52
531. **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.
542. Run `git status` and examine every conflicting file thoroughly.
553. Present to the user:
56 - What the local changes are doing and why they were made
57 - What the incoming changes are doing and why
58 - Why the two sides conflict
59 - Possible resolution strategies, with a light recommendation and brief reasoning for the recommended approach
604. **Wait for the user to confirm or direct** a resolution.
615. After resolving: `git add` the resolved files, then `GIT_EDITOR=true git rebase --continue`.
62
63### Bulk mechanical changes (import renames, formatting, etc.)
64
65Large-scale rename or formatting commits often produce massive conflict diffs that obscure the actual change. The typical resolution pattern is:
66
671. Check out HEAD's version of the affected files: `git checkout HEAD -- <paths>`
682. Re-apply the mechanical transformation (e.g. `fd <pattern> -x sd '<old>' '<new>'`)
693. Run the project's formatter
704. `git add` and continue
71
72**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.
73
74## Fork rebase: feature branches
75
76The user will already be in the correct worktree or have the branch checked out. Rebase only the current branch.
77
781. `git fetch upstream`
792. Check divergence: `git log --oneline upstream/<canonical>..HEAD`
803. `GIT_EDITOR=true git rebase upstream/<canonical>`
814. Resolve conflicts per [Handling conflicts](#handling-conflicts).
825. Run format/lint/typecheck.
836. **Stop and wait.** The user may want to test manually, push, or move on.
84
85## Fork rebase: drop-and-repick (personal branch)
86
87This 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.
88
89**Do not start the rebase immediately.** Analyze and confirm first.
90
91### Step 1: Verify feature branches are rebased
92
93For each local feature branch (exclude the canonical branch and the current personal branch), check whether it has already been rebased onto upstream:
94
95```sh
96git log --oneline upstream/<canonical>..<branch>
97```
98
99If any feature branch appears to need rebasing, warn the user or remind them to do it first.
100
101To enumerate branches: use `wt l` or `git worktree list` in worktree layouts, `git branch` in branch-based layouts.
102
103### Step 2: Examine the personal branch
104
105```sh
106git log --oneline upstream/<canonical>..HEAD
107```
108
109### Step 3: Correlate and categorize personal commits
110
111Each commit in the personal branch falls into one of three categories:
112
1131. **Feature branch representation**: matches a feature branch, drop and re-pick fresh
1142. **Already merged upstream**: exists in upstream's history, drop without re-picking
1153. **Personal-only**: matches nothing, stays as-is
116
117#### 3a: Match commits to feature branches
118
119For each feature branch, find its commits beyond upstream:
120
121```sh
122git log --oneline --reverse upstream/<canonical>..<branch>
123```
124
125The **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.
126
127**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.
128
129#### 3b: Detect commits merged upstream
130
131For each unmatched commit in the personal branch, check if it was merged upstream:
132
133```sh
134git log --oneline --grep="<subject>" upstream/<canonical>
135```
136
137If the subject appears in upstream's history, the commit is likely merged and can be dropped without re-picking. Mark it as "merged upstream."
138
139#### 3c: Request user categorization for ambiguous commits
140
141After automatic correlation, some commits may remain ambiguous:
142
143- Subject matches multiple feature branches
144- Subject partially matches but isn't exact
145- No match found, but commit doesn't look like a personal customization
146
147**Do not guess.** Present all unmatched/ambiguous commits to the user and ask them to categorize each as:
148
149- **(a) Merged upstream** — drop without re-picking
150- **(b) Feature branch** — specify which branch; drop and re-pick
151- **(c) Personal-only** — keep as-is
152
153Wait for the user to respond before proceeding to Step 4.
154
155### Step 4: Present the plan
156
157Show the user:
158
159- **Commits to drop**: hash, subject, which feature branch they correspond to
160- **Commits to keep**: hash, subject (personal-only customizations)
161- **Feature branches to re-pick**: branch name, commit count, planned re-pick order
162
163Preserve the order the feature-branch commits originally appeared in the personal branch. Consistent ordering lets rerere help with any previously-resolved conflicts.
164
165**Wait for explicit confirmation before proceeding.**
166
167### Step 5: Rebase with drops and break
168
169The 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.
170
171Build a sed command that:
1721. Changes `pick` to `drop` for each identified commit
1732. Inserts a `break` line after the last `drop`, before the first remaining `pick`
174
175Use short hashes from `git log --oneline`; these match the abbreviation the rebase todo uses.
176
177```sh
178GIT_EDITOR=true \
179GIT_SEQUENCE_EDITOR="sed -i \
180 -e 's/^pick <hash1>/drop <hash1>/' \
181 -e 's/^pick <hash2>/drop <hash2>/' \
182 -e '/^drop <hash2>/a break'" \
183 git rebase -i upstream/<canonical>
184```
185
186Where `<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.
187
188The 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](#handling-conflicts).
189
190### Step 6: Re-pick from feature branches (during break)
191
192The rebase is paused. Cherry-pick feature branches back in now, before the personal-only commits are replayed.
193
194For each feature branch, in the order confirmed in step 4:
195
1961. Get commits oldest-first:
197 ```sh
198 git log --oneline --reverse upstream/<canonical>..<branch>
199 ```
200
2012. **If the branch has a single commit**, cherry-pick it:
202 ```sh
203 git cherry-pick <hash>
204 ```
205
2063. **If the branch has multiple commits**, cherry-pick the first, then fixup the rest:
207 ```sh
208 git cherry-pick <first-hash>
209 git cherry-pick --no-commit <second-hash>
210 git commit --amend --no-edit
211 git cherry-pick --no-commit <third-hash>
212 git commit --amend --no-edit
213 ```
214 Continue for all remaining commits.
215
2164. **If `cherry-pick --no-commit` hits a conflict**: resolve per [Handling conflicts](#handling-conflicts), `git add` resolved files, then `git cherry-pick --quit` to clear the cherry-pick state while preserving staged changes, then `git commit --amend --no-edit`.
217
2185. Run format/lint/typecheck after completing each feature branch's re-pick.
219
220### Step 7: Continue the rebase
221
222After all feature branches are re-picked:
223
224```sh
225GIT_EDITOR=true git rebase --continue
226```
227
228This replays the personal-only commits on top of the freshly cherry-picked feature work. Handle conflicts per [Handling conflicts](#handling-conflicts).
229
230### Step 8: Final verification
231
2321. Review the log:
233 ```sh
234 git log --oneline upstream/<canonical>..HEAD
235 ```
236 Confirm the expected structure: personal-only commits and one squashed commit per re-picked feature branch, in the expected order.
237
2382. Run the full test suite.
239
2403. **Stop and wait.**