What rewrites history and what does not
Beginner Core Git Git CLI
Why this matters
Git can change the past: fix a commit message, squash five commits into one, remove a file from history. Those powers are useful on your own work and destructive on work you have shared. One rule separates the two, and knowing it on day one prevents the most stressful Git situations a team can have.
Two kinds of operations
Recall from lesson 2.4 that a commit's hash includes its parent's hash. So:
- Adding a commit on top leaves every existing commit untouched, with its hash. Anyone who already has those commits still has exactly the same history, plus your new commit. This is what
commit,merge,revert,tag,push,fetchandpulldo. - Rewriting a commit produces a different commit with a different hash, and therefore different hashes for everything after it. The old commits are not deleted immediately, but no branch points at them any more. This is what
commit --amend,rebase,reset(when it moves a branch backwards),push --force, interactive rebase (squash, reorder, drop) andfilter-repodo.
What it looks like when the rule is broken
Ana pushed a commit, then noticed a typo in the message and amended it:
$ git commit --amend -m "docs: explain how to add a trail (#27)"[main 7bc7f5f] docs: explain how to add a trail (#27)
Date: Sat Sep 5 11:00:00 2026 +0200
1 file changed, 5 insertions(+)The commit 2f543f1 she pushed has been replaced locally by 7bc7f5f. Her local main and the remote now disagree about what the latest commit is:
$ git statusOn branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
(use "git pull" if you want to integrate the remote branch with yours)
nothing to commit, working tree clean"Diverged, 1 and 1": the remote has the old commit, she has the new one, and they are not related as parent and child. A plain git push is now rejected. Her options are both unpleasant: pull (which merges the old commit back in, creating a duplicate) or force-push (which overwrites the remote and breaks everyone who already pulled the old commit). Had she amended before pushing, none of this would exist.
The rule
Rewrite only commits that exist nowhere but your computer. Once a commit is pushed to a branch other people use, treat it as permanent: fix mistakes by adding a new commit (
git revert, or simply a follow-up commit), never by rewriting.
Corollaries your team may add:
- Feature branches that only you work on are yours to rewrite before the merge request is reviewed; many teams squash them at merge time anyway.
mainand release branches are never rewritten by anyone; platforms enforce this with protected branches (lesson 6.9).git push --forceon a shared branch needs a conversation first, and--force-with-leaseat minimum (lesson 12.8).
The lists
| Adds to history (safe to share) | Rewrites history (unpushed work only) |
|---|---|
git commit |
git commit --amend |
git merge |
git rebase, git rebase -i |
git revert |
git reset moving a branch backwards |
git tag |
git push --force, --force-with-lease |
git push, git fetch, git pull |
git filter-repo, BFG |
git cherry-pick (adds a copy) |
deleting and re-creating a branch at a different commit |
Not on either list because they touch no commits: status, diff, log, add, restore, stash, switch. git reset deserves a note: with --soft or --mixed on unpushed commits it is an ordinary tidy-up; with --hard it also discards working-tree changes; on pushed commits it is history rewriting. Section 12 treats each carefully.
Common mistakes
- Amending after pushing. The example above. If you notice at once and nobody has pulled,
git push --force-with-leaseon a personal branch is acceptable; onmain, live with the typo. - "Fixing" a diverged branch with
--forcewithout knowing why it diverged. You may erase a colleague's commits. Fetch, look atgit log --oneline --all, understand first. - Believing rewritten commits are gone. They remain in the reflog for weeks; lesson 12.1 uses that to recover from mistakes.
Try it yourself
Goal: amend an unpushed commit safely and observe the hash change.
- In the playground, make a small commit with a deliberate typo in its message:
git commit --allow-empty -m "docs: fix typoo"(--allow-emptylets you commit without changes, useful for practice). - Run
git log --oneline -1and note the hash. - Run
git commit --amend -m "docs: fix typo"andgit log --oneline -1again.
Expected result: the message changed and so did the hash; the old commit is no longer in the log. Because it was never pushed, nobody is affected.
Show solution
Amending creates a new commit with the corrected message and the same parent, and moves main to it. The old commit still exists in .git for a while but no branch points at it. If your playground has a remote and you had pushed first, git status would now say "diverged" — which is the whole lesson in one word.