From 05d53d1528d159a8135147442dd9826a391c407f Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Sun, 12 Jul 2026 10:15:42 +0200 Subject: [PATCH] docs(advanced-git): add --onto replay-tip rebase for stale branches A /retro session on 2026-07-12 rebased four 4-month-old MR branches whose base predated 381 commits on the target. Each branch was ~20 bootstrap commits plus one real commit, and the target had independently absorbed that bootstrap work, so a plain `git rebase origin/master` produced add/add conflicts on every recreated file. Replaying only the tip commit with `git rebase --onto` was the clean fix. Documents the technique and the "whole file shown as new (@@ -0,0 +1,N @@)" add/add signature that flags a merge-base predating the file. Signed-off-by: Sebastian Mendel --- .../git-workflow/references/advanced-git.md | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/skills/git-workflow/references/advanced-git.md b/skills/git-workflow/references/advanced-git.md index d3a7e5b..4cdcbaa 100644 --- a/skills/git-workflow/references/advanced-git.md +++ b/skills/git-workflow/references/advanced-git.md @@ -21,6 +21,48 @@ git rebase -i abc1234^ # x, exec - run shell command ``` +### Replaying Only the Tip Onto a Moved Base (`--onto`) + +Use when a long-lived branch is *N bootstrap commits + a few real commits*, and +the base branch has since absorbed that bootstrap work through a different path +(different SHAs). A plain `git rebase ` replays **all** N commits and hits +an add/add conflict on every file the base already recreated — and even after +resolving them the result is wrong. + +```bash +# Symptom: the MR/PR diff shows an ENTIRE file as newly added (@@ -0,0 +1,N @@) +# even though the base already has that file. The merge-base predates the file, +# so base and branch each "add" it → add/add conflict. Don't trust the +# diff-vs-base; inspect the branch's own tip commit instead: +git show # the real change this branch introduces +git cherry -v # '+' = unique to branch, '-' = already in base + # (patch-id match; plain `log ..` + # still lists absorbed commits under new SHAs) +git merge-base # confirm how far back it forks + +# Replay ONLY the commits after onto the current base, dropping the +# redundant bootstrap history: +git rebase --onto origin/main +# └ new base └ everything up to AND INCLUDING this is dropped + +# For a single tip commit, is its parent: +git rebase --onto origin/main ~1 +``` + +Each replayed commit is 3-way merged against its own parent tree, so as long as +the lines it touches still exist verbatim in the new base it applies cleanly no +matter how far the base has moved. Verify the result is exactly the intended +change and nothing else: + +```bash +git rev-list --count origin/main..HEAD # == the number of real commits you kept +git diff origin/main..HEAD # == the intended delta only +``` + +This is equivalent to cherry-picking just the tip commits onto the new base; +`--onto` does it in one step and preserves author and author-date. Force-push the +rewritten branch with `--force-with-lease`. + ### Squashing Commits ```bash