Skip to main content

Section 4.5 "Undoing" in Git

At any stage, you may want to undo something. Here, we’ll review a few tools for undoing changes that you’ve made. Be careful though. This is one of the areas in Git where you may lose some work if you do it wrong, because you can’t always undo some of these undos.

Section 4.5.1 Undoing Changes

One of the common undos takes place when you commit too early and possibly forget to add some files, or you mess up your commit message. If you want to redo that commit, make the additional changes you forgot, stage them, and commit again using the --amend option:
$ git commit --amend
This command takes your staging area and uses it for the commit. If you’ve made no changes since your last commit (for instance, you run this command immediately after your previous commit), then your snapshot will look exactly the same, and all you’ll change is your commit message.
The same commit-message editor fires up, but it already contains the message of your previous commit. You can edit the message the same as always, but it overwrites your previous commit.
As an example, if you commit and then realize you forgot to stage the changes in a file you wanted to add to this commit, you can do something like this:
$ git commit -m 'Initial commit'
$ git add forgotten_file
$ git commit --amend
You end up with a single commit — the second commit replaces the results of the first.
Note: It’s important to understand that when you’re amending your last commit, you’re not so much fixing it as replacing it entirely with a new, improved commit that pushes the old commit out of the way and puts the new commit in its place. Effectively, it’s as if the previous commit never happened, and it won’t show up in your repository history.
The obvious value to amending commits is to make minor improvements to your last commit, without cluttering your repository history with commit messages of the form, “Oops, forgot to add a file” or “Darn, fixing a typo in last commit”.
Note: Only amend commits that are still local and have not been pushed somewhere. Amending previously pushed commits and force pushing the branch will cause problems for your collaborators.

Unstaging a Staged File.

The next two sections demonstrate how to work with your staging area and working directory changes. The nice part is that the command you use to determine the state of those two areas also reminds you how to undo changes to them. For example, let’s say you’ve changed two files and want to commit them as two separate changes, but you accidentally type git add * and stage them both. How can you unstage one of the two? The git status command reminds you:
$ git add *
$ git status
On branch main
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README
    modified:   CONTRIBUTING.md
Right below the “Changes to be committed” text, it says use git reset HEAD <file>…​ to unstage. So, let’s use that advice to unstage the CONTRIBUTING.md file:
$ git reset HEAD CONTRIBUTING.md
Unstaged changes after reset:
M   CONTRIBUTING.md
$ git status
On branch main
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md
The command is a bit strange, but it works. The CONTRIBUTING.md file is modified but once again unstaged.
Note: It’s true that git reset can be a dangerous command, especially if you provide the --hard flag. However, in the scenario described above, the file in your working directory is not touched, so it’s relatively safe.
For now this magic invocation is all you need to know about the git reset command.

Unmodifying a Modified File.

What if you realize that you don’t want to keep your changes to the CONTRIBUTING.md file? How can you easily unmodify it — revert it back to what it looked like when you last committed (or initially cloned, or however you got it into your working directory)? Luckily, git status tells you how to do that, too. In the last example output, the unstaged area looks like this:
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md
It tells you pretty explicitly how to discard the changes you’ve made. Let’s do what it says:
$ git checkout -- CONTRIBUTING.md
$ git status
On branch main
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README
You can see that the changes have been reverted.
Important: It’s important to understand that git checkout -- <file> is a dangerous command. Any local changes you made to that file are gone — Git just replaced that file with the last staged or committed version. Don’t ever use this command unless you absolutely know that you don’t want those unsaved local changes.
If you would like to keep the changes you’ve made to that file but still need to get it out of the way for now, we’ll go over stashing and branching in the following section on Git Branching; these are generally better ways to go.
Remember, anything that is committed in Git can almost always be recovered. Even commits that were on branches that were deleted or commits that were overwritten with an --amend commit can be recovered. However, anything you lose that was never committed is likely never to be seen again.

Undoing things with git restore.

Git version 2.23.0 introduced a new command: git restore. It’s basically an alternative to git reset which we just covered. From Git version 2.23.0 onwards, Git will use git restore instead of git reset for many undo operations.
Let’s retrace our steps, and undo things with git restore instead of git reset.

Unstaging a Staged File with git restore.

The next two sections demonstrate how to work with your staging area and working directory changes with git restore. The nice part is that the command you use to determine the state of those two areas also reminds you how to undo changes to them. For example, let’s say you’ve changed two files and want to commit them as two separate changes, but you accidentally type git add * and stage them both. How can you unstage one of the two? The git status command reminds you:
$ git add *
$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    modified:   CONTRIBUTING.md
    renamed:    README.md -> README
Right below the “Changes to be committed” text, it says use git restore --staged <file>…​ to unstage. So, let’s use that advice to unstage the CONTRIBUTING.md file:
$ git restore --staged CONTRIBUTING.md
$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    renamed:    README.md -> README

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:   CONTRIBUTING.md
The CONTRIBUTING.md file is modified but once again unstaged.

Unmodifying a Modified File with git restore.

What if you realize that you don’t want to keep your changes to the CONTRIBUTING.md file? How can you easily unmodify it — revert it back to what it looked like when you last committed (or initially cloned, or however you got it into your working directory)? Luckily, git status tells you how to do that, too. In the last example output, the unstaged area looks like this:
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:   CONTRIBUTING.md
It tells you pretty explicitly how to discard the changes you’ve made. Let’s do what it says:
$ git restore CONTRIBUTING.md
$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    renamed:    README.md -> README
Important: It’s important to understand that git restore <file> is a dangerous command. Any local changes you made to that file are gone — Git just replaced that file with the last staged or committed version. Don’t ever use this command unless you absolutely know that you don’t want those unsaved local changes.
Checkpoint 4.5.1. Read External Git Undoing Summary.
For a humorous summary of undoing in Git, go to Oh Shit, Git!?!
 1 
ohshitgit.com/

Section Summary.

As you have learned from this section, Git provides several tools for undoing changes that you have made. One of the most common undos is when you commit too early and forget to add some files, or you mess up your commit message or if you accidentally stage a file that you didn’t mean to. However, it’s important to be careful when using these commands because you can’t always undo some of these undos.

Checkpoint 4.5.2.

    Suppose you have inadvertently committed one or more changes to a file and want to discard the changes to revert it back to its last committed state. Which Git command should you use? Select all that apply.
  • git checkout -- <file>
  • Corect! This command allows you to discard changes made to a specific file and revert it back to its last committed state.
  • git reset --hard HEAD
  • This command resets the entire working directory to the state of the last commit. Be careful, as it will discard all changes, including those in other files.
  • git restore <file>
  • Correct! This command allows you to restore a file to a previous state, but it requires specifying the commit or reference from which to restore the file.
  • git revert HEAD
  • The command git revert HEAD is used to create a new commit that undoes the changes introduced by the last commit, which is pointed to by the HEAD reference
  • git --unamend <file>
  • This is not a valid git command.
  • git reset <file>
  • This command unstages the file but does not revert any changes made in it.
You have attempted of activities on this page.