| Merge-Base Computation and paint_down_to_common() |
| ================================================== |
| |
| The function `paint_down_to_common()` in `commit-reach.c` computes merge |
| bases by walking the commit graph backwards from two sets of tips and |
| finding where their ancestry meets. |
| |
| Use cases |
| --------- |
| |
| Computing merge bases is used in two different ways: |
| |
| 1. *Finding all merge bases* (`merge-base --all`, `merge-tree`, |
| `merge`, `rebase`). A merge base is a common ancestor that is |
| not itself an ancestor of another common ancestor. |
| |
| 2. *Ancestry checks* (`in_merge_bases`, used by `merge-base |
| --is-ancestor`, `branch -d`, `fetch`). These ask: "is commit A |
| an ancestor of commit B?" If a common ancestor equals one of the |
| inputs, that input is necessarily the only merge base -- no other |
| common ancestor can be both as recent and not an ancestor of it. |
| |
| Both use cases share the same algorithm and implementation. |
| |
| Algorithm |
| --------- |
| |
| Given a commit `one` and a set of commits `twos[]`, the walk paints |
| commits with two colors: |
| |
| - PARENT1: reachable from `one` |
| - PARENT2: reachable from any commit in `twos[]` |
| |
| The walk uses a priority queue ordered by generation number |
| (highest first), breaking ties by commit date. Each step dequeues |
| the highest-priority commit and propagates its paint flags to its |
| parents, enqueuing any parent that gained new flags. When a |
| commit receives both PARENT1 and PARENT2, it is a merge-base |
| candidate. A candidate gains the STALE flag so its ancestors |
| propagate staleness -- any deeper common ancestor is necessarily |
| redundant. |
| |
| [[generation-regions]] |
| Topologically ordered and unordered generation regions |
| ------------------------------------------------------ |
| |
| Commits fall into two regions based on whether their generation |
| numbers provide a topological ordering guarantee: |
| |
| .... |
| +------------------------------------------+ |
| | Unordered region | |
| | generation = INFINITY or V1_MAX | |
| | queue order: heuristic (commit date) | |
| +------------------------------------------+ |
| | |
| v |
| +------------------------------------------+ |
| | Ordered region | |
| | generation = finite, unsaturated | |
| | queue order: topological | |
| +------------------------------------------+ |
| .... |
| |
| In the ordered region, a child's generation is strictly greater |
| than its parent's. Same-generation commits are necessarily |
| independent, so the queue always processes children before |
| their parents. |
| |
| In the unordered region, parent-child pairs can share the same |
| generation number, so topological order is not guaranteed. The |
| queue uses commit-date as a heuristic, which typically produces |
| a reasonable traversal order but may process a parent before |
| its child. |
| |
| Commits not in the commit-graph have generation INFINITY; v1 |
| commit-graphs saturate at V1_MAX. Both place commits in the |
| unordered region. Any optimization that depends on generation |
| ordering must account for this saturation boundary. The early |
| exit gates compare against a topological ceiling -- |
| `GENERATION_NUMBER_V1_MAX` for v1 graphs and |
| `GENERATION_NUMBER_INFINITY` for v2 graphs -- so that saturated |
| commits are treated as unordered. |
| |
| With generation ordering, values in the unordered region exceed |
| those in the ordered region. The walk may therefore transition |
| from the unordered region into the ordered region, but never in |
| the reverse direction. Without a commit-graph, every commit has INFINITY |
| and the walk operates entirely in the unordered region. |
| |
| In the ordered region, paint on a dequeued commit is final -- |
| no future step can add flags to it. In the unordered region, |
| a dequeued commit may later gain additional paint. Paint flags |
| are only added, never removed, bounding the number of |
| re-enqueues per commit. |
| |
| Termination |
| ----------- |
| |
| The walk tracks the number of commits of each type in the queue |
| (PARENT1-only, PARENT2-only, pending merge-base). The main loop |
| ends when one of the following conditions holds: |
| |
| 1. The queue is empty. |
| 2. The queue contains only stale entries. |
| 3. Generation cutoff: the dequeued commit's generation is below |
| a caller-supplied `min_generation` threshold. |
| 4. Single result: the caller only needs one merge base, one has |
| been found, and the walk has entered the ordered region. |
| 5. Side exhaustion: no pure PARENT1 or pure PARENT2 commits |
| remain in the queue, no pending merge-base candidates exist, |
| and the walk has entered the ordered region. |
| |
| Stale entry condition |
| ~~~~~~~~~~~~~~~~~~~~~ |
| Once all queued entries are stale, no new merge-base candidates can |
| be discovered -- that requires at least one non-stale commit from |
| each side meeting. Continuing the walk could still invalidate |
| existing candidates by proving one is an ancestor of another, but |
| `remove_redundant()` handles that as a post-processing step, so it |
| is safe to exit early. |
| |
| Side-exhaustion condition |
| ~~~~~~~~~~~~~~~~~~~~~~~~~ |
| A new merge-base requires commits from both sides to meet. When one |
| side's exclusive counter reaches zero and there are no pending |
| merge-base candidates, no future traversal step can produce a new |
| candidate. This optimization only activates in the ordered region, |
| where paint flags are final at visit time; in the unordered region, |
| a side that appears exhausted could reappear through late paint |
| propagation. |
| |
| Generation cutoff |
| ~~~~~~~~~~~~~~~~~ |
| Some callers (notably `remove_redundant()`) supply a `min_generation` |
| threshold equal to the minimum generation of the input commits. |
| These callers only need to determine reachability among the inputs, |
| not find deep merge bases, so the walk can safely terminate when it |
| dequeues a commit below this threshold. |
| |
| Single result |
| ~~~~~~~~~~~~~ |
| When only one merge base is needed and the walk is in the |
| ordered region with generation ordering, the first candidate |
| found is necessarily the highest-generation common ancestor. |
| No remaining commit in the queue can be a descendant of this |
| candidate (generation ordering guarantees children are visited |
| first), so it cannot be redundant and the walk can stop |
| immediately. |
| |
| Related documentation |
| --------------------- |
| |
| - `Documentation/technical/commit-graph.adoc` -- generation numbers |
| and the reachability closure property. |