Refactor Commits API to use CommitsFinder + GitalyKeysetPager

Summary

Refactor the Commits API endpoint to use CommitsFinder and GitalyKeysetPager, replacing the inline pagination logic with the standard thin-controller pattern.

Parent Issue

This is part of #595504 (closed) (Epic: Refactor Commits API to use standard keyset pagination patterns)

Background

This is the final implementation MR that brings everything together. After CommitsFinder is created and integrated with GitalyKeysetPager, we can simplify the API endpoint significantly.

Current State (lib/api/commits.rb)

~90 lines of inline conditional logic:

get ':id/repository/commits' do
  if params[:pagination] == 'keyset'
    # ~40 lines of keyset pagination logic
  else
    # ~40 lines of offset pagination logic
  end
end

Proposed State

~15 lines using finder + pager:

get ':id/repository/commits' do
  not_found! 'Repository' unless user_project.repository_exists?

  finder = CommitsFinder.new(
    user_project,
    current_user,
    declared_params(include_missing: false)
  )

  serializer = params[:with_stats] ? Entities::CommitWithStats : Entities::Commit

  commits = Gitlab::Pagination::GitalyKeysetPager.new(self, user_project).paginate(finder)

  present commits, with: serializer
rescue ArgumentError => e
  render_api_error!(e.message, 400)
end

Acceptance Criteria

  • Replace inline pagination logic with CommitsFinder + GitalyKeysetPager
  • Remove ~70 lines of code from API endpoint
  • All existing tests continue to pass
  • Add new API tests for keyset pagination edge cases:
    • with_stats: true with keyset pagination
    • since/until filters with keyset pagination
    • author filter with keyset pagination
    • ref_name (non-all mode) with keyset pagination
  • Feature flag controls keyset pagination availability
  • Backward compatible - existing offset pagination unchanged

API Parameters

New parameters added:

  • pagination=keyset - Opt-in to cursor-based pagination
  • page_token=<sha> - Cursor for next page

Unsupported with keyset (return 400):

  • path
  • first_parent
  • order (non-default)
  • trailers
  • follow

References