It's doing something weirder than that, but "it applies a diff between a specified commit and its parent on top of your current work" is more accurate/intuitive than "it does a three-way merge with a common ancestor". No common ancestor is involved.

The first hint that it's doing something interesting is that the implementation of cherry-pick is in revert.c, because it's implemented as a variant of revert: https://github.com/git/git/blob/v2.35.1/builtin/revert.c#L23...

Both the "revert" and "cherry-pick" operations initialize a sequencer object with a single operation in the sequence. (This is the same mechanism underlying "git rebase -i").

From there we can find the call to sequencer_pick_revisions, which leads us to the sequencer implementation, where there's a fast-path for ordinary cherry-picks leading us to a short function single_pick, which in turn calls do_pick_commit: https://github.com/git/git/blob/v2.35.1/sequencer.c#L2066

This operation then does the following:

- Determine what we're applying a diff on top of: https://github.com/git/git/blob/v2.35.1/sequencer.c#L2083-L2...

- Find a parent commit. If the commit has more than one parent, it requires you to specify a "-m" option indicating which parent to diff against. https://github.com/git/git/blob/v2.35.1/sequencer.c#L2109-L2...

- Set up the commit message and some other stuff.

- Set the variable "base" to the parent it found and "next" to the commit being cherry-picked. https://github.com/git/git/blob/v2.35.1/sequencer.c#L2187-L2...

- Assuming no merge strategy (or either of the standard strategies "ort" or "recursive") was specified on the command line, call do_recursive_merge. https://github.com/git/git/blob/v2.35.1/sequencer.c#L2237-L2...

In turn, do_recursive_merge calculates a head_tree from the current HEAD (https://github.com/git/git/blob/v2.35.1/sequencer.c#L645), and then calls either merge_incore_nonrecursive (the current default, from the new "ort" merge strategy) or merge_trees (from the older "recursive" merge strategy) with three trees (i.e., with no further ancestry information): the base is the parent it found, and the two sides being merged are your current HEAD and the (tree of) the commit being cherry-picked.

That is to say, at no point does it care whether the two commits even have a common ancestor! It's just doing an operation on trees. It is doing a three-way merge, yes, but the graph of the merge it's doing is one that potentially doesn't actually exist in reality.

Or, in other words, it's trying to compute the tree that could be equally well described as the result of

- applying the diff of the commit you're cherry-picking to your current HEAD

- applying the diff between your current HEAD and the parent of the commit you're cherry-picking to the commit you're cherry-picking

So this is more powerful than purely applying a diff with no information about the base of the diff, but it is very much like applying a diff.

One way you can test this without drilling into source code is to make two independent commit histories (using either two git repos, or git checkout --orphan, or whatever) where a commit in one history has a diff that would apply to a file in the other history if applied with the "patch" command. Then try cherry-picking it into the second history. It should work.

(In the revert case, it more or less does the same thing, just with "base" and "next" flipped. That is, if you have commits A, B, and C, and you want to revert B, it does a three-way merge where the base is the tree after B, one side is the tree after A, and the other side is the tree after C!)