Remove Accidental Content from Git Commit

When I teach git I try to show loads of good practice about how to inspect what's staged before commit, etc etc. Good practice is good of course, but knowing how to undo a mess you created is better - and mistakes will happen. For example, accidentally including a node_modules directory in your otherwise excellent and useful commit. This post will walk you through how to fix the problem without losing any of your work.

Oops, I committed node_modules to my repo

This is a real scenario (and it wasn't actually my mistake this time but honestly, I'm confident fixing this thing because I've done it more than once!!), a developer unintentionally included node_modules/ in the work he was doing. At this point, the project looks like this:

$ git log --oneline
7a6528c (HEAD -> awesome-feature) Try to fix the problem
2144ece Make a brilliant change
70dbb37 (master) Project going well

It's pretty common to try to remove things that shouldn't have been included in a repo by adding a commit to remove it but in lots of cases that doesn't solve the problem. If you accidentally committed your API credentials for example, they're still present in git history. Equally, if you included dependencies as this example does, and the problem is that they are too large to be accepted by GitHub: you still won't be able to push because the intermediate commit has the large files in.

So what to do? First of all, keep calm!

I also like to create an extra branch to point to the current HEAD commit so I don't lose anything before I perform git surgery of any kind. Try this:

git branch safety

Get back to the original problem

Here, it's the second commit in the list that's a problem - but I already tried to fix it by adding another commit. That's not the right solution, so I'm going to start by throwing away the last commit (that tries to fix the problem) completely.

Here's git log again so we can check what's going on:

$ git log --oneline
7a6528c (HEAD -> awesome-feature, safety) Try to fix the problem
2144ece Make a brilliant change
70dbb37 (master) Project going well

Pro-tip: Check which branch you're on before you proceed. I have the branch name in my prompt but git status will also include the current branch name.

To undo the "fix" commit (that didn't fix things at all), make your current branch point to the previous commit (if you run git reset --hard with uncommitted changes, they will be lost forever. Commit, make the safety branch, and breathe deeply before you rush in to this):

git reset --hard HEAD~1

Where I used HEAD~1, you can also supply the git commit reference. Using HEAD~1 just means "the commit before this one that I'm on now".

Undo your commit but not your changes

For your next trick, we're going to use another reset command but this time without the --hard. The git reset command can affect either the local disk state (what's in the real files on your computer), or the git metadata such as what commit HEAD points to, or both. You already did git reset --hard which does both and puts your working directory exactly to the state of a specific commit (and brings the HEAD pointer to the same place).

Now you're going to use git reset without the --hard to move the HEAD pointer but not change any files at all.

git reset HEAD~1

Run git log --oneline again and we're pointing to the earliest commit, before all the changes (good and unintentional) were made. But check git status next and you should see all your changes there and ready for you to stage only the ones you intended to include! Some tips:
* if you only need to add parts of a file, try git add -p
* use .gitignore files to avoid committing dependencies, config files with secrets in, build directories ... whatever makes sense
* run git status one more time before you commit (this is advice for every day for me!)

When you're happy that only the good changes are included, go ahead and commit.

Pushing changes

If you already pushed changes, and then undid and redid them following this post, your push will be rejected. That's because git never allows us to rewrite history without leaving evidence that we did that!

To push a branch that has changed history, you need to force push - and before you do that, you need to understand the consequences:

  • If anyone has pushed to the branch since you did, you will lose their changes. Do git pull --rebase to make sure you have everything
  • If anyone has started a new branch from your branch, their work will be affected and while it's not unrecoverable, it's a tricky situation

This is why we work on feature branches, kids. It's also why most master branches will not allow force pushes.

If you're sure that force pushing is the right thing, for example if you are the only collaborator on the branch, then go ahead:

git push -f

And you're done! Well done for rescuing what could have been a tricky situation without having to do all that work again :) Go ahead and delete that safety branch while you're thinking about it.


Also published on Medium.

Leave a Reply

Please use [code] and [/code] around any source code you wish to share.

This site uses Akismet to reduce spam. Learn how your comment data is processed.