Undoing Changes in Git

One of the key advantages of Git is its ability to allow you to undo changes at various stages of your workflow. Whether you made a simple mistake in the staging area, committed something too early, or need to revert an entire feature branch, Git has powerful mechanisms to recover and fix mistakes.

In this chapter, we will explore how to undo changes in Git from the most basic to the most advanced operations. By the end of this section, you will have mastered everything from unstaging files to rewriting history.

Why You May Need to Undo Changes in Git

Undoing changes in Git is essential in many situations:

  • You staged or committed the wrong files.
  • You realized there’s a mistake in your last commit.
  • You need to reset your branch to a previous point in history.
  • You want to remove changes without losing work.

Understanding how to safely undo changes ensures that mistakes are handled efficiently without affecting the progress of your project.

Basic Undo Operations

Unstaging Files

Let’s say you’ve staged a file but realized it wasn’t supposed to be added to the next commit. You can “unstage” a file using the git restore command.

Example:

				
					$ git add myfile.txt
$ git restore --staged myfile.txt

				
			
  • The git add command stages myfile.txt for the next commit.
  • git restore --staged removes the file from the staging area, but it doesn’t delete the changes from the working directory.

Discarding Changes in the Working Directory

If you’ve modified a file but want to discard the changes and return it to the last committed version:

				
					$ git restore myfile.txt

				
			

This command discards any uncommitted changes to the file and restores it to the state from the last commit.

Example:

				
					$ echo "New changes" > myfile.txt
$ git restore myfile.txt

				
			

After running this command, myfile.txt will revert to the version from the latest commit, discarding any new modifications.

Undoing Commits

Sometimes, you realize you’ve made a mistake right after committing changes. Git provides multiple ways to handle this depending on the situation.

Amending the Last Commit

If you’ve committed but want to modify the commit (e.g., you forgot to add a file or want to change the commit message), you can use the --amend option.

Example:

				
					$ git commit -m "Initial commit"
$ git add forgottenfile.txt
$ git commit --amend

				
			

This command does the following:

  • Combines the staged changes (forgottenfile.txt) with the previous commit.
  • Allows you to modify the previous commit’s message.

After running git commit --amend, Git will replace the previous commit with a new one, and your commit history will be rewritten.

Undoing the Last Commit (Keeping Changes)

If you want to undo the last commit but keep your changes in the working directory, use git reset with the --soft option:

				
					$ git reset --soft HEAD~1

				
			

This moves the HEAD pointer back by one commit, effectively “undoing” the last commit. However, the changes remain staged.

Undoing the Last Commit (Removing Changes)

If you want to completely remove the last commit and discard its changes, use:

				
					$ git reset --hard HEAD~1

				
			

The --hard option resets both the commit history and the working directory to the previous commit. Be cautious with this command, as changes will be lost.

Advanced Resetting with Git

The git reset command is a powerful tool for undoing commits and controlling which changes are kept or discarded. There are three levels of reset: --soft, --mixed, and --hard.

Understanding Git Reset

Let’s break down the options:

  • --soft: Moves the HEAD pointer to the previous commit but leaves all changes in the staging area.
  • --mixed (default): Moves the HEAD pointer to the previous commit, removes changes from the staging area, but keeps them in the working directory.
  • --hard: Moves the HEAD pointer to the previous commit and removes all changes from both the staging area and the working directory.

Example:

Suppose you have the following commit history:

				
					A---B---C (HEAD)

				
			

To reset the branch back to commit B:

				
					$ git reset --soft B

				
			

The result:

				
					A---B (HEAD)

				
			

The changes from commit C will remain staged.

Using Git Reset for Multiple Commits

You can also reset multiple commits at once. If you’ve made several erroneous commits, you can reset to an earlier point in the history:

				
					$ git reset --hard HEAD~3

				
			

This moves the branch pointer back three commits and discards all changes introduced by those commits.

Reverting Commits

Unlike git reset, which rewrites history, git revert creates a new commit that undoes the changes introduced by a previous commit. This is a safer option in collaborative environments because it preserves the commit history.

Reverting a Single Commit

If you want to undo the changes made by a specific commit:

				
					$ git revert <commit-hash>

				
			

This creates a new commit that negates the changes made in the specified commit.

Example:

				
					$ git revert 3a5b7c2

				
			

Git will open an editor allowing you to write a commit message for the revert commit.

Reverting Multiple Commits

You can revert a range of commits as well:

				
					$ git revert HEAD~3..HEAD

				
			

This command reverts the last three commits, creating individual revert commits for each.

Undoing Changes with Checkout

In older versions of Git, git checkout was used for undoing changes, but it’s now replaced by git restore for many use cases. However, you can still use checkout to undo changes to entire branches.

Switching to a Previous Commit

To switch your working directory to a previous commit (without modifying your branch’s history):

				
					$ git checkout <commit-hash>

				
			

While this detaches the HEAD (puts you in a “detached HEAD” state), it allows you to explore an older version of your project without making any changes to your current branch.

Example:

				
					$ git checkout 5d41402

				
			

Your working directory will reflect the state of the project at that specific commit.

Reverting to the Last Commit

If you’ve made changes in your working directory that you don’t want to keep, you can reset your directory to match the latest commit:

				
					$ git checkout -- .

				
			

This command discards all uncommitted changes in your working directory.

Stashing: Temporarily Saving Changes

Sometimes, you want to save changes without committing them, for example, when you need to switch branches but don’t want to commit incomplete work. git stash allows you to temporarily store your modifications.

Stashing Changes

To stash your changes:

				
					$ git stash

				
			

Your working directory is reset to the last commit, and the changes are saved in the stash stack.

Applying Stashed Changes

Later, when you’re ready to reapply the stashed changes:

				
					$ git stash apply

				
			

This applies the most recent stash to your working directory without removing it from the stash list.

Stashing with a Message

To identify your stashes more easily, you can provide a message:

				
					$ git stash save "WIP: refactor header"

				
			

Viewing the Stash List

To see all stashes:

				
					$ git stash list

				
			

Dropping a Stash

Once you no longer need a stash, you can delete it:

				
					$ git stash drop

				
			

Recovering Deleted Branches and Commits

Sometimes you may delete a branch or reset your repository, thinking certain commits are gone forever. Fortunately, Git keeps track of many actions, and you can recover lost commits or branches.

Recovering a Deleted Branch

If you accidentally delete a branch:

				
					$ git branch -d feature

				
			

You can recover it as long as you know the commit hash:

				
					$ git checkout -b feature <commit-hash>

				
			

Using git reflog to Recover Commits

Git maintains a history of every action performed, which can be accessed using git reflog. This is extremely helpful when trying to recover a lost commit or branch.

To view the reflog:

				
					$ git reflog

				
			

You can use this to recover a lost commit:

				
					$ git checkout <commit-hash>

				
			

In Git, the ability to undo changes is an essential part of managing and maintaining a clean, efficient workflow. Whether you're dealing with minor mistakes like staging the wrong file or more significant challenges like recovering deleted commits or branches, Git offers various commands to address these situations. From simple commands like git restore to manage files, to more complex operations like git reset, git revert, and git reflog, you have powerful tools at your disposal to correct mistakes and keep your project history intact. Happy coding !❤️

Table of Contents