Skip to content

Commit d016d12

Browse files
committed
Introduce git service interface and refactor gitrepo module
1 parent cf52cc9 commit d016d12

File tree

11 files changed

+221
-80
lines changed

11 files changed

+221
-80
lines changed

modules/gitrepo/branch.go

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,45 +5,55 @@ package gitrepo
55

66
import (
77
"context"
8+
"errors"
9+
"strings"
810

911
"code.gitea.io/gitea/modules/git"
1012
)
1113

12-
// GetBranchesByPath returns a branch by its path
14+
// GetBranches returns branch names by repository
1315
// if limit = 0 it will not limit
14-
func GetBranchesByPath(ctx context.Context, repo Repository, skip, limit int) ([]*git.Branch, int, error) {
15-
gitRepo, err := OpenRepository(ctx, repo)
16-
if err != nil {
17-
return nil, 0, err
18-
}
19-
defer gitRepo.Close()
20-
21-
return gitRepo.GetBranches(skip, limit)
16+
func GetBranches(ctx context.Context, repo Repository, skip, limit int) ([]string, int, error) {
17+
branchNames := []string{}
18+
countAll, err := curService.WalkShowRef(ctx, repo, git.TrustedCmdArgs{git.BranchPrefix, "--sort=-committerdate"}, skip, limit, func(_, branchName string) error {
19+
branchName = strings.TrimPrefix(branchName, git.BranchPrefix)
20+
branchNames = append(branchNames, branchName)
21+
22+
return nil
23+
})
24+
return branchNames, countAll, err
2225
}
2326

2427
func GetBranchCommitID(ctx context.Context, repo Repository, branch string) (string, error) {
25-
gitRepo, err := OpenRepository(ctx, repo)
28+
gitRepo, err := curService.OpenRepository(ctx, repo)
2629
if err != nil {
2730
return "", err
2831
}
2932
defer gitRepo.Close()
30-
31-
return gitRepo.GetBranchCommitID(branch)
33+
return gitRepo.GetRefCommitID(branch)
3234
}
3335

3436
// SetDefaultBranch sets default branch of repository.
3537
func SetDefaultBranch(ctx context.Context, repo Repository, name string) error {
36-
_, _, err := git.NewCommand(ctx, "symbolic-ref", "HEAD").
37-
AddDynamicArguments(git.BranchPrefix + name).
38-
RunStdString(&git.RunOpts{Dir: repoPath(repo)})
39-
return err
38+
cmd := git.NewCommand(ctx, "symbolic-ref", "HEAD").
39+
AddDynamicArguments(git.BranchPrefix + name)
40+
return RunGitCmd(ctx, repo, cmd, &git.RunOpts{})
4041
}
4142

4243
// GetDefaultBranch gets default branch of repository.
4344
func GetDefaultBranch(ctx context.Context, repo Repository) (string, error) {
44-
return git.GetDefaultBranch(ctx, repoPath(repo))
45+
cmd := git.NewCommand(ctx, "symbolic-ref", "HEAD")
46+
stdout, _, err := RunGitCmdStdString(ctx, repo, cmd, &git.RunOpts{})
47+
if err != nil {
48+
return "", err
49+
}
50+
stdout = strings.TrimSpace(stdout)
51+
if !strings.HasPrefix(stdout, git.BranchPrefix) {
52+
return "", errors.New("the HEAD is not a branch: " + stdout)
53+
}
54+
return strings.TrimPrefix(stdout, git.BranchPrefix), nil
4555
}
4656

4757
func GetWikiDefaultBranch(ctx context.Context, repo Repository) (string, error) {
48-
return git.GetDefaultBranch(ctx, wikiPath(repo))
58+
return GetDefaultBranch(ctx, wikiRepo(repo))
4959
}

modules/gitrepo/command.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package gitrepo
5+
6+
import (
7+
"bytes"
8+
"context"
9+
"fmt"
10+
11+
"code.gitea.io/gitea/modules/git"
12+
"code.gitea.io/gitea/modules/util"
13+
)
14+
15+
// RunGitCmd runs the command with the RunOpts
16+
func RunGitCmd(ctx context.Context, repo Repository, c *git.Command, opts *git.RunOpts) error {
17+
if opts.Dir != "" {
18+
return fmt.Errorf("dir field must be empty")
19+
}
20+
opts.Dir = repoRelativePath(repo)
21+
return curService.Run(ctx, c, opts)
22+
}
23+
24+
type runStdError struct {
25+
err error
26+
stderr string
27+
errMsg string
28+
}
29+
30+
func (r *runStdError) Error() string {
31+
// the stderr must be in the returned error text, some code only checks `strings.Contains(err.Error(), "git error")`
32+
if r.errMsg == "" {
33+
r.errMsg = git.ConcatenateError(r.err, r.stderr).Error()
34+
}
35+
return r.errMsg
36+
}
37+
38+
func (r *runStdError) Unwrap() error {
39+
return r.err
40+
}
41+
42+
func (r *runStdError) Stderr() string {
43+
return r.stderr
44+
}
45+
46+
// RunStdBytes runs the command with options and returns stdout/stderr as bytes. and store stderr to returned error (err combined with stderr).
47+
func RunGitCmdStdBytes(ctx context.Context, repo Repository, c *git.Command, opts *git.RunOpts) (stdout, stderr []byte, runErr git.RunStdError) {
48+
if opts == nil {
49+
opts = &git.RunOpts{}
50+
}
51+
if opts.Stdout != nil || opts.Stderr != nil {
52+
// we must panic here, otherwise there would be bugs if developers set Stdin/Stderr by mistake, and it would be very difficult to debug
53+
panic("stdout and stderr field must be nil when using RunStdBytes")
54+
}
55+
stdoutBuf := &bytes.Buffer{}
56+
stderrBuf := &bytes.Buffer{}
57+
58+
// We must not change the provided options as it could break future calls - therefore make a copy.
59+
newOpts := &git.RunOpts{
60+
Env: opts.Env,
61+
Timeout: opts.Timeout,
62+
UseContextTimeout: opts.UseContextTimeout,
63+
Stdout: stdoutBuf,
64+
Stderr: stderrBuf,
65+
Stdin: opts.Stdin,
66+
PipelineFunc: opts.PipelineFunc,
67+
}
68+
69+
err := RunGitCmd(ctx, repo, c, newOpts)
70+
stderr = stderrBuf.Bytes()
71+
if err != nil {
72+
return nil, stderr, &runStdError{err: err, stderr: util.UnsafeBytesToString(stderr)}
73+
}
74+
// even if there is no err, there could still be some stderr output
75+
return stdoutBuf.Bytes(), stderr, nil
76+
}
77+
78+
// RunStdString runs the command with options and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr).
79+
func RunGitCmdStdString(ctx context.Context, repo Repository, c *git.Command, opts *git.RunOpts) (stdout, stderr string, runErr git.RunStdError) {
80+
stdoutBytes, stderrBytes, err := RunGitCmdStdBytes(ctx, repo, c, opts)
81+
stdout = util.UnsafeBytesToString(stdoutBytes)
82+
stderr = util.UnsafeBytesToString(stderrBytes)
83+
if err != nil {
84+
return stdout, stderr, &runStdError{err: err, stderr: stderr}
85+
}
86+
// even if there is no err, there could still be some stderr output, so we just return stdout/stderr as they are
87+
return stdout, stderr, nil
88+
}

modules/gitrepo/gitrepo.go

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,30 @@ package gitrepo
66
import (
77
"context"
88
"io"
9-
"path/filepath"
109
"strings"
1110

1211
"code.gitea.io/gitea/modules/git"
13-
"code.gitea.io/gitea/modules/setting"
1412
)
1513

1614
type Repository interface {
1715
GetName() string
1816
GetOwnerName() string
1917
}
2018

21-
func repoPath(repo Repository) string {
22-
return filepath.Join(setting.RepoRootPath, strings.ToLower(repo.GetOwnerName()), strings.ToLower(repo.GetName())+".git")
19+
type wikiRepository struct {
20+
Repository
2321
}
2422

25-
func wikiPath(repo Repository) string {
26-
return filepath.Join(setting.RepoRootPath, strings.ToLower(repo.GetOwnerName()), strings.ToLower(repo.GetName())+".wiki.git")
23+
func (w wikiRepository) GetName() string {
24+
return w.Repository.GetName() + ".wiki"
2725
}
2826

29-
// OpenRepository opens the repository at the given relative path with the provided context.
30-
func OpenRepository(ctx context.Context, repo Repository) (*git.Repository, error) {
31-
return git.OpenRepository(ctx, repoPath(repo))
27+
func wikiRepo(repo Repository) Repository {
28+
return wikiRepository{repo}
3229
}
3330

34-
func OpenWikiRepository(ctx context.Context, repo Repository) (*git.Repository, error) {
35-
return git.OpenRepository(ctx, wikiPath(repo))
31+
func repoRelativePath(repo Repository) string {
32+
return strings.ToLower(repo.GetOwnerName()) + "/" + strings.ToLower(repo.GetName()) + ".git"
3633
}
3734

3835
// contextKey is a value for use with context.WithValue.
@@ -51,7 +48,7 @@ func repositoryFromContext(ctx context.Context, repo Repository) *git.Repository
5148
}
5249

5350
if gitRepo, ok := value.(*git.Repository); ok && gitRepo != nil {
54-
if gitRepo.Path == repoPath(repo) {
51+
if strings.HasSuffix(gitRepo.Path, repoRelativePath(repo)) {
5552
return gitRepo
5653
}
5754
}

modules/gitrepo/init.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package gitrepo
5+
6+
import (
7+
"context"
8+
9+
"code.gitea.io/gitea/modules/setting"
10+
)
11+
12+
var curService Service
13+
14+
func Init(ctx context.Context) error {
15+
curService = &localServiceImpl{
16+
repoRootDir: setting.RepoRootPath,
17+
}
18+
return nil
19+
}

modules/gitrepo/repository.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package gitrepo
5+
6+
import (
7+
"context"
8+
9+
"code.gitea.io/gitea/modules/git"
10+
)
11+
12+
// OpenRepository opens the repository at the given relative path with the provided context.
13+
func OpenRepository(ctx context.Context, repo Repository) (*git.Repository, error) {
14+
return curService.OpenRepository(ctx, repo)
15+
}
16+
17+
func OpenWikiRepository(ctx context.Context, repo Repository) (*git.Repository, error) {
18+
return curService.OpenRepository(ctx, wikiRepo(repo))
19+
}

modules/gitrepo/service.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package gitrepo
5+
6+
import (
7+
"context"
8+
"path/filepath"
9+
10+
"code.gitea.io/gitea/modules/git"
11+
)
12+
13+
type Service interface {
14+
OpenRepository(ctx context.Context, repo Repository) (*git.Repository, error)
15+
Run(ctx context.Context, c *git.Command, opts *git.RunOpts) error
16+
RepoGitURL(repo Repository) string
17+
WalkShowRef(ctx context.Context, repo Repository, extraArgs git.TrustedCmdArgs, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error)
18+
}
19+
20+
var _ Service = &localServiceImpl{}
21+
22+
type localServiceImpl struct {
23+
repoRootDir string
24+
}
25+
26+
func (s *localServiceImpl) Run(ctx context.Context, c *git.Command, opts *git.RunOpts) error {
27+
opts.Dir = s.absPath(opts.Dir)
28+
return c.Run(opts)
29+
}
30+
31+
func (s *localServiceImpl) absPath(relativePaths ...string) string {
32+
for _, p := range relativePaths {
33+
if filepath.IsAbs(p) {
34+
// we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug
35+
panic("dir field must be relative path")
36+
}
37+
}
38+
path := append([]string{s.repoRootDir}, relativePaths...)
39+
return filepath.Join(path...)
40+
}
41+
42+
func (s *localServiceImpl) OpenRepository(ctx context.Context, repo Repository) (*git.Repository, error) {
43+
return git.OpenRepository(ctx, s.absPath(repoRelativePath(repo)))
44+
}
45+
46+
func (s *localServiceImpl) RepoGitURL(repo Repository) string {
47+
return s.absPath(repoRelativePath(repo))
48+
}
49+
50+
func (s *localServiceImpl) WalkShowRef(ctx context.Context, repo Repository, extraArgs git.TrustedCmdArgs, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
51+
return git.WalkShowRef(ctx, s.absPath(repoRelativePath(repo)), extraArgs, skip, limit, walkfn)
52+
}

modules/gitrepo/url.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
package gitrepo
55

66
func RepoGitURL(repo Repository) string {
7-
return repoPath(repo)
7+
return curService.RepoGitURL(repo)
88
}
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
// Copyright 2024 The Gitea Authors. All rights reserved.
22
// SPDX-License-Identifier: MIT
33

4-
//go:build !gogit
5-
64
package gitrepo
75

86
import (
97
"context"
10-
11-
"code.gitea.io/gitea/modules/git"
128
)
139

1410
// WalkReferences walks all the references from the repository
1511
func WalkReferences(ctx context.Context, repo Repository, walkfn func(sha1, refname string) error) (int, error) {
16-
return git.WalkShowRef(ctx, repoPath(repo), nil, 0, 0, walkfn)
12+
return curService.WalkShowRef(ctx, repo, nil, 0, 0, walkfn)
1713
}

modules/gitrepo/walk_gogit.go

Lines changed: 0 additions & 40 deletions
This file was deleted.

services/mirror/mirror_pull.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,14 +405,14 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
405405
}
406406

407407
log.Trace("SyncMirrors [repo: %-v]: invalidating mirror branch caches...", m.Repo)
408-
branches, _, err := gitrepo.GetBranchesByPath(ctx, m.Repo, 0, 0)
408+
branches, _, err := gitrepo.GetBranches(ctx, m.Repo, 0, 0)
409409
if err != nil {
410410
log.Error("SyncMirrors [repo: %-v]: failed to GetBranches: %v", m.Repo, err)
411411
return nil, false
412412
}
413413

414414
for _, branch := range branches {
415-
cache.Remove(m.Repo.GetCommitsCountCacheKey(branch.Name, true))
415+
cache.Remove(m.Repo.GetCommitsCountCacheKey(branch, true))
416416
}
417417

418418
m.UpdatedUnix = timeutil.TimeStampNow()

0 commit comments

Comments
 (0)