Module 10 — Rewriting History
Updated 7 September 2026
🧠 concept → 🧩 analogy → 🧪 exercise → ✅ expected result (hidden) → 🎯 interview questions (hidden)
Part A — Rebase: replaying commits on a new base
A1. What rebase actually does
git rebase main (run on a feature branch) says: take my branch's commits — everything since the merge base — and replay them, one by one, on top of main's current tip. For each original commit, Git computes its patch and applies it to the new base, minting a new commit. New parent, new content-context, new timestamp → new hash, necessarily (Module 3's arithmetic — a rebase cannot preserve IDs even in principle). The branch pointer then moves to the last replayed commit; the original commits still exist, unreferenced, reachable through the reflog (Module 7) until GC.
The result: your branch sits directly on top of main, as if you had started work this morning instead of last week. The divergence is gone — so the eventual merge into main is a clean fast-forward, and history reads as one straight line.
Diagram source
flowchart TB
subgraph B["after: git rebase main"]
M2["main"] --> F2["focaccia note"] --> BA2["merge base"]
FB2["feature"] --> N2["naan' (new hash)"] --> P2["pita' (new hash)"] --> F2
end
subgraph A["before: diverged"]
M1["main"] --> F1["focaccia note"] --> BA1["merge base"]
FB1["feature"] --> N1["naan notes"] --> P1["pita notes"] --> BA1
end🧪 Exercise 10.1 — diverge, rebase, fast-forward: linear history end to end
cd ~/git-course/kitchen # on main, clean
git switch -c feature/flatbread
echo "pita: pocket forms from steam" > pita.txt
git add pita.txt && git commit -q -m "Add pita notes"
echo "naan: cook in dry skillet" > naan.txt
git add naan.txt && git commit -q -m "Add naan notes"
git switch main # meanwhile, main moves
echo "focaccia: dimple with fingers" > focaccia.txt
git add focaccia.txt && git commit -q -m "Add focaccia note"
git switch feature/flatbread
git rev-parse --short HEAD # note the tip hash BEFORE
git rebase main
git rev-parse --short HEAD # ...and AFTER
git log --oneline --graph --all | head -4
git switch main && git merge feature/flatbread | head -2
git branch -d feature/flatbread✅ Expected result — click to reveal
df83bb3
Successfully rebased and updated refs/heads/feature/flatbread.
2ed3850
* 2ed3850 Add naan notes
* 9e86253 Add pita notes
* a83861c Add focaccia note
* <your previous main tip>
Updating a83861c..2ed3850
Fast-forwardWhat to read out of it: the tip hash changed — df83bb3 → 2ed3850 — without you editing anything: replayed commits are new objects, same messages, same changes, different identity and parentage. The graph is the payoff: no fork, no |/ — pita and naan now sit after focaccia as if written later, and the merge back is a pure fast-forward (compare with Module 6's Exercise 6.2, where the same shape of situation produced a merge commit and two lanes). Same content as merging, different story. Which story is better is Part B's debate — but notice what you already know: the old commits still exist (git reflog shows the pre-rebase tip), so even a botched rebase is a Module 7 recovery, not a loss.
A merge is a documentary edit: two cameras filmed in parallel, and the editor splices both reels with a caption saying "these happened simultaneously" (the merge commit). A rebase is a reshoot: the director looks at today's set (new main) and refilms your scenes on it, in order, so the final cut plays as one continuous take. The reshot scenes are genuinely new footage — same script, new film.
Where the analogy stops working. Reshoots cost what the original shoot cost; rebasing is nearly free, because each "scene" is a patch mechanically re-applied (conflicts are the exceptions where the new set no longer fits the script — C2). And film cuts destroy the outtakes; Git keeps them — the original commits linger in the reflog, so the documentary version remains reconstructable for weeks. The real cost of the reshoot is different in kind: anyone who already watched the original footage now holds obsolete hashes — which is exactly the golden rule's territory (Part B).
A2. Merge vs rebase — the actual answer
Both integrate the same changes; they differ in what history claims happened. Merge preserves truth: work happened in parallel, and the merge commit records the join — at the cost of a graph full of bubbles and cross-lanes. Rebase curates a story: work happened sequentially — at the cost of rewriting commits (new hashes) and flattening the record of parallelism. Neither is "correct"; they optimize for different readers. Merge optimizes for provenance (what really happened, when, revertable per-feature via -m 1). Rebase optimizes for readability (bisectable straight lines — Module 13 will thank you — and no merge-commit noise).
| Axis | Merge | Rebase |
|---|---|---|
| Commit hashes | Preserved | Rewritten (new commits) |
| History shape | True parallel graph, merge commits | Linear, as-if-sequential |
| Safe on shared branches | Always | No — the golden rule (Part B) |
| Conflict handling | Once, at the merge | Possibly per replayed commit (rerere helps — Module 15) |
| Typical team policy | Merging into main (PR button) | Updating your feature branch from main; tidying before review |
The pragmatic synthesis most teams land on: rebase your own unshared work (keep feature branches current with git rebase main or git pull --rebase; tidy commits before review with Part C), merge everything shared (PRs land via merge or squash on the host). You get readable branches and truthful mainline joins.
🎯 Interview questions — Part A
🎯 "What is the difference between git merge and git rebase?" — asked verbatim in InterviewDrill's Git & GitHub Interview Questions, May 2026
Both integrate one branch's changes into another; they write different histories. Merge creates a single new merge commit with two parents, preserving every existing commit and the true parallel structure — nothing is rewritten, so it is safe anywhere, including shared branches. Rebase replays the branch's commits one-by-one onto the new base, minting new commits (new hashes, new parentage) and producing linear history with no merge commit; the branch reads as if written after the base. Conflicts use the same three-way machinery in both, but rebase can hit them per-commit rather than once.
The details that separate candidates: an average answer says "merge keeps history, rebase makes it linear." A strong answer explains why rebase must mint new hashes (a commit's ID covers its parent — Module 3's arithmetic, not a design whim), derives the safety rule from it (rewriting shared commits strands everyone holding the old IDs), and gives the standard synthesis: rebase private work for cleanliness, merge shared work for safety. Naming the operational tiebreakers — linear history bisects better; merge commits give per-feature revert (-m 1) — turns preference into engineering.
Part B — The golden rule, and force-push done right
B1. Never rebase what others have
The golden rule: do not rebase commits that exist outside your repository — anything pushed to a branch others build on. The mechanism, not the commandment: rebasing replaces commits with new-hash copies. Anyone who fetched the originals now has history that diverges from the rewritten branch at every replaced commit. Their next pull produces a bewildering merge of the branch with itself; their in-flight work is based on commits your branch disowned; and the shared record everyone reasoned about silently changed underneath them. One rebase of a shared branch can cost a team an afternoon of confused archaeology.
The rule has a precise boundary, which is what makes it usable. Your own feature branch, pushed but not yet built upon by others — rebasing it is normal and expected (tidying before review!); you then need git push --force-with-lease, because the rewritten branch no longer fast-forwards from the remote's copy (Module 8's rule, correctly refusing). The lease matters: it aborts if the remote moved since your last fetch — protection against overwriting a reviewer's surprise commit. And git pull --rebase — the third option from Module 8's divergence menu — is the golden rule's safest daily application: it rebases only your unpushed local commits onto the incoming remote tip, producing linear history with zero shared commits rewritten. Many engineers set git config --global pull.rebase true and never think about the divergence prompt again.
🎯 Interview questions — Part B
🎯 "Merge vs rebase — the difference and when to use each." — asked verbatim in Greenroom's Git Interview Questions, Jun 2026
Difference in one line: merge adds a two-parent commit joining intact histories; rebase rewrites the branch as new commits replayed onto the target, linearizing history. When to use each, by ownership: rebase private history — keeping your feature branch current (git rebase main, or git pull --rebase for daily syncing), and tidying commits before review (interactive rebase) — then push --force-with-lease to your own branch; merge shared history — landing reviewed work into main (merge commit or squash per team policy), and never rewriting anything teammates have fetched: the golden rule, because replaced hashes strand everyone downstream.
The details that separate candidates: an average answer restates the difference and says "team preference." A strong answer draws the decision boundary at who else holds these commits — the golden rule as a derivation, not dogma — and names the enforcement reality: protected branches forbid the force-push that rebasing shared history would require, so the rule is usually mechanical, not social. Two closers that signal practice: the ours/theirs inversion during rebase conflicts, and --force-with-lease as the only acceptable force on published feature branches.
Part C — Interactive rebase: editing commits before the world sees them
C1. The todo list
git rebase -i HEAD~N opens your editor on a todo list: one line per commit (oldest first — note, the reverse of git log), each starting with a command you can change. The vocabulary: pick — keep as-is; reword — keep, but stop to edit the message; squash — meld into the previous commit, combining messages; fixup — meld, discarding this commit's message; edit — stop at this commit for amending (this is where "rewrite an old commit's content or author" happens); drop (or delete the line) — remove the commit entirely; and reordering lines reorders commits. Save and close; Git replays per your script, pausing where told. It is Module 7's reset-and-recommit workflow (Ticket 2), automated and multiplied — and like all rebasing, strictly for unshared commits.
🧪 Exercise 10.2 — squash three messy WIP commits into one clean one
cd ~/git-course/kitchen # on main
git switch -c feature/rolls
echo "step 1" > rolls.txt && git add rolls.txt && git commit -q -m "WIP rolls"
echo "step 1 and 2" > rolls.txt && git commit -qa -m "wip more"
echo "shape rolls, proof 30 min, bake 15" > rolls.txt && git commit -qa -m "fix typo"
git log --oneline -3 # the mess, for the record
git rebase -i HEAD~3Your editor opens with three pick lines, oldest first. Change lines 2 and 3 to squash, save, close. A second editor combines all three messages — delete them and write one line: Add dinner roll method. Save, close. Then:
git log --oneline -1
git show --stat --format="%h %s" HEAD | tail -2✅ Expected result — click to reveal
49ae39e fix typo
07c7c15 wip more
519362c WIP rolls
Successfully rebased and updated refs/heads/feature/rolls.
d570d3e Add dinner roll method
rolls.txt | 1 +
1 file changed, 1 insertion(+)What to read out of it: three commits became one — new hash, clean message, and the final content (the squash keeps the end state; intermediate fumbling leaves no trace). The stat confirms the combined commit contains exactly what the three added together. This is the pre-review ritual on rebase-friendly teams: work in as many sloppy checkpoints as you like (they are free and reflog-protected — Module 7's lesson), then present reviewers a curated sequence. If the rebase ever goes sideways mid-flight: git rebase --abort returns you to the pre-rebase state, exactly like merge's abort.
C2. When a replay conflicts
Each replayed commit is a small three-way merge, so any of them can conflict — and the rebase pauses at that commit, mid-sequence. The prompt-hints tell you everything: resolve and git add, then git rebase --continue (not commit! — continue re-commits the fixed replay and proceeds); or git rebase --skip to drop the troublesome commit; or git rebase --abort to rewind the whole operation to the starting line. Same conflict markers, same resolution skills as Module 6 — plus the ours/theirs inversion from Part B's trap.
🧪 Exercise 10.3 — a rebase that conflicts, read calmly, then aborted
cd ~/git-course/kitchen
git switch main && git branch -D feature/rolls # (rolls merges in Part D; drop it here)
git switch -c feature/temp
echo "oven: 240C max heat" > baking.txt && git commit -qa -m "Try max heat"
git switch main
echo "oven: 200C gentler bake" > baking.txt && git commit -qa -m "Gentler bake"
git switch feature/temp
git rebase main
echo "exit code: $?"
git status | head -4
git rebase --abort
git log --oneline -1 # untouched, as if nothing happened
git switch main && git branch -D feature/temp && git checkout -- baking.txt 2>/dev/null✅ Expected result — click to reveal (the rebase stops on purpose)
Auto-merging baking.txt
CONFLICT (content): Merge conflict in baking.txt
error: could not apply e18d07a... Try max heat
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
exit code: 1
interactive rebase in progress; onto dd915fe
Last command done (1 command done):
pick e18d07a Try max heat
No commands remaining.
e18d07a Try max heatWhat to read out of it: the error names which commit failed to apply (could not apply e18d07a… Try max heat) — in a multi-commit rebase this tells you how far you got, and status shows the todo progress (1 command done… No commands remaining). The three exits are printed right in the hints: continue, skip, abort. After --abort, the branch tip is the original e18d07a — hash unchanged, because nothing was ever completed; a rebase only "happens" when it finishes. Practice reading this pause until it feels administrative rather than alarming — that calm is the actual skill.
🎯 Interview questions — Part C
🎯 "How do you squash multiple commits into one?" — asked verbatim in InterviewDrill's Git & GitHub Interview Questions, May 2026
Interactive rebase: git rebase -i HEAD~N for the last N commits; in the todo list (oldest first) leave the first pick and change the rest to squash (combine messages) or fixup (keep only the first message); save, write the final message, done — N commits become one new commit. Alternatives worth knowing: git reset --soft HEAD~N && git commit achieves the same single-commit result without the rebase machinery (Module 7's tools); and at merge time, the host's squash-merge button squashes an entire PR without anyone rebasing locally. All of these rewrite history, so: unshared commits only, and a published feature branch needs push --force-with-lease afterward.
The details that separate candidates: an average answer recites rebase -i + squash. A strong answer distinguishes squash from fixup, mentions the todo list's oldest-first ordering (the classic first-timer stumble), offers the reset-soft equivalent (showing the operation is "recommit the same tree", not magic), and places squashing in workflow context: pre-review tidying vs squash-merge policy, and the Module 9 caveat that squash-merged branches defeat --merged cleanup detection.
🎯 "How do you rewrite authorship of past commits?" — asked verbatim in InterviewDrill's Git & GitHub Interview Questions, May 2026
For a few recent commits: interactive rebase with edit — git rebase -i <before-the-oldest-affected>, mark the commits edit; at each pause run git commit --amend --author="Right Name <right@email>" --no-edit, then git rebase --continue. For the last commit only, skip the rebase: git commit --amend --author=…. For many commits or whole-history fixes (the classic "committed for months with the wrong email"), per-commit editing does not scale — use git filter-repo with a mailmap/callback to rewrite authorship across all of history in one pass (the maintained successor to the deprecated filter-branch; Module 15 territory). Every variant rewrites hashes from the first affected commit forward.
The details that separate candidates: an average answer knows --amend --author. A strong answer scales the tool to the size of the problem (amend → rebase-edit → filter-repo), states the cascade (every descendant hash changes — Module 3), and therefore the coordination cost: on shared history this is an announced, all-hands migration event, not a quiet fix. The prevention footnote reads well too: per-repo identity config and includeIf (Module 1) make the mistake structurally unlikely.
Part D — cherry-pick: copying single commits
D1. One commit, transplanted
git cherry-pick <hash> takes one existing commit's change (its diff against its parent) and applies it as a new commit on your current branch — original untouched, copy minted with your committer stamp and today's date (author and author-date preserved, like amend). It is rebase's mechanism aimed at a single commit of your choosing. Canonical uses: backporting — a fix landed on main must also reach release/2.4 without dragging along everything else on main; rescue — one good commit sits on an abandoned or misdirected branch (Module 5's wrong-branch capstone, now with its proper tool). Conflicts pause with --continue/--abort exactly like revert (they are literally inverse operations — revert applies a diff backwards, cherry-pick forwards). For auditability across branches, -x appends (cherry picked from commit <hash>) to the copied message — use it when backporting on shared release branches.
🧪 Exercise 10.4 — backport one fix, leave the rest behind
cd ~/git-course/kitchen # on main
git switch -c hotfix-source # simulate a branch with a fix + noise
echo "salt: use 2 percent of flour weight" > salt.txt
git add salt.txt && git commit -q -m "Fix salt ratio"
echo "extra note" > extra.txt
git add extra.txt && git commit -q -m "Unrelated extra work"
git switch main # main has moved on independently
echo "advance main" > advance.txt
git add advance.txt && git commit -q -m "Advance main separately"
git log --oneline hotfix-source | grep "Fix salt ratio" # find the fix's hash
git cherry-pick <that-hash> # copy ONLY the fix
git log --oneline -2
git branch -D hotfix-source # the noise never lands anywhere✅ Expected result — click to reveal
daf73bf Fix salt ratio
[main 3edb76c] Fix salt ratio
Date: Mon Sep 7 17:34:56 2026 +0000
1 file changed, 1 insertion(+)
create mode 100644 salt.txt
3edb76c Fix salt ratio
a438792 Advance main separatelyWhat to read out of it: the fix exists twice now — daf73bf on the doomed branch, 3edb76c on main — same change, same message, same author date (the Date: line, familiar from amend), different commits. Unrelated extra work never touched main: that selectivity is the whole point. The -D force-delete is honest about abandoning the branch (its remaining commit strands — deliberate here). One caution to carry: a cherry-picked commit is not the original, so Git does not know they correspond — merging hotfix-source later would try to bring Fix salt ratio again (usually resolving as an empty no-op, occasionally as a confusing conflict). Cherry-pick copies content, not identity; use it to move changes across lines of history, not as a substitute for merging within one.
🎯 Interview questions — Part D
🎯 "What does git cherry-pick do?" — asked verbatim in InterviewDrill's Git & GitHub Interview Questions, May 2026
git cherry-pick <commit> applies the change introduced by an existing commit (its diff against its parent) to the current branch as a new commit — preserving the original author, author date, and message, while minting a new hash under your committer identity. It moves a specific change between lines of history without merging the branches: backporting a main-branch fix to a release branch, promoting one urgent commit ahead of its branch, or rescuing work from a dead branch. Ranges work too (git cherry-pick A..B), conflicts pause with --continue/--abort, and -x stamps the copy with its origin hash for traceability.
The details that separate candidates: an average answer says "applies a commit from another branch." A strong answer is precise that it applies the patch, creating an unrelated-by-identity duplicate — and derives the consequences: later merges of the source branch may produce empty duplicates or odd conflicts, which is why cherry-pick is for crossing release boundaries, not routine integration. Operational polish: -x on release branches as audit practice, and knowing platforms' "backport bot" workflows are automated cherry-picks of merged PRs onto release branches.
Part E — Production practice
E1. Symptom → cause → diagnosis → fix
| Symptom | What is really happening | What to run | The fix |
|---|---|---|---|
| After rebasing, push is rejected (non-fast-forward) | Expected: the branch was rewritten; the remote holds the old commits | Confirm the branch is yours alone (PR open? anyone based on it?) | git push --force-with-lease — never plain --force; if others based work on it, stop and coordinate |
| Teammates see duplicate commits / a "merge of the branch with itself" after pulling | Someone rebased a shared branch — golden-rule violation; old and new copies of each commit now coexist | git log --oneline --graph (the tell: pairs of same-message commits) | Team-coordinated reset of the branch to one agreed tip; institute protection rules to prevent recurrence |
| Rebase conflict, and checkout --ours kept the wrong side | ours/theirs invert during rebase: ours = the base branch, theirs = your replayed commit | git status (confirms rebase in progress) — read markers by content | git checkout --theirs <file> for your own change; or resolve manually; --abort if disoriented |
| Mid-rebase, ran git commit and things got weird | Continue expects to make the replay-commit itself; a manual commit inserts an extra one | git status · git log --oneline -3 | Prefer git rebase --abort and redo cleanly; the rule: resolve → add → --continue, never commit |
| "The rebase ate my commits!" | Almost never true — originals are unreferenced, not destroyed; or a drop/--skip was chosen accidentally | git reflog — the pre-rebase tip is right there | git reset --hard <pre-rebase-entry> (Module 7) or git branch rescue <hash>; redo the rebase |
| Cherry-picked fix causes an empty/strange conflict when the source branch later merges | The copy and the original are different commits with equivalent content | git cherry main <branch> — flags patch-equivalent commits | Usually resolves as no-op; long-term: prefer merges within a line of history, cherry-pick only across release boundaries, with -x |
E2. Capstone — four tickets
Worked answer: two cherry-picks with audit stamps: git switch release/3.1 && git cherry-pick -x f00dfac, resolve any drift-conflicts (the older the release, the more the surrounding code differs — resolve to preserve the fix's intent, then run that branch's tests), push; repeat on release/2.4. -x writes (cherry picked from commit f00dfac) into each copy, so a year from now git log --grep=f00dfac --all finds every branch that received the fix — the traceability auditors and future-you both want. If the fix spans several commits, cherry-pick the range in order (git cherry-pick A^..B). Tag new patch releases on each branch (Module 12). This flow — fix once on main, cherry-pick outward — is the standard Git Flow hotfix answer at versioned-product shops (Module 9's Ticket 2, now with mechanics).
Worked answer: the branch is shared-in-location but not shared-in-dependency — no reviews, no downstream work — so the golden rule permits rewriting, with the courtesy of a heads-up comment on the PR. The sequence: git rebase main first (current base, conflicts dealt with once), then git rebase -i main to curate: reorder related commits together, fixup the noise into its parents, reword survivors into Module 2-grade messages — aiming for 3–5 commits that each build and make one reviewable point. Verify the content did not change: git diff <old-tip> (from the reflog) should be empty. Then git push --force-with-lease, and note on the PR that history was tidied. The judgment line for interviews: rewrite freely before review engagement; after reviewers have commented on specific commits, prefer appending fixup commits and squashing at merge instead — rewriting under an active review destroys the reviewers' anchors.
Worked answer: triage, restore, prevent. Triage: freeze pushes to main (announce; tighten protection now if it was missing). Restore: the pre-rebase tip still exists — in the force-pusher's reflog, any teammate's origin/main photo (Module 8: whoever hasn't fetched since holds the old hash), or CI logs; verify content (git log, tag comparison), then force-push the original history back with --force-with-lease and have everyone git fetch — clones that never pulled the bad main need nothing else; anyone who built on it rescues via their reflog. Prevent: protection rules denying force-push to main (this class of accident should be impossible, not discouraged — Module 9), and channel the tidiness instinct into squash-merges at the PR boundary, where curation belongs. Close the incident review with the golden rule stated as mechanism, so it sticks.
Worked answer (the one-pager): Your branch, before review: rebase freely — git pull --rebase daily, git rebase main to stay current, rebase -i to curate; force-with-lease to your own branch only. Landing to main: via PR button only — squash-merge for typical features (one clean commit, no branch noise; accept that --merged cleanup breaks — host auto-delete handles it), merge-commit for multi-commit features worth preserving as units (revertable via -m 1). Forbidden, enforced by protection rules: direct pushes to main, any force-push to main, rebasing anything another person has based work on. Escape hatches: reflog for local accidents; incident process (Ticket 3) for shared ones. One page, mechanisms attached — policies without the why decay into cargo cult; this module is the why.
E3. Documentation reference
| Topic | Official source | What it covers |
|---|---|---|
| Rebasing (tutorial + perils) | Git Book §3.6 — Rebasing | Replay mechanics, golden rule, rebase-vs-merge |
| git rebase | git-rebase manual | All modes, -i, --onto, conflict flow |
| Rewriting history | Git Book §7.6 — Rewriting History | Interactive rebase recipes: reword, squash, split, edit |
| git cherry-pick | git-cherry-pick manual | Single commits, ranges, -x, conflict flow |
| git push force options | git-push manual | --force-with-lease semantics |
E4. Self-assessment
Answer each aloud, from memory, before moving on. Every one is answered on this page.
- Describe what git rebase main does, commit by commit — and why new hashes are mathematically unavoidable.
- After a rebase, where are the original commits, and for how long?
- Merge vs rebase: what does each optimize for? Give the two operational tiebreakers.
- State the golden rule as a mechanism — what exactly goes wrong for teammates when it is broken?
- What does git pull --rebase rebase, and why is it always golden-rule-safe?
- Why does a rebased branch need force-push, and what does --force-with-lease check?
- In a rebase conflict, which side is ours? Derive it from how rebase works.
- Todo-list vocabulary: pick, reword, squash, fixup, edit, drop — one phrase each. Which order does the list run in?
- Mid-rebase conflict: the three exits, and why plain git commit is not one of them.
- What does cherry-pick copy, and what does it not copy? What follows for later merges of the source branch?
- When is rewriting a pushed branch acceptable? Name the boundary condition and the courtesy that goes with it.
- Recite your team-policy one-pager: rebase where, merge where, forbidden what, enforced how?
E5. Sources
🗒️ Cheat sheet — Module 10
| Command | What it does |
|---|---|
| git rebase main | Replay the current branch's commits onto main's tip (new hashes; linearizes) |
| git pull --rebase · git config --global pull.rebase true | Rebase your unpushed commits onto the incoming tip — the safe daily rebase |
| git rebase -i HEAD~N · git rebase -i main | Edit the last N commits · edit everything since main (todo list, oldest first) |
| pick / reword / squash / fixup / edit / drop | Keep / re-message / meld+combine messages / meld silently / pause to amend / delete |
| git rebase --continue · --skip · --abort | After resolving+add · drop the conflicting commit · rewind the whole rebase |
| git push --force-with-lease | Publish a rewritten branch — refuses if the remote moved since your last fetch |
| git cherry-pick <hash> · -x · A^..B | Copy one commit's change here · stamp the origin hash · copy a range |
| git cherry main <branch> | Detect patch-equivalent (already cherry-picked) commits |
| git commit --amend --author="Name <email>" | Fix authorship at an edit pause (or on the last commit directly) |
| git reflog → git reset --hard <pre-rebase-tip> | The universal rebase undo (Module 7, on call here) |
Key concepts: rebase = per-commit replay onto a new base; new parents ⇒ new hashes, originals linger in the reflog · merge preserves provenance, rebase buys readability; synthesize: rebase private, merge shared · the golden rule is a mechanism, not a manner: rewriting shared commits strands every downstream holder of the old hashes · pull --rebase rewrites only unpushed work — always safe · rewritten branches force-push with --force-with-lease only · during rebase, ours = the base, theirs = your replayed commit · interactive todo runs oldest-first; resolve → add → --continue, never bare commit · cherry-pick copies patches, not identity — cross release boundaries with -x, don't substitute for merging · every rebase disaster is a reflog recovery.