The following is a guest post written by Carlos Schults.
Going back in time is a crucial feature in any VCS. Git is no exception, but being more powerful and flexible than its centralized competitors, it can be somewhat confusing for beginners. Today, we’re here to help people looking to understand what “git revert commit” is and how it differs from other tools.
We’ll start with a warning that the revert command in Git is a different animal from its namesake in Subversion. Then we’ll proceed to show the various ways in which you can undo or revert a commit in Git. Before parting ways, we share some additional tips on undoing things in common scenarios people might have questions about. Let’s begin!
Git Revert Vs. SVN Revert
Many newcomers to Git were previously users of Subversion (SVN). And Subversion, like Git, has a “revert” command. The problem is that, despite having the same names, the results these commands achieve are totally different! So if you’re a former Subversion user, keep that in mind and keep reading to understand how these commands differ and to learn the Git equivalent of Subversion’s revert.
How Do I Undo a Git Commit?
Undoing a commit is often tricky for Git beginners, especially because it can mean a number of different things. We’ll now cover several ways to undo commits in Git, detailing the pros and cons of each command and explaining what the use cases for each one of them are.
“Deleting” a Commit
Let’s say you want to get rid of your latest commit and move the state of your repository to the way it was before that commit. Let’s see how to do that in practice with an example.
A Hands-On Example
First, let’s create a repository and add some commits to it:
mkdir git-revert-demo cd git-revert-demo git init touch file1.txt git add . git commit -m "Add first file" touch file2.txt git add . git commit -m "Add second file" touch file3.txt git add . git commit -m "Add third file"
Now you can use the git log –oneline command to see the commits created. The result should look like this (the hashes for the commits will be different, though):
c1a373a Add third file 354afd1 Add second file f728594 Add first file
Now let’s say the last commit (the one with the “Add third file” message) is wrong for some reason and you want to get rid of it. You want the history to look like that commit never existed in the first place. In other words, you want to be able to run the git log –oneline command again and see this:
354afd1 Add second file f728594 Add first file
To do this, run the following command:
git reset --hard HEAD~1
Explaining What We Did
The last command might seem hard to parse, but it’s simple once you understand its parts. The command we’re using is git reset. HEAD basically means “the current point where we are now in the repository.” In our scenario, HEAD means the most recent commit. HEAD~1 refers to the commit before that—that is, the second-to-last commit. So, with the command above, we’re saying to Git to reset our index and working directory to the commit specified.
If you run the git log command now, you’ll only see the first and second commits, as I said earlier. Does that mean the last commit has been deleted? No. The commit is still there. It’s just inaccessible. Take a look a the image below:
The diagram represents the state of the repository after you added the first commit. As you can see, we have three commits, a reference (the master branch) pointing to the third commit, and another reference (HEAD) pointing to the master branch. Now consider the following image:
As you can see, the third commit is still there. Git didn’t delete it; it only made it inaccessible. This is actually a good thing since it means you can recover commits that you “delete” by accident.
This Command Can Be Dangerous
Using git reset with the –hard option makes this a dangerous command. If you have uncommitted changes when running the command, these changes will be lost for good. So before resetting with the –hard option, makes sure your repository is clean. That’s to say, running git status returns a result like this:
On branch master nothing to commit, working tree clean
If that’s your case, you can “reset hard” to your heart’s content.
The second caveat has to do with the fact that the reset command changes the history of your repository, and in some scenarios, doing so is problematic. What does changing history mean, and why would it be a problem?
To understand that, let’s create yet another commit:
touch file4.txt git add . git commit -m "Create fourth file"
Now when I use the git log command, this is what I get:
d6a261b Create fourth file 354afd1 Add second file f728594 Add first file
The repository now looks like this:
To understand why this might be a problem, imagine that the commit c1a373a had already been pushed to the shared repository your team uses to collaborate, and one of your coworkers based their work upon it. Then you use the revert command and push the “deletion” of that commit. When they try to push their commits, things won’t work because they’re basing their work on a commit that “doesn’t exist” anymore.
Reverting a Commit
What to do if you need to “undo” the changes of a commit which is already public? Easy: create a new commit with opposite changes. That way, you “erase” the changes you want without changing public history.
Things will be clearer with an example. Let’s say you want to revert the first commit, which adds the first file. In my case, it’s the commit identified by f728594. In your local repository, use the log command to obtain the hash for the first commit. Then you can run the following command, replacing the hash with the correct one for your repository:
git revert f728594
After doing that, Git will open your default text editor and prompt you to write a commit message. Since my default text editor is Vim, this is what I see:
No matter your editor, the process is the same: edit the commit message as you see fit, save it and close your editor, and the commit will be complete. Now when I use git log –oneline, I can still see the first commit , but there’s also an additional commit that reverts it:
cb8fcf5 Revert "Add first file" d6a261b Create fourth file 354afd1 Add second file f728594 Add first file
So the git revert command is the way to go when you need to undo a commit that is already public.
Bonus Tips: Git Revert Commit in Different Flavors
Often people search for how to undo a commit when they have a problem with an easier solution. That’s why we’re going to include tips to address some additional scenarios before we go.
Fixing a Commit Message or Its Content
Imagine you’ve just committed your work only to realize you’ve made a terrible typo in the commit message. Do you have to use the revert command and then commit again? That’s possible, but there’s an easier solution: the –amend option for the commit command. Take a look at the following example:
touch file5.txt git add . git commit -m "Add the 'file4.txt' file"
There’s a typo in the commit message: the correct name for the file should be file5.txt. To fix the message, just run git commit –amend. Your default text editor will be open, and by now, you know the drill: edit the commit message, save it and close the editor, and the commit will be fixed. (Actually, the commit isn’t really fixed; Git creates a new one and updates the branch to point to it, making the old one inaccessible.)
How Do I Revert a File in Git?
Another common question related to going back in time is how to revert a single file to a previous version. Let’s say you want to discard uncommitted changes made to a file. Let’s see an example. First, add some text to a file:
echo sdfsdfsdf > file2.txt
If you realize you need to get rid of this change, run git checkout — file2.txt, and the changes will be gone. Alternatively, if you use Git 2.23 or a newer version, you can use the git restore command:
git restore file2.txt
But what if you went a step further, adding the file to the stage? To unstage the file you could use:
git reset HEAD file2.txt
That will unstage the file but keep the changes. There’s also an equivalent using the restore command:
git restore --staged file2.txt
In case you’re curious, and I bet you are, the commands above are roughly the equivalent to svn revert.
Git Revert Commit: What Now?
After reading today’s post, you should be comfortable with the main ways to undo a commit in Git. Just remember to not change public history, and beware when applying the –hard option to the reset command, and you’ll be fine. What should your next steps be?
Well, there are lots of things to learn about Git. You could, for instance, learn more about branches and, after that, maybe learn why using lots of branches isn’t really that great. Many Git users also use GitHub, so learning how to solve conflicts on GitHub might also come in handy, as well as learning about the main options for integrating tools with it.
That’s it for today. Thanks for reading, and until next time.
This post was written by Carlos Schults. Carlos is a .NET software developer with experience in both desktop and web development, and he’s now trying his hand at mobile. He has a passion for writing clean and concise code, and he’s interested in practices that help you improve app health, such as code review, automated testing, and continuous build.