2

So our team is pretty new to Git. We got a situation like this one, only much worse: a developer made a bad merge, and the bad merge has since been buried under about 20 subsequent commits, because we only noticed the bad merge a day later.

How do we fix this?

EDIT: Looks like the git revert -m command might be the trick, but there's only one file we really need to revert... a big edmx file that got badly mangled. Any way to restrict the revert to just that file?

Community
  • 1
  • 1
Shaul Behr
  • 36,951
  • 69
  • 249
  • 387
  • `git revert merge-id`? – Ry- May 20 '15 at 22:34
  • @minitech It requires the -m option... from the docs it seems like that needs a parent number, which is 1? Would it ever be anything other than 1? – Shaul Behr May 20 '15 at 22:47
  • Sorry. Yes, it could be something other than 1 – just whenever you want one that isn’t 1 – but do try 1. – Ry- May 20 '15 at 22:49
  • @minitech see my edit - need to revert only one file from that merge – Shaul Behr May 20 '15 at 22:50
  • The parent number on `git revert` of a merge tells which "side" of the merge to revert. That is, git is going to do a `git diff` of the merge commit against each of its various parents. The changes made in one parent (vs the merge-base) are retained; the changes made in the other parent (vs the merge-base) are reverted. Given your edit, probably you don't want either one completely reverted. – torek May 20 '15 at 22:52
  • Hi Shaul. You can revert the commit, then checkout all the other files that you don't want to revert, leaving your edmx file before you actually commit the reverted changes. – scrowler May 20 '15 at 22:55

2 Answers2

5

If there's only one corrupted file, you could find a commit where it was alright and checkout it from there:

git checkout <revision> -- path/to/file 

And then save it in the next commit.

Nick Volynkin
  • 14,023
  • 6
  • 43
  • 67
5

Given your edit, I'd suggest extracting a good copy of the file "pre-merge" plus any needed changes. There are multiple ways to do this: you can reverse-apply changes brought in by the merge, hoping that any subsequent changes don't conflict or overlap; you can extract a good pre-merge version and check for any updates needed; and so on.

For example, if <revspec> is a commit with a known-good version of the file:

$ git log <revspec>..HEAD -- path/to/file # find changes to file

(this will show all commits reachable from HEAD but not from <revspec> or its parents, that touched the file; this should include the merge itself, which obviously you don't want to keep, but will also show commits that made changes you might want to keep after all).

$ git checkout <revspec> -- path/to/file

(this simply extracts the old version into the work directory, writing it to the index as well, i.e., it's staged for commit already).

Now if there are revisions that you do want changes from:

$ git show <rev> -- path/to/file | git apply
... repeat for all <rev>s, make sure they all apply;
    do manual work if needed ...
$ git add path/to/file

If three are no such revisions you don't need to apply anything, nor add the result; you're now ready to git commit.


A different method, but now you must pick which "side" of the merge introduced bad stuff. Let's say the bad stuff came in from the second parent (the branch merged-in, rather than the branch-merged-into):

$ git diff <mergerev>^1 <mergerev> -- path/to/file | git apply -R

Here we get git diff to show how the file changed from "good" (first-parent of mergerev) to "bad" (mergerev, with changes brought in from second parent), and "reverse apply" those changes to the work-tree.

If all goes well, git add the fixed-up file and commit.

torek
  • 448,244
  • 59
  • 642
  • 775
  • Answer credit for the effort in writing up a pretty comprehensive answer, thanks! In the end, I solved it by doing a `git checkout` on the file, then doing a manual compare on the changes from the head version. – Shaul Behr May 21 '15 at 07:27