Image of A Comprehensive Guide to Undoing Changes In Git

ADVERTISEMENT

Table of Contents

Introduction

Git is a powerful version control system for tracking source code changes, for small and large projects alike. Sometimes you’ll encounter situations that require you to undo changes you’ve made to a Git repository.

Git changes cannot be undone with a simple back button. This is intended to protect the integrity of the codebase. Instead, you’ll need to learn the proper Git commands and the appropriate situations for using each command.

What Commands are available to Undo Changes in Git?

There are 6 main commands, git checkout, git clean, git restore, git revert, git reset, and git rm that can be used to undo changes in Git. At each stage of project development using Git, there is a command available for undoing file changes. Each command is best suited for a different scenario, and it’s important to know when and how to use each. Beyond those 6 primary commands, there are a few other commands at your disposal. Check out all of your options below:

CommandSituation
git checkoutMove around the commit tree and change branches
git stashSave local unstaged changes in a local cache
git cleanDelete unstaged files
git rmDelete staged files
git restore --stagedRestore local staged changes
git commit --amendUndo most recent commit
git revertRemove changes to locally committed files, while retaining commit history
git resetRemove changes to committed files, while removing commit history

Git is designed to protect your work, along the development path, but in some cases, when you undo a change, you will not be able to recover that work later.

Undo Commits with Git Checkout

The git checkout command provides a mechanism to move around the commit tree and change branches. Before delving into the checkout command, it’s important to understand the basics of Git HEAD.

In simple terms, HEAD points to the commit you are currently working on. When using git checkout to move to a previous commit in the branch, you will be using Git in a detached HEAD state. This means that the current HEAD you are accessing is not a branch head. If you're not careful, any commits you make on a detached HEAD will become orphaned commits when you switch back to your main development line. Orphaned commits are cleaned up periodically by Git's garbage collection.

To undo one or more commits using the git checkout command, follow the basic outline below:

  1. It is recommended to first gather the recent commit history using the git log command. See the commit history for the last 4 commits below:
$ git log
		
commit a3ebe8e06c80207d8c3a8adce267f0a09c3be910 (HEAD -> master, FreelanceWork/master)
Author: John Doe <11111+johndoe@users.noreply.github.com>
Date: Sun Feb 27 06:34:04 2022 -0500
		
        Final Draft

commit  c1e92010bb37601441bd1f3259b3263eebc256e4
Author: John Doe <11111+johndoe@users.noreply.github.com>
Date: Sun Feb 27 05:34:04 2022 -0500
		
        Passive Voice Edits pt 1

commit 4f7d793e07d58a9e9bccccea6382eb115460b7
Author: John Doe <11111+johndoe@users.noreply.github.com>
Date: Sun Feb 27 04:34:04 2022 -0500
		
        Critical Security Trends RD

commit 5ac7e5d45c029b4108e091ce393c474ba486e745
Author: John Doe <11111+johndoe@users.noreply.github.com>
Date: Sun Feb 27 03:34:04 2022 -0500
		
        Added Vision paragraph2
  1. The git log command only displays the commits for the current branch. If the commit you are looking for is not found in the log, you have two options to locate the necessary commit. You can use git log -- branches=* to access a list of every commit for all branches. Or you can use git branch -a to access a list of all branches, if you recognize the specific branch you can call the git log for that branch only.
  2. Collect the SHA-1 identifying hash for the commit you want to access, from the logs. For example, the Critical Security Trends RD commit’s identifying hash from the image above is 4f7d793e07d58a9e9bccccea6382eb115460b7.
  3. To view the Critical Security Trends RD commit, use the git checkout command combined with the identifying hash: git checkout 4f7d793e07d58a9e9bccccea6382eb115460b7
  4. Now you can view files and make changes without affecting your current revision. If you want to "undo" this commit, you can effectively remove it from the original repo timeline. You can do this by creating a new branch starting at the commit immediately before the one you want to undo. First, use the checkout command on the previous commit. In this example, the commit immediately preceding Critical Security Trends RD is the commit added Vision paragraph2 with the identifying hash 5ac7e5d45c029b4108e091ce393c474ba486e745. First, use the checkout command on this hash to access this commit: git checkout 5ac7e5d45c029b4108e091ce393c474ba486e745
  5. Create a new branch, and the Critical Security Trends RD will no longer exist on the new repo timeline: git checkout -b new_branch_without_critical_security_trends_rd

This method is effective if the first branch is not your main branch. If you need to maintain that previous branch, this method for undoing commits is not the best option.

Undo Untracked Files with Git Clean

Git only knows about files that are tracked or staged, using the git add command. If you haven’t used this command for a file, it is untracked and can be deleted using the git clean command. When using the git clean command, there are multiple steps involved to help verify that the untracked files you are removing, are the correct ones:

  1. Enter the command git clean -n for a dry run, to get a list of files that would be removed by the command. For example:
$ git clean -n
Would remove article-one.txt
Would remove article-two.txt
Would remove article-three.txt
  1. If the files that are listed, are the files you want to delete, proceed with using the git clean --force command to remove those files.

  2. Any files in the .gitignore file will not be removed by the git clean command. If you would like to include these files to be removed, add -x, so the command would now be git clean --force -x.

  3. Remove directories using the -d switch, displayed as git clean -d.

  4. Combine all of these options into a single command with git clean -fdx.

$ git clean -fdx
Would remove freelance.class
Would remove science-writing/

Undo Staged Changes with Git Restore

The git restore command can be used to remove changes to files that have already been staged, but not committed. Using git restore, you can discard local file changes and even undo the git add command, essentially unstaging a file. See how git restore can be used in the steps below:

  1. Add the name of the file you want to restore, to the end of the command, git restore <file_name>. For example: git restore article-one.txt
  2. Restore multiple files by appending to the end of the command: git restore article-one.txt article-two.txt article-three.txt
  3. If you need to remove all local changes: git restore .
  4. Once a file has been staged, it can be removed by adding the --staged identifier, git restore --staged <filename>

Undo Commit with Git Revert

Once a file has been added using the git add command, it is staged to track changes. Now you can commit the file and record changes locally using git commit. The command git revert can be used to undo changes to a file without removing the commit history.

Essentially, this command will revert to the previous file state. It will undo the changes contained in that commit by creating a new commit at the end of the branch. For example, if you edited a file to add 10 lines of code, then when you use git revert, these 10 lines of code will be removed. You will still be able to see that you added and then removed these 10 lines. This is the power of this command. This allows you to experiment and try different things and if you encounter any issues, undo the changes, but still retain the code changes to later fix or improve upon.

Checkout this example below to see how to use git revert:

  1. Use the command git log to retrieve the most recent commit hash.
$ git log

commit <last commit hash>
Author: John Doe <example@email.com>
Date:   Wed April 15 20:00:40 2022 +0000

<commit message>
(...)
  1. If this format is confusing, consider using git log --oneline:
$ git log --oneline

<last commit hash> <commit message>
cdb76bf Added 5 lines of code
d425161 Added 10 lines of code
(...)
  1. Now use the commit hash, to revert the changes git revert <commit hash>. Using a commit from the example above: git revert cdb76bf
  2. You can also use git revert to cancel the most recent change: git revert HEAD
  3. Following the revert command, Git will ask for the commit message, because a new commit is being created. If the commit message is empty, the commit will be aborted.
  4. Use git log again to verify that the changes from the most recent commit were reversed:
$ git log --oneline

af683sd Revert "Added 5 lines of code"
cdb76bf Added 5 lines of code
d425161 Added 10 lines of code

Undoing Commit using Git Reset

The git reset command is useful for removing commits that do not need to be referenced or accessed later. Git reset differs from the git checkout command because it moves the HEAD and branch refs. If you are 100% sure about undoing any file changes, then you can use this method.

There are three arguments associated with git reset available for use, --soft, --mixed, and --hard. These three command-line arguments coincide with the three areas of Git, the Git directory, working directory, and staging area:

Git directory: is a folder that contains metadata and compressed project files.
Working directory: files are moved from the Git directory to the working directory on your local machine
Staging area: stores any file changes that you intend to commit. Files move into staging area by using the git add command.

Learn how to use git reset below:

  1. If you want to reset the HEAD, use the soft argument combined with git reset. The staging area will remain unaffected and everything within that area will stay staged: git reset --soft <filename>
  2. If you want to remove changed files from the staging area, but keep them in your working repository use the mixed argument: git reset --mixed <filename>
  3. If you are completely comfortable removing the changes to a file (or files) then you can use the hard argument. Hard resets will reset the staging area and working tree. You will lose these file changes and not be able to recover any changes: git reset --hard <filename>

Undo Tracked Files with Git RM

The git rm command can be used to delete a file or group of files from your repository. Using git rm will remove the selected files from both the working repository and directory of staged files. The git rm command has a bunch of options associated with the command. The important ones are outlined below:

  1. Use a dry run to capture a printout of what files will be deleted. This can prevent any files from being unexpectedly deleted. Collect the file name and combine with git rm like: git rm -n article-one.txt
  2. You can include multiple files by listing them after git rm: git rm article-one.txt article-two.txt article-three.txt

If you try to remove a file that does not match the files in the HEAD exactly, Git will block this action. You can continue and force the removal using git rm -f.

git rm article-four.txt -f

Undoing Changes at each Stage of Git Workflow

Now that you have learned the Git commands that can be used to unto changes to your projects, it’s time to learn how to apply these commands to your Git workflow. There are multiple ways to approach working with Git, but the standard Git workflow is as follows:

  1. Clone the repository to your local machine or create a new repository on your local machine.
  2. Create a new branch for you to work from.
  3. As you edit or create files, they will exist in the unstaged state. At this time, Git is completely unaware these files exist or changes have occurred.
  4. Use git add to add any new files or changes to your local repository. The changes are now staged.
  5. Use the command git commit to commit files to your local repository.
  6. Use git push when you are ready to push the commits to the remote repository. At this time, colleagues or other developers will gain access to your changes.
Git flow diagram

Source: StackOverflow

Now we’re going to break down the best practices for undoing changes or commits, starting at step 3 in the Git workflow above.

Undoing Unstaged Changes (Before git add)

If you have files with edits in your working directory that are not yet staged, you can easily undo these changes using a few different commands:

  1. First, check the status of the file to make sure it is unstaged and has not been accidentally staged using the git status command.
  2. Decide whether you want to overwrite changes, stash local changes for later use, or completely remove the changes:

Overwrite changes: git checkout <filename>
Stash changes for later use: git stash <filename>
Completely & permanently remove changes: git reset --hard <filename>

Undo Staged Changes ( After git add)

Once you have staged files using git add you have a new set of options available if you need to undo changes at this stage of development:

  1. Use git status to again verify that the file is staged, as expected.
  2. Decide how permanently you want to remove the changes:

Stash changes for later use: git stash <filename>
Completely & permanently remove changes: git reset --hard <filename>
Undo git add command, but retain changes: git restore --staged <filename>
Unstage all files but retain changes: git reset

Undo Local Commits (After git commit)

You’ve proceeded further in the Git workflow and committed files to your repository. Now you need to undo these commits, on your local machine. The commands outlined below will allow you to retain commit history, in case you need to refer to the history later:

Use git log command to find the specific SHA-1 identifying hash for the commit you want to undo.

Select which command to use to undo your changes:

Revert file to previous state: git revert <commit SHA>
Undo commit, but keep changes staged: git checkout <commit SHA><filename>
Undo commit, but keep changes unstaged: git reset <commitSHA><filename>

Undo Remote Changes (After git push)

Undoing changes once you’ve pushed your commits to a remote repository, involves a more careful approach. Your commits are now available to other users, and if they fetch those commits, you could impact their working directory. Try and review all commits a final time, before pushing them to any remote repository, to prevent this issue.

If this occurs and you catch the problem immediately, you can simply create a new commit that fixes the file’s issue. Push this new commit to the remote repository for your colleagues to use. But if this window of opportunity passes or the commit needs to be completely undone, you need to take a different action. Avoid using git reset in this situation, or risk losing data and affecting other devs. Instead create a new commit where the changes of the previous commit have been undone, using git revert. A reminder that this command is used as follows:

git revert <commit SHA>

Undo Changes With Ease

If you’ve followed along with this guide, you are sure to have gained a great understanding of the Git commands available for undoing any mistakes. You can undo changes at every stage of your workflow, but it’s important to know which commands to use in which situations.

Using a command at an improper time can result in unrecoverable code/file losses and negatively affect the repository of other devs. You can review the Git commands available for undoing changes or find where you are in your Git workflow, to learn which commands can be applied.

While this guide was designed for a beginner, it still requires some basic knowledge of version control systems and Git. So if this guide was too advanced, take a few steps back and dive into a great beginner-friendly Git book, like Learn Version Control with Git. Alternatively, if you’re an advanced user and this guide is mostly a refresh, consider learning more about Git’s base code with Baby Git.

Next Steps

If you're interested in learning more about how Git works under the hood, check out our Baby Git Guidebook for Developers, which dives into Git's code in an accessible way. We wrote it for curious developers to learn how Git works at the code level. To do this we documented the first version of Git's code and discuss it in detail.

We hope you enjoyed this post! Feel free to shoot me an email at jacob@initialcommit.io with any questions or comments.

References

  1. Stackoverflow - https://stackoverflow.com/questions/6143285/git-add-vs-push-vs-commit

Final Notes