Resetting Changes in Git

Anoth­er way that we can undo changes in our repos­i­to­ry is by reset­ing them using git-reset. I want to note right up front that git-reset is a destruc­tive way of undo­ing changes in your repos­i­to­ry. Unlike git-revert, using git-reset to undo com­mits doesn’t keep the undone com­mits intact; it’s a per­ma­nent undo of the repos­i­to­ry history.

How it works

Git-reset has a few ver­sions that do dif­fer­ent things:

  • git reset — the plain com­mand will reset the stag­ing area to match the most recent com­mit. The files are unstaged but the changes in the files are not over­writ­ten. The work­ing direc­to­ry is also left alone.
  • git reset index.html — This unstages the index.html (or any file you spec­i­fy) and leaves the work­ing direc­to­ry as-is.
  • git reset [SHA1] — This resets back to the com­mit you spec­i­fy and resets the stag­ing direc­to­ry. The work­ing direc­to­ry is left as-is. Any changes that are rolled back as part of the reset are stored in the work­ing direc­to­ry as mod­i­fied files. You can then recom­mit them.
  • git reset --hard — The --hard option will reset the stag­ing area and work­ing direc­to­ry to the state of the most recent com­mit. Any changes in the work­ing direc­to­ry will be over­writ­ten, remov­ing any uncom­mit­ted changes.
  • git reset --hard [SHA1] — This option resets back to the spec­i­fied com­mit, resets any changes in stag­ing and any work­ing direc­to­ry changes. You will lose work with this option. If you pick a com­mit fur­ther back then the most recent, git-reset will destroy all com­mits that came after it. You are edit­ing the his­to­ry of the repository.

This method should only be used local changes, not reset­ing changes that are part of a pub­lic or shared repos­i­to­ry. You almost nev­er want to change the repos­i­to­ry his­to­ry by remov­ing com­mits when oth­ers are expect­ing the his­to­ry to be as it was. 

The best time to use git-reset is when you want to undo changes in your stag­ing area or work­ing tree.

Using git-reset

Let’s say we have staged too many changed files and don’t want all of them to be part of the same com­mit. Why would we ever want to do this? A rec­om­mend­ed approach in Git is to make com­mits as atom­ic as pos­si­ble. This means we don’t want to bun­dle up dozens of changes — espe­cial­ly unre­lat­ed changes — in the same commit. 

To do this we can use the basic git-reset command:

$	git reset

This will remove all files from the stag­ing area but doesn’t over­write the changes we made to the files. We can now selec­tive­ly stage and com­mit the files.

We can be file-spe­cif­ic in the reset, too, in case we only want to unstage one file:

git reset app.js

Like before, this won’t undo the changes in the file, just unstage it. This is handy if you acci­den­tal­ly staged a file you don’t want to include in a commit.

If we weren’t hap­py with a com­mit we made — per­haps it includ­ed too many unre­lat­ed changes — we can undo that com­mit by rolling back to the com­mit pre­vi­ous to it. 

$ git reset d3828dh3

This will leave the work­ing direc­to­ry as-is. Any changes that were part of the com­mit or com­mits we between HEAD and the com­mit we spec­i­fied, will be rolled back and stored in the work­ing direc­to­ry as mod­i­fied files. We can then recom­mit them in a way that makes more sense for us.

Anoth­er option and one you can use if things real­ly have gone wrong is git-reset with the --hard option.

git reset --hard

This will remove all files from stag­ing and over­writes the changes so the files are all back to the same state they were in the HEAD com­mit. This includes mod­i­fied files in the work­ing directory.

Final­ly, we can reset back to a spe­cif­ic com­mit with the --hard option. 

git reset --hard d3828dh3

When we run this, Git will roll back to state of the repos­i­to­ry at the spec­i­fied com­mit, reset both the stag­ing and work­ing direc­to­ries and destroy all com­mits that came after the one we spec­i­fied.

I always pro­ceed with cau­tion when using git-reset. It is an impor­tant tool for fix­ing prob­lems in Git but also very pow­er­ful. And you know what they say about power.