This is really undefined when there is more than one commit.

Instead you should look at the strategy, and then enforce some sanity on the commits member.  E.g. add a member to MergeStrategy to help us decide if the number of commits is reasonable.  Then the already-up-to-date and fast-forward-only strategies only need to accept 1 commit, and reject functioning when more than 1 commit is given.

I do see that you are trying to collapse multiple commits that are reachable all together in this loop.  But that's probably just a pre-processing step that we can do at the beginning of the function, no matter what strategy was configured.  Even in an octopus merge, it doesn't make sense to create an 8-way merge if it can really be a 2-way because the other 6 commits are already reachable.

Gerrit Code Review has some logic to do this in [1].  Ignore the CAN_MERGE stuff, that's there only to support the code review workflow and ensure a commit was approved.  The bulk of the sorting logic is about computing the commits that will be merged, and removing them from incoming (what the caller asked us to merge) if they are already reachable.

[1] https://android.git.kernel.org/?p=tools/gerrit.git;a=blob;f=gerrit-server/src/main/java/com/google/gerrit/server/git/MergeSorter.java;hb=HEAD

You know what, maybe for right now just enforce that commits.size() == 1 at the start of the method (and maybe also in include, assert that its empty), and simplify the code around that assertion.  commits.size() > 1 will only be useful once we have all other aspects of the merge code working... and we can come back and revisit how to sort the heads and determine up-to-date in the octopus case.