Course outline · 0% complete

0/28 lessons0%

Course overview →

amend, revert, and the reflog safety net

lesson 4-3 · ~11 min · 12/28

Fixing the last commit: --amend

Committed, then noticed a typo in the message or a file you forgot? --amend redoes the most recent commit.

$ git commit --amend -m "Add pancake recipe with photo"

Stage a forgotten file first and it gets folded in.

$ git add photo.jpg
$ git commit --amend -m "Add pancake recipe with photo"

One caution to file away. Amend does not edit the old commit, it replaces it with a brand-new commit that has a different hash. That follows directly from lesson 1-2: the id is computed from the contents, so different contents mean a different id, and there is no such thing as changing a commit in place.

Replacing a commit is harmless while it only exists on your machine. Once commits have been shared with teammates in unit 7, replacing shared history causes real trouble, and unit 9 formalizes the rule about when rewriting is acceptable.

A recorded session

This session walks an amend. Add pancake recipe was just committed, and photo.jpg belonged in it.

Each step below shows the command and the output it printed.

Step 1. Stage the forgotten file.

~/recipe-book $ git add photo.jpg

Step 2. Redo the last commit so it includes the staged file, with the message Add pancake recipe with photo.

~/recipe-book $ git commit --amend -m "Add pancake recipe with photo"
[main b2c3d4e] Add pancake recipe with photo
 2 files changed, 4 insertions(+)

The summary now counts two files, since the amended commit contains both the recipe and the photo.

Step 3. Check the log.

~/recipe-book $ git log --oneline
b2c3d4e Add pancake recipe with photo

There is still exactly one commit, not two, so the amend genuinely replaced rather than added. Its hash is b2c3d4e where the original was something else entirely, which is the replacement showing through: same position in history, different commit.

Undoing an old commit safely: revert

reset rewinds history, which is fine for commits nobody else has seen. For anything older or already shared, use git revert:

$ git revert a1b2c3d
[main f0e9d8c] Revert "Add pancake recipe"

Revert doesn't delete anything. It creates a new commit containing the opposite changes (every added line removed, every removed line re-added). History stays intact and honest: the mistake happened, and here's the commit that fixed it.

git resetgit revert
What it doeserases commits from the end of historyadds a new commit that cancels an old one
Historyrewrittenpreserved
Safe on shared worknoyes
reset: the bad commit is dropped from the branchno longer inthe historymainrevert: a new commit undoes the change, history intactbad commit staysRevert commit:applies the opposite
A reset dropping the last commit off the branch compared with a revert that keeps it and appends a new commit applying the opposite change.

For a bad commit already in your team's shared history, undo its effect with git revert <hash>, which adds a new commit that cancels it.

Revert is the shared-history tool. It leaves the original commit in place and appends a commit containing the opposite changes, so everyone's copy of the history stays consistent and the record stays honest about what happened.

reset would be the wrong choice for two reasons. It erases commits from the end of history, so undoing something from last week would take a week of good commits with it, and it rewrites history that teammates already have, which turns their next pull into a mess.

The safety net: git reflog

Here is the secret that makes Git nearly panic-proof. Even destroyed commits usually still exist.

git reflog prints a local log recording every position HEAD has ever had on your machine. Every commit, reset, switch, and merge moved HEAD, and each move was written down, which makes the reflog something like Git's private diary.

$ git reflog
f0e9d8c HEAD@{0}: reset: moving to HEAD~1
c7d8e9f HEAD@{1}: commit: Add photo of finished pancakes
a1b2c3d HEAD@{2}: commit: Add pancake recipe

Suppose that reset was a terrible mistake and c7d8e9f held real work. It is no longer in git log, but the reflog still knows its hash, so git reset --hard c7d8e9f brings it straight back. This works because a commit removed from the visible history is only unreferenced, not deleted.

The limits are worth knowing precisely. The reflog is local to your machine, so it cannot help with a teammate's mistake, and entries expire after around 90 days. More importantly, it rescues committed work only. Changes you never committed are the one thing Git genuinely cannot resurrect, which is the deepest argument for committing early and often.

The command showing every place HEAD has been is git reflog.

It lists each position with its hash, including the ones no longer reachable from the visible history. Finding the entry from just before the bad reset gives you the lost commit's hash, and git reset --hard <that-hash> restores it.

The distinction that makes this work is between the public history and the local diary. git log walks the chain of parent pointers from where you currently are, so a commit dropped off the end becomes invisible to it. The reflog is a plain record of HEAD movements, kept independently, so it still remembers the hash long after the history stopped pointing at it.