Resetting Changes in Git
Another way that we can undo changes in our repository is by reseting them using git-reset
. I want to note right up front that git-reset
is a destructive way of undoing changes in your repository. Unlike git-revert
, using git-reset
to undo commits doesn’t keep the undone commits intact; it’s a permanent undo of the repository history.
How it works
Git-reset has a few versions that do different things:
git reset
— the plain command will reset the staging area to match the most recent commit. The files are unstaged but the changes in the files are not overwritten. The working directory is also left alone.git reset index.html
— This unstages theindex.html
(or any file you specify) and leaves the working directory as-is.git reset [SHA1]
— This resets back to the commit you specify and resets the staging directory. The working directory is left as-is. Any changes that are rolled back as part of the reset are stored in the working directory as modified files. You can then recommit them.git reset --hard
— The--hard
option will reset the staging area and working directory to the state of the most recent commit. Any changes in the working directory will be overwritten, removing any uncommitted changes.git reset --hard [SHA1]
— This option resets back to the specified commit, resets any changes in staging and any working directory changes. You will lose work with this option. If you pick a commit further back then the most recent,git-reset
will destroy all commits that came after it. You are editing the history of the repository.
This method should only be used local changes, not reseting changes that are part of a public or shared repository. You almost never want to change the repository history by removing commits when others are expecting the history to be as it was.
The best time to use git-reset is when you want to undo changes in your staging area or working 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 commit. Why would we ever want to do this? A recommended approach in Git is to make commits as atomic as possible. This means we don’t want to bundle up dozens of changes — especially unrelated 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 staging area but doesn’t overwrite the changes we made to the files. We can now selectively stage and commit the files.
We can be file-specific 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 accidentally staged a file you don’t want to include in a commit.
If we weren’t happy with a commit we made — perhaps it included too many unrelated changes — we can undo that commit by rolling back to the commit previous to it.
$ git reset d3828dh3
This will leave the working directory as-is. Any changes that were part of the commit or commits we between HEAD and the commit we specified, will be rolled back and stored in the working directory as modified files. We can then recommit them in a way that makes more sense for us.
Another option and one you can use if things really have gone wrong is git-reset with the --hard
option.
git reset --hard
This will remove all files from staging and overwrites the changes so the files are all back to the same state they were in the HEAD commit. This includes modified files in the working directory.
Finally, we can reset back to a specific commit with the --hard
option.
git reset --hard d3828dh3
When we run this, Git will roll back to state of the repository at the specified commit, reset both the staging and working directories and destroy all commits that came after the one we specified.
I always proceed with caution when using git-reset. It is an important tool for fixing problems in Git but also very powerful. And you know what they say about power.