node_modulesdirectory 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
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
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
.gitignore files to avoid committing dependencies, config files with secrets in, build directories … whatever makes sense
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.
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 --rebaseto 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.