DB at PS
DB at PS

Reputation: 111

git cherry-pick conflict includes unwanted code

My scenario: I have branch B which was created off of branch A. I make a few commits in branch B that I want to cherry-pick into branch C (which is similar to A). I don't want to do a merge of B into C because I don't want certain things from A to get in there.

My problem: When I do the cherry-pick of B's commits I have some spots where there is conflicts in branch C. This isn't an issue to resolve them, except that in branch B's block of changes in the conflict, some stuff from A is there.

So why am I seeing anything from A show up in C when it wasn't directly added in B and I'm cherry picking the commits? I'm guessing this is related to the conflict because nothing else from A is coming over, just the stuff around the conflict line.

In my screenshot below everything in the red bracket was originally in A but doesn't exist in C. The only line I actually want is the last one. This also happened in another file as well.

enter image description here

Upvotes: 3

Views: 725

Answers (1)

torek
torek

Reputation: 488003

When there is a conflict in the material to be cherry-picked, Git does the same thing it always does:

  • Look at the merge base version;
  • look at the HEAD version; and
  • look at the to-be-merged version.

If you set merge.conflictStyle to diff3 (I do), you will see all three versions, rather than just two of these three, in the conflict area. I find that this helps to see what Git was seeing.

So why am I seeing anything from A show up in C when it wasn't directly added in B and I'm cherry picking the commits? I'm guessing this is related to the conflict because nothing else from A is coming over, just the stuff around the conflict line.

That's correct: the issue is that Git must find a merge base commit in order to decide "what you changed"—this is the result of comparing the merge base to your current or HEAD commit—and "what they changed", which is the result of comparing the merge base to "their" commit.

You mentioned "branches" at the beginning of all of this:

I have branch B which was created off of branch A. I make a few commits in branch B that I want to cherry-pick into branch C (which is similar to A).

But in fact, Git mostly doesn't care about branches at all. Git only cares about commits—branches mainly matter to Git in that branch names allow Git to find individual commits. It helps to draw out the actual commits (and, if you like, include the branch labels that let us—and Git—find these commits):

             H--I--J   <-- branch-B
            /
...--E--F--G   <-- branch-A
      \
       K--L   <-- branch-C

I've made this graph up from whole cloth, and it may look very little like your graph, but it now allows us to illustrate the odd way git cherry-pick identifies merge base commits inside Git.

If we're "on branch-C" as git status might say, our HEAD commit is commit L, the tip of branch-C. If we then run git cherry-pick <hash-of-commit-I>, Git makes these assignments:

  • merge-base = commit H
  • HEAD or --ours = commit L
  • --theirs = commit I

Git will then diff (as in git diff) commit H against commit I to see what they did—that's the cherry we would like to pick—but also diff commit H against commit L, to see what we changed and look for conflicts.

If there are conflicts, Git shows us what both sides did to the merge base version H, without actually showing us merge base version H itself. This may include stuff we didn't want: stuff we "deleted" by going "backwards" from H to L. Had Git showed up the merge-base version H as well, we could see that we "deleted" the stuff we don't want, so we should "keep it deleted" when resolving the merge conflict.

Upvotes: 3

Related Questions