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 (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:
```ruby
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:
```ruby
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
- Parent epic: #595504
- Depends on: CommitsFinder issue, GitalyKeysetPager integration issue
- Original MR: !229705
- Original bug: #586997
issue