fangorn/ex_git_objectstore
public
ref:37bb565ea81d8b8b205000801431400cfd07e3eb
feat: Graph queries — ancestor?, ahead_behind, commits_between (#16)
PR 2 of 4 for fangorn/ex_git_objectstore#26. Adds the query API that makes the commit-graph useful to Anvil's PR list and PR show paths (fangorn/anvil#55). Strictly additive — no callers yet.
## Queries
- **\`Graph.ancestor?/3\`** — generation-pruned BFS from descendant back. If \`gen(ancestor) > gen(descendant)\`, return false immediately; otherwise walk parents, skipping any SHA with generation less than \`gen(ancestor)\`.
- **\`Graph.ahead_behind/3\`** — generation-ordered priority queue. Tag each SHA with \`:a\` / \`:b\` / \`:both\`; commits marked \`:both\` (and their transitive ancestors) are excluded from both counts.
- **\`Graph.commits_between/3\`** — the head-side set from the ahead_behind walk, sorted newest-first by corrected commit date.
Each returns \`{:error, :missing_commit}\` if either SHA isn't in the graph — callers fall back to a reference walker.
## Tests
45 graph tests total. New:
- 5 \`ancestor?\` (self, parent→child, unrelated, merge parents, missing)
- 7 \`ahead_behind\` (identical, linear, diverged, merge-base on both sides, disjoint roots, shared-merge non-double-counting, missing)
- 4 \`commits_between\` (empty, linear, excludes base ancestors, merge with ordering)
- 3 equivalence tests × 10 random DAGs each, comparing every pair against a brute-force \`cat_object\` walker
Full suite: **681 tests, 0 failures** (was 661 on main post-#15). Credo: unchanged from main.
## Benchmark
\`bench/graph_build.exs\` extended. 5000-commit linear chain, in-memory storage:
\`\`\`
Walker: full ancestry scan (for ahead_behind) 57 ms
Graph.ahead_behind (in-memory) 6 ms (9.4x)
Graph.commits_between (in-memory) 7 ms
\`\`\`
On S3, where each \`cat_object\` is a network round-trip, the walker scales with \`commit_count × latency\` while \`Graph.ahead_behind\` is bounded by the in-memory walk length. That is the gap Anvil's prod is currently paying for.
## What's next
- PR 3: top-level \`ExGitObjectstore.{ahead_behind, commits_between, ancestor?}\` with auto-load + fallback to the existing walker when the graph is missing or a SHA isn't present yet. Plus \`ExGitObjectstore.rebuild_graph/1\` for explicit seeding.
- Anvil PR: swap \`lib/anvil/git/objectstore.ex\` to the new API + a Mix task to seed graphs in dev/prod.
- (Later) PR 4: incremental \`Graph.update/3\` wired into the write paths.
SHA:
37bb565ea81d8b8b205000801431400cfd07e3eb
Author:
Anvil <noreply@anvil.fangorn.io>
Date:
2026-04-18 16:52
Parents:
330a5b0
3 files changed
+676
-3
| Type | ||
|---|---|---|
|
|
bench/graph_build.exs | +35 −3 |
|
||