fix traverse all parents to show full commit history

The commit walker previously only followed first-parent links, which caused commits reachable only through second parents of merge commits to be invisible. This affected roughly half the history in repos with merge commits. Replace the linear walk with a BFS that enqueues all parents, sorted by author date descending, with deduplication via a string set. This produces the same output as 'git log' (all ancestors in date order).

Commit
4ec1dd31549b8c8898f3e549d0978b91963f35db
Author
Claude Sonnet 4 <claude@anthropic.invalid>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
lib/resolvers.ml
index 32741139..57123535 100644..100644
@@ -295,21 +295,39 @@
295 295 of_hash repository hash
296 296
297 297 let recent_matching_from repository hash count predicate =
298 Removed: let rec walk commits hash remaining =
299 Removed: if remaining <= 0 then Lwt_result.return (List.rev commits)
298 Added: (* BFS traversal following all parents, ordered by author date descending *)
299 Added: let module S = Set.Make (String) in
300 Added: let rec walk collected seen queue remaining =
301 Added: if remaining <= 0 then Lwt_result.return (List.rev collected)
300 302 else
301 Removed: let* commit = of_id repository hash in
302 Removed: let commits, remaining =
303 Removed: if predicate commit then (commit :: commits, remaining - 1)
304 Removed: else (commits, remaining)
305 Removed: in
306 Removed: if remaining <= 0 then Lwt_result.return (List.rev commits)
307 Removed: else
308 Removed: match commit.parents with
309 Removed: | parent_hash :: _ -> walk commits parent_hash remaining
310 Removed: | [] -> Lwt_result.return (List.rev commits)
303 Added: match queue with
304 Added: | [] -> Lwt_result.return (List.rev collected)
305 Added: | (_, h) :: rest ->
306 Added: if S.mem h seen then walk collected seen rest remaining
307 Added: else
308 Added: let seen = S.add h seen in
309 Added: let* commit = of_id repository h in
310 Added: (* Enqueue all parents *)
311 Added: let new_queue =
312 Added: List.filter_map
313 Added: (fun p ->
314 Added: if S.mem p seen then None
315 Added: else Some (commit.author.Git.User.date, p))
316 Added: commit.parents
317 Added: in
318 Added: (* Merge into queue sorted by date descending *)
319 Added: let queue =
320 Added: List.merge
321 Added: (fun ((a_ts, _), _) ((b_ts, _), _) -> Int64.compare b_ts a_ts)
322 Added: rest new_queue
323 Added: in
324 Added: let collected, remaining =
325 Added: if predicate commit then (commit :: collected, remaining - 1)
326 Added: else (collected, remaining)
327 Added: in
328 Added: walk collected seen queue remaining
311 329 in
312 Removed: walk [] hash count
330 Added: walk [] S.empty [ ((Int64.max_int, None), hash) ] count
313 331
314 332 let recent_from repository hash count =
315 333 recent_matching_from repository hash count (Fun.const true)