feat(ui): add "follow rename" to file commit history list (#34994)

Fix #28253

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
Thomas Sayen
2026-06-03 19:40:38 +02:00
committed by GitHub
parent 735e940a61
commit b2748d7654
17 changed files with 169 additions and 82 deletions

View File

@@ -222,17 +222,20 @@ type CommitsByFileAndRangeOptions struct {
Page int
Since string
Until string
// when using FollowRename, there is no quick way to know the total count, so use hasMore to indicate if there are more commits to load
FollowRename bool
}
// CommitsByFileAndRange return the commits according revision file and the page
func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions) ([]*Commit, error) {
gitCmd := gitcmd.NewCommand("rev-list").
AddOptionFormat("--max-count=%d", setting.Git.CommitsRangeSize).
func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions) (commits []*Commit, hasMore bool, _ error) {
limit := setting.Git.CommitsRangeSize
gitCmd := gitcmd.NewCommand("--no-pager", "log").
AddArguments("--pretty=tformat:%H").
AddOptionFormat("--max-count=%d", limit+1).
AddOptionFormat("--skip=%d", (opts.Page-1)*setting.Git.CommitsRangeSize)
gitCmd.AddDynamicArguments(opts.Revision)
if opts.Not != "" {
gitCmd.AddOptionValues("--not", opts.Not)
if opts.FollowRename {
gitCmd.AddArguments("--follow")
}
if opts.Since != "" {
gitCmd.AddOptionFormat("--since=%s", opts.Since)
@@ -240,9 +243,12 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
if opts.Until != "" {
gitCmd.AddOptionFormat("--until=%s", opts.Until)
}
gitCmd.AddDynamicArguments(opts.Revision)
if opts.Not != "" {
gitCmd.AddOptionValues("--not", opts.Not)
}
gitCmd.AddDashesAndList(opts.File)
var commits []*Commit
stdoutReader, stdoutReaderClose := gitCmd.MakeStdoutPipe()
defer stdoutReaderClose()
err := gitCmd.WithDir(repo.Path).
@@ -274,7 +280,12 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
}
}).
RunWithStderr(repo.Ctx)
return commits, err
hasMore = len(commits) > limit
if hasMore {
commits = commits[:limit]
}
return commits, hasMore, err
}
// FilesCountBetween return the number of files changed between two commits

View File

@@ -6,8 +6,10 @@ package git
import (
"os"
"path/filepath"
"strings"
"testing"
"gitea.dev/modules/git/gitcmd"
"gitea.dev/modules/setting"
"gitea.dev/modules/test"
@@ -140,11 +142,52 @@ func TestCommitsByFileAndRange(t *testing.T) {
defer bareRepo1.Close()
// "foo" has 3 commits in "master" branch
commits, err := bareRepo1.CommitsByFileAndRange(CommitsByFileAndRangeOptions{Revision: "master", File: "foo", Page: 1})
commits, hasMore, err := bareRepo1.CommitsByFileAndRange(CommitsByFileAndRangeOptions{Revision: "master", File: "foo", Page: 1})
require.NoError(t, err)
assert.True(t, hasMore)
assert.Len(t, commits, 2)
commits, err = bareRepo1.CommitsByFileAndRange(CommitsByFileAndRangeOptions{Revision: "master", File: "foo", Page: 2})
commits, hasMore, err = bareRepo1.CommitsByFileAndRange(CommitsByFileAndRangeOptions{Revision: "master", File: "foo", Page: 2})
require.NoError(t, err)
assert.Len(t, commits, 1)
assert.False(t, hasMore)
repoFollowRenameDir := filepath.Join(t.TempDir(), "repo.git")
require.NoError(t, gitcmd.NewCommand("init").AddDynamicArguments(repoFollowRenameDir).Run(t.Context()))
_, _, runErr := gitcmd.NewCommand("fast-import").WithDir(repoFollowRenameDir).WithStdinBytes([]byte(strings.TrimSpace(`
blob
mark :1
data 0
reset refs/heads/master
commit refs/heads/master
mark :2
author Chi-Iroh <user@example.com> 1778660718 +0200
committer Chi-Iroh <user@example.com> 1778660718 +0200
data 10
Add a.txt
M 100644 :1 a.txt
commit refs/heads/master
mark :3
author Chi-Iroh <user@example.com> 1778660741 +0200
committer Chi-Iroh <user@example.com> 1778660741 +0200
data 22
Rename a.txt to b.txt
from :2
D a.txt
M 100644 :1 b.txt
`))).RunStdString(t.Context())
require.NoError(t, runErr)
repoFollowRename, err := OpenRepository(t.Context(), repoFollowRenameDir)
require.NoError(t, err)
defer repoFollowRename.Close()
commits, _, err = repoFollowRename.CommitsByFileAndRange(CommitsByFileAndRangeOptions{Revision: "master", File: "b.txt", Page: 1})
require.NoError(t, err)
assert.Len(t, commits, 1)
commits, _, err = repoFollowRename.CommitsByFileAndRange(CommitsByFileAndRangeOptions{Revision: "master", File: "b.txt", Page: 1, FollowRename: true})
require.NoError(t, err)
assert.Len(t, commits, 2)
}