feat(walk): rev_list_range/4 — base..head with exclude_merges #47
fix/337-rev-list-range
into main
Adds ExGitObjectstore.rev_list_range/4: commits reachable from head but not base (the base..head range), topological order oldest-first, with an :exclude_merges option.
Combined, these linearize a range exactly the way git rebase (without --rebase-merges) replays it: merge commits are dropped while the commits they merged in — reachable via any parent — are kept.
Why
Anvil’s rebase merge strategy currently builds its replay list by walking the head branch first-parent-only and includes merge commits, then cherry-picks each. Cherry-picking a merge commit fails with {:merge_commit_needs_mainline, sha}, so any PR whose head history contains a merge commit 422s on rebase-merge instead of merging or reporting a conflict. This primitive is the plumbing Anvil needs to flatten base..head correctly. See fangorn/anvil#337.
Implementation
- Bounded two-color
limit_listpainting (INTERESTING from head, UNINTERESTING from base;:udominates), stopping once no interesting commit remains in the frontier — touches only the range plus its immediate boundary, not all of base’s history. - Topological oldest-first via Kahn’s algorithm with a
(committer-date, sha)ready-queue for deterministic ordering.
Tests
- 8 new tests in
walk_test.exs: diamond ordering,exclude_mergeskeeps both merge sides, empty range, linear range, and the integration-branch regression (an integration branch built by merging feature branches into main replays cleanly onto base with all content, instead of:merge_commit_needs_mainline). - Full suite green;
mix credo --strictclean on changed files;mix formatclean.