Gastón Ramos

Personal blog

Undoing Changes in Git

Originally written over two mornings, September 29–30, 2024.

Leer el artículo original en español

Git logo

The idea

This post reviews three fundamental ways to undo changes in Git. These are useful operations in day-to-day work, so let's get started.

1. Change the latest commit with git commit --amend

Suppose you have just created a commit and realize that you forgot to add a file, or you want to improve the commit message. You can replace the latest commit with git commit --amend.

Imagine that you did this:

$ git add my_file_A.rb
$ git commit -m "Adding some new files"
[master (root-commit) c798093] Adding some new files
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 my_file_A.rb

You then realize that my_file_B.rb should have been part of the same commit. Add it and amend the commit:

$ git add my_file_B.rb
$ git commit --amend

Git opens your configured editor with the previous commit message. You can edit the message, and the staged file now appears alongside the first one:

Adding some new files

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Fri Sep 27 12:19:31 2024 -0300
#
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   new file:   my_file_A.rb
#   new file:   my_file_B.rb

Save and exit, and you're done. Keep in mind that this replaces the commit, so its hash changes. Be careful when amending a commit that you have already pushed and shared with other people.

2. Unstage a file with git restore --staged

Next, consider a file that you have already staged for the next commit. Start by modifying two files:

$ echo "hi" >> my_file_A.rb
$ echo "hi-B" >> my_file_B.rb
$ 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:   my_file_A.rb
        modified:   my_file_B.rb

no changes added to commit (use "git add" and/or "git commit -a")

Stage both files:

$ git add my_file_A.rb my_file_B.rb
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   my_file_A.rb
        modified:   my_file_B.rb

Now you decide to create two commits, one for each file. First, remove my_file_B.rb from the staging area:

$ git restore --staged my_file_B.rb

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   my_file_A.rb

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:   my_file_B.rb

You can now commit the staged my_file_A.rb, then stage and commit my_file_B.rb separately.

3. Discard changes in a file with git restore

If you have modified a file but don't want to keep those changes, Git can restore the version from the latest commit. Be careful: any uncommitted changes in that file will be lost.

Modify my_file_A.rb, then restore it:

$ echo "hi, me again" >> my_file_A.rb
$ 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:   my_file_A.rb

no changes added to commit (use "git add" and/or "git commit -a")

$ git restore my_file_A.rb
$ git status
On branch master
nothing to commit, working tree clean

That's all. I wrote this article partly for myself because it is always useful to revisit the fundamentals. I hope it helps you too.

Thanks for reading. Gramos ;)


 ::: If you'd like to comment, email me: ramos.gaston AT gmail.com :::