How to undo a git commit

git

#1

I must admit that I often google how to undo a git commit (for a commit that has not been pushed). After you post a response, my plan is to practice it several times to help me remember the sequence.

(I realize that my original question wasn’t clear enough. I’m looking for the steps to remove a commit from head.)


#2

It should be git revert [commit hash] to revert a specific commit—you can look up recent commit hashes (that is, the long alphanumeric string with each commit) using git log.


#3

git revert is the most recommended and common way, although there are a few other ways to do it too, and it can be useful to be aware of them.

When you use git revert, you aren’t really “undoing” the commit. Rather, you’re creating a new commit that’s the inverse of the commit in question. This means you can still checkout the old commit and see the contents of it, even if master no longer has the contents. This is typically what you want, because it’s safe and you don’t loose the previous information that was there.

However, let’s say you committed something that shouldn’t be public, like a private API key or a password. In this case, you don’t want that information to still be in the commit history. Much of the time you can’t do anything about this, but there are some very specific circumstances where you can. If you have not pushed the commit in question to GitHub or your repository hosting service of choice, then you have more flexibility. You can run git reset --soft HEAD~[number of commits you want to undo], e.g. git reset --soft HEAD~1 to undo the latest commit. This does remove the commit from history, unlike git revert.

BEWARE: using git reset can be dangerous. If you have pushed your code anywhere else, and then you run this command, you can get into some really strange and very hard to fix edge cases if anyone else is working on this repo with the commit you just removed. So be sure that you only use this command if you have not pushed your code anywhere else.


#4

There are some useful and quite funny git tips over at http://ohshitgit.com/. Enjoy :grinning:


#5

My recommendation:

  • For published changes, use git revert.
  • For local changes that exist only locally on your desktop/laptop, use git reset --hard [ref].

This avoids polluting public history with useless noise and also avoids the chaos that would result from a hard reset of a published commit.


#6
 git reset HEAD~
 git add -p ...
 git commit -c ORIG_HEAD       

#7

The --soft in that command could also be --hard or a few other things. I find it easiest to explain them by what it would take to redo your commit, assuming you reset one commit.

  • --soft makes it exactly as though you just didn’t run git commit, running git commit would redo the commit.
  • --hard gets rid of the changes entirely, you would have to edit the files again, add them, and commit them to redo the commit.
  • --mixed is the default, the changes are in the files, but you’d need to git add them then git commit them to redo the commit.

I don’t know the other options well enough to explain.


#8

I agree with the other answers that have already been given. But since @nebrius mentioned accidental commits of private information I wanted to add a link to the GitHub help page for removing sensitive data that explains how to purge that information from your Git repository, even if it has already been pushed. Note this won’t purge it from anyone who has already either forked or cloned your repository, but it is the next best thing. There’s also a good bit at the end to help you avoid pushing sensitive data in the future. I realize this may be tangential to the OP’s original question.

Also I wanted to offer a different perspective on force pushing to GitHub. IMHO @nebrius warning was too emphatic. IMO the main issue is that once you push your commits to a shared GitHub repository, if anyone has cloned or forked the repository, then when you force push your commits, they will still have the old commits in their forks or clones. Therefore IMO you should communicate with other collaborators to avoid surprises such as:

  1. when they pull the new commits it drastically changes their code
  2. when they push their commits, Git refuses saying their head has diverged from upstream, and they need to pull first
  3. they don’t know that sensitive data was accidentally published, and they should purge their clones or forks too

AFAIK Git is smart enough to handle most if not all of the edge cases that occur when you force-push so please do it if you need to without fear, but understand the implications for your collaborators.


#9

I didn’t know about this. Nice addition to the thread. :+1:


#10

I just wanted to pitch in with something that helped me grok Git in general. If you know with how pointers work (eg in c/c++), you can think about git commit hashes as memory addresses, which makes git reset --soft a command that tells git to assign the HEAD pointer to a different address/commit.

This is why it’s considered dangerous - because you can assign HEAD to point to a commit that is part of an entirely different branch in the commit tree (in the process losing the reference to possibly an entire branch of commits). The only way to get it back is to use another git reset command.

Note that you can use git reflog to give you a short history of all the commits your HEAD has pointed to - useful if you find yourself feeling lost after a git reset (or even just lots of branch switching in general). Note also that commits are garbage collected so you won’t immediately lose a commit if you move a branch pointer away from it using git reset.


#11

Another useful resource is the “Git Flight Rules” project: https://github.com/k88hudson/git-flight-rules

It’s basically a list of situations or questions, with the necessary git commands to resolve them.