Advanced

Undoing yourself on purpose

git reset was the command I was most afraid of until I drew the branch as a pointer and watched where it moved. Undoing a commit is just moving a label a few steps back.

What you will see

HEAD is now at abc4567 initial v1

About 2 min of reading

Quick check

Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.

Try It

Move a branch pointer back to undo a commit I just made, in three different ways. Each mode handles the staged and working files differently, so I watch what stays and what goes.

1

Commit two times so the branch has a history. git log --oneline shows both commits sitting on top of the first one.

printf 'v1\n' > app.py git add app.py git commit -m initial printf 'v2\n' > app.py git add app.py git commit -m second git log --oneline

Before you run it, say out loud what it will print. Then open the answer.

What it prints
def7890 second abc4567 initial
The hashes change every run, def7890 and abc4567 are just representatives. Read them as placeholders for the real short hashes on your machine.
2

Undo the last commit but keep everything staged with --soft. The branch moves back one commit and your changes reappear in the staging box as ready to save.

git reset --soft HEAD~1 git status --short

Before you run it, say out loud what it will print. Then open the answer.

What it prints
M app.py
`M app.py` (M with two spaces) means the file is staged AND still modified. Nothing was thrown away, `--soft` only moved the label.
3

Undo the last commit and clear the staging box but leave the edits in your working folder. This is the default reset with no flag, the gentlest useful one.

git reset HEAD~1 git status --short

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Unstaged changes after reset: M app.py M app.py
Here the file is only ` M`: unstaged and modified. The commit is undone but your edit is still there waiting to be staged again.
4

Undo the last commit and throw away every change with --hard. There is no way back for the edit once you do this, so only use it when you are sure the work should simply vanish.

git reset --hard HEAD~1 git status --short cat app.py

Before you run it, say out loud what it will print. Then open the answer.

What it prints
HEAD is now at abc4567 initial v1
The empty status line means the folder is clean and app.py is back to "v1". `--hard` deleted the second commit and its edit for good; the only escape hatch is `git reflog`.

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

Reset moves the branch pointer back; the mode decides what happens to the changes that were staged.
1 Pick a reset mode
↓
2 Move the branch pointer back
↓
3 Clear or keep the staging area
↓
4 Keep or discard the working files
↓
5 The change is undone in history
Why this works
What reset does to each area

Reset moves the branch pointer back and treats the three areas differently:

ModeCommitsStagingFiles
--softundonekeptkept
--mixedundoneclearedkept
--hardundonecleareddeleted

All three move the pointer. The only real question is what you do with the changes that were already there.

Getting back what you lost

Even the scary resets are usually recoverable, because git keeps a ledger of every HEAD movement:

git reflog

The reflog lists where the branch has been, including commits you reset away. Copy a lost commit's hash from there and checkout it to bring the work back. This is the reason --hard only becomes permanent until you actually discard something in a way reflog cannot find.

Examples

Uncommit but keep everything

git reset --soft HEAD~1

--soft moves the branch back one commit and puts your changes straight back into the staging area. I use it to rewrite a commit message I rushed, or to fold a commit I made too early back into a cleaner one.

The default that unstages

git reset HEAD~1

Without a flag, reset uses --mixed: it undoes the commit and clears the staging box, but leaves your edits sitting in the working folder untouched. The gentlest useful reset, and the one I reach for when a commit needs rearranging.the one I reach for when a commit needs rearranging.

The one that actually deletes

git reset --hard HEAD~1

--hard moves back one commit and throws away every change along with it. There is no undo button on this one. The first time I used it I lost three hours of work in a second. Only use it when you are certain the past commits and their edits should simply cease to exist.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Pigeonholing uncommitted work
Previous Lesson
Pigeonholing uncommitted work
Next Lesson
Stealing a single commit
Stealing a single commit →