mirror of
https://github.com/go-gitea/gitea.git
synced 2026-06-29 14:21:25 +00:00
fix(api): respect since/until when counting commits for X-Total-Count (#38204)
The repository commits API (`GET /repos/{owner}/{repo}/commits`) accepts
`since` and `until` query parameters and filters the returned page of
commits by commit date. However, the `X-Total-Count` and `X-Total`
response headers reported the *unfiltered* total number of commits, so
the advertised total could be far larger than the number of commits
actually returned for the requested date range. With a range that
matches no commits, the page is correctly empty while the headers still
claim the full repository total.
## Root cause
`gitrepo.CommitsCount` declared `Since` and `Until` options and the API
handler populated them, but the function never appended
`--since`/`--until` to the underlying `git rev-list --count` invocation.
The date filters were silently dropped, so the count always reflected
the entire revision history.
## Fix
Pass the `Since`/`Until` options through to `git rev-list`, mirroring
the existing commit-listing path (`commitsByRangeWithTime`). The
reported total now matches the filtered range used to build the page.
## Testing
Added `TestCommitsCountWithSinceUntil` in
`modules/gitrepo/commit_test.go`, a table-driven unit test against the
`repo1_bare` fixture covering `since`, `until`, and a bounded
`since`+`until` range. It fails on the pre-fix code (every case returns
the full count of 3) and passes after the change. Existing
`CommitsCount` tests remain green.
## Notes
- No new settings, no default changes; this corrects an incorrect header
value and is backward compatible. Clients that depend on `since`/`until`
already filter the returned commits, and the headers now agree with that
filtering.
Fixes #35886.
---
*AI-assistance disclosure:* this change was developed with the
assistance of Claude Code (Claude Opus 4.8). I have reviewed and
understand the change and take responsibility for it.
This commit is contained in:
@@ -32,6 +32,14 @@ func CommitsCount(ctx context.Context, repo Repository, opts CommitsCountOptions
|
||||
cmd.AddOptionValues("--not", opts.Not)
|
||||
}
|
||||
|
||||
if opts.Since != "" {
|
||||
cmd.AddOptionFormat("--since=%s", opts.Since)
|
||||
}
|
||||
|
||||
if opts.Until != "" {
|
||||
cmd.AddOptionFormat("--until=%s", opts.Until)
|
||||
}
|
||||
|
||||
if len(opts.RelPath) > 0 {
|
||||
cmd.AddDashesAndList(opts.RelPath...)
|
||||
}
|
||||
|
||||
@@ -34,6 +34,37 @@ func TestCommitsCountWithoutBase(t *testing.T) {
|
||||
assert.Equal(t, int64(2), commitsCount)
|
||||
}
|
||||
|
||||
func TestCommitsCountWithSinceUntil(t *testing.T) {
|
||||
bareRepo1 := &mockRepository{path: "repo1_bare"}
|
||||
revision := []string{"8006ff9adbf0cb94da7dad9e537e53817f9fa5c0"}
|
||||
|
||||
// The three commits on this revision are dated 2018-04-18, 2017-12-19 and 2017-12-19.
|
||||
cases := []struct {
|
||||
name string
|
||||
since string
|
||||
until string
|
||||
expected int64
|
||||
}{
|
||||
{name: "no filter", expected: 3},
|
||||
{name: "since keeps newer commits", since: "2018-01-01", expected: 1},
|
||||
{name: "until keeps older commits", until: "2018-01-01", expected: 2},
|
||||
{name: "since and until bound the range", since: "2017-12-19T22:16:00-08:00", until: "2018-01-01", expected: 1},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
commitsCount, err := CommitsCount(t.Context(), bareRepo1,
|
||||
CommitsCountOptions{
|
||||
Revision: revision,
|
||||
Since: tc.since,
|
||||
Until: tc.until,
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.expected, commitsCount)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLatestCommitTime(t *testing.T) {
|
||||
bareRepo1 := &mockRepository{path: "repo1_bare"}
|
||||
lct, err := GetLatestCommitTime(t.Context(), bareRepo1)
|
||||
|
||||
Reference in New Issue
Block a user