feat: Refactor Commits API list endpoint to use CommitsFinder

Why

Part of epic &21573, closing #595508 (closed). Refactors the Commits API list endpoint (GET /projects/:id/repository/commits) to use Repositories::CommitsFinder (merged in !235257 (merged)) and GitalyKeysetPager (merged in!239139).

Keyset mode also fixes #586997, where page/cursor were silently ignored with all=true.

What

  • Endpoint now delegates to CommitsFinder + GitalyKeysetPager for the new keyset mode and keeps existing code untouched.
  • Keyset mode gated by commits_keyset_pagination FF (default off); when off, behavior is unchanged.
  • Params unsupported with keyset (path, first_parent, non-default order, trailers, follow) return 400. The finder raises Repositories::CommitsFinder::UnsupportedKeysetParamError (a subclass of ArgumentError), and the endpoint rescues that class to surface the 400 — no error-message string matching.

How to validate locally

How to validate locally

Note: The commits_keyset_pagination flag is cached per Puma worker. After enabling or disabling it, run gdk restart rails-web (wait ~15s) before testing — otherwise requests hitting stale workers give inconsistent 400/200 results.

Set a token and pick a project with enough commit history to paginate:

export TOKEN=<your_gdk_token>
export ID=<project_id>

1. Enable the flag

In rails console:

Feature.enable(:commits_keyset_pagination)

Then restart: gdk restart rails-web.

2. Keyset pagination advances the cursor (fixes #586997)

Fetch page 1 and inspect the Link header:

curl -s -D - --header "PRIVATE-TOKEN: $TOKEN" \
  "http://gdk.test:3000/api/v4/projects/$ID/repository/commits?pagination=keyset&all=true&per_page=5"

Copy the page_token=<SHA> from the Link: ...; rel="next" header, then fetch page 2:

curl -s -D - --header "PRIVATE-TOKEN: $TOKEN" \
  "http://gdk.test:3000/api/v4/projects/$ID/repository/commits?pagination=keyset&all=true&per_page=5&page_token=<SHA>"

Expected: page 2 returns different commits than page 1, and its Link header carries a new page_token. (Before the fix, all=true returned the same commits on every page.)

3. Unsupported params return 400 (keyset mode only)

curl -s -o /dev/null -w '%{http_code}\n' --header "PRIVATE-TOKEN: $TOKEN" \
  "http://gdk.test:3000/api/v4/projects/$ID/repository/commits?pagination=keyset&first_parent=true"

Expected: 400 for each of path, first_parent, order (non-default), trailers, and follow. The same params without pagination=keyset still return 200 (offset mode is unaffected).

4. Flag off = unchanged legacy behavior

In rails console:

Feature.disable(:commits_keyset_pagination)

Then restart (gdk restart rails-web) and confirm offset pagination still works:

curl -s -D - --header "PRIVATE-TOKEN: $TOKEN" \
  "http://gdk.test:3000/api/v4/projects/$ID/repository/commits?all=true&per_page=5&page=2"

Expected: 200 with normal offset results; page>1 slicing works, and X-Total / rel="last" headers are absent (offset-without-count behavior, unchanged from master).

Edited by Hunter Stewart

Merge request reports

Loading
Loading