git merge-base A B finds the best common ancestor commit of two
branch tips -- what git merge uses as the three-way merge base, and
what git log A..B uses to figure out which commits are actually new
on B. The commit graph is a DAG, not a tree: a merge commit has MORE
THAN ONE parent, so "the" parent chain from a commit isn't a single
path -- every parent has to be followed to find every ancestor.
Right now, get_merge_base silently returns the WRONG (too-old)
common ancestor whenever the correct answer is only reachable by
walking through a merge commit's second (or later) parent -- because
_collect_ancestors only ever follows commit_parents[0].
Your task: find the bug in _collect_ancestors and fix it. The
overall structure of get_merge_base -- collect both commits'
ancestor sets via _collect_ancestors, intersect them, and keep only
the most recent common ancestor -- is already correct; don't change it.
Example that currently fails
parents = {
"A": [], "B": ["A"], "C": ["A"], "D": ["B"], "E": ["C"],
"M": ["D", "E"], # a merge commit -- TWO parents
"X": ["M"], "Y": ["E"],
}
get_merge_base(parents, "X", "Y")
# should be "E" (X reaches E through M's SECOND parent; Y reaches E
# directly)
# currently: "A" (only M's first parent, D, gets walked -- E and C are
# never visited from X's side at all, so the intersection collapses
# all the way back to the root)
Real-world grounding
- Git's merge-base computation (
get_merge_basesand the ancestor walk it's built on, including how it handles a commit with multiple parents):commit-reach.c
Real Git's version additionally handles the case where there's more than one equally good merge base (a criss-cross merge), using commit generation numbers and a priority queue to stay efficient on huge histories instead of this exercise's plain BFS-and-intersect -- but "every parent of a merge commit is part of its ancestry, not just the first one" is the exact mechanism this bug breaks.