Rewriting History
Amend, rebase, reset and reflog
Introduction
You enter a secret wing of the Archives, hidden behind an ancient tapestry. The room is dimly lit by floating candles. Scratched-out, scraped, and rewritten scrolls pile up on the shelves. The Master Archivist closes the door behind you with a solemn gesture.
"Apprentice, you now master branches and the merging of chronicles. But there exists an older, more powerful - and more dangerous - art. Certain elite Archivists knew how to rewrite History itself - modify a past entry, reorganize the chronology, erase an event as if it had never happened. This art is useful, but it obeys one absolute golden rule: you never rewrite a chronicle that has already been shared with other guilds."
Modify the last commit - git commit --amend
You just recorded an entry in the archives, but you notice a mistake. No need to create a new entry - you can modify the last one:
Fix the last commit message
git commit --amend -m "New corrected message" Add a forgotten file to the last commit
git add forgotten-file.txt
git commit --amend --no-edit The --no-edit option keeps the original message. The commit is replaced by a new one (with a different hash).
When to use --amend?
- You made a typo in the message
- You forgot to add a file
- You want to adjust the content of the last commit
--amend does not modify the existing commit - it replaces it. The old commit disappears from the visible history (but remains recoverable via git reflog).
Rebase - Rewriting the chronology
git rebase is one of the most powerful tools in Git. It allows you to replay your commits on a different base, as if you had started your work from a more recent point.
Principle
Imagine you are working on a feature branch created from main. In the meantime, other commits have been added to main. Rebase takes your commits and replays them one by one on top of main:
Before the rebase:
C---D (feature)
/
A---B---E---F (main) After git rebase main (from feature):
C'---D' (feature)
/
A---B---E---F (main) Commits C and D become C' and D' - these are new commits (new hashes) with the same content, but repositioned.
How to rebase
# Switch to the branch to rebase
git switch feature
# Rebase onto main
git rebase main Your commits from feature are now on top of the latest commits from main. The history is linear and clean.
Rebase vs Merge - The great debate
Both allow you to integrate changes from one branch into another, but in very different ways.
Merge (fusion)
git switch main
git merge feature C---D (feature)
/ \
A---B---E---F---G (main, merge commit) - Creates a merge commit
- Preserves the history as it actually happened
- The history can become complex with many branches
Rebase
git switch feature
git rebase main C'---D' (feature)
/
A---B---E---F (main) - No merge commit - linear history
- Rewrites the history (new hashes)
- Easier to read
When to use which?
| Situation | Recommendation |
|---|---|
Update your branch with the latest changes from main | Rebase |
Integrate a completed branch into main | Merge |
| Branch already shared (pushed) | Merge (never rebase!) |
| Clean up history before sharing | Rebase |
The Golden Rule
Interactive rebase - A preview
Interactive rebase (git rebase -i) allows you to manipulate multiple commits at once: reorder, squash, edit, drop. It is an extremely powerful tool that we will not practice in this exercise, but you should know about:
git rebase -i HEAD~3 This command opens an editor with the last 3 commits and options like:
pick- keep the commit as isreword- change the messagesquash- merge with the previous commitdrop- delete the commit
It is the ultimate tool for cleaning up a history before sharing it.
Git Reset - Going back in time
git reset allows you to move HEAD (and potentially the branch) to a previous commit. There are three modes, increasingly destructive:
git reset --soft HEAD~1
git reset --soft HEAD~1 - Undoes the last commit
- Files remain in the staging area (ready to be re-committed)
- Useful for: rephrasing a commit or changing its content
git reset --mixed HEAD~1 (default)
git reset HEAD~1 - Undoes the last commit
- Files return to the working directory (unstaged)
- This is the default behavior (no need for
--mixed) - Useful for: reworking modifications before re-committing them
git reset --hard HEAD~1
git reset --hard HEAD~1 WARNING - DANGER! This command is destructive and irreversible (except via reflog). It removes the commit AND all associated modifications. Modified files are permanently lost. Use it only if you are absolutely sure you want to erase everything.
Visual summary
| Mode | Commit | Staging | Files |
|---|---|---|---|
--soft | Undone | Preserved | Preserved |
--mixed (default) | Undone | Cleared | Preserved |
--hard | Undone | Cleared | Deleted |
Git Reflog - The safety net
Made a mistake? One too many reset --hard? Don't panic. Git keeps a secret record of all HEAD movements in the reflog:
git reflog The reflog displays every action that modified HEAD: commits, rebase, reset, checkout... Even "deleted" commits appear there for at least 30 days.
Recovering a lost commit
# 1. Find the hash of the lost commit in the reflog
git reflog
# 2. Return to that commit
git reset --hard <hash-of-lost-commit> The reflog is your ultimate safety net. As long as a commit has existed, you can find it again.
Before any risky operation (rebase, reset --hard), note the hash of the current commit with git log --oneline -1. In case of trouble, you can always go back to it.
Practical exercise - Cleaning the chronicles
The Master Archivist entrusts you with a poorly maintained register. Entries contain errors, the chronology is confused. Your mission: restore order using --amend and rebase.
"These chronicles are a disaster. Mistakes in the entries, events in the wrong order... Clean it all up. Show me that you have mastered the art of rewriting History."
Step 1 - Create the register
Create a new folder and initialize a Git repository:
mkdir chroniques-nettoyees
cd chroniques-nettoyees
git init -b main Step 2 - Record three entries (with an error)
Create a file registre.txt and make 3 commits:
echo "Day 1: Founding of the kingdom" > registre.txt
git add registre.txt
git commit -m "Add Day 1 entry"
echo "Day 2: Alliance with the elves" >> registre.txt
git add registre.txt
git commit -m "Add Day 2 entry"
echo "Day 3: Discovery of the gold mine" >> registre.txt
git add registre.txt
git commit -m "Addd Day 3 entry" Oops! The message of the last commit contains a typo: "Addd" instead of "Add".
Step 3 - Fix with --amend
Fix the last commit message:
git commit --amend -m "Add Day 3 entry" Verify with git log --oneline that the message is corrected.
Step 4 - Create a parallel branch
Create a refonte-archives branch and add entries to it:
git switch -c refonte-archives
echo "Day 4: Construction of the fortress" >> registre.txt
git add registre.txt
git commit -m "Add Day 4 entry"
echo "Day 5: Peace treaty with the dwarves" >> registre.txt
git add registre.txt
git commit -m "Add Day 5 entry" Step 5 - Add a commit on main
Return to main and add a commit:
git switch main
echo "Appendix: List of kings" >> annexes.txt
git add annexes.txt
git commit -m "Add the list of kings as appendix" Step 6 - Rebase the branch
Rebase refonte-archives onto main to get a linear history:
git switch refonte-archives
git rebase main Step 7 - Verify the result
Admire the clean, linear history:
git log --oneline --graph --all You should see all commits aligned on a single line, with no forks or merge commits.
Step 8 - Run the verification
Run the verification script to validate your quest:
Bash (Linux / macOS / Git Bash on Windows):
bash verifier.sh PowerShell (Windows):
.\verifier.ps1 The script checks:
- You are in a Git repository
- You have used --amend (visible in the reflog)
- You have performed a rebase (visible in the reflog)
- At least 3 commits exist in the history
The Master Archivist examines the rewritten chronicles, running his finger along the carefully reorganized lines.