mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-09 00:59:57 +02:00
Merge branch 'main' into ssh-mirror-migrations
This commit is contained in:
Generated
-5
File diff suppressed because one or more lines are too long
@@ -23,7 +23,7 @@ require (
|
||||
github.com/Necoro/html2text v0.0.0-20250804200300-7bf1ce1c7347
|
||||
github.com/ProtonMail/go-crypto v1.4.1
|
||||
github.com/PuerkitoBio/goquery v1.12.0
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.8.3
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.10.0
|
||||
github.com/alecthomas/chroma/v2 v2.25.0
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.16
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.33.14
|
||||
@@ -195,7 +195,6 @@ require (
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/flatbuffers v25.12.19+incompatible // indirect
|
||||
github.com/google/go-querystring v1.2.0 // indirect
|
||||
github.com/google/go-tpm v0.9.8 // indirect
|
||||
|
||||
@@ -71,8 +71,8 @@ github.com/RoaringBitmap/roaring/v2 v2.16.0 h1:Kys1UNf49d5W8Tq3bpuAhIr/Z8/yPB+59
|
||||
github.com/RoaringBitmap/roaring/v2 v2.16.0/go.mod h1:eq4wdNXxtJIS/oikeCzdX1rBzek7ANzbth041hrU8Q4=
|
||||
github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
|
||||
github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.8.3 h1:ikrUPushSxbpr9mmDhSgz1KyUTChjwkDrxY/jzv2OTQ=
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.8.3/go.mod h1:bnXbvnI9Mfqdj4L3Y9aCsB1A/ztuYLNRzgVKsJFgR/U=
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.10.0 h1:LvK7+C6qgz8BPnmn7xGekf8vTkcqTvyBBHaLYIMxx0g=
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.10.0/go.mod h1:I28hc9eaiqKCoOB+9Wh/P1IOScfm0xCgyWC6DAo0lmo=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
|
||||
@@ -367,8 +367,6 @@ github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomodule/redigo v1.9.3 h1:dNPSXeXv6HCq2jdyWfjgmhBdqnR6PRO3m/G05nvpPC8=
|
||||
github.com/gomodule/redigo v1.9.3/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/flatbuffers v25.12.19+incompatible h1:haMV2JRRJCe1998HeW/p0X9UaMTK6SDo0ffLn2+DbLs=
|
||||
github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
|
||||
@@ -46,10 +46,8 @@ func syncGitConfig(ctx context.Context) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
if DefaultFeatures().CheckVersionAtLeast("2.10") {
|
||||
if err := configSet(ctx, "receive.advertisePushOptions", "true"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := configSet(ctx, "receive.advertisePushOptions", "true"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if DefaultFeatures().CheckVersionAtLeast("2.18") {
|
||||
|
||||
+1
-8
@@ -22,7 +22,7 @@ import (
|
||||
"github.com/hashicorp/go-version"
|
||||
)
|
||||
|
||||
const RequiredVersion = "2.6.0" // the minimum Git version required
|
||||
const RequiredVersion = "2.13.0" // the minimum Git version required
|
||||
|
||||
type Features struct {
|
||||
gitVersion *version.Version
|
||||
@@ -173,13 +173,6 @@ func InitFull() (err error) {
|
||||
if err = InitSimple(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if setting.LFS.StartServer {
|
||||
if !DefaultFeatures().CheckVersionAtLeast("2.1.2") {
|
||||
return errors.New("LFS server support requires Git >= 2.1.2")
|
||||
}
|
||||
}
|
||||
|
||||
return syncGitConfig(context.Background())
|
||||
}
|
||||
|
||||
|
||||
@@ -16,13 +16,7 @@ import (
|
||||
|
||||
// GetRemoteAddress returns remote url of git repository in the repoPath with special remote name
|
||||
func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string, error) {
|
||||
var cmd *gitcmd.Command
|
||||
if DefaultFeatures().CheckVersionAtLeast("2.7") {
|
||||
cmd = gitcmd.NewCommand("remote", "get-url").AddDynamicArguments(remoteName)
|
||||
} else {
|
||||
cmd = gitcmd.NewCommand("config", "--get").AddDynamicArguments("remote." + remoteName + ".url")
|
||||
}
|
||||
|
||||
cmd := gitcmd.NewCommand("remote", "get-url").AddDynamicArguments(remoteName)
|
||||
result, _, err := cmd.WithDir(repoPath).RunStdString(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
+31
-49
@@ -7,7 +7,6 @@ package git
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -222,17 +221,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 +242,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 +279,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
|
||||
@@ -398,7 +408,7 @@ func (repo *Repository) commitsBefore(id ObjectID, limit int) ([]*Commit, error)
|
||||
|
||||
commits := make([]*Commit, 0, len(formattedLog))
|
||||
for _, commit := range formattedLog {
|
||||
branches, err := repo.getBranches(os.Environ(), commit.ID.String(), 2)
|
||||
branches, err := repo.getBranches(nil, commit.ID.String(), 2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -422,46 +432,17 @@ func (repo *Repository) getCommitsBeforeLimit(id ObjectID, num int) ([]*Commit,
|
||||
}
|
||||
|
||||
func (repo *Repository) getBranches(env []string, commitID string, limit int) ([]string, error) {
|
||||
if DefaultFeatures().CheckVersionAtLeast("2.7.0") {
|
||||
stdout, _, err := gitcmd.NewCommand("for-each-ref", "--format=%(refname:strip=2)").
|
||||
AddOptionFormat("--count=%d", limit).
|
||||
AddOptionValues("--contains", commitID, BranchPrefix).
|
||||
WithDir(repo.Path).
|
||||
WithEnv(env).
|
||||
RunStdString(repo.Ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
branches := strings.Fields(stdout)
|
||||
return branches, nil
|
||||
}
|
||||
|
||||
stdout, _, err := gitcmd.NewCommand("branch").
|
||||
stdout, _, err := gitcmd.NewCommand("for-each-ref", "--format=%(refname:strip=2)").
|
||||
AddOptionFormat("--count=%d", limit).
|
||||
AddOptionValues("--contains", commitID).
|
||||
WithDir(repo.Path).
|
||||
AddArguments(BranchPrefix).
|
||||
WithEnv(env).
|
||||
WithDir(repo.Path).
|
||||
RunStdString(repo.Ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refs := strings.Split(stdout, "\n")
|
||||
|
||||
var maxNum int
|
||||
if len(refs) > limit {
|
||||
maxNum = limit
|
||||
} else {
|
||||
maxNum = len(refs) - 1
|
||||
}
|
||||
|
||||
branches := make([]string, maxNum)
|
||||
for i, ref := range refs[:maxNum] {
|
||||
parts := strings.Fields(ref)
|
||||
|
||||
branches[i] = parts[len(parts)-1]
|
||||
}
|
||||
return branches, nil
|
||||
return strings.Fields(stdout), nil
|
||||
}
|
||||
|
||||
// GetCommitsFromIDs get commits from commit IDs
|
||||
@@ -504,16 +485,17 @@ func (repo *Repository) GetCommitBranchStart(env []string, branch, endCommitID s
|
||||
|
||||
parts := bytes.SplitSeq(bytes.TrimSpace(stdout), []byte{'\n'})
|
||||
|
||||
// check the commits one by one until we find a commit contained by another branch
|
||||
// check the commits one by one until we find a commit contained by another branch,
|
||||
// and we think this commit is the divergence point
|
||||
for commitID := range parts {
|
||||
branches, err := repo.getBranches(env, string(commitID), 2)
|
||||
for part := range parts {
|
||||
commitID := string(part)
|
||||
branches, err := repo.getBranches(env, commitID, 2)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, b := range branches {
|
||||
if b != branch {
|
||||
return string(commitID), nil
|
||||
return commitID, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.dev/modules/git/gitcmd"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/test"
|
||||
|
||||
@@ -36,7 +37,7 @@ func TestRepository_GetCommitBranches(t *testing.T) {
|
||||
for _, testCase := range testCases {
|
||||
commit, err := bareRepo1.GetCommit(testCase.CommitID)
|
||||
assert.NoError(t, err)
|
||||
branches, err := bareRepo1.getBranches(os.Environ(), commit.ID.String(), 2)
|
||||
branches, err := bareRepo1.getBranches(nil, commit.ID.String(), 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testCase.ExpectedBranches, branches)
|
||||
}
|
||||
@@ -140,11 +141,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)
|
||||
}
|
||||
|
||||
@@ -37,10 +37,11 @@ type Paginator struct {
|
||||
total int // total rows count, -1 means unknown
|
||||
totalPages int // total pages count, -1 means unknown
|
||||
current int // current page number
|
||||
curRows int // current page rows count
|
||||
|
||||
pagingNum int // how many rows in one page
|
||||
numPages int // how many pages to show on the UI
|
||||
|
||||
hasNext *bool // used for total=-1 ("unlimited paging")
|
||||
}
|
||||
|
||||
// New initialize a new pagination calculation and returns a Paginator as result.
|
||||
@@ -60,15 +61,13 @@ func New(total, pagingNum, current, numPages int) *Paginator {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Paginator) SetCurRows(rows int) {
|
||||
func (p *Paginator) SetUnlimitedPaging(curRows int, hasNext bool) {
|
||||
// For "unlimited paging", we need to know the rows of current page to determine if there is a next page.
|
||||
// There is still an edge case: when curRows==pagingNum, then the "next page" will be an empty page.
|
||||
// Ideally we should query one more row to determine if there is really a next page, but it's impossible in current framework.
|
||||
p.curRows = rows
|
||||
if p.total == -1 && p.current == 1 && !p.HasNext() {
|
||||
p.hasNext = &hasNext
|
||||
if p.total == -1 && p.current == 1 && !hasNext {
|
||||
// if there is only one page for the "unlimited paging", set total rows/pages count
|
||||
// then the tmpl could decide to hide the nav bar.
|
||||
p.total = rows
|
||||
p.total = curRows
|
||||
p.totalPages = util.Iif(p.total == 0, 0, 1)
|
||||
}
|
||||
}
|
||||
@@ -92,8 +91,8 @@ func (p *Paginator) Previous() int {
|
||||
|
||||
// HasNext returns true if there is a next page relative to current page.
|
||||
func (p *Paginator) HasNext() bool {
|
||||
if p.total == -1 {
|
||||
return p.curRows >= p.pagingNum
|
||||
if p.hasNext != nil {
|
||||
return *p.hasNext
|
||||
}
|
||||
return p.current*p.pagingNum < p.total
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ func (r *Reader) Close() error {
|
||||
type SeekableWriter struct {
|
||||
buf []byte
|
||||
n int
|
||||
w seekable.Writer
|
||||
w *seekable.Writer
|
||||
}
|
||||
|
||||
var _ io.WriteCloser = (*SeekableWriter)(nil)
|
||||
@@ -114,7 +114,7 @@ func (w *SeekableWriter) Close() error {
|
||||
}
|
||||
|
||||
type SeekableReader struct {
|
||||
r seekable.Reader
|
||||
r *seekable.Reader
|
||||
c func() error
|
||||
}
|
||||
|
||||
|
||||
@@ -1333,6 +1333,7 @@
|
||||
"repo.editor.fork_branch_exists": "Branch \"%s\" already exists in your fork. Please choose a new branch name.",
|
||||
"repo.commits.desc": "Browse source code change history.",
|
||||
"repo.commits.commits": "Commits",
|
||||
"repo.commits.history_enable_follow_renames": "Include renames",
|
||||
"repo.commits.no_commits": "No commits in common. \"%s\" and \"%s\" have entirely different histories.",
|
||||
"repo.commits.nothing_to_compare": "There are no differences to show.",
|
||||
"repo.commits.search.tooltip": "You can prefix keywords with \"author:\", \"committer:\", \"after:\", or \"before:\", e.g. \"revert author:Alice before:2019-01-13\".",
|
||||
|
||||
@@ -1321,6 +1321,7 @@
|
||||
"repo.editor.fork_branch_exists": "Tá brainse \"%s\" ann cheana féin i do fhorc. Roghnaigh ainm brainse nua le do thoil.",
|
||||
"repo.commits.desc": "Brabhsáil stair athraithe cód foinse.",
|
||||
"repo.commits.commits": "Tiomáintí",
|
||||
"repo.commits.history_enable_follow_renames": "Cuir athainmneacha san áireamh",
|
||||
"repo.commits.no_commits": "Níl aon ghealltanas i gcoiteann. Tá stair iomlán difriúil ag \"%s\" agus \"%s\".",
|
||||
"repo.commits.nothing_to_compare": "Níl aon difríochtaí le taispeáint.",
|
||||
"repo.commits.search.tooltip": "Is féidir eochairfhocail a réamhfhostú le “údar:”, “committer:”, “after:”, nó “before:”, e.g. \"fill an t-údar:Alice roimh: 2019-01-13\".",
|
||||
|
||||
@@ -306,6 +306,10 @@ func ListBranches(ctx *context.APIContext) {
|
||||
// in: query
|
||||
// description: page size of results
|
||||
// type: integer
|
||||
// - name: q
|
||||
// in: query
|
||||
// description: branch name substring to filter by
|
||||
// type: string
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/BranchList"
|
||||
@@ -314,6 +318,7 @@ func ListBranches(ctx *context.APIContext) {
|
||||
var apiBranches []*api.Branch
|
||||
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
keyword := ctx.FormString("q")
|
||||
|
||||
if !ctx.Repo.Repository.IsEmpty {
|
||||
if ctx.Repo.GitRepo == nil {
|
||||
@@ -325,6 +330,7 @@ func ListBranches(ctx *context.APIContext) {
|
||||
ListOptions: listOptions,
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
IsDeletedBranch: optional.Some(false),
|
||||
Keyword: keyword,
|
||||
}
|
||||
var err error
|
||||
totalNumOfBranches, err = db.Count[git_model.Branch](ctx, branchOpts)
|
||||
|
||||
@@ -262,7 +262,7 @@ func GetAllCommits(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
commits, err = ctx.Repo.GitRepo.CommitsByFileAndRange(
|
||||
commits, _, err = ctx.Repo.GitRepo.CommitsByFileAndRange(
|
||||
git.CommitsByFileAndRangeOptions{
|
||||
Revision: sha,
|
||||
File: path,
|
||||
|
||||
@@ -435,7 +435,7 @@ func ListPageRevisions(ctx *context.APIContext) {
|
||||
page := max(ctx.FormInt("page"), 1)
|
||||
|
||||
// get Commit Count
|
||||
commitsHistory, err := wikiRepo.CommitsByFileAndRange(
|
||||
commitsHistory, _, err := wikiRepo.CommitsByFileAndRange(
|
||||
git.CommitsByFileAndRangeOptions{
|
||||
Revision: ctx.Repo.Repository.DefaultWikiBranch,
|
||||
File: pageFilename,
|
||||
|
||||
@@ -20,7 +20,7 @@ func ShowFileFeed(ctx *context.Context, repo *repo.Repository, formatType string
|
||||
if len(fileName) == 0 {
|
||||
return
|
||||
}
|
||||
commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange(
|
||||
commits, _, err := ctx.Repo.GitRepo.CommitsByFileAndRange(
|
||||
git.CommitsByFileAndRangeOptions{
|
||||
Revision: ctx.Repo.RefFullName.ShortName(), // FIXME: legacy code used ShortName
|
||||
File: fileName,
|
||||
|
||||
+31
-16
@@ -214,37 +214,52 @@ func FileHistory(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
commitsCount, err := gitrepo.FileCommitsCount(ctx, ctx.Repo.Repository, ctx.Repo.RefFullName.ShortName(), ctx.Repo.TreePath)
|
||||
if err != nil {
|
||||
ctx.ServerError("FileCommitsCount", err)
|
||||
return
|
||||
} else if commitsCount == 0 {
|
||||
ctx.NotFound(nil)
|
||||
return
|
||||
}
|
||||
followRename := ctx.FormBool("follow-rename")
|
||||
ctx.Data["ShowFollowRename"] = true
|
||||
ctx.Data["FollowRenameChecked"] = followRename
|
||||
|
||||
page := max(ctx.FormInt("page"), 1)
|
||||
|
||||
commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange(
|
||||
commits, hasMore, 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)
|
||||
return
|
||||
}
|
||||
|
||||
var commitsCount int64
|
||||
if followRename {
|
||||
// there is no quick method to know the total count when "follow rename"
|
||||
commitsCount = -1
|
||||
} else {
|
||||
commitsCount, err = gitrepo.FileCommitsCount(ctx, ctx.Repo.Repository, ctx.Repo.RefFullName.ShortName(), ctx.Repo.TreePath)
|
||||
if err != nil {
|
||||
ctx.ServerError("FileCommitsCount", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(commits) == 0 {
|
||||
ctx.NotFound(nil)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["FileTreePath"] = ctx.Repo.TreePath
|
||||
ctx.Data["CommitCount"] = commitsCount
|
||||
ctx.Data["Commits"], err = processGitCommits(ctx, commits)
|
||||
if err != nil {
|
||||
ctx.ServerError("processGitCommits", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["FileTreePath"] = ctx.Repo.TreePath
|
||||
ctx.Data["CommitCount"] = commitsCount
|
||||
|
||||
pager := context.NewPagination(commitsCount, setting.Git.CommitsRangeSize, page, 5)
|
||||
if commitsCount == -1 {
|
||||
pager.WithUnlimitedPaging(len(commits), hasMore)
|
||||
}
|
||||
pager.AddParamFromRequest(ctx.Req)
|
||||
ctx.Data["Page"] = pager
|
||||
ctx.HTML(http.StatusOK, tplCommits)
|
||||
|
||||
@@ -351,7 +351,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
|
||||
page := max(ctx.FormInt("page"), 1)
|
||||
|
||||
// get Commit Count
|
||||
commitsHistory, err := wikiGitRepo.CommitsByFileAndRange(
|
||||
commitsHistory, _, err := wikiGitRepo.CommitsByFileAndRange(
|
||||
git.CommitsByFileAndRangeOptions{
|
||||
Revision: ctx.Repo.Repository.DefaultWikiBranch,
|
||||
File: pageFilename,
|
||||
|
||||
@@ -111,6 +111,7 @@ func Dashboard(ctx *context.Context) {
|
||||
|
||||
prepareHeatmapURL(ctx)
|
||||
|
||||
pageSize := setting.UI.User.RepoPagingNum
|
||||
feeds, count, err := feed_service.GetFeedsForDashboard(ctx, activities_model.GetFeedsOptions{
|
||||
RequestedUser: ctxUser,
|
||||
RequestedTeam: ctx.Org.Team,
|
||||
@@ -119,17 +120,17 @@ func Dashboard(ctx *context.Context) {
|
||||
OnlyPerformedBy: false,
|
||||
IncludeDeleted: false,
|
||||
Date: ctx.FormString("date"),
|
||||
ListOptions: db.ListOptions{
|
||||
Page: page,
|
||||
PageSize: setting.UI.FeedPagingNum,
|
||||
},
|
||||
ListOptions: db.ListOptions{Page: page, PageSize: pageSize},
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("GetFeeds", err)
|
||||
return
|
||||
}
|
||||
|
||||
pager := context.NewPagination(count, setting.UI.FeedPagingNum, page, 5).WithCurRows(len(feeds))
|
||||
// FIXME: UNLIMITE-PAGING-ONE-MORE-ROW: here is still an edge case: when curRows==pagingNum, then the "next page" will be an empty page.
|
||||
// Ideally we should query one more row to determine if there is really a next page, but it's impossible in current framework.
|
||||
pager := context.NewPagination(count, pageSize, page, 5).WithUnlimitedPaging(len(feeds), len(feeds) == pageSize)
|
||||
|
||||
pager.AddParamFromRequest(ctx.Req)
|
||||
ctx.Data["Page"] = pager
|
||||
ctx.Data["Feeds"] = feeds
|
||||
|
||||
@@ -312,7 +312,8 @@ func prepareUserProfileTabData(ctx *context.Context, profileDbRepo *repo_model.R
|
||||
|
||||
pager := context.NewPagination(total, pagingNum, page, 5)
|
||||
if tab == "activity" {
|
||||
pager.WithCurRows(curRows)
|
||||
// FIXME: UNLIMITE-PAGING-ONE-MORE-ROW: see another comment
|
||||
pager.WithUnlimitedPaging(curRows, curRows == pagingNum)
|
||||
}
|
||||
pager.AddParamFromRequest(ctx.Req)
|
||||
ctx.Data["Page"] = pager
|
||||
|
||||
@@ -33,8 +33,8 @@ func NewPagination(total int64, pagingNum, current, numPages int) *Pagination {
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Pagination) WithCurRows(n int) *Pagination {
|
||||
p.Paginater.SetCurRows(n)
|
||||
func (p *Pagination) WithUnlimitedPaging(curRows int, hasNext bool) *Pagination {
|
||||
p.Paginater.SetUnlimitedPaging(curRows, hasNext)
|
||||
return p
|
||||
}
|
||||
|
||||
|
||||
@@ -32,4 +32,24 @@ func TestPagination(t *testing.T) {
|
||||
params.Del("foo")
|
||||
v, _ = url.ParseQuery(string(p.GetParams()))
|
||||
assert.Equal(t, params, v)
|
||||
|
||||
p = NewPagination(-1, 1, 1, 1)
|
||||
p.WithUnlimitedPaging(0, false)
|
||||
assert.Zero(t, p.Paginater.TotalPages())
|
||||
assert.False(t, p.Paginater.HasNext())
|
||||
|
||||
p = NewPagination(-1, 1, 1, 1)
|
||||
p.WithUnlimitedPaging(10, false)
|
||||
assert.Equal(t, 1, p.Paginater.TotalPages()) // first page, no next, so it should know that the total page number is 1
|
||||
assert.False(t, p.Paginater.HasNext())
|
||||
|
||||
p = NewPagination(-1, 1, 2, 1)
|
||||
p.WithUnlimitedPaging(10, false)
|
||||
assert.Equal(t, -1, p.Paginater.TotalPages())
|
||||
assert.False(t, p.Paginater.HasNext())
|
||||
|
||||
p = NewPagination(-1, 1, 1, 1)
|
||||
p.WithUnlimitedPaging(10, true)
|
||||
assert.Equal(t, -1, p.Paginater.TotalPages())
|
||||
assert.True(t, p.Paginater.HasNext())
|
||||
}
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
<h4 class="ui top attached header commits-table flex-left-right">
|
||||
<div class="commits-table-left flex-text-block">
|
||||
{{if or .PageIsCommits (gt .CommitCount 0)}}
|
||||
{{.CommitCount}} {{ctx.Locale.Tr "repo.commits.commits"}}
|
||||
<div class="ui top attached header flex-left-right">
|
||||
<div class="flex-text-block">
|
||||
{{if .Commits}}
|
||||
{{if gt .CommitCount 0}}{{.CommitCount}}{{end}}{{/* CommitCount is -1 when "follow rename" */}}
|
||||
{{ctx.Locale.Tr "repo.commits.commits"}}
|
||||
{{else if .IsNothingToCompare}}
|
||||
{{ctx.Locale.Tr "repo.commits.nothing_to_compare"}}
|
||||
{{else}}
|
||||
{{ctx.Locale.Tr "repo.commits.no_commits" $.BaseBranch $.HeadBranch}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{if .IsDiffCompare}}
|
||||
<div class="commits-table-right tw-whitespace-nowrap">
|
||||
<div class="flex-text-block">
|
||||
{{if .ShowFollowRename}}
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" {{if .FollowRenameChecked}}checked{{end}} data-global-init="initCommitHistoryFollowRename">
|
||||
<label>{{ctx.Locale.Tr "repo.commits.history_enable_follow_renames"}}</label>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .IsDiffCompare}}
|
||||
<div class="flex-text-inline">
|
||||
<a href="{{$.CommitRepoLink}}/commit/{{.BeforeCommitID | PathEscape}}" class="ui green sha label tw-mx-0">{{template "repo/commits_ref_name" .CompareInfo.BaseRef}}</a>
|
||||
{{$.CompareInfo.CompareSeparator}}
|
||||
<a href="{{$.CommitRepoLink}}/commit/{{.AfterCommitID | PathEscape}}" class="ui green sha label tw-mx-0">{{template "repo/commits_ref_name" .CompareInfo.HeadRef}}</a>
|
||||
</div>
|
||||
{{end}}
|
||||
</h4>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{if .PageIsCommits}}
|
||||
<div class="ui attached segment">
|
||||
@@ -29,7 +38,7 @@
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if and .Commits (gt .CommitCount 0)}}
|
||||
{{if .Commits}}
|
||||
{{template "repo/commits_list" .}}
|
||||
{{end}}
|
||||
|
||||
|
||||
Generated
+6
@@ -7241,6 +7241,12 @@
|
||||
"description": "page size of results",
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "branch name substring to filter by",
|
||||
"name": "q",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
||||
Generated
+8
@@ -18313,6 +18313,14 @@
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "branch name substring to filter by",
|
||||
"in": "query",
|
||||
"name": "q",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
||||
@@ -128,3 +128,20 @@ func TestAPIRepoBranchesMirror(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.JSONEq(t, "{\"message\":\"Git Repository is a mirror.\",\"url\":\""+setting.AppURL+"api/swagger\"}", string(bs))
|
||||
}
|
||||
|
||||
func TestAPIRepoBranchesSearch(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
token := getUserToken(t, "user1", auth_model.AccessTokenScopeWriteRepository)
|
||||
|
||||
// "test" matches "test_branch" but not "master"
|
||||
resp := MakeRequest(t, NewRequestf(t, "GET", "/api/v1/repos/org3/repo3/branches?q=test").AddTokenAuth(token), http.StatusOK)
|
||||
branches := DecodeJSON(t, resp, []api.Branch{})
|
||||
assert.Len(t, branches, 1)
|
||||
assert.Equal(t, "test_branch", branches[0].Name)
|
||||
|
||||
// no match returns empty list
|
||||
resp = MakeRequest(t, NewRequestf(t, "GET", "/api/v1/repos/org3/repo3/branches?q=doesnotexist").AddTokenAuth(token), http.StatusOK)
|
||||
branches = DecodeJSON(t, resp, []api.Branch{})
|
||||
assert.Empty(t, branches)
|
||||
}
|
||||
|
||||
@@ -323,6 +323,10 @@ func logUnexpectedResponse(t testing.TB, recorder *httptest.ResponseRecorder) {
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeJSON decodes then response as JSON into typed variable and return it
|
||||
// HINT: don't use it on existing variable (reuse existing variable):
|
||||
// if the existing var already contains some values but the new input doesn't, then it leads to wrong test result in edge cases.
|
||||
// For slice decoding, use: v := DecodeJSON(t, resp, []T{})
|
||||
func DecodeJSON[T any](t testing.TB, resp *httptest.ResponseRecorder, v T) (ret T) {
|
||||
t.Helper()
|
||||
|
||||
|
||||
@@ -1816,14 +1816,6 @@ tbody.commit-list {
|
||||
box-shadow: 0 0.5rem 1rem var(--color-shadow) !important;
|
||||
}
|
||||
|
||||
.commits-table .commits-table-right form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75em;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.repository.view.issue .comment-list,
|
||||
.repository.view.issue .comment-list .timeline-item {
|
||||
@@ -1848,22 +1840,6 @@ tbody.commit-list {
|
||||
flex-basis: auto !important;
|
||||
margin-bottom: 0.5rem !important;
|
||||
}
|
||||
.commits-table {
|
||||
flex-direction: column;
|
||||
}
|
||||
.commits-table .commits-table-left {
|
||||
align-items: initial !important;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.commits-table .commits-table-right form > div:nth-child(1) {
|
||||
order: 1; /* the "commit search" input */
|
||||
}
|
||||
.commits-table .commits-table-right form > div:nth-child(2) {
|
||||
order: 3; /* the "search all" checkbox */
|
||||
}
|
||||
.commits-table .commits-table-right form > button:nth-child(3) {
|
||||
order: 2; /* the "search" button */
|
||||
}
|
||||
.commit-table {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
@@ -24,3 +24,13 @@ export function initCommitStatuses() {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function initCommitFileHistoryFollowRename() {
|
||||
registerGlobalInitFunc('initCommitHistoryFollowRename', (el: HTMLInputElement) => {
|
||||
el.addEventListener('change', () => {
|
||||
const url = new URL(window.location.toString());
|
||||
url.searchParams.set('follow-rename', `${el.checked}`);
|
||||
window.location.assign(url.toString());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
+2
-1
@@ -21,7 +21,7 @@ import {initMarkupContent} from './markup/content.ts';
|
||||
import {initRepoFileView} from './features/file-view.ts';
|
||||
import {initUserExternalLogins, initUserCheckAppUrl} from './features/user-auth.ts';
|
||||
import {initRepoPullRequestReview, 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';
|
||||
@@ -123,6 +123,7 @@ const initPerformanceTracer = callInitFunctions([
|
||||
initRepoCodeView,
|
||||
initBranchSelectorTabs,
|
||||
initRepoEllipsisButton,
|
||||
initCommitFileHistoryFollowRename,
|
||||
initRepoDiffCommitBranchesAndTags,
|
||||
initRepoEditor,
|
||||
initRepoGraphGit,
|
||||
|
||||
Reference in New Issue
Block a user