So, to stop myself from pulling my hair out looking for all the right StackOverflow answers that I KNOW I'VE SEEN THE SOLUTION BEFORE WHERE THE HELL IS IT???, I am writing the steps down to remind myself.
SCENARIO:
- I have committed some files in a previous commit that should not be there
- I want to REMOVE those files from that commit
- I want to preserve all other commits after that bad commit
CAVEATS:
- I am working in a local branch
- I will be removing those files forever
- I AM WORKING IN A LOCAL BRANCH*
SOLUTION:
1. Find out which commit you want to go back to.
$ git log
This should give you something like:
zarah.dominguez@R5003334 swipe-to-refresh-demo (master) $ git log commit a6c00638b3d466a61e3381a98e6b44cf2d085164 Author: Zarah Dominguez Date: Tue Jun 17 16:34:50 2014 +0800 Removed dependency on ButterKnife. commit e25c6862a79270921a24d6bf2a9eb07cc3f03b36 Author: Zarah Dominguez Date: Tue Jun 17 16:07:33 2014 +0800 First commit
If you just want the commit messages:
$ git log --oneline
2. Create a new branch based on the bad commit.
$ git checkout -b fix-that-shit e25c686
What this does is create a new branch named
fix-that-shit
, whose HEAD
points to commit e25c686
. The -b
switch tells git to go to that newly-created branch.3. Do your thing. In this case, I want to remove files.
$ git rm BadFile.java
rm 'BadFile.java'
$ git rm AnotherBadFile.java
rm 'AnotherBadFile.java'
4. Let git know that you've overcome your stupidity and are now saying sorry.
$ git commit --amend
An editor will open, and here you can edit the commit message.
5. Go back to the original branch. In my case, it is master.
$ git checkout master
6. Give this branch your changes.
$ git rebase fix-that-shit
7. Check your log. git might try and do it's thing, and do funny merges. So you might end up with a new commit in your history, something like:
zarah.dominguez@R5003334 swipe-to-refresh-demo (master) $ git log commit 3e08701339c35301caf269058eab6359c9d87ecd Author: Zarah Dominguez Date: Tue Jun 17 16:34:50 2014 +0800 Removed dependency on ButterKnife. commit ca57ac7974d7a422a2225612404cb6bd555acfc4 Author: Zarah Dominguez Date: Tue Jun 17 16:07:33 2014 +0800 First commit commit 679ad41bedb2d61fbea36e39e334074b7de66dcd Author: Zarah Dominguez Date: Tue Jun 17 16:07:33 2014 +0800 First commit
7b. Examine the two commits, and you'll notice that the second in the list contains the file we just removed. I want to chuck that out completely, so I will do a rebase. This will take me back to the first commit in interactive mode:
$ git rebase -i --root
Now I can trash that bad commit by adding a pound sign (or fine, hashtag) before that commit's SHA:
pick 679ad41 First commit
#pick ca57ac7 First commit
pick 3e08701 Removed dependency on ButterKnife.
8. Check your logs again, and verify that the file is now nowhere to be found.
zarah.dominguez@R5003334 swipe-to-refresh-demo (master) $ git log commit a6c00638b3d466a61e3381a98e6b44cf2d085164 Author: Zarah Dominguez Date: Tue Jun 17 16:34:50 2014 +0800 Removed dependency on ButterKnife. commit e25c6862a79270921a24d6bf2a9eb07cc3f03b36 Author: Zarah Dominguez Date: Tue Jun 17 16:07:33 2014 +0800 First commit
9. Now kill and bury that shit:
$ git branch -d fix-that-shit
10. You will then need to force-push your changes if you have a remote branch. Hence the DO NOT DO UNLESS YOU ARE THE ONLY ONE USING THE BRANCH.
$ git push master --force
I am sure there is a more concise way to do this, but doing the verbose solution here for posterity.
-----------
* I cannot stress this enough. DO NOT DO THIS IF YOU ARE SHARING YOUR BRANCH WITH SOMEBODY ELSE. If you do, you automatically give them license to punch you in the face. (It can be a remote branch, as long as YOU ARE NOT SHARING IT WITH SOMEBODY ELSE)