views:

20893

answers:

7

I had a conflict in a merge. Git creates filename.orig and filename.remote when a conflict happens. I resolved the conflict, but somehow filename.orig got added to the repository several commits ago without me noticing it until now. Is it possible to rewrite the change history such that filename.orig was never added to the repository in the first place?

A: 

I think you should have a look at what you can do with git rebase. I haven't done this before, but it seems that may be where you need to head.

dylanfm
+3  A: 

Have a read of http://www.kernel.org/pub/software/scm/git/docs/user-manual.html#rewriting-one-commit

Although beware the implications around this if there are branches created AFTER that point.

madlep
+2  A: 

This is what git filter-branch was designed for.

CesarB
+21  A: 

Please don't use this recipe if your situation is not the one described in the question. This recipe is for fixing a bad merge, and replaying your good commits onto a fixed merge.

Although filter-branch will do what you want, it is quite a complex command and I would probably choose to do this with git rebase. It's probably a personal preference. filter-branch can do it in a single, slightly more complex command, whereas the rebase solution is performing the equivalent logical operations one step at a time.

Try the following recipe:

# create and check out a temporary branch at the location of the bad merge
git checkout -b tmpfix <sha1-of-merge>

# remove the incorrectly added file
git rm somefile.orig

# commit the amended merge
git commit --amend

# go back to the master branch
git checkout master

# replant the master branch onto the corrected merge
git rebase tmpfix

# delete the temporary branch
git branch -d tmpfix

(Note that you don't actually need a temporary branch, you can do this with a 'detached HEAD', but you need to take a note of the commit id generated by the git commit --amend step to supply to the git rebase command rather than using the temporary branch name.)

Charles Bailey
This is the best thing I've read all day!!! I wish all answers were this clear. +1
Jarrett Meyer
Wouldn't a `git rebase -i` be faster and still as easy? $ git rebase -i <sh1-of-merge> Mark the correct one as "edit" $ git rm somefile.orig $ git commit --amend $ git rebase --continueHowever for some reason I still have that file somewhere the last time I did that. Probably missing something.
Wernight
`git rebase -i` is very useful, especially when you have multiple rebase-y operations to perform, but it's a right pain to describe accurately when you're not actually pointing over someone's shoulder and can see what they're doing with their editor. I use vim, but not everyone would be happy with: "ggjcesquash<Esc>jddjp:wq" and instructions like "Move the top line to after the current second line and change the first word on line four to 'edit' now save and quit" quickly seem more complex than the actual steps are. You normally end up with some `--amend` and `--continue` actions, as well.
Charles Bailey
I did this but a new commit was reapplied on top of the amended one, with the same message. Apparently git did a 3 way merge between the old, unamended commit containing the unwanted file, and the fixed commit from the other branch, and so it created a new commit on top of the old one, to re-apply the file.
faB
I tried to do this when I realized an OS X .DS_Store was added but 1) the commit where it was added was duplicated by the rebase 2) Even with a rebase --onto (already complicating the recipe), of course more recent versions had a changed .DS_Store, so filter-branch ended up doing the trick for me
UncleCJ
@UncleCJ: Was your file added in a merge commit? This is important. This recipe is designed to cope with a bad merge commit. It's not going to work if your unwanted file was added in a normal commit in history.
Charles Bailey
+7  A: 

If you haven't committed anything since, just git rm the file and git commit --amend.

If you have, git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename.orig' merge-point..HEAD will go through each change from merge-point to HEAD, delete filename.orig and rewrite the change. Using --ignore-unmatch means the command won't fail if for some reason filename.orig is missing from a change. That's the recommended way from the Examples section in the git-filter-branch man page.

Schwern
Thanks! git filter-branch worked for me where the rebase example given as an answer didn't: The steps seemed to work, but then pushing failed. Did a pull, then pushed successfully, but the file was still around. Tried to redo the rebase steps and then it went all messy with merge conflicts. I used a slightly different filter-branch command though, the "An Improved Method" one given here: http://github.com/guides/completely-remove-a-file-from-all-revisionsgit filter-branch -f --index-filter 'git update-index --remove filename' <introduction-revision-sha1>..HEAD
i5m
I'm not sure which one is the *improved* method. Git official documentation of `git-filter-branch` seem to give the first one.
Wernight
A: 

you can also use "git reset HEAD file/path". :)

paolo granada lim
If the file has been added to a commit then this doesn't even remove the file from the index, it just resets the index to the HEAD version of the file.
Charles Bailey
oh, thanks for the tip! :)
paolo granada lim
+1  A: 

This is the best way: http://github.com/guides/completely-remove-a-file-from-all-revisions just be sure to backup the copies of the files first.

Darren