Time Travel with Git: How to View, Undo, and Go Back To Commits

If you want to view, go back, or remove an old commit, there are three Git commands that are essential to know. First, I’ll cover how to easily view an old commit with the git checkout command, then how to gracefully go back to an old commit while maintaining the current branch history with git revert, and finally, the different ways to use the git reset command, which deletes branch history.

View an Old Commit

If you just want to remind yourself what you did a few commits ago, using git checkout is the command you want!

While most people probably, including me, think of the checkout command as the way to switch between branches, it can also be very handy for viewing old commits.

Just find the commit that you want to look at and use the following command:

$ git checkout [commit_hash]

This will put you in a detached HEAD state which allows you to explore the state of a git repo when a commit was made.

To go back to where you were, just checkout the branch that you were working on:

$ git checkout [branch_name]

Remove an Old Commit

If you want to undo changes from an old commit, without deleting your commit history, you can use git revert! This command will create a new commit that removes changes from a specific commit.

Find the commit that has the code you no longer want and use the following command:

$ git revert [commit_hash]

Remove a Range of Commits

You can also use git revert to make a new commit that removes changes from multiple commits.

The syntax is as follows:

$ git revert [older_commit_hash]^..[newer_commit_hash]

The ^ says to include the older commit in the range of commits that are being removed.

Git Revert Example

Suppose we have the following commit history (git log –oneline):

* f4321cd (HEAD -> main) Add Z feature
* c321abc Add Y feature
* b2123fa Add X feature
* a1234ef Initial commit

But we decide that we no longer want features X and Y. To remove the code that was written in those commits, we can use the following command:

$ git revert b2123fa^..c321abc

After running the above command, the commit history looks like this!

* h6543gh (HEAD -> main) Revert "Add X feature"
* g5432fg Revert "Add Y feature"
* f4321cd Add Z feature
* c321abc Add Y feature
* b2123fa Add X feature
* a1234ef Initial commit

For every commit that was reverted, a new commit was created. Git will introduce the reversions in the reverse order of how they were originally committed. This is because later commits might depend on earlier ones, so reverting them in reverse order ensures the changes can be undone correctly.

Reset to a Commit

Sometimes you want to delete commits and move your branch pointer to a previous commit - this is what git reset is for. Out of the above two commands, this is by far the most extreme way of going back in time in Git as it completely deletes commits from a branch’s history.

You can use git reset in three different ways:

Soft Reset

A soft reset moves the head pointer to the specified commit and moves everything that was removed into the staging area.

$ git reset --soft [commit_hash]

Mixed Reset (default)

The default setting moves the head pointer to the specific commit but keeps all of the code from the removed commits in the working directory instead of in the staging area (like –soft).

$ git reset --mixed [commit_hash]

or just

$ git reset [commit_hash]

Hard Reset

A hard reset moves the branch pointer to the specified commit and discards all changes in both the staging area and working directory.

$ git reset --hard [commit_hash]

Git Reset Example

Let's say that I have the following commit history:

$ git log -oneline

Output:

0e97e97 (HEAD -> main) created file3
cd16007 created file2
013c2dd created file1

If I do a soft reset to the “013c2dd” commit, as you’ll see below, the work committed in the “create file3” and “create file2” commits will be put in the staging area:

$ git reset --soft 013c2dd

The head pointer now points to the “013c2dd” commit:

$ git log -oneline

Output:

013c2dd (HEAD -> main) created file1

And if we look at which files are staged, we can see that all of the changes from the deleted commits are now staged:

$ git diff --staged

Output:

diff --git a/file2 b/file2
new file mode 100644
index 0000000..e69de29
diff --git a/file3 b/file3
new file mode 100644
index 0000000..e69de29

If we start from the beginning but use the mixed flag, the files from the removed commits will not be staged but rather it'll just be kept in our working directory:

$ git reset --mixed 013c2dd

The head pointer again points to the “013c2dd” commit:

$ git log -oneline

Output:

013c2dd (HEAD -> main) created file1

git diff –staged now returns nothing.

Now for the –hard flag:

Again, starting with the three commits from the beginning of the example:

$ git reset --hard 013c2dd    

Output:

HEAD is now at 013c2dd created file1

Now, file2 and file3 are completely gone!

Don’t forget: If you've already pushed commits to a remote repository and then decide to reset your branch to an older commit, you'll need to force push to overwrite the commits in the remote repository:

$ git push origin [branch_name] --force

Hopefully you now have a better understanding of how to view an old commit with git checkout, remove some commits with git revert, or go back to a commit with git reset!

About Me

Thanks for reading my article! I'm Jacob Padilla - a student at NYU Stern studying Business and Computer Science. Besides programming, my main interests are rock climbing, sailing, ceramics, and photography!

Feel free to check out my open-source projects on GitHub, and follow me on Twitter or LinkedIn to stay up-to-date on my latest articles and other interesting activities.