tags:

views:

67

answers:

3

I removed a class a feature from my code a while back and committed the removal. The feature was implemented as a single class with a BDD-style spec and a few other classes were altered in the removal commit as they made use of the feature. The commit is clean, in that the only change in the commit is the removal of this one feature.

Now, I've changed my mind and decided I want the feature back only since I removed it I've made a whole bunch of changes. (I knew this was a possibility but I didn't keep a copy because I knew I could pull it out of version control). I found the commit: cb2a6b1 and I can see the removed lines when I: git show cb2a6b1. The question is how can I apply those lines back into my code now?

git diff cb2a6b1 cb2a6b1~1 produces a diff that reframes the removal as an addition but it won't git apply, mainly because the line numbers have changed. I can just manually put the stuff back in with some manual text editing but is there a more automatic alternative?

+7  A: 

Try git revert cb2a6b1.

If it's not automatically able to figure out the right thing to do, it should present you with merge conflicts that you can fix up by hand.

Phil
A: 

Check out this article from git ready.

Ryan Lynch
+1  A: 

Another option is git rebase -i. It's more involved than git revert, but you have more options also.

Let's say you have a git repo that only contains a README file:

$ cat README
This is line alpha.
Line Beta.
Line Gamma.

Then you delete line beta and commit your change:

(make changes)
$ git commit -am 'removed beta'

Now you add some more lines, committing along the way:

(make changes)
$git commit -am 'added delta'
(make changes)
$git commit -am 'added epsilon'

Now our README file and git log look like this:

$ cat README
This is line alpha.
Line Gamma.
Line Delta.
Line Epsilon.
$ git log --pretty=oneline
56ae2db58905607270434045b2a7218237d3716e added epsilon
058ec4b92fb279370f6e5b81c4aebb6250f93c6b added delta
19801b380956b60f36b4922796ea86935a75e569 removed beta
bc9057cebf05352eb5596eaf753708a14de589d2 initial commit

Now we decide that we shouldn't have removed the beta line. git rebase to the rescue! Whenever I do any rebasing I like to use a temporary branch, since I'm changing the history.

$ git checkout -b fixingthings

Now we want to rebase everything that's happened since our initial commit. Looking at the log, that's commit bc9057c....

$ git rebase -i bc9057c

Now you'll see this in your editor:

pick 19801b3 removed beta
pick 058ec4b added delta
pick 56ae2db added epsilon

# Rebase bc9057c..56ae2db onto bc9057c
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

The comments at the bottom are helpful. Just delete the line with the commit you want to get rid of, save and quit and git will take care of the rest. Now your README looks like this:

$ cat README
This is line alpha.
Line Beta.
Line Gamma.
Line Delta.
Line Epsilon.

It's like magic!

If you like, you can merge back into your master branch now and delete your temporary branch.

$ git checkout master
Switched to branch "master"
$ git merge fixingthings
Auto-merging README
Merge made by recursive.
$ git branch -d fixed
Deleted branch fixingthings (was 1247141).

What git rebase does is consider each commit as a patch and replays them in order (or not, as you tell it). That means you can have merge conflicts. If this happens to you, rebase will pause and have you resolve the conflicts. There are lots of tutorials about what to do in that situation. This one looked good to me.

Neall