How to Undo and Remove a Specific Git Commit from Current Branch

GitBeginner
Practice Now

Introduction

In this lab, you will learn how to manage your Git commit history. You will practice undoing changes with git reset, safely reverting public commits with git revert, and removing specific commits from your branch using an interactive rebase. You will also learn how to use the reflog to recover commits that have been removed. These are essential skills for maintaining a clean and understandable project history.

Inspecting the Commit History

Before modifying the history, you first need to know how to view it. The git log command is the primary tool for this. For this lab, a Git repository has been initialized with several commits to simulate a real project history.

Let's start by inspecting the commit history. We will use the --oneline flag to show each commit on a single line and --graph to display the commit history as a graph, which is useful for visualizing branches and merges.

Execute the following command in your terminal:

git log --oneline --graph

You should see a list of the five commits that were created for you, with the most recent commit at the top. Each line shows a unique commit hash (a short version of it) and the commit message.

* 7d3d24a (HEAD -> master) chore: Add last-commit file
* 8a9f1b3 docs: Update documentation in file1
* c2e4d6f fix: Add a temporary file that should be removed
* 5e1a3b2 feat: Add file2.txt
* 1b4c0a9 feat: Add file1.txt

Note: Your commit hashes will be different from the example above. This is expected, as each hash is unique.

Take a moment to review the history. You will be manipulating these commits in the following steps.

Undoing the Last Commit with git reset

Sometimes you make a commit and immediately realize you made a mistake. The git reset command is perfect for this scenario. It can move the current branch's HEAD pointer to a previous commit, effectively "undoing" one or more commits.

We will use the --soft option, which undoes the commit but leaves the changes from that commit in your staging area (the index). This is useful if you want to re-commit the changes, perhaps with a different message or combined with other changes.

Let's undo the most recent commit, "chore: Add last-commit file".

git reset --soft HEAD~1

Here, HEAD~1 refers to the commit right before the current HEAD.

Now, check the status of your repository:

git status

You will see that last-commit.txt is now listed under "Changes to be committed". This means the commit itself was undone, but the file changes are still staged.

On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   last-commit.txt

Finally, view the log again to confirm the commit is gone from the history:

git log --oneline --graph
* 8a9f1b3 (HEAD -> master) docs: Update documentation in file1
* c2e4d6f fix: Add a temporary file that should be removed
* 5e1a3b2 feat: Add file2.txt
* 1b4c0a9 feat: Add file1.txt

As you can see, the last commit has been removed from the log, but your work is safely waiting in the staging area.

Reverting a Commit with git revert

While git reset is great for local changes, it rewrites history, which can be problematic if you've already shared your commits with others. A safer way to undo changes on a public branch is git revert. This command creates a new commit that applies the inverse of the changes from a specified commit.

First, let's clean up our working directory by re-committing the changes from the previous step.

git commit -m "chore: Add last-commit file again"

Now, let's say we want to undo the commit that added file2.txt. This commit is further back in our history. We will revert it. We can refer to it by its position relative to HEAD. Looking at the log from Step 1, "feat: Add file2.txt" is the fourth commit from the top, so we can reference it as HEAD~3.

Run the revert command:

git revert HEAD~3

This will open your default text editor (vim by default) with a pre-populated commit message like "Revert 'feat: Add file2.txt'". You can simply save and close the file to accept the default message.

In vim, type :wq and press Enter to save and exit.

Now, check the log again:

git log --oneline --graph

You will see a new commit at the top, "Revert 'feat: Add file2.txt'".

* 1e2d3f4 (HEAD -> master) Revert "feat: Add file2.txt"
* 7d3d24a chore: Add last-commit file again
* 8a9f1b3 docs: Update documentation in file1
* c2e4d6f fix: Add a temporary file that should be removed
* 5e1a3b2 feat: Add file2.txt
* 1b4c0a9 feat: Add file1.txt

The original commit is still in the history, but its changes have been undone by the new revert commit. You can verify this by listing the files in the directory.

ls

You will notice that file2.txt is no longer present.

Removing a Commit with Interactive Rebase

For more complex history manipulation, such as removing a commit from the middle of a branch, you can use interactive rebase (git rebase -i). This is a very powerful command that rewrites commit history, so it should be used with caution, especially on branches that are shared with other developers.

Our goal is to remove the commit with the message "fix: Add a temporary file that should be removed". Looking at the current log, this commit is now HEAD~3.

Start the interactive rebase process:

git rebase -i HEAD~4

This command will open your text editor (vim by default) with a list of the last 4 commits.

pick fd5a181 fix: Add a temporary file that should be removed
pick 5f27a4d docs: Update documentation in file1
pick 284be6f chore: Add last-commit file again
pick 8a460c5 Revert "feat: Add file2.txt"

## Rebase 9403080..8a460c5 onto 9403080 (4 commands)
#
## Commands:
## p, pick <commit> = use commit
## d, drop <commit> = remove commit
## ...

To remove the commit, change the word pick to drop (or d) for the line containing "fix: Add a temporary file that should be removed".

Change this:

pick fd5a181 fix: Add a temporary file that should be removed

To this:

drop fd5a181 fix: Add a temporary file that should be removed

In vim, press i to enter insert mode, make your changes, then press Esc to exit insert mode. Save and exit by typing :wq and pressing Enter. Git will replay the remaining commits on top of the new history.

Check the log to see the result:

git log --oneline --graph

The "bad" commit is now gone from the history.

* a5b4c3d (HEAD -> master) chore: Add last-commit file again
* f9e8d7c docs: Update documentation in file1
* 1e2d3f4 Revert "feat: Add file2.txt"
* 5e1a3b2 feat: Add file2.txt
* 1b4c0a9 feat: Add file1.txt

Also, check the files in the directory. The file bad-commit-file.txt has been removed.

ls

Restoring a Removed Commit with git reflog

What if you remove a commit by mistake? Git keeps a record of all changes to the HEAD pointer in a special log called the reflog. This is your safety net. You can use it to find and restore lost commits.

Let's view the reflog to find the history we just rewrote.

git reflog

You will see a list of actions you've performed. Look for the line that says rebase (finish): returning to refs/heads/master. The entry right below it, likely HEAD@{1}, represents the state of your branch before the rebase.

a5b4c3d (HEAD -> master) HEAD@{0}: rebase (finish): returning to refs/heads/master
1e2d3f4 HEAD@{1}: rebase (start): checkout HEAD~3
...

We can restore our branch to this previous state using git reset --hard. This command is destructive and will discard any uncommitted changes, so use it carefully.

Let's reset our branch to the state before the rebase, which is HEAD@{1} in this case.

git reset --hard HEAD@{1}

You will see a message confirming that HEAD is now at the commit before the rebase started.

HEAD is now at 1e2d3f4 Revert "feat: Add file2.txt"

Now, check the log one last time:

git log --oneline --graph

You should see the history as it was before the rebase:

* f461400 (HEAD -> master) Revert "feat: Add file2.txt"
* acea45c chore: Add last-commit file again
* c04b3f5 docs: Update documentation in file1
* 9403080 feat: Add file2.txt
* ee39412 feat: Add file1.txt

Notice that the commit "fix: Add a temporary file that should be removed" is still not present - this is correct! The git reset --hard HEAD@{1} command restored us to the state before the interactive rebase started, not before we removed the commit. The interactive rebase successfully removed that unwanted commit from our history. You can run ls to confirm that bad-commit-file.txt is still not present. The reflog is an invaluable tool for recovering from mistakes in Git.

Summary

In this lab, you have learned practical techniques for managing your Git commit history. You started by inspecting the history with git log. You then practiced undoing commits using git reset --soft, safely reverting changes with git revert, and removing specific commits with the powerful git rebase -i command. Finally, you learned how to use git reflog as a safety net to restore commits that were removed from the history. These skills are essential for maintaining a clean and manageable project history.