pull/34994/merge
Thomas Sayen 2025-12-04 10:30:25 +07:00 committed by GitHub
commit b04d9cbabb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 80 additions and 25 deletions

@ -151,17 +151,25 @@ func AllCommitsCount(ctx context.Context, repoPath string, hidePRRefs bool, file
// CommitsCountOptions the options when counting commits
type CommitsCountOptions struct {
RepoPath string
Not string
Revision []string
RelPath []string
Since string
Until string
RepoPath string
Not string
Revision []string
RelPath []string
Since string
Until string
FollowRename bool
}
// CommitsCount returns number of total commits of until given revision.
func CommitsCount(ctx context.Context, opts CommitsCountOptions) (int64, error) {
cmd := gitcmd.NewCommand("rev-list", "--count")
var cmd *gitcmd.Command
followRename := len(opts.RelPath) > 0 && opts.FollowRename
if followRename {
cmd = gitcmd.NewCommand("--no-pager", "log", "--pretty=format:%H")
} else {
cmd = gitcmd.NewCommand("rev-list", "--count")
}
cmd.AddDynamicArguments(opts.Revision...)
@ -170,6 +178,9 @@ func CommitsCount(ctx context.Context, opts CommitsCountOptions) (int64, error)
}
if len(opts.RelPath) > 0 {
if opts.FollowRename {
cmd.AddOptionValues("--follow")
}
cmd.AddDashesAndList(opts.RelPath...)
}
@ -177,7 +188,9 @@ func CommitsCount(ctx context.Context, opts CommitsCountOptions) (int64, error)
if err != nil {
return 0, err
}
if followRename {
return int64(len(strings.Split(stdout, "\n"))), nil
}
return strconv.ParseInt(strings.TrimSpace(stdout), 10, 64)
}

@ -217,22 +217,29 @@ func (repo *Repository) FileChangedBetweenCommits(filename, id1, id2 string) (bo
}
// FileCommitsCount return the number of files at a revision
func (repo *Repository) FileCommitsCount(revision, file string) (int64, error) {
func (repo *Repository) FileCommitsCount(revision, file string, followRenameOptional ...bool) (int64, error) {
followRename := false
if len(followRenameOptional) > 0 {
followRename = followRenameOptional[0]
}
return CommitsCount(repo.Ctx,
CommitsCountOptions{
RepoPath: repo.Path,
Revision: []string{revision},
RelPath: []string{file},
RepoPath: repo.Path,
Revision: []string{revision},
RelPath: []string{file},
FollowRename: followRename,
})
}
type CommitsByFileAndRangeOptions struct {
Revision string
File string
Not string
Page int
Since string
Until string
Revision string
File string
Not string
Page int
Since string
Until string
FollowRename bool
}
// CommitsByFileAndRange return the commits according revision file and the page
@ -244,9 +251,18 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
}()
go func() {
stderr := strings.Builder{}
gitCmd := gitcmd.NewCommand("rev-list").
AddOptionFormat("--max-count=%d", setting.Git.CommitsRangeSize).
var gitCmd *gitcmd.Command
if !opts.FollowRename {
gitCmd = gitcmd.NewCommand("rev-list")
} else {
gitCmd = gitcmd.NewCommand("--no-pager", "log").
AddOptionFormat("--pretty=tformat:%%H").
AddOptionFormat("--follow")
}
gitCmd = gitCmd.AddOptionFormat("--max-count=%d", setting.Git.CommitsRangeSize).
AddOptionFormat("--skip=%d", (opts.Page-1)*setting.Git.CommitsRangeSize)
gitCmd.AddDynamicArguments(opts.Revision)
if opts.Not != "" {

@ -1424,6 +1424,7 @@ editor.fork_branch_exists = Branch "%s" already exists in your fork. Please choo
commits.desc = Browse source code change history.
commits.commits = Commits
commits.history_follow_rename = Include renames
commits.no_commits = No commits in common. "%s" and "%s" have entirely different histories.
commits.nothing_to_compare = These branches are equal.
commits.search.tooltip = You can prefix keywords with "author:", "committer:", "after:", or "before:", e.g. "revert author:Alice before:2019-01-13".

@ -217,12 +217,14 @@ func SearchCommits(ctx *context.Context) {
// FileHistory show a file's reversions
func FileHistory(ctx *context.Context) {
followRename := strings.Contains(ctx.Req.RequestURI, "history_follow_rename=true")
if ctx.Repo.TreePath == "" {
Commits(ctx)
return
}
commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(ctx.Repo.RefFullName.ShortName(), ctx.Repo.TreePath)
commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(ctx.Repo.RefFullName.ShortName(), ctx.Repo.TreePath, followRename)
if err != nil {
ctx.ServerError("FileCommitsCount", err)
return
@ -235,9 +237,10 @@ func FileHistory(ctx *context.Context) {
commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange(
git.CommitsByFileAndRangeOptions{
Revision: ctx.Repo.RefFullName.ShortName(), // FIXME: legacy code used ShortName
File: ctx.Repo.TreePath,
Page: page,
Revision: ctx.Repo.RefFullName.ShortName(), // FIXME: legacy code used ShortName
File: ctx.Repo.TreePath,
Page: page,
FollowRename: followRename,
})
if err != nil {
ctx.ServerError("CommitsByFileAndRange", err)

@ -8,6 +8,10 @@
{{ctx.Locale.Tr "repo.commits.no_commits" $.BaseBranch $.HeadBranch}}
{{end}}
</div>
<div class="commits-table-left tw-flex tw-items-center">
<input type="checkbox" name="history-enable-follow-renames" class="tw-mr-[5px]"/>
<label for="history-enable-follow-renames">{{ctx.Locale.Tr "repo.commits.history_follow_rename"}}</label>
</div>
{{if .IsDiffCompare}}
<div class="commits-table-right tw-whitespace-nowrap">
<a href="{{$.CommitRepoLink}}/commit/{{.BeforeCommitID | PathEscape}}" class="ui green sha label tw-mx-0">{{if not .BaseIsCommit}}{{if .BaseIsBranch}}{{svg "octicon-git-branch"}}{{else if .BaseIsTag}}{{svg "octicon-tag"}}{{end}}{{.BaseBranch}}{{else}}{{ShortSha .BaseBranch}}{{end}}</a>

@ -24,3 +24,20 @@ export function initCommitStatuses() {
});
});
}
export function initCommitFileHistoryFollowRename() {
const checkbox : HTMLInputElement | null = document.querySelector('input[name=history-enable-follow-renames]');
if (!checkbox) {
return;
}
const url = new URL(window.location.toString());
checkbox.checked = url.searchParams.has('history_follow_rename', 'true');
checkbox.addEventListener('change', () => {
const url = new URL(window.location.toString());
url.searchParams.set('history_follow_rename', `${checkbox.checked}`);
window.location.replace(url);
});
}

@ -21,7 +21,7 @@ import {initMarkupContent} from './markup/content.ts';
import {initRepoFileView} from './features/file-view.ts';
import {initUserAuthOauth2, initUserCheckAppUrl} from './features/user-auth.ts';
import {initRepoPullRequestAllowMaintainerEdit, initRepoPullRequestReview, initRepoIssueSidebarDependency, initRepoIssueFilterItemLabel} from './features/repo-issue.ts';
import {initRepoEllipsisButton, initCommitStatuses} from './features/repo-commit.ts';
import {initRepoEllipsisButton, initCommitStatuses, initCommitFileHistoryFollowRename} from './features/repo-commit.ts';
import {initRepoTopicBar} from './features/repo-home.ts';
import {initAdminCommon} from './features/admin/common.ts';
import {initRepoCodeView} from './features/repo-code.ts';
@ -146,6 +146,7 @@ const initPerformanceTracer = callInitFunctions([
initRepoRecentCommits,
initCommitStatuses,
initCommitFileHistoryFollowRename,
initCaptcha,
initUserCheckAppUrl,