cherry-pick is a merge, of the diffs from your cherry-pick's parent to the cherry-pick, with the diffs from your cherry-pick's parent to your checked-out tip. That's it. Git doesn't have to know any more than that. It doesn't care "where" any of the commits are, it cares about merging those two sets of diffs.
revert is a merge of the diffs from your revert to its parent with the diffs from your revert to your checked-out tip. That's it. Git doesn't have to know any more.
Here: try this:
git init test; cd $_
printf %s\n 1 2 3 4 5 >file; git add .; git commit -m1
sed -si 2s,$,x, file; git commit -am2
sed -si 4s,$,x, file; git commit -am3
Run git diff :/1 :/2
and git diff :/1 :/3
. Those are the diffs git runs when you say git cherry-pick :/2
here. The first diff changes line 2, and the second commit changes lines 2 and 4; the line 4 change does not abut any changes in the first diff and the line 2 change is identical in both. There's nothing left to do, all the :/1
-:/2
changes are also in :/1
-:/3
.
Now before you start on what follows, let me say this: this is harder to explain in prose than it is to just see. Do the example sequence above and look at the output. It is much, much easier to see what's going on by looking at it than by reading any description of it. Everybody goes through a stretch where this is too new and maybe a little orientation will help, and that's what the paragraphs below are for, but again: the prose, alone, is harder to understand than the diffs. Run the diffs, try to understand what you're looking at, if you need a little help over what I promise is a very small hump follow along in the text below. When it snaps into focus see if you don't at least mentally slap your forehead and think "wow why was that so hard to see?", just like, well, just about everybody.
Git's merge rules are pretty straightforward: identical changes to overlapping or abutting lines are accepted as-is. Changes to lines with no changes in one diff for changed lines, or lines abutting changed lines, in the other, are accepted as is. Different changes to any overlapping or abutting lines, well, there's an awful lot of history to look at and nobody's ever found a rule that will predict what the results of that should be every time, so git declares the changes conflict, dumps both sets of results into the file and lets you decide what the result should be.
So what happens if you now change line 3?
sed -si 3s,$,x, file; git commit -amx
run git diff :/1 :/2
and git diff :/1 :/x
, and you'll see that where, relative to the cherry-pick's parent, :/2
changed line 2 and your tip changed lines 2,3 and 4. 2 and 3 abut, that's historically too close for automated genies to handle properly, so yay, you get to do it: git cherry-pick :/2
now will declare a conflict, showing you the change to line 2 and the two different versions of lines 3 and 4 (:/2 changed neither, your tip changed both, in context here it's clear the line 3 and 4 changes are fine as-is but again: nobody's ever figured out an automatic rule for reliably identifying such contexts).
You can ring changes on this setup to test out how reverts work. Also stash pops, and merges, and git checkout -m
which runs a quick ad-hoc merge with your index.
Your git cherry-pick B^..C
is a cherry-pick of two commits, B
and C
. It does them one after another, exactly as described above. Since you've reverted B
and C
, and then cherry-picked them again, this has the exact same effect as applying B
and C
and then cherry-picking B
(with the intent of then cherry-picking C
). I conclude that B
and C
touch overlapping or abutting lines, so git diff B^ B
will show changes that overlap or abut changes in git diff B^ C'
, and that's what Git's not going to just pick for you, because whatever looks right here, in other circumstances nobody can write a rule for identifying, an identical-looking choice will be wrong. So git says the two sets of changes conflict and you get to sort it out.