38

In a normal Git merge conflict, the three versions of a file in play for the three-way merge are roughly as follows:

  • LOCAL: the version from my branch
  • REMOTE: the version from the other branch
  • BASE: the version from the common ancestor of the two branches (in particular, the common ancestor of my branch's HEAD and the other branch's HEAD)

When a Git cherry-pick generates a merge conflict, there is no common ancestor, properly speaking, so how are these things determined? The same could be asked about rebase.

Chris
  • 9,986
  • 8
  • 48
  • 56

1 Answers1

48

cherry-pick

Unless I have misled myself, then if you do "git cherry-pick <commit C>", then you get:

  • LOCAL: the commit you're merging on top of (ie the HEAD of your branch)
  • REMOTE: the commit you're cherry picking (i.e. <commit C>)
  • BASE: the parent of the commit you're cherry-picking (ie C^, ie the parent of C)

If it's not immediately clear why BASE should be C^, see the "why" section below.

Meanwhile, let's take an example, and see that BASE can be but often won't be a common ancestor during a cherry-pick. Suppose the commit graph looks like this

E <-- master
|
D 
| C <-- foo_feature(*)
|/
B
|
A

and you are in branch foo_feature (hence the asterisk). If you do "git cherry-pick <commit D>", then BASE for that cherry-pick will be commit B, which is a common ancestor of C and D. (C will be LOCAL and D will be REMOTE.) However, if you instead do "git cherry-pick <commit E>, then BASE will be commit D. (C will be LOCAL and E will be REMOTE.)

rebase

For background context, rebase is approximately iterated cherry-picking. In particular, rebasing topic on top of master (ie "git checkout topic; git rebase master") means approximately :

git checkout master # switch to master's HEAD commit
git checkout -b topic_rebased # create new branch rooted there
for each commit C in master..topic # for each topic commit not already in master...
    git cherry-pick C # bring it over to the new branch
finally, forget what "topic" used to mean and now defined "topic" as the HEAD of topic_rebased.

The labels that apply during this process are extensions of the normal cherry-pick rules:

  • LOCAL: the commit you're cherry-picking on top of
    • This is the HEAD of the new topic_rebased branch
    • For the first commit only, this will be the same as the HEAD of master
  • REMOTE: the commit you're cherry picking (i.e. <commit C>)
  • BASE: the parent of the commit you're cherry-picking (C^, ie the parent of C)

This implies something to keep in mind about LOCAL vs REMOTE, if you want to avoid confusion:

Even though you were on branch topic when you initiated the rebase, LOCAL never refers to a commit on the topic branch while a rebase is in progress. Instead, LOCAL always refers to a commit on the new branch being created (topic_rebased).

(If one fails to keep this in mind, then during a nasty merge one may start asking oneself, "Wait, why is it saying these are local changes? I swear they were changes made on master, not on my branch.")

To be more concrete, here is an example:

Say we have commit graph

D <-- foo_feature(*)
|
| C <-- master
B |
|/
|
A

and we are currently on branch foo_feature (indicated by "*"). If we run "git rebase master", the rebase will proceed in two steps:

First, changes from B will be replayed on top of C. During this, C is LOCAL, B is REMOTE, and A is BASE. Note that A is a real common ancestor of B and C. After this first step, you have a graph approximately like so:

   B' <-- foo_feature
D  |
|  |
|  C <-- master
B /
|/
|
A

(In real life, B and D might have already been pruned out of the tree at this point, but I'm leaving them in here, in order to make it easier to spot any potential common ancestors.)

Second, changes from D will be replayed on top of B'. During this, B' is LOCAL, D is REMOTE, and B is BASE. Note that B is not a relevant common ancestor of anything. (For example, it's not a common ancestor of the current LOCAL and REMOTE, B' and D. And it's not a common ancestor of the original branch heads, C and D). After this step, you have a branch approximately like so:

   D' <-- foo_feature
   |
   B'
D  |
|  |
|  C <-- master
B /
|/
|
A

For completeness, note by the end of the rebase B and D are removed from the graph, yielding:

D' <-- foo_feature
|
B'
|
C <-- master
|
A

Why is BASE defined as it is?

As noted above, both for a cherry-pick and for a rebase, BASE is the parent (C^) of the the commit C being pulled in. In the general case C^ isn't a common ancestor, so why call it BASE? (In a normal merge BASE is a common ancestor. And part of git's successes in merging are due to its ability to find a good common ancestor.)

Essentially, one does this as a way to implement "patch" functionality via the normal three-way merge algorithm. In particular you get these "patchy" properties:

  • If <commit C> doesn't modify a given given region of the file, then the version of that region from your branch will prevail. (This is, regions that the "patch" doesn't call for changing don't get patched.)
  • If <commit C> modifies a given region of the file and your branch leaves that region alone, then the version of that region from <commit x> will prevail. (That is, regions that the "patch" calls for changing get patched.)
  • If <commit C> modifies a given region of the file but your branch has also modified that region, then you get a merge conflict.
Chris
  • 9,986
  • 8
  • 48
  • 56
  • Answering your own question within a minute when you're not completely sure it's correct is a little bit poor form. – Cascabel Apr 07 '12 at 21:22
  • 1
    @Jefromi Would you suggest I include tentative answers as part of the original question instead? (I thought it'd be helpful as an independent answer, because that way it could be discussed/upvoted/downvoted/etc. separate from the original Q.) I guess the alternative is to not include them at all. – Chris Apr 07 '12 at 23:15
  • I'd personally have waited to see if a more definite answer was posted quickly. – Cascabel Apr 08 '12 at 00:25
  • 1
    I *think* you did get it right, though. The normal names for this, by the way, are "ours" (the merged-into/applied-onto version), "theirs" (the merged/applied version), and "base" (the common ancestor). – Cascabel Apr 08 '12 at 00:33
  • 1
    @Jefromi Ok, good Stack Overflow tip. As for ours/theirs/base, I have seen that terminology in other git contexts. I think LOCAL/REMOTE/BASE is the terminology of git's mergetool ([manpage here](http://schacon.github.com/git/git-mergetool.html)), which, in my setup anyway, causes those terms to surface in the filenames passed to kdiff3. – Chris Apr 08 '12 at 02:27
  • OMG I always thought the BASE is the common ancestor and I was always very very confused all this time. *\*facepalm\** – Qwerty Mar 31 '21 at 03:31
  • Why does Git need to know about BASE at all? I thought a commit was essentially a diff, containing all the information about changes. So why does Git need to do anything with BASE? – FreelanceConsultant Jun 14 '23 at 13:15