Restoring, Reverting & Resetting Changes in Git
11 min read·Jan 1, 2025
In Git, there are essentially three ways to rollback changes made in the working tree, the staging area, or the commit history, which are:
- Restoring
- Reverting
- Resetting
The most common reasons for undoing commits are usually:
- Files that were included in the commit by mistake.
- Typos that were introduced in the commit message.
- New code that causes unforeseen bugs or accidental code changes.
Restoring uncommitted files
In Git, restoring consists in undoing changes made in the files present in the working tree or the staging area.
Discarding changes in the working tree
It may happen that certain changes, such as the addition of temporary logs for debugging purposes, need to be removed before they are staged.
To discard the changes made to the files in the working tree and revert them to the state they were in at the last commit, you can use the git restore command:
$ git restore file ...
Alternatively, to discard all the changes made in all the files of the working tree, you can specify the current directory as an argument of the git restore command:
$ git restore .
❗ Warning: This command should be used with caution as it permanently removes any local changes that haven't been committed.
Example
Let's consider this file named index.js:
$ cat index.js
console.log("Good morning");
When edited, Git will mark it as "modified":
$ echo 'console.log("Guten Tag");' > index.js
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: index.js
no changes added to commit (use "git add" and/or "git commit -a")
And when restored, Git will consider the working tree clean:
$ git restore index.js
$ git status
On branch master
nothing to commit, working tree clean
$ cat index.js
console.log("Good morning");
Discarding all changes
To simultaneously remove all the files present in the staging area and revert the files in the working tree to the state they were in at the last commit, you can use the git reset command with the --hard flag:
$ git reset --hard HEAD
Where HEAD is a reference to the last commit in the history.
❗ Warning: This command will forcefully update the working directory to match the state of the last commit, which means that any untracked files and directories will be permanently deleted.
Example
Let's consider this repository that contains a tracked file named .env.development and an untracked file named .env.production:
$ ls
.env.development .env.production
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
.env.production
nothing added to commit but untracked files present (use "git add" to track)
When adding the .env.production file to the staging area, Git will mark this file as "new":
$ git add .env.production
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: .env.production
When resetting the repository, Git will remove the .env.production file both from the staging area and the working tree:
$ git reset --hard HEAD
HEAD is now at bdac15a add development environment
$ git status
On branch master
nothing to commit, working tree clean
$ ls
.env.development
Reverting the last commit in the history
It often happens that the changes introduced by the last commit in the history cause bugs, errors, or unexpected behaviors.
The safest way to cancel these changes is to create a new opposite commit in the history that reverts the changes introduced by the previous one.
To create a revert commit, you can use the git revert command:
$ git revert [--no-edit|--no-commit] HEAD
Where:
HEADis a reference to the last commit in the history.--no-editis a flag used to keep the default commit message suggested by Git and prevent the text editor from opening.--no-commitis a flag used to revert changes to the working tree and the staging area without directly committing them to the history.
By default, this command opens the default command-line text editor, allowing you edit the commit message of the reverse commit.
Once saved and closed, the commit will automatically be added to the history.
Note: In order to execute this command, your working tree needs to be in a clean state, as otherwise, Git will throw the following error:
error: Your local changes to the following files would be overwritten by merge: Please commit your changes or stash them before you merge. Aborting
Example
Let's consider this history, where HEAD points to the commit hash e97698a:
$ git log --oneline
e97698a (HEAD -> master) third commit
cd2bbfe second commit
9e01fd9 first commit
When reverting the repository:
$ git revert HEAD
Git will open the default text editor asking you to enter a message for the new commit:
Revert "third commit"
This reverts commit e97698ac66bec08055e23e3cc3d1885a4b63908.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# Your branch is up to date with 'origin/master'.
#
# Changes to be committed:
# modified: index.js
#
Once the file is saved and the text editor closed, Git will add a new commit (fr853b1) on top of the previous one (e97698a) and move the head pointer to that new commit.
$ git log --oneline
fr853b1 (HEAD -> master) Revert "third commit"
e97698a third commit
cd2bbfe second commit
9e01fd9 first commit
Removing the last commits from the history
Another riskier method for cancelling the changes introduced by the last n commits is to physically remove them from the history.
To remove the last n commits, you can use the git reset command with the --hard flag:
$ git reset --hard HEAD~n
Where HEAD~n is a reference to the commit that is n steps before the last commit.
This command will forcefully update the working directory to match the state of commit before the last n commits and remove the last n commits from the history, which means that any untracked files and directories will be permanently deleted.
Example
Let's consider this Git history:
$ git log --oneline
e97698a (HEAD -> master) third commit
cd2bbfe second commit
9e01fd9 first commit
This command will remove the last commit from the history:
$ git reset --hard HEAD~1
$ git log --oneline
cd2bbfe (HEAD -> master) second commit
9e01fd9 first commit
Summary
Here's a summary of what you've learned in this lesson:
- The
git restorecommand is used to discard changes made in the working tree. - The
git reset --hard HEADcommand is used to remove all the files from the staging area and discard the changes made in the working tree. - The
git revert HEADcommand is used to create a revert commit of the last commit in the history - The
git reset --hard HEAD~1command is used to remove the last commit from the history and update the working tree.
Enjoying the courses?
I've made these courses completely free so anyone can learn from them. If they've helped you and you'd like to actively support the work behind BackendBrewery, you can leave a tip:
Support BackendBrewery