Course outline · 0% complete

0/28 lessons0%

Course overview →

git reset: rewinding commits

lesson 4-2 · ~11 min · 11/28

Taking back a commit

git restore handles uncommitted changes. The harder case is regretting something already committed, and that is git reset, which moves your current position, the HEAD from lesson 3-1, back to an earlier commit.

First the address notation, since reset needs a target. HEAD~1 means one commit before HEAD, HEAD~2 means two before, and the pattern continues. So the everyday incantation is this.

$ git reset --soft HEAD~1

Read it as "move me back one commit." The commit disappears from git log, but the changes inside it do not vanish into thin air. Where they land depends on the mode flag, and understanding that is the entire trick to understanding reset.

Worth being clear about what disappears. The commit is removed from the history your branch points at, so git log stops listing it, but the object itself survives in .git for a while, which is what makes the safety net in lesson 4-3 possible.

working directorystaging arearepository--soft--mixed(default)--hard
How far each reset mode reaches. --soft only rewrites history, --mixed also clears the staging area, --hard additionally overwrites your working directory. Longer bar, more destruction.

The three modes

CommandHistoryStaging areaWorking directory
git reset --soft HEAD~1rewoundkeeps the changes, stageduntouched
git reset HEAD~1 (mixed, default)rewoundclearedkeeps the changes, unstaged
git reset --hard HEAD~1rewoundclearedoverwritten, changes gone

Memory hook: soft is the gentlest landing (everything still staged, ready to re-commit), mixed drops the changes back to your desk, hard throws them in the shredder.

--hard is the second dangerous command of the unit. Before running it, say out loud what you expect to lose. (Lesson 4-3 reveals a safety net that can often save you anyway.)

Each mode rewinds history. They differ in what else they clear.historystaging areaworking directory--soft--mixed--hardchanges stay stagedchanges stay on diskyour uncommitted edits are overwritten
Three bars of increasing length showing that soft reset touches only history, mixed also clears staging, and hard reaches the working directory too.

Re-slicing a bad commit

Here is the reset engineers actually run weekly. You hurried and committed two unrelated changes as one blob.

$ git log --oneline
9c8b7a6 wip stuff
a1b2c3d Add pancake recipe

That wip stuff commit contains a bug fix and a new recipe mashed together, which breaks lesson 2-3's rule of one logical change per commit. Unpacking it takes a mixed reset and two clean commits.

$ git reset HEAD~1
$ git add soup.txt
$ git commit -m "Fix soup salt amount"
$ git add cake.txt
$ git commit -m "Add chocolate cake recipe"

The reset undid the commit and dropped both changes back into the working directory, unstaged. From there each file is staged and committed on its own, so the sloppy commit is gone and two honest ones stand in its place.

Nothing was ever at risk here, which is worth saying explicitly given how much of this unit comes with warnings. A mixed reset never touches the working directory. It rewinds history and clears the staging area, leaving your files exactly as they were so you can re-stage them in better slices.

The mode that discards uncommitted file contents along with rewinding the commit is --hard.

It reaches all the way into the working directory and overwrites your files with the older snapshot, so any edits not preserved in another commit are gone.

--soft and --mixed both stop short of that. They rewind history and keep the changes available, staged in the first case and unstaged in the second, which is why they are recoverable mistakes and --hard frequently is not.

In git reset --soft HEAD~1, the target HEAD~1 refers to the commit one step before your current position.

HEAD is where you are standing, and the ~1 steps one parent back along the chain of parent pointers from lesson 1-2. HEAD~3 would step back three commits the same way.

So the whole command reads as move my position to the previous commit. That is also why the notation appears throughout this unit: reset, revert, and rebase all need a way to name a commit relative to where you are, and counting back from HEAD is easier than looking up a hash.

To undo the commit while keeping everything staged, use git reset --soft HEAD~1.

The --soft mode rewinds the commit and leaves all of its changes sitting in the staging area, ready to go straight back into a new commit. From there you stage the extra file with git add and run git commit again.

This is the gentlest of the three modes, disturbing history and nothing else. For this specific situation, adding one more file to the commit you just made, lesson 4-3 introduces a shorter route with --amend, which does the rewind and the re-commit in a single step.