0

I had two branches, master and develop, and I inadvertently updated a 3rd party library on each branch with different versions. The master branch has the latest version of the library, and I want that to be used in develop.

When I tried updating develop with the latest changes in master, I received a merge conflict, so I used git mergetool with opendiff to handle the merging. After a painstaking selection of "choose right" for every file, this process completed and I committed my changes.

At this point, I assumed the two branches would match on this library's directory, but that is not the case. Using standard Unix diff, I compare library's directory in the two branches, and there's a lot of differences in the files' content.

Is there a way to simply undo all changes to this particular directory on develop so that it matches master's?

Dolan Antenucci
  • 15,432
  • 17
  • 74
  • 100
  • `git merge` usually creates a new branch with a 3-way comparison, so you should actually expect a new branch to appear in your history after a merge, you shouldn't check between the old 2, doesn't make sense in git-terms for a merge. At least this is what I recall from git. – user2485710 Dec 13 '13 at 17:05

1 Answers1

2

1: What (probably) happened, by example

You only get conflicts on the parts, that, well, conflict.

For instance, suppose branches A and B start out with v1 of this third-party library.

Then, on branch A (develop), you put in v2, and on branch B (master), you put in v3.

Now if you merge A into B or vice versa, git will compare "what happened on A" with "what happened on B".

Suppose some file F had this change in v1->v2:

 la la la
 this file has some stuff
+and we added a line

but then in v3 the added line was backed out again, so that file F is not modified at all from v1 to v3.

Combining branches A and B in any way will see that one branch changed F and the other did not, and hence keep this change.

(Where v3 modified the same file(s) as v2, but in a different and conflicting way, you will have gotten the conflict and been able to choose the "v3" version. Where v3 modified the files in a completely compatible way, e.g., a change from v1 to v2 was preserved into v3 so that v1-vs-v3 is the same as v1-vs-v2, you will get no conflict and will have the v3 version, but that's the same as having the v2 version.)

2: What to do about it

I don't know if you want to back out your merge, or just check in a new tree "post-merge" that has the latest version of the library.

To back out the merge, assuming you have not published it yet, use git reset (see Undo a Git merge?—be careful with reset --hard). You'll then have to re-do the merge, though, including resolving any conflicts outside the third-party library. For the third-party library part you get to cheat; read on. (You might want to save the merge temporarily, e.g., set a tag or extra branch name for it. You can then check out various merged files in the same way you will below, just, using this other name.)

To leave the merge in place and just add a new commit, skip over the "back out the merge, then do it over again" part. :-)

Now that you are either back in the semi-merged state or just ready to update the library, you just need to replace all the files in that directory (it sounds like it's confined to some sub-directory, I've used dir/with/lib3 below). To do so:

git rm -r dir/with/lib3               # remove entire 3rd party library ...
git checkout master -- dir/with/lib3  # and then re-create it from master

The second command, the git checkout with a branch name (master) and one or more paths (here dir/with/lib3), tells git to extract the named file(s) and/or directory/ies from the named branch, into the current work directory via—this is important—the staging area ("index").

In this case, for every file that exists in the tip of branch master that lives under dir/with/lib3, git will simultaneously:

  • add the master branch version of the file to the index, and
  • put that master branch version in place in the working directory.

(That's why this same trick can be used to grab the already-merged version out of a temporary tag or branch name set on the earlier merge you are resetting, if you are resetting. Git will just extract the named file(s) from the named revision, via the staging-area. This makes re-doing the merge a lot easier!)

Finally, run any tests and fix regressions or problems; use git status as usual to make sure everything is ready to check in; and once it is, run git commit to commit the new version.

Community
  • 1
  • 1
torek
  • 448,244
  • 59
  • 642
  • 775
  • there is also `rebase` that makes everything more "linear" and is probably easier to think about instead of `merge`. – user2485710 Dec 13 '13 at 17:49
  • @user2485710: whether to rebase or merge is more or less independent of the above. I'm not in either of the "always rebase" or "always merge" camps, myself; I pick which to do based on current and future intentions. In this case I was assuming the original poster was pretty sure he wanted a merge (which might not be a good assumption!).... – torek Dec 13 '13 at 17:53
  • @torek -- thanks for the detailed explanation! I went with your "leave the merge in place and just add a new commit" approach, which worked out perfectly. – Dolan Antenucci Dec 13 '13 at 19:36