For recursive merges with multiple merge bases, how does git determine the order in which to merge the merge bases into temporary commits? Anecdotally, it seems to start with the two oldest merge bases. For example:
Question: How does git choose what order to consider multiple merge bases when performing the recursive inner merge? Is it based on sorting the merge bases by commit date, or something else?
I'm going to answer as if you'd asked "how do I make Git merge unusually-wide recursive base sets in a sequence that looks like it'll produce easier resolutions for me than the one it picked itself?".
You can do any merges you want in any order you want. The convenience commands are set up to make ordinary work go well, but all the underlying machinery is there for you to use in whatever way works for your specific case. If you've got some history with three equivalent bases for a merge you want to do, and you don't like the order ort or recursive picks, do the base merges yourself in the order you want, use that result as the base for your final merge, and then record that result with the correct ancestry.
So, to merge feature
into main
when you've managed to weave a set of three criss-cross merges, A, B and C, into their recent history and want to produce the merge base specifically by merging A and C then that with B,
git checkout A # the merges git's internal recursion will run for you, but
git merge C # you don't like the sequence it picks, so do them yourself
git merge B # .
base=`git rev-parse HEAD` # the recursive-merge-in-your-preferred-order result
git checkout $(git commit-tree -p $base -m m main:) # merge `main` and `feature but using
git merge $(git commit-tree -p $base -m f feature:) # the prepped base
# and record the result with the current `main` and `feature` tips as parents.
git checkout -B main $(git commit-tree -p main -p feature -m "Merge branch 'feature'" HEAD:)
Note that the above script only keeps the final commit (created by that commit-tree on the last line), the payload merge you're constructing. Being a scripted sequence it has to communicate content and structure between scripted command steps through recorded commits that aren't needed after the work is done, they're just the gruntwork steps that produce the result: they're the recursive internal merge you're doing.