mirror of
https://github.com/go-gitea/gitea.git
synced 2025-10-27 05:55:21 +08:00
Merge bb6645f7a8 into bc50431e8b
This commit is contained in:
commit
0676b74b21
@ -5,17 +5,13 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
@ -130,65 +126,6 @@ func CommitChanges(ctx context.Context, repoPath string, opts CommitChangesOptio
|
||||
return err
|
||||
}
|
||||
|
||||
// AllCommitsCount returns count of all commits in repository
|
||||
func AllCommitsCount(ctx context.Context, repoPath string, hidePRRefs bool, files ...string) (int64, error) {
|
||||
cmd := gitcmd.NewCommand("rev-list")
|
||||
if hidePRRefs {
|
||||
cmd.AddArguments("--exclude=" + PullPrefix + "*")
|
||||
}
|
||||
cmd.AddArguments("--all", "--count")
|
||||
if len(files) > 0 {
|
||||
cmd.AddDashesAndList(files...)
|
||||
}
|
||||
|
||||
stdout, _, err := cmd.WithDir(repoPath).RunStdString(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return strconv.ParseInt(strings.TrimSpace(stdout), 10, 64)
|
||||
}
|
||||
|
||||
// CommitsCountOptions the options when counting commits
|
||||
type CommitsCountOptions struct {
|
||||
RepoPath string
|
||||
Not string
|
||||
Revision []string
|
||||
RelPath []string
|
||||
Since string
|
||||
Until string
|
||||
}
|
||||
|
||||
// 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")
|
||||
|
||||
cmd.AddDynamicArguments(opts.Revision...)
|
||||
|
||||
if opts.Not != "" {
|
||||
cmd.AddOptionValues("--not", opts.Not)
|
||||
}
|
||||
|
||||
if len(opts.RelPath) > 0 {
|
||||
cmd.AddDashesAndList(opts.RelPath...)
|
||||
}
|
||||
|
||||
stdout, _, err := cmd.WithDir(opts.RepoPath).RunStdString(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return strconv.ParseInt(strings.TrimSpace(stdout), 10, 64)
|
||||
}
|
||||
|
||||
// CommitsCount returns number of total commits of until current revision.
|
||||
func (c *Commit) CommitsCount() (int64, error) {
|
||||
return CommitsCount(c.repo.Ctx, CommitsCountOptions{
|
||||
RepoPath: c.repo.Path,
|
||||
Revision: []string{c.ID.String()},
|
||||
})
|
||||
}
|
||||
|
||||
// CommitsByRange returns the specific page commits before current revision, every page's number default by CommitsRangeSize
|
||||
func (c *Commit) CommitsByRange(page, pageSize int, not, since, until string) ([]*Commit, error) {
|
||||
return c.repo.commitsByRangeWithTime(c.ID, page, pageSize, not, since, until)
|
||||
@ -371,85 +308,6 @@ func (c *Commit) GetBranchName() (string, error) {
|
||||
return strings.SplitN(strings.TrimSpace(data), "~", 2)[0], nil
|
||||
}
|
||||
|
||||
// CommitFileStatus represents status of files in a commit.
|
||||
type CommitFileStatus struct {
|
||||
Added []string
|
||||
Removed []string
|
||||
Modified []string
|
||||
}
|
||||
|
||||
// NewCommitFileStatus creates a CommitFileStatus
|
||||
func NewCommitFileStatus() *CommitFileStatus {
|
||||
return &CommitFileStatus{
|
||||
[]string{}, []string{}, []string{},
|
||||
}
|
||||
}
|
||||
|
||||
func parseCommitFileStatus(fileStatus *CommitFileStatus, stdout io.Reader) {
|
||||
rd := bufio.NewReader(stdout)
|
||||
peek, err := rd.Peek(1)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if peek[0] == '\n' || peek[0] == '\x00' {
|
||||
_, _ = rd.Discard(1)
|
||||
}
|
||||
for {
|
||||
modifier, err := rd.ReadString('\x00')
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
file, err := rd.ReadString('\x00')
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
file = file[:len(file)-1]
|
||||
switch modifier[0] {
|
||||
case 'A':
|
||||
fileStatus.Added = append(fileStatus.Added, file)
|
||||
case 'D':
|
||||
fileStatus.Removed = append(fileStatus.Removed, file)
|
||||
case 'M':
|
||||
fileStatus.Modified = append(fileStatus.Modified, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetCommitFileStatus returns file status of commit in given repository.
|
||||
func GetCommitFileStatus(ctx context.Context, repoPath, commitID string) (*CommitFileStatus, error) {
|
||||
stdout, w := io.Pipe()
|
||||
done := make(chan struct{})
|
||||
fileStatus := NewCommitFileStatus()
|
||||
go func() {
|
||||
parseCommitFileStatus(fileStatus, stdout)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
stderr := new(bytes.Buffer)
|
||||
err := gitcmd.NewCommand("log", "--name-status", "-m", "--pretty=format:", "--first-parent", "--no-renames", "-z", "-1").
|
||||
AddDynamicArguments(commitID).
|
||||
WithDir(repoPath).
|
||||
WithStdout(w).
|
||||
WithStderr(stderr).
|
||||
Run(ctx)
|
||||
w.Close() // Close writer to exit parsing goroutine
|
||||
if err != nil {
|
||||
return nil, gitcmd.ConcatenateError(err, stderr.String())
|
||||
}
|
||||
|
||||
<-done
|
||||
return fileStatus, nil
|
||||
}
|
||||
|
||||
// GetFullCommitID returns full length (40) of commit ID by given short SHA in a repository.
|
||||
func GetFullCommitID(ctx context.Context, repoPath, shortID string) (string, error) {
|
||||
commitID, _, err := gitcmd.NewCommand("rev-parse").
|
||||
|
||||
@ -14,33 +14,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCommitsCountSha256(t *testing.T) {
|
||||
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare_sha256")
|
||||
|
||||
commitsCount, err := CommitsCount(t.Context(),
|
||||
CommitsCountOptions{
|
||||
RepoPath: bareRepo1Path,
|
||||
Revision: []string{"f004f41359117d319dedd0eaab8c5259ee2263da839dcba33637997458627fdc"},
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(3), commitsCount)
|
||||
}
|
||||
|
||||
func TestCommitsCountWithoutBaseSha256(t *testing.T) {
|
||||
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare_sha256")
|
||||
|
||||
commitsCount, err := CommitsCount(t.Context(),
|
||||
CommitsCountOptions{
|
||||
RepoPath: bareRepo1Path,
|
||||
Not: "main",
|
||||
Revision: []string{"branch1"},
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(2), commitsCount)
|
||||
}
|
||||
|
||||
func TestGetFullCommitIDSha256(t *testing.T) {
|
||||
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare_sha256")
|
||||
|
||||
@ -157,39 +130,3 @@ func TestHasPreviousCommitSha256(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, selfNot)
|
||||
}
|
||||
|
||||
func TestGetCommitFileStatusMergesSha256(t *testing.T) {
|
||||
bareRepo1Path := filepath.Join(testReposDir, "repo6_merge_sha256")
|
||||
|
||||
commitFileStatus, err := GetCommitFileStatus(t.Context(), bareRepo1Path, "d2e5609f630dd8db500f5298d05d16def282412e3e66ed68cc7d0833b29129a1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
expected := CommitFileStatus{
|
||||
[]string{
|
||||
"add_file.txt",
|
||||
},
|
||||
[]string{},
|
||||
[]string{
|
||||
"to_modify.txt",
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, expected.Added, commitFileStatus.Added)
|
||||
assert.Equal(t, expected.Removed, commitFileStatus.Removed)
|
||||
assert.Equal(t, expected.Modified, commitFileStatus.Modified)
|
||||
|
||||
expected = CommitFileStatus{
|
||||
[]string{},
|
||||
[]string{
|
||||
"to_remove.txt",
|
||||
},
|
||||
[]string{},
|
||||
}
|
||||
|
||||
commitFileStatus, err = GetCommitFileStatus(t.Context(), bareRepo1Path, "da1ded40dc8e5b7c564171f4bf2fc8370487decfb1cb6a99ef28f3ed73d09172")
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, expected.Added, commitFileStatus.Added)
|
||||
assert.Equal(t, expected.Removed, commitFileStatus.Removed)
|
||||
assert.Equal(t, expected.Modified, commitFileStatus.Modified)
|
||||
}
|
||||
|
||||
@ -13,33 +13,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCommitsCount(t *testing.T) {
|
||||
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
|
||||
|
||||
commitsCount, err := CommitsCount(t.Context(),
|
||||
CommitsCountOptions{
|
||||
RepoPath: bareRepo1Path,
|
||||
Revision: []string{"8006ff9adbf0cb94da7dad9e537e53817f9fa5c0"},
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(3), commitsCount)
|
||||
}
|
||||
|
||||
func TestCommitsCountWithoutBase(t *testing.T) {
|
||||
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
|
||||
|
||||
commitsCount, err := CommitsCount(t.Context(),
|
||||
CommitsCountOptions{
|
||||
RepoPath: bareRepo1Path,
|
||||
Not: "master",
|
||||
Revision: []string{"branch1"},
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(2), commitsCount)
|
||||
}
|
||||
|
||||
func TestGetFullCommitID(t *testing.T) {
|
||||
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
|
||||
|
||||
@ -212,134 +185,6 @@ func TestHasPreviousCommit(t *testing.T) {
|
||||
assert.False(t, selfNot)
|
||||
}
|
||||
|
||||
func TestParseCommitFileStatus(t *testing.T) {
|
||||
type testcase struct {
|
||||
output string
|
||||
added []string
|
||||
removed []string
|
||||
modified []string
|
||||
}
|
||||
|
||||
kases := []testcase{
|
||||
{
|
||||
// Merge commit
|
||||
output: "MM\x00options/locale/locale_en-US.ini\x00",
|
||||
modified: []string{
|
||||
"options/locale/locale_en-US.ini",
|
||||
},
|
||||
added: []string{},
|
||||
removed: []string{},
|
||||
},
|
||||
{
|
||||
// Spaces commit
|
||||
output: "D\x00b\x00D\x00b b/b\x00A\x00b b/b b/b b/b\x00A\x00b b/b b/b b/b b/b\x00",
|
||||
removed: []string{
|
||||
"b",
|
||||
"b b/b",
|
||||
},
|
||||
modified: []string{},
|
||||
added: []string{
|
||||
"b b/b b/b b/b",
|
||||
"b b/b b/b b/b b/b",
|
||||
},
|
||||
},
|
||||
{
|
||||
// larger commit
|
||||
output: "M\x00go.mod\x00M\x00go.sum\x00M\x00modules/ssh/ssh.go\x00M\x00vendor/github.com/gliderlabs/ssh/circle.yml\x00M\x00vendor/github.com/gliderlabs/ssh/context.go\x00A\x00vendor/github.com/gliderlabs/ssh/go.mod\x00A\x00vendor/github.com/gliderlabs/ssh/go.sum\x00M\x00vendor/github.com/gliderlabs/ssh/server.go\x00M\x00vendor/github.com/gliderlabs/ssh/session.go\x00M\x00vendor/github.com/gliderlabs/ssh/ssh.go\x00M\x00vendor/golang.org/x/sys/unix/mkerrors.sh\x00M\x00vendor/golang.org/x/sys/unix/syscall_darwin.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_linux.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go\x00M\x00vendor/modules.txt\x00",
|
||||
modified: []string{
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"modules/ssh/ssh.go",
|
||||
"vendor/github.com/gliderlabs/ssh/circle.yml",
|
||||
"vendor/github.com/gliderlabs/ssh/context.go",
|
||||
"vendor/github.com/gliderlabs/ssh/server.go",
|
||||
"vendor/github.com/gliderlabs/ssh/session.go",
|
||||
"vendor/github.com/gliderlabs/ssh/ssh.go",
|
||||
"vendor/golang.org/x/sys/unix/mkerrors.sh",
|
||||
"vendor/golang.org/x/sys/unix/syscall_darwin.go",
|
||||
"vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go",
|
||||
"vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go",
|
||||
"vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go",
|
||||
"vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go",
|
||||
"vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go",
|
||||
"vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go",
|
||||
"vendor/golang.org/x/sys/unix/zerrors_linux.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go",
|
||||
"vendor/modules.txt",
|
||||
},
|
||||
added: []string{
|
||||
"vendor/github.com/gliderlabs/ssh/go.mod",
|
||||
"vendor/github.com/gliderlabs/ssh/go.sum",
|
||||
},
|
||||
removed: []string{},
|
||||
},
|
||||
{
|
||||
// git 1.7.2 adds an unnecessary \x00 on merge commit
|
||||
output: "\x00MM\x00options/locale/locale_en-US.ini\x00",
|
||||
modified: []string{
|
||||
"options/locale/locale_en-US.ini",
|
||||
},
|
||||
added: []string{},
|
||||
removed: []string{},
|
||||
},
|
||||
{
|
||||
// git 1.7.2 adds an unnecessary \n on normal commit
|
||||
output: "\nD\x00b\x00D\x00b b/b\x00A\x00b b/b b/b b/b\x00A\x00b b/b b/b b/b b/b\x00",
|
||||
removed: []string{
|
||||
"b",
|
||||
"b b/b",
|
||||
},
|
||||
modified: []string{},
|
||||
added: []string{
|
||||
"b b/b b/b b/b",
|
||||
"b b/b b/b b/b b/b",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, kase := range kases {
|
||||
fileStatus := NewCommitFileStatus()
|
||||
parseCommitFileStatus(fileStatus, strings.NewReader(kase.output))
|
||||
|
||||
assert.Equal(t, kase.added, fileStatus.Added)
|
||||
assert.Equal(t, kase.removed, fileStatus.Removed)
|
||||
assert.Equal(t, kase.modified, fileStatus.Modified)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCommitFileStatusMerges(t *testing.T) {
|
||||
bareRepo1Path := filepath.Join(testReposDir, "repo6_merge")
|
||||
|
||||
commitFileStatus, err := GetCommitFileStatus(t.Context(), bareRepo1Path, "022f4ce6214973e018f02bf363bf8a2e3691f699")
|
||||
assert.NoError(t, err)
|
||||
|
||||
expected := CommitFileStatus{
|
||||
[]string{
|
||||
"add_file.txt",
|
||||
},
|
||||
[]string{
|
||||
"to_remove.txt",
|
||||
},
|
||||
[]string{
|
||||
"to_modify.txt",
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, expected.Added, commitFileStatus.Added)
|
||||
assert.Equal(t, expected.Removed, commitFileStatus.Removed)
|
||||
assert.Equal(t, expected.Modified, commitFileStatus.Modified)
|
||||
}
|
||||
|
||||
func Test_GetCommitBranchStart(t *testing.T) {
|
||||
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
|
||||
repo, err := OpenRepository(t.Context(), bareRepo1Path)
|
||||
|
||||
@ -32,11 +32,6 @@ type GPGSettings struct {
|
||||
|
||||
const prettyLogFormat = `--pretty=format:%H`
|
||||
|
||||
// GetAllCommitsCount returns count of all commits in repository
|
||||
func (repo *Repository) GetAllCommitsCount() (int64, error) {
|
||||
return AllCommitsCount(repo.Ctx, repo.Path, false)
|
||||
}
|
||||
|
||||
func (repo *Repository) ShowPrettyFormatLogToList(ctx context.Context, revisionRange string) ([]*Commit, error) {
|
||||
// avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git <command> [<revision>...] -- [<file>...]'
|
||||
logs, _, err := gitcmd.NewCommand("log").AddArguments(prettyLogFormat).
|
||||
|
||||
@ -11,7 +11,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
@ -216,16 +215,6 @@ func (repo *Repository) FileChangedBetweenCommits(filename, id1, id2 string) (bo
|
||||
return len(strings.TrimSpace(string(stdout))) > 0, nil
|
||||
}
|
||||
|
||||
// FileCommitsCount return the number of files at a revision
|
||||
func (repo *Repository) FileCommitsCount(revision, file string) (int64, error) {
|
||||
return CommitsCount(repo.Ctx,
|
||||
CommitsCountOptions{
|
||||
RepoPath: repo.Path,
|
||||
Revision: []string{revision},
|
||||
RelPath: []string{file},
|
||||
})
|
||||
}
|
||||
|
||||
type CommitsByFileAndRangeOptions struct {
|
||||
Revision string
|
||||
File string
|
||||
@ -433,25 +422,6 @@ func (repo *Repository) CommitsBetweenIDs(last, before string) ([]*Commit, error
|
||||
return repo.CommitsBetween(lastCommit, beforeCommit)
|
||||
}
|
||||
|
||||
// CommitsCountBetween return numbers of commits between two commits
|
||||
func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) {
|
||||
count, err := CommitsCount(repo.Ctx, CommitsCountOptions{
|
||||
RepoPath: repo.Path,
|
||||
Revision: []string{start + ".." + end},
|
||||
})
|
||||
|
||||
if err != nil && strings.Contains(err.Error(), "no merge base") {
|
||||
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
|
||||
// previously it would return the results of git rev-list before last so let's try that...
|
||||
return CommitsCount(repo.Ctx, CommitsCountOptions{
|
||||
RepoPath: repo.Path,
|
||||
Revision: []string{start, end},
|
||||
})
|
||||
}
|
||||
|
||||
return count, err
|
||||
}
|
||||
|
||||
// commitsBefore the limit is depth, not total number of returned commits.
|
||||
func (repo *Repository) commitsBefore(id ObjectID, limit int) ([]*Commit, error) {
|
||||
cmd := gitcmd.NewCommand("log", prettyLogFormat)
|
||||
@ -564,23 +534,6 @@ func (repo *Repository) IsCommitInBranch(commitID, branch string) (r bool, err e
|
||||
return len(stdout) > 0, err
|
||||
}
|
||||
|
||||
func (repo *Repository) AddLastCommitCache(cacheKey, fullName, sha string) error {
|
||||
if repo.LastCommitCache == nil {
|
||||
commitsCount, err := cache.GetInt64(cacheKey, func() (int64, error) {
|
||||
commit, err := repo.GetCommit(sha)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return commit.CommitsCount()
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repo.LastCommitCache = NewLastCommitCache(commitsCount, fullName, repo, cache.GetCache())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCommitBranchStart returns the commit where the branch diverged
|
||||
func (repo *Repository) GetCommitBranchStart(env []string, branch, endCommitID string) (string, error) {
|
||||
cmd := gitcmd.NewCommand("log", prettyLogFormat)
|
||||
|
||||
14
modules/gitrepo/cat_file.go
Normal file
14
modules/gitrepo/cat_file.go
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package gitrepo
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
)
|
||||
|
||||
func NewBatch(ctx context.Context, repo Repository) (*git.Batch, error) {
|
||||
return git.NewBatch(ctx, repoPath(repo))
|
||||
}
|
||||
96
modules/gitrepo/commit.go
Normal file
96
modules/gitrepo/commit.go
Normal file
@ -0,0 +1,96 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package gitrepo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
)
|
||||
|
||||
// CommitsCountOptions the options when counting commits
|
||||
type CommitsCountOptions struct {
|
||||
Not string
|
||||
Revision []string
|
||||
RelPath []string
|
||||
Since string
|
||||
Until string
|
||||
}
|
||||
|
||||
// CommitsCount returns number of total commits of until given revision.
|
||||
func CommitsCount(ctx context.Context, repo Repository, opts CommitsCountOptions) (int64, error) {
|
||||
cmd := gitcmd.NewCommand("rev-list", "--count")
|
||||
|
||||
cmd.AddDynamicArguments(opts.Revision...)
|
||||
|
||||
if opts.Not != "" {
|
||||
cmd.AddOptionValues("--not", opts.Not)
|
||||
}
|
||||
|
||||
if len(opts.RelPath) > 0 {
|
||||
cmd.AddDashesAndList(opts.RelPath...)
|
||||
}
|
||||
|
||||
stdout, _, err := cmd.WithDir(repoPath(repo)).RunStdString(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return strconv.ParseInt(strings.TrimSpace(stdout), 10, 64)
|
||||
}
|
||||
|
||||
// CommitsCountBetween return numbers of commits between two commits
|
||||
func CommitsCountBetween(ctx context.Context, repo Repository, start, end string) (int64, error) {
|
||||
count, err := CommitsCount(ctx, repo, CommitsCountOptions{
|
||||
Revision: []string{start + ".." + end},
|
||||
})
|
||||
|
||||
if err != nil && strings.Contains(err.Error(), "no merge base") {
|
||||
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
|
||||
// previously it would return the results of git rev-list before last so let's try that...
|
||||
return CommitsCount(ctx, repo, CommitsCountOptions{
|
||||
Revision: []string{start, end},
|
||||
})
|
||||
}
|
||||
|
||||
return count, err
|
||||
}
|
||||
|
||||
// FileCommitsCount return the number of files at a revision
|
||||
func FileCommitsCount(ctx context.Context, repo Repository, revision, file string) (int64, error) {
|
||||
return CommitsCount(ctx, repo,
|
||||
CommitsCountOptions{
|
||||
Revision: []string{revision},
|
||||
RelPath: []string{file},
|
||||
})
|
||||
}
|
||||
|
||||
// CommitsCountOfCommit returns number of total commits of until current revision.
|
||||
func CommitsCountOfCommit(ctx context.Context, repo Repository, commitID string) (int64, error) {
|
||||
return CommitsCount(ctx, repo, CommitsCountOptions{
|
||||
Revision: []string{commitID},
|
||||
})
|
||||
}
|
||||
|
||||
// AllCommitsCount returns count of all commits in repository
|
||||
func AllCommitsCount(ctx context.Context, repo Repository, hidePRRefs bool, files ...string) (int64, error) {
|
||||
cmd := gitcmd.NewCommand("rev-list")
|
||||
if hidePRRefs {
|
||||
cmd.AddArguments("--exclude=" + git.PullPrefix + "*")
|
||||
}
|
||||
cmd.AddArguments("--all", "--count")
|
||||
if len(files) > 0 {
|
||||
cmd.AddDashesAndList(files...)
|
||||
}
|
||||
|
||||
stdout, err := RunCmdString(ctx, repo, cmd)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return strconv.ParseInt(strings.TrimSpace(stdout), 10, 64)
|
||||
}
|
||||
93
modules/gitrepo/commit_file.go
Normal file
93
modules/gitrepo/commit_file.go
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package gitrepo
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
// CommitFileStatus represents status of files in a commit.
|
||||
type CommitFileStatus struct {
|
||||
Added []string
|
||||
Removed []string
|
||||
Modified []string
|
||||
}
|
||||
|
||||
// NewCommitFileStatus creates a CommitFileStatus
|
||||
func NewCommitFileStatus() *CommitFileStatus {
|
||||
return &CommitFileStatus{
|
||||
[]string{}, []string{}, []string{},
|
||||
}
|
||||
}
|
||||
|
||||
func parseCommitFileStatus(fileStatus *CommitFileStatus, stdout io.Reader) {
|
||||
rd := bufio.NewReader(stdout)
|
||||
peek, err := rd.Peek(1)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if peek[0] == '\n' || peek[0] == '\x00' {
|
||||
_, _ = rd.Discard(1)
|
||||
}
|
||||
for {
|
||||
modifier, err := rd.ReadString('\x00')
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
file, err := rd.ReadString('\x00')
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
file = file[:len(file)-1]
|
||||
switch modifier[0] {
|
||||
case 'A':
|
||||
fileStatus.Added = append(fileStatus.Added, file)
|
||||
case 'D':
|
||||
fileStatus.Removed = append(fileStatus.Removed, file)
|
||||
case 'M':
|
||||
fileStatus.Modified = append(fileStatus.Modified, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetCommitFileStatus returns file status of commit in given repository.
|
||||
func GetCommitFileStatus(ctx context.Context, repo Repository, commitID string) (*CommitFileStatus, error) {
|
||||
stdout, w := io.Pipe()
|
||||
done := make(chan struct{})
|
||||
fileStatus := NewCommitFileStatus()
|
||||
go func() {
|
||||
parseCommitFileStatus(fileStatus, stdout)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
stderr := new(bytes.Buffer)
|
||||
err := gitcmd.NewCommand("log", "--name-status", "-m", "--pretty=format:", "--first-parent", "--no-renames", "-z", "-1").
|
||||
AddDynamicArguments(commitID).
|
||||
WithDir(repoPath(repo)).
|
||||
WithStdout(w).
|
||||
WithStderr(stderr).
|
||||
Run(ctx)
|
||||
w.Close() // Close writer to exit parsing goroutine
|
||||
if err != nil {
|
||||
return nil, gitcmd.ConcatenateError(err, stderr.String())
|
||||
}
|
||||
|
||||
<-done
|
||||
return fileStatus, nil
|
||||
}
|
||||
175
modules/gitrepo/commit_file_test.go
Normal file
175
modules/gitrepo/commit_file_test.go
Normal file
@ -0,0 +1,175 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package gitrepo
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseCommitFileStatus(t *testing.T) {
|
||||
type testcase struct {
|
||||
output string
|
||||
added []string
|
||||
removed []string
|
||||
modified []string
|
||||
}
|
||||
|
||||
kases := []testcase{
|
||||
{
|
||||
// Merge commit
|
||||
output: "MM\x00options/locale/locale_en-US.ini\x00",
|
||||
modified: []string{
|
||||
"options/locale/locale_en-US.ini",
|
||||
},
|
||||
added: []string{},
|
||||
removed: []string{},
|
||||
},
|
||||
{
|
||||
// Spaces commit
|
||||
output: "D\x00b\x00D\x00b b/b\x00A\x00b b/b b/b b/b\x00A\x00b b/b b/b b/b b/b\x00",
|
||||
removed: []string{
|
||||
"b",
|
||||
"b b/b",
|
||||
},
|
||||
modified: []string{},
|
||||
added: []string{
|
||||
"b b/b b/b b/b",
|
||||
"b b/b b/b b/b b/b",
|
||||
},
|
||||
},
|
||||
{
|
||||
// larger commit
|
||||
output: "M\x00go.mod\x00M\x00go.sum\x00M\x00modules/ssh/ssh.go\x00M\x00vendor/github.com/gliderlabs/ssh/circle.yml\x00M\x00vendor/github.com/gliderlabs/ssh/context.go\x00A\x00vendor/github.com/gliderlabs/ssh/go.mod\x00A\x00vendor/github.com/gliderlabs/ssh/go.sum\x00M\x00vendor/github.com/gliderlabs/ssh/server.go\x00M\x00vendor/github.com/gliderlabs/ssh/session.go\x00M\x00vendor/github.com/gliderlabs/ssh/ssh.go\x00M\x00vendor/golang.org/x/sys/unix/mkerrors.sh\x00M\x00vendor/golang.org/x/sys/unix/syscall_darwin.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go\x00M\x00vendor/golang.org/x/sys/unix/zerrors_linux.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go\x00M\x00vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go\x00M\x00vendor/modules.txt\x00",
|
||||
modified: []string{
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"modules/ssh/ssh.go",
|
||||
"vendor/github.com/gliderlabs/ssh/circle.yml",
|
||||
"vendor/github.com/gliderlabs/ssh/context.go",
|
||||
"vendor/github.com/gliderlabs/ssh/server.go",
|
||||
"vendor/github.com/gliderlabs/ssh/session.go",
|
||||
"vendor/github.com/gliderlabs/ssh/ssh.go",
|
||||
"vendor/golang.org/x/sys/unix/mkerrors.sh",
|
||||
"vendor/golang.org/x/sys/unix/syscall_darwin.go",
|
||||
"vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go",
|
||||
"vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go",
|
||||
"vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go",
|
||||
"vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go",
|
||||
"vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go",
|
||||
"vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go",
|
||||
"vendor/golang.org/x/sys/unix/zerrors_linux.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go",
|
||||
"vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go",
|
||||
"vendor/modules.txt",
|
||||
},
|
||||
added: []string{
|
||||
"vendor/github.com/gliderlabs/ssh/go.mod",
|
||||
"vendor/github.com/gliderlabs/ssh/go.sum",
|
||||
},
|
||||
removed: []string{},
|
||||
},
|
||||
{
|
||||
// git 1.7.2 adds an unnecessary \x00 on merge commit
|
||||
output: "\x00MM\x00options/locale/locale_en-US.ini\x00",
|
||||
modified: []string{
|
||||
"options/locale/locale_en-US.ini",
|
||||
},
|
||||
added: []string{},
|
||||
removed: []string{},
|
||||
},
|
||||
{
|
||||
// git 1.7.2 adds an unnecessary \n on normal commit
|
||||
output: "\nD\x00b\x00D\x00b b/b\x00A\x00b b/b b/b b/b\x00A\x00b b/b b/b b/b b/b\x00",
|
||||
removed: []string{
|
||||
"b",
|
||||
"b b/b",
|
||||
},
|
||||
modified: []string{},
|
||||
added: []string{
|
||||
"b b/b b/b b/b",
|
||||
"b b/b b/b b/b b/b",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, kase := range kases {
|
||||
fileStatus := NewCommitFileStatus()
|
||||
parseCommitFileStatus(fileStatus, strings.NewReader(kase.output))
|
||||
|
||||
assert.Equal(t, kase.added, fileStatus.Added)
|
||||
assert.Equal(t, kase.removed, fileStatus.Removed)
|
||||
assert.Equal(t, kase.modified, fileStatus.Modified)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCommitFileStatusMerges(t *testing.T) {
|
||||
bareRepo6 := &mockRepository{path: "repo6_merge"}
|
||||
|
||||
commitFileStatus, err := GetCommitFileStatus(t.Context(), bareRepo6, "022f4ce6214973e018f02bf363bf8a2e3691f699")
|
||||
assert.NoError(t, err)
|
||||
|
||||
expected := CommitFileStatus{
|
||||
[]string{
|
||||
"add_file.txt",
|
||||
},
|
||||
[]string{
|
||||
"to_remove.txt",
|
||||
},
|
||||
[]string{
|
||||
"to_modify.txt",
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, expected.Added, commitFileStatus.Added)
|
||||
assert.Equal(t, expected.Removed, commitFileStatus.Removed)
|
||||
assert.Equal(t, expected.Modified, commitFileStatus.Modified)
|
||||
}
|
||||
|
||||
func TestGetCommitFileStatusMergesSha256(t *testing.T) {
|
||||
bareRepo6Sha256 := &mockRepository{path: "repo6_merge_sha256"}
|
||||
|
||||
commitFileStatus, err := GetCommitFileStatus(t.Context(), bareRepo6Sha256, "d2e5609f630dd8db500f5298d05d16def282412e3e66ed68cc7d0833b29129a1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
expected := CommitFileStatus{
|
||||
[]string{
|
||||
"add_file.txt",
|
||||
},
|
||||
[]string{},
|
||||
[]string{
|
||||
"to_modify.txt",
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, expected.Added, commitFileStatus.Added)
|
||||
assert.Equal(t, expected.Removed, commitFileStatus.Removed)
|
||||
assert.Equal(t, expected.Modified, commitFileStatus.Modified)
|
||||
|
||||
expected = CommitFileStatus{
|
||||
[]string{},
|
||||
[]string{
|
||||
"to_remove.txt",
|
||||
},
|
||||
[]string{},
|
||||
}
|
||||
|
||||
commitFileStatus, err = GetCommitFileStatus(t.Context(), bareRepo6Sha256, "da1ded40dc8e5b7c564171f4bf2fc8370487decfb1cb6a99ef28f3ed73d09172")
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, expected.Added, commitFileStatus.Added)
|
||||
assert.Equal(t, expected.Removed, commitFileStatus.Removed)
|
||||
assert.Equal(t, expected.Modified, commitFileStatus.Modified)
|
||||
}
|
||||
35
modules/gitrepo/commit_test.go
Normal file
35
modules/gitrepo/commit_test.go
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package gitrepo
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCommitsCount(t *testing.T) {
|
||||
bareRepo1 := &mockRepository{path: "repo1_bare"}
|
||||
|
||||
commitsCount, err := CommitsCount(t.Context(), bareRepo1,
|
||||
CommitsCountOptions{
|
||||
Revision: []string{"8006ff9adbf0cb94da7dad9e537e53817f9fa5c0"},
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(3), commitsCount)
|
||||
}
|
||||
|
||||
func TestCommitsCountWithoutBase(t *testing.T) {
|
||||
bareRepo1 := &mockRepository{path: "repo1_bare"}
|
||||
|
||||
commitsCount, err := CommitsCount(t.Context(), bareRepo1,
|
||||
CommitsCountOptions{
|
||||
Not: "master",
|
||||
Revision: []string{"branch1"},
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(2), commitsCount)
|
||||
}
|
||||
@ -218,7 +218,7 @@ func (b *Indexer) addDelete(filename string, repo *repo_model.Repository, batch
|
||||
func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha string, changes *internal.RepoChanges) error {
|
||||
batch := inner_bleve.NewFlushingBatch(b.inner.Indexer, maxBatchSize)
|
||||
if len(changes.Updates) > 0 {
|
||||
gitBatch, err := git.NewBatch(ctx, repo.RepoPath())
|
||||
gitBatch, err := gitrepo.NewBatch(ctx, repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -210,7 +210,7 @@ func (b *Indexer) addDelete(filename string, repo *repo_model.Repository) elasti
|
||||
func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha string, changes *internal.RepoChanges) error {
|
||||
reqs := make([]elastic.BulkableRequest, 0)
|
||||
if len(changes.Updates) > 0 {
|
||||
batch, err := git.NewBatch(ctx, repo.RepoPath())
|
||||
batch, err := gitrepo.NewBatch(ctx, repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/cachegroup"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
@ -72,7 +73,7 @@ func ToAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.U
|
||||
committerUsername = committer.Name
|
||||
}
|
||||
|
||||
fileStatus, err := git.GetCommitFileStatus(ctx, repo.RepoPath(), commit.Sha1)
|
||||
fileStatus, err := gitrepo.GetCommitFileStatus(ctx, repo, commit.Sha1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("FileStatus [commit_sha1: %s]: %w", commit.Sha1, err)
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||
@ -222,8 +223,7 @@ func GetAllCommits(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
// Total commit count
|
||||
commitsCountTotal, err = git.CommitsCount(ctx.Repo.GitRepo.Ctx, git.CommitsCountOptions{
|
||||
RepoPath: ctx.Repo.GitRepo.Path,
|
||||
commitsCountTotal, err = gitrepo.CommitsCount(ctx, ctx.Repo.Repository, gitrepo.CommitsCountOptions{
|
||||
Not: not,
|
||||
Revision: []string{baseCommit.ID.String()},
|
||||
Since: since,
|
||||
@ -245,9 +245,8 @@ func GetAllCommits(ctx *context.APIContext) {
|
||||
sha = ctx.Repo.Repository.DefaultBranch
|
||||
}
|
||||
|
||||
commitsCountTotal, err = git.CommitsCount(ctx,
|
||||
git.CommitsCountOptions{
|
||||
RepoPath: ctx.Repo.GitRepo.Path,
|
||||
commitsCountTotal, err = gitrepo.CommitsCount(ctx, ctx.Repo.Repository,
|
||||
gitrepo.CommitsCountOptions{
|
||||
Not: not,
|
||||
Revision: []string{sha},
|
||||
RelPath: []string{path},
|
||||
|
||||
@ -1165,7 +1165,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
|
||||
baseRef := ctx.Repo.GitRepo.UnstableGuessRefByShortName(baseRefToGuess)
|
||||
headRef := headGitRepo.UnstableGuessRefByShortName(headRefToGuess)
|
||||
|
||||
log.Trace("Repo path: %q, base ref: %q->%q, head ref: %q->%q", ctx.Repo.GitRepo.Path, baseRefToGuess, baseRef, headRefToGuess, headRef)
|
||||
log.Trace("Repo path: %q, base ref: %q->%q, head ref: %q->%q", ctx.Repo.Repository.RelativePath(), baseRefToGuess, baseRef, headRefToGuess, headRef)
|
||||
|
||||
baseRefValid := baseRef.IsBranch() || baseRef.IsTag() || git.IsStringLikelyCommitID(git.ObjectFormatFromName(ctx.Repo.Repository.ObjectFormatName), baseRef.ShortName())
|
||||
headRefValid := headRef.IsBranch() || headRef.IsTag() || git.IsStringLikelyCommitID(git.ObjectFormatFromName(headRepo.ObjectFormatName), headRef.ShortName())
|
||||
|
||||
@ -193,7 +193,7 @@ func getWikiPage(ctx *context.APIContext, wikiName wiki_service.WebPath) *api.Wi
|
||||
}
|
||||
|
||||
// get commit count - wiki revisions
|
||||
commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
|
||||
commitsCount, _ := gitrepo.FileCommitsCount(ctx, ctx.Repo.Repository.WikiStorageRepo(), ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
|
||||
|
||||
// Get last change information.
|
||||
lastCommit, err := wikiRepo.GetCommitByPath(pageFilename)
|
||||
@ -429,7 +429,7 @@ func ListPageRevisions(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
// get commit count - wiki revisions
|
||||
commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
|
||||
commitsCount, _ := gitrepo.FileCommitsCount(ctx, ctx.Repo.Repository.WikiStorageRepo(), ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
|
||||
|
||||
page := max(ctx.FormInt("page"), 1)
|
||||
|
||||
|
||||
@ -222,7 +222,7 @@ func FileHistory(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(ctx.Repo.RefFullName.ShortName(), ctx.Repo.TreePath)
|
||||
commitsCount, err := gitrepo.FileCommitsCount(ctx, ctx.Repo.Repository, ctx.Repo.RefFullName.ShortName(), ctx.Repo.TreePath)
|
||||
if err != nil {
|
||||
ctx.ServerError("FileCommitsCount", err)
|
||||
return
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
stdCtx "context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@ -39,7 +40,7 @@ const (
|
||||
)
|
||||
|
||||
// calReleaseNumCommitsBehind calculates given release has how many commits behind release target.
|
||||
func calReleaseNumCommitsBehind(repoCtx *context.Repository, release *repo_model.Release, countCache map[string]int64) error {
|
||||
func calReleaseNumCommitsBehind(ctx stdCtx.Context, repoCtx *context.Repository, release *repo_model.Release, countCache map[string]int64) error {
|
||||
target := release.Target
|
||||
if target == "" {
|
||||
target = repoCtx.Repository.DefaultBranch
|
||||
@ -59,7 +60,7 @@ func calReleaseNumCommitsBehind(repoCtx *context.Repository, release *repo_model
|
||||
return fmt.Errorf("GetBranchCommit(DefaultBranch): %w", err)
|
||||
}
|
||||
}
|
||||
countCache[target], err = commit.CommitsCount()
|
||||
countCache[target], err = gitrepo.CommitsCountOfCommit(ctx, repoCtx.Repository, commit.ID.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("CommitsCount: %w", err)
|
||||
}
|
||||
@ -122,7 +123,7 @@ func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions)
|
||||
}
|
||||
|
||||
if !r.IsDraft {
|
||||
if err := calReleaseNumCommitsBehind(ctx.Repo, r, countCache); err != nil {
|
||||
if err := calReleaseNumCommitsBehind(ctx, ctx.Repo, r, countCache); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,7 +158,7 @@ func TestCalReleaseNumCommitsBehind(t *testing.T) {
|
||||
|
||||
countCache := make(map[string]int64)
|
||||
for _, release := range releases {
|
||||
err := calReleaseNumCommitsBehind(ctx.Repo, release, countCache)
|
||||
err := calReleaseNumCommitsBehind(ctx, ctx.Repo, release, countCache)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
|
||||
@ -70,7 +70,7 @@ func CommitInfoCache(ctx *context.Context) {
|
||||
ctx.ServerError("GetBranchCommit", err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount()
|
||||
ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetCommitsCount", err)
|
||||
return
|
||||
|
||||
@ -310,7 +310,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
||||
}
|
||||
|
||||
// get commit count - wiki revisions
|
||||
commitsCount, _ := wikiGitRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
|
||||
commitsCount, _ := gitrepo.FileCommitsCount(ctx, ctx.Repo.Repository.WikiStorageRepo(), ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
|
||||
ctx.Data["CommitCount"] = commitsCount
|
||||
|
||||
return wikiGitRepo, entry
|
||||
@ -350,7 +350,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
|
||||
}
|
||||
|
||||
// get commit count - wiki revisions
|
||||
commitsCount, _ := wikiGitRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
|
||||
commitsCount, _ := gitrepo.FileCommitsCount(ctx, ctx.Repo.Repository.WikiStorageRepo(), ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
|
||||
ctx.Data["CommitCount"] = commitsCount
|
||||
|
||||
// get page
|
||||
|
||||
@ -202,14 +202,14 @@ func (r *Repository) CanCreateIssueDependencies(ctx context.Context, user *user_
|
||||
}
|
||||
|
||||
// GetCommitsCount returns cached commit count for current view
|
||||
func (r *Repository) GetCommitsCount() (int64, error) {
|
||||
func (r *Repository) GetCommitsCount(ctx context.Context) (int64, error) {
|
||||
if r.Commit == nil {
|
||||
return 0, nil
|
||||
}
|
||||
contextName := r.RefFullName.ShortName()
|
||||
isRef := r.RefFullName.IsBranch() || r.RefFullName.IsTag()
|
||||
return cache.GetInt64(r.Repository.GetCommitsCountCacheKey(contextName, isRef), func() (int64, error) {
|
||||
return r.Commit.CommitsCount()
|
||||
return gitrepo.CommitsCountOfCommit(ctx, r.Repository, r.Commit.ID.String())
|
||||
})
|
||||
}
|
||||
|
||||
@ -219,11 +219,10 @@ func (r *Repository) GetCommitGraphsCount(ctx context.Context, hidePRRefs bool,
|
||||
|
||||
return cache.GetInt64(cacheKey, func() (int64, error) {
|
||||
if len(branches) == 0 {
|
||||
return git.AllCommitsCount(ctx, r.Repository.RepoPath(), hidePRRefs, files...)
|
||||
return gitrepo.AllCommitsCount(ctx, r.Repository, hidePRRefs, files...)
|
||||
}
|
||||
return git.CommitsCount(ctx,
|
||||
git.CommitsCountOptions{
|
||||
RepoPath: r.Repository.RepoPath(),
|
||||
return gitrepo.CommitsCount(ctx, r.Repository,
|
||||
gitrepo.CommitsCountOptions{
|
||||
Revision: branches,
|
||||
RelPath: files,
|
||||
})
|
||||
@ -820,7 +819,7 @@ func RepoRefByDefaultBranch() func(*Context) {
|
||||
ctx.Repo.RefFullName = git.RefNameFromBranch(ctx.Repo.Repository.DefaultBranch)
|
||||
ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
|
||||
ctx.Repo.Commit, _ = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.BranchName)
|
||||
ctx.Repo.CommitsCount, _ = ctx.Repo.GetCommitsCount()
|
||||
ctx.Repo.CommitsCount, _ = ctx.Repo.GetCommitsCount(ctx)
|
||||
ctx.Data["RefFullName"] = ctx.Repo.RefFullName
|
||||
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
||||
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
|
||||
@ -857,7 +856,7 @@ func RepoRefByType(detectRefType git.RefType) func(*Context) {
|
||||
if err == nil && len(brs) != 0 {
|
||||
refShortName = brs[0]
|
||||
} else if len(brs) == 0 {
|
||||
log.Error("No branches in non-empty repository %s", ctx.Repo.GitRepo.Path)
|
||||
log.Error("No branches in non-empty repository %s", ctx.Repo.Repository.RelativePath())
|
||||
} else {
|
||||
log.Error("GetBranches error: %v", err)
|
||||
}
|
||||
@ -969,7 +968,7 @@ func RepoRefByType(detectRefType git.RefType) func(*Context) {
|
||||
|
||||
ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch() // only used by the branch selector dropdown: AllowCreateNewRef
|
||||
|
||||
ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount()
|
||||
ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetCommitsCount", err)
|
||||
return
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
@ -190,7 +191,7 @@ func ToCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Rep
|
||||
|
||||
// Retrieve files affected by the commit
|
||||
if opts.Files {
|
||||
fileStatus, err := git.GetCommitFileStatus(gitRepo.Ctx, repo.RepoPath(), commit.ID.String())
|
||||
fileStatus, err := gitrepo.GetCommitFileStatus(ctx, repo, commit.ID.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -303,7 +303,7 @@ func (g *GiteaLocalUploader) CreateReleases(ctx context.Context, releases ...*ba
|
||||
return fmt.Errorf("GetTagCommit[%v]: %w", rel.TagName, err)
|
||||
}
|
||||
rel.Sha1 = commit.ID.String()
|
||||
rel.NumCommits, err = commit.CommitsCount()
|
||||
rel.NumCommits, err = gitrepo.CommitsCountOfCommit(ctx, g.repo, commit.ID.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("CommitsCount: %w", err)
|
||||
}
|
||||
|
||||
@ -148,7 +148,7 @@ func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Rel
|
||||
}
|
||||
|
||||
rel.Sha1 = commit.ID.String()
|
||||
rel.NumCommits, err = commit.CommitsCount()
|
||||
rel.NumCommits, err = gitrepo.CommitsCountOfCommit(ctx, rel.Repo, commit.ID.String())
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("CommitsCount: %w", err)
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
)
|
||||
|
||||
// CacheRef cachhe last commit information of the branch or the tag
|
||||
@ -19,7 +20,9 @@ func CacheRef(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Rep
|
||||
}
|
||||
|
||||
if gitRepo.LastCommitCache == nil {
|
||||
commitsCount, err := cache.GetInt64(repo.GetCommitsCountCacheKey(fullRefName.ShortName(), true), commit.CommitsCount)
|
||||
commitsCount, err := cache.GetInt64(repo.GetCommitsCountCacheKey(fullRefName.ShortName(), true), func() (int64, error) {
|
||||
return gitrepo.CommitsCountOfCommit(ctx, repo, commit.ID.String())
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -11,7 +11,9 @@ import (
|
||||
"strings"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/lfs"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
@ -125,7 +127,20 @@ func GetFileContents(ctx context.Context, repo *repo_model.Repository, gitRepo *
|
||||
return getFileContentsByEntryInternal(ctx, repo, gitRepo, refCommit, entry, opts)
|
||||
}
|
||||
|
||||
func getFileContentsByEntryInternal(_ context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, entry *git.TreeEntry, opts GetContentsOrListOptions) (*api.ContentsResponse, error) {
|
||||
func addLastCommitCache(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, cacheKey, fullName, sha string) error {
|
||||
if gitRepo.LastCommitCache == nil {
|
||||
commitsCount, err := cache.GetInt64(cacheKey, func() (int64, error) {
|
||||
return gitrepo.CommitsCountOfCommit(ctx, repo, sha)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gitRepo.LastCommitCache = git.NewLastCommitCache(commitsCount, fullName, gitRepo, cache.GetCache())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getFileContentsByEntryInternal(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, entry *git.TreeEntry, opts GetContentsOrListOptions) (*api.ContentsResponse, error) {
|
||||
refType := refCommit.RefName.RefType()
|
||||
commit := refCommit.Commit
|
||||
selfURL, err := url.Parse(repo.APIURL() + "/contents/" + util.PathEscapeSegments(opts.TreePath) + "?ref=" + url.QueryEscape(refCommit.InputRef))
|
||||
@ -147,7 +162,7 @@ func getFileContentsByEntryInternal(_ context.Context, repo *repo_model.Reposito
|
||||
}
|
||||
|
||||
if opts.IncludeCommitMetadata || opts.IncludeCommitMessage {
|
||||
err = gitRepo.AddLastCommitCache(repo.GetCommitsCountCacheKey(refCommit.InputRef, refType != git.RefTypeCommit), repo.FullName(), refCommit.CommitID)
|
||||
err = addLastCommitCache(ctx, repo, gitRepo, repo.GetCommitsCountCacheKey(refCommit.InputRef, refType != git.RefTypeCommit), repo.FullName(), refCommit.CommitID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user