History

Simplify how git repositories are opened (#28937)

## Purpose This is a refactor toward building an abstraction over managing git repositories. Afterwards, it does not matter anymore if they are stored on the local disk or somewhere remote. ## What this PR changes We used `git.OpenRepository` everywhere previously. Now, we should split them into two distinct functions: Firstly, there are temporary repositories which do not change: ```go git.OpenRepository(ctx, diskPath) ``` Gitea managed repositories having a record in the database in the `repository` table are moved into the new package `gitrepo`: ```go gitrepo.OpenRepository(ctx, repo_model.Repo) ``` Why is `repo_model.Repository` the second parameter instead of file path? Because then we can easily adapt our repository storage strategy. The repositories can be stored locally, however, they could just as well be stored on a remote server. ## Further changes in other PRs - A Git Command wrapper on package `gitrepo` could be created. i.e. `NewCommand(ctx, repo_model.Repository, commands...)`. `git.RunOpts{Dir: repo.RepoPath()}`, the directory should be empty before invoking this method and it can be filled in the function only. #28940 - Remove the `RepoPath()`/`WikiPath()` functions to reduce the possibility of mistakes. ---------

Lunny Xiao committed 2 years ago

Commit 5f82ead1 parent 60e4a9884 files · +426 −273Browse files

Showing the changes to modules (16 of 84 files). Show all

Unified · Split

16 files
  1. modules/context/api.go +4 −5
  2. modules/context/context.go +2 −2
  3. modules/context/repo.go +5 −5
  4. modules/contexttest/context_tests.go +3 −3
  5. modules/git/repo_base.go +0 −44
  6. modules/git/repo_branch.go +0 −23
  7. modules/git/repo_branch_gogit.go +0 −29
  8. modules/git/repo_branch_nogogit.go +4 −9
  9. modules/gitrepo/branch.go +32 −0
  10. modules/gitrepo/gitrepo.go +103 −0
  11. modules/gitrepo/walk_gogit.go +40 −0
  12. modules/gitrepo/walk_nogogit.go +17 −0
  13. modules/indexer/stats/db.go +2 −1
  14. modules/repository/branch.go +3 −2
  15. modules/repository/generate.go +2 −1
  16. modules/repository/repo.go +2 −1
modules/context/api.go+4−5
@@ -11,11 +11,11 @@ import ( · 10 unmodified lines
11 "net/url"11 "net/url"
12 "strings"12 "strings"
13 13
14 repo_model "code.gitea.io/gitea/models/repo"
15 "code.gitea.io/gitea/models/unit"14 "code.gitea.io/gitea/models/unit"
16 user_model "code.gitea.io/gitea/models/user"15 user_model "code.gitea.io/gitea/models/user"
17 mc "code.gitea.io/gitea/modules/cache"16 mc "code.gitea.io/gitea/modules/cache"
18 "code.gitea.io/gitea/modules/git"17 "code.gitea.io/gitea/modules/git"
18 "code.gitea.io/gitea/modules/gitrepo"
19 "code.gitea.io/gitea/modules/httpcache"19 "code.gitea.io/gitea/modules/httpcache"
20 "code.gitea.io/gitea/modules/log"20 "code.gitea.io/gitea/modules/log"
21 "code.gitea.io/gitea/modules/setting"21 "code.gitea.io/gitea/modules/setting"
@@ -224,7 +224,7 @@ func APIContexter() func(http.Handler) http.Handler { · 202 unmodified lines
224 defer baseCleanUp()224 defer baseCleanUp()
225 225
226 ctx.Base.AppendContextValue(apiContextKey, ctx)226 ctx.Base.AppendContextValue(apiContextKey, ctx)
227 ctx.Base.AppendContextValueFunc(git.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })227 ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
228 228
229 // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.229 // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
230 if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {230 if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
@@ -278,10 +278,9 @@ func ReferencesGitRepo(allowEmpty ...bool) func(ctx *APIContext) (cancel context · 47 unmodified lines
278 278
279 // For API calls.279 // For API calls.
280 if ctx.Repo.GitRepo == nil {280 if ctx.Repo.GitRepo == nil {
281 repoPath := repo_model.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)281 gitRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
282 gitRepo, err := git.OpenRepository(ctx, repoPath)
283 if err != nil {282 if err != nil {
284 ctx.Error(http.StatusInternalServerError, "RepoRef Invalid repo "+repoPath, err)283 ctx.Error(http.StatusInternalServerError, fmt.Sprintf("Open Repository %v failed", ctx.Repo.Repository.FullName()), err)
285 return cancel284 return cancel
286 }285 }
287 ctx.Repo.GitRepo = gitRepo286 ctx.Repo.GitRepo = gitRepo
modules/context/context.go+2−2
@@ -17,7 +17,7 @@ import ( · 16 unmodified lines
17 "code.gitea.io/gitea/models/unit"17 "code.gitea.io/gitea/models/unit"
18 user_model "code.gitea.io/gitea/models/user"18 user_model "code.gitea.io/gitea/models/user"
19 mc "code.gitea.io/gitea/modules/cache"19 mc "code.gitea.io/gitea/modules/cache"
20 "code.gitea.io/gitea/modules/git"20 "code.gitea.io/gitea/modules/gitrepo"
21 "code.gitea.io/gitea/modules/httpcache"21 "code.gitea.io/gitea/modules/httpcache"
22 "code.gitea.io/gitea/modules/setting"22 "code.gitea.io/gitea/modules/setting"
23 "code.gitea.io/gitea/modules/templates"23 "code.gitea.io/gitea/modules/templates"
@@ -163,7 +163,7 @@ func Contexter() func(next http.Handler) http.Handler { · 139 unmodified lines
163 ctx.Data["PageData"] = ctx.PageData163 ctx.Data["PageData"] = ctx.PageData
164 164
165 ctx.Base.AppendContextValue(WebContextKey, ctx)165 ctx.Base.AppendContextValue(WebContextKey, ctx)
166 ctx.Base.AppendContextValueFunc(git.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })166 ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
167 167
168 ctx.Csrf = PrepareCSRFProtector(csrfOpts, ctx)168 ctx.Csrf = PrepareCSRFProtector(csrfOpts, ctx)
169 169
modules/context/repo.go+5−5
@@ -23,6 +23,7 @@ import ( · 22 unmodified lines
23 user_model "code.gitea.io/gitea/models/user"23 user_model "code.gitea.io/gitea/models/user"
24 "code.gitea.io/gitea/modules/cache"24 "code.gitea.io/gitea/modules/cache"
25 "code.gitea.io/gitea/modules/git"25 "code.gitea.io/gitea/modules/git"
26 "code.gitea.io/gitea/modules/gitrepo"
26 code_indexer "code.gitea.io/gitea/modules/indexer/code"27 code_indexer "code.gitea.io/gitea/modules/indexer/code"
27 "code.gitea.io/gitea/modules/log"28 "code.gitea.io/gitea/modules/log"
28 repo_module "code.gitea.io/gitea/modules/repository"29 repo_module "code.gitea.io/gitea/modules/repository"
@@ -633,7 +634,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc { · 604 unmodified lines
633 return nil634 return nil
634 }635 }
635 636
636 gitRepo, err := git.OpenRepository(ctx, repo_model.RepoPath(userName, repoName))637 gitRepo, err := gitrepo.OpenRepository(ctx, repo)
637 if err != nil {638 if err != nil {
638 if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") {639 if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") {
639 log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RepoPath(), err)640 log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RepoPath(), err)
@@ -645,7 +646,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc { · 5 unmodified lines
645 }646 }
646 return nil647 return nil
647 }648 }
648 ctx.ServerError("RepoAssignment Invalid repo "+repo_model.RepoPath(userName, repoName), err)649 ctx.ServerError("RepoAssignment Invalid repo "+repo.FullName(), err)
649 return nil650 return nil
650 }651 }
651 if ctx.Repo.GitRepo != nil {652 if ctx.Repo.GitRepo != nil {
@@ -920,10 +921,9 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context · 268 unmodified lines
920 )921 )
921 922
922 if ctx.Repo.GitRepo == nil {923 if ctx.Repo.GitRepo == nil {
923 repoPath := repo_model.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)924 ctx.Repo.GitRepo, err = gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
924 ctx.Repo.GitRepo, err = git.OpenRepository(ctx, repoPath)
925 if err != nil {925 if err != nil {
926 ctx.ServerError("RepoRef Invalid repo "+repoPath, err)926 ctx.ServerError(fmt.Sprintf("Open Repository %v failed", ctx.Repo.Repository.FullName()), err)
927 return nil927 return nil
928 }928 }
929 // We opened it, we should close it929 // We opened it, we should close it
modules/contexttest/context_tests.go+3−3
@@ -18,7 +18,7 @@ import ( · 17 unmodified lines
18 "code.gitea.io/gitea/models/unittest"18 "code.gitea.io/gitea/models/unittest"
19 user_model "code.gitea.io/gitea/models/user"19 user_model "code.gitea.io/gitea/models/user"
20 "code.gitea.io/gitea/modules/context"20 "code.gitea.io/gitea/modules/context"
21 "code.gitea.io/gitea/modules/git"21 "code.gitea.io/gitea/modules/gitrepo"
22 "code.gitea.io/gitea/modules/templates"22 "code.gitea.io/gitea/modules/templates"
23 "code.gitea.io/gitea/modules/translation"23 "code.gitea.io/gitea/modules/translation"
24 "code.gitea.io/gitea/modules/web/middleware"24 "code.gitea.io/gitea/modules/web/middleware"
@@ -107,7 +107,7 @@ func LoadRepoCommit(t *testing.T, ctx gocontext.Context) { · 82 unmodified lines
107 assert.FailNow(t, "context is not *context.Context or *context.APIContext")107 assert.FailNow(t, "context is not *context.Context or *context.APIContext")
108 }108 }
109 109
110 gitRepo, err := git.OpenRepository(ctx, repo.Repository.RepoPath())110 gitRepo, err := gitrepo.OpenRepository(ctx, repo.Repository)
111 assert.NoError(t, err)111 assert.NoError(t, err)
112 defer gitRepo.Close()112 defer gitRepo.Close()
113 branch, err := gitRepo.GetHEADBranch()113 branch, err := gitRepo.GetHEADBranch()
@@ -137,7 +137,7 @@ func LoadUser(t *testing.T, ctx gocontext.Context, userID int64) { · 23 unmodified lines
137func LoadGitRepo(t *testing.T, ctx *context.Context) {137func LoadGitRepo(t *testing.T, ctx *context.Context) {
138 assert.NoError(t, ctx.Repo.Repository.LoadOwner(ctx))138 assert.NoError(t, ctx.Repo.Repository.LoadOwner(ctx))
139 var err error139 var err error
140 ctx.Repo.GitRepo, err = git.OpenRepository(ctx, ctx.Repo.Repository.RepoPath())140 ctx.Repo.GitRepo, err = gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
141 assert.NoError(t, err)141 assert.NoError(t, err)
142}142}
143 143
modules/git/repo_base.go+0−44
@@ -3,48 +3,4 @@ · 2 unmodified lines
3 3
4package git4package git
5 5
6import (
7 "context"
8 "io"
9)
10
11var isGogit bool6var isGogit bool
12
13// contextKey is a value for use with context.WithValue.
14type contextKey struct {
15 name string
16}
17
18// RepositoryContextKey is a context key. It is used with context.Value() to get the current Repository for the context
19var RepositoryContextKey = &contextKey{"repository"}
20
21// RepositoryFromContext attempts to get the repository from the context
22func RepositoryFromContext(ctx context.Context, path string) *Repository {
23 value := ctx.Value(RepositoryContextKey)
24 if value == nil {
25 return nil
26 }
27
28 if repo, ok := value.(*Repository); ok && repo != nil {
29 if repo.Path == path {
30 return repo
31 }
32 }
33
34 return nil
35}
36
37type nopCloser func()
38
39func (nopCloser) Close() error { return nil }
40
41// RepositoryFromContextOrOpen attempts to get the repository from the context or just opens it
42func RepositoryFromContextOrOpen(ctx context.Context, path string) (*Repository, io.Closer, error) {
43 gitRepo := RepositoryFromContext(ctx, path)
44 if gitRepo != nil {
45 return gitRepo, nopCloser(nil), nil
46 }
47
48 gitRepo, err := OpenRepository(ctx, path)
49 return gitRepo, gitRepo, err
50}
modules/git/repo_branch.go+0−23
@@ -86,29 +86,6 @@ func (repo *Repository) GetBranch(branch string) (*Branch, error) { · 85 unmodified lines
86 }, nil86 }, nil
87}87}
88 88
89// GetBranchesByPath returns a branch by it's path
90// if limit = 0 it will not limit
91func GetBranchesByPath(ctx context.Context, path string, skip, limit int) ([]*Branch, int, error) {
92 gitRepo, err := OpenRepository(ctx, path)
93 if err != nil {
94 return nil, 0, err
95 }
96 defer gitRepo.Close()
97
98 return gitRepo.GetBranches(skip, limit)
99}
100
101// GetBranchCommitID returns a branch commit ID by its name
102func GetBranchCommitID(ctx context.Context, path, branch string) (string, error) {
103 gitRepo, err := OpenRepository(ctx, path)
104 if err != nil {
105 return "", err
106 }
107 defer gitRepo.Close()
108
109 return gitRepo.GetBranchCommitID(branch)
110}
111
112// GetBranches returns a slice of *git.Branch89// GetBranches returns a slice of *git.Branch
113func (repo *Repository) GetBranches(skip, limit int) ([]*Branch, int, error) {90func (repo *Repository) GetBranches(skip, limit int) ([]*Branch, int, error) {
114 brs, countAll, err := repo.GetBranchNames(skip, limit)91 brs, countAll, err := repo.GetBranchNames(skip, limit)
modules/git/repo_branch_gogit.go+0−29
@@ -7,7 +7,6 @@ · 6 unmodified lines
7package git7package git
8 8
9import (9import (
10 "context"
11 "sort"10 "sort"
12 "strings"11 "strings"
13 12
@@ -95,34 +94,6 @@ func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) { · 81 unmodified lines
95 return branchNames, len(branchData), nil94 return branchNames, len(branchData), nil
96}95}
97 96
98// WalkReferences walks all the references from the repository
99// refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty.
100func WalkReferences(ctx context.Context, repoPath string, walkfn func(sha1, refname string) error) (int, error) {
101 repo := RepositoryFromContext(ctx, repoPath)
102 if repo == nil {
103 var err error
104 repo, err = OpenRepository(ctx, repoPath)
105 if err != nil {
106 return 0, err
107 }
108 defer repo.Close()
109 }
110
111 i := 0
112 iter, err := repo.gogitRepo.References()
113 if err != nil {
114 return i, err
115 }
116 defer iter.Close()
117
118 err = iter.ForEach(func(ref *plumbing.Reference) error {
119 err := walkfn(ref.Hash().String(), string(ref.Name()))
120 i++
121 return err
122 })
123 return i, err
124}
125
126// WalkReferences walks all the references from the repository97// WalkReferences walks all the references from the repository
127func (repo *Repository) WalkReferences(arg ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {98func (repo *Repository) WalkReferences(arg ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
128 i := 099 i := 0
modules/git/repo_branch_nogogit.go+4−9
@@ -65,11 +65,6 @@ func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) { · 64 unmodified lines
65 return callShowRef(repo.Ctx, repo.Path, BranchPrefix, TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"}, skip, limit)65 return callShowRef(repo.Ctx, repo.Path, BranchPrefix, TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"}, skip, limit)
66}66}
67 67
68// WalkReferences walks all the references from the repository
69func WalkReferences(ctx context.Context, repoPath string, walkfn func(sha1, refname string) error) (int, error) {
70 return walkShowRef(ctx, repoPath, nil, 0, 0, walkfn)
71}
72
73// WalkReferences walks all the references from the repository68// WalkReferences walks all the references from the repository
74// refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty.69// refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty.
75func (repo *Repository) WalkReferences(refType ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {70func (repo *Repository) WalkReferences(refType ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
@@ -81,12 +76,12 @@ func (repo *Repository) WalkReferences(refType ObjectType, skip, limit int, walk · 5 unmodified lines
81 args = TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"}76 args = TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"}
82 }77 }
83 78
84 return walkShowRef(repo.Ctx, repo.Path, args, skip, limit, walkfn)79 return WalkShowRef(repo.Ctx, repo.Path, args, skip, limit, walkfn)
85}80}
86 81
87// callShowRef return refs, if limit = 0 it will not limit82// callShowRef return refs, if limit = 0 it will not limit
88func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs TrustedCmdArgs, skip, limit int) (branchNames []string, countAll int, err error) {83func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs TrustedCmdArgs, skip, limit int) (branchNames []string, countAll int, err error) {
89 countAll, err = walkShowRef(ctx, repoPath, extraArgs, skip, limit, func(_, branchName string) error {84 countAll, err = WalkShowRef(ctx, repoPath, extraArgs, skip, limit, func(_, branchName string) error {
90 branchName = strings.TrimPrefix(branchName, trimPrefix)85 branchName = strings.TrimPrefix(branchName, trimPrefix)
91 branchNames = append(branchNames, branchName)86 branchNames = append(branchNames, branchName)
92 87
@@ -95,7 +90,7 @@ func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs Tru · 2 unmodified lines
95 return branchNames, countAll, err90 return branchNames, countAll, err
96}91}
97 92
98func walkShowRef(ctx context.Context, repoPath string, extraArgs TrustedCmdArgs, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) {93func WalkShowRef(ctx context.Context, repoPath string, extraArgs TrustedCmdArgs, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) {
99 stdoutReader, stdoutWriter := io.Pipe()94 stdoutReader, stdoutWriter := io.Pipe()
100 defer func() {95 defer func() {
101 _ = stdoutReader.Close()96 _ = stdoutReader.Close()
@@ -189,7 +184,7 @@ func walkShowRef(ctx context.Context, repoPath string, extraArgs TrustedCmdArgs, · 87 unmodified lines
189// GetRefsBySha returns all references filtered with prefix that belong to a sha commit hash184// GetRefsBySha returns all references filtered with prefix that belong to a sha commit hash
190func (repo *Repository) GetRefsBySha(sha, prefix string) ([]string, error) {185func (repo *Repository) GetRefsBySha(sha, prefix string) ([]string, error) {
191 var revList []string186 var revList []string
192 _, err := walkShowRef(repo.Ctx, repo.Path, nil, 0, 0, func(walkSha, refname string) error {187 _, err := WalkShowRef(repo.Ctx, repo.Path, nil, 0, 0, func(walkSha, refname string) error {
193 if walkSha == sha && strings.HasPrefix(refname, prefix) {188 if walkSha == sha && strings.HasPrefix(refname, prefix) {
194 revList = append(revList, refname)189 revList = append(revList, refname)
195 }190 }
modules/gitrepo/branch.go+32−0
@@ -0,0 +1,32 @@
1// Copyright 2024 The Gitea Authors. All rights reserved.
2// SPDX-License-Identifier: MIT
3
4package gitrepo
5
6import (
7 "context"
8
9 "code.gitea.io/gitea/modules/git"
10)
11
12// GetBranchesByPath returns a branch by its path
13// if limit = 0 it will not limit
14func 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)
22}
23
24func GetBranchCommitID(ctx context.Context, repo Repository, branch string) (string, error) {
25 gitRepo, err := OpenRepository(ctx, repo)
26 if err != nil {
27 return "", err
28 }
29 defer gitRepo.Close()
30
31 return gitRepo.GetBranchCommitID(branch)
32}
modules/gitrepo/gitrepo.go+103−0
@@ -0,0 +1,103 @@
1// Copyright 2024 The Gitea Authors. All rights reserved.
2// SPDX-License-Identifier: MIT
3
4package gitrepo
5
6import (
7 "context"
8 "io"
9 "path/filepath"
10 "strings"
11
12 "code.gitea.io/gitea/modules/git"
13 "code.gitea.io/gitea/modules/setting"
14)
15
16type Repository interface {
17 GetName() string
18 GetOwnerName() string
19}
20
21func repoPath(repo Repository) string {
22 return filepath.Join(setting.RepoRootPath, strings.ToLower(repo.GetOwnerName()), strings.ToLower(repo.GetName())+".git")
23}
24
25func wikiPath(repo Repository) string {
26 return filepath.Join(setting.RepoRootPath, strings.ToLower(repo.GetOwnerName()), strings.ToLower(repo.GetName())+".wiki.git")
27}
28
29// OpenRepository opens the repository at the given relative path with the provided context.
30func OpenRepository(ctx context.Context, repo Repository) (*git.Repository, error) {
31 return git.OpenRepository(ctx, repoPath(repo))
32}
33
34func OpenWikiRepository(ctx context.Context, repo Repository) (*git.Repository, error) {
35 return git.OpenRepository(ctx, wikiPath(repo))
36}
37
38// contextKey is a value for use with context.WithValue.
39type contextKey struct {
40 name string
41}
42
43// RepositoryContextKey is a context key. It is used with context.Value() to get the current Repository for the context
44var RepositoryContextKey = &contextKey{"repository"}
45
46// RepositoryFromContext attempts to get the repository from the context
47func repositoryFromContext(ctx context.Context, repo Repository) *git.Repository {
48 value := ctx.Value(RepositoryContextKey)
49 if value == nil {
50 return nil
51 }
52
53 if gitRepo, ok := value.(*git.Repository); ok && gitRepo != nil {
54 if gitRepo.Path == repoPath(repo) {
55 return gitRepo
56 }
57 }
58
59 return nil
60}
61
62type nopCloser func()
63
64func (nopCloser) Close() error { return nil }
65
66// RepositoryFromContextOrOpen attempts to get the repository from the context or just opens it
67func RepositoryFromContextOrOpen(ctx context.Context, repo Repository) (*git.Repository, io.Closer, error) {
68 gitRepo := repositoryFromContext(ctx, repo)
69 if gitRepo != nil {
70 return gitRepo, nopCloser(nil), nil
71 }
72
73 gitRepo, err := OpenRepository(ctx, repo)
74 return gitRepo, gitRepo, err
75}
76
77// repositoryFromContextPath attempts to get the repository from the context
78func repositoryFromContextPath(ctx context.Context, path string) *git.Repository {
79 value := ctx.Value(RepositoryContextKey)
80 if value == nil {
81 return nil
82 }
83
84 if repo, ok := value.(*git.Repository); ok && repo != nil {
85 if repo.Path == path {
86 return repo
87 }
88 }
89
90 return nil
91}
92
93// RepositoryFromContextOrOpenPath attempts to get the repository from the context or just opens it
94// Deprecated: Use RepositoryFromContextOrOpen instead
95func RepositoryFromContextOrOpenPath(ctx context.Context, path string) (*git.Repository, io.Closer, error) {
96 gitRepo := repositoryFromContextPath(ctx, path)
97 if gitRepo != nil {
98 return gitRepo, nopCloser(nil), nil
99 }
100
101 gitRepo, err := git.OpenRepository(ctx, path)
102 return gitRepo, gitRepo, err
103}
modules/gitrepo/walk_gogit.go+40−0
@@ -0,0 +1,40 @@
1// Copyright 2024 The Gitea Authors. All rights reserved.
2// SPDX-License-Identifier: MIT
3
4//go:build gogit
5
6package gitrepo
7
8import (
9 "context"
10
11 "github.com/go-git/go-git/v5/plumbing"
12)
13
14// WalkReferences walks all the references from the repository
15// refname is empty, ObjectTag or ObjectBranch. All other values should be treated as equivalent to empty.
16func WalkReferences(ctx context.Context, repo Repository, walkfn func(sha1, refname string) error) (int, error) {
17 gitRepo := repositoryFromContext(ctx, repo)
18 if gitRepo == nil {
19 var err error
20 gitRepo, err = OpenRepository(ctx, repo)
21 if err != nil {
22 return 0, err
23 }
24 defer gitRepo.Close()
25 }
26
27 i := 0
28 iter, err := gitRepo.GoGitRepo().References()
29 if err != nil {
30 return i, err
31 }
32 defer iter.Close()
33
34 err = iter.ForEach(func(ref *plumbing.Reference) error {
35 err := walkfn(ref.Hash().String(), string(ref.Name()))
36 i++
37 return err
38 })
39 return i, err
40}
modules/gitrepo/walk_nogogit.go+17−0
@@ -0,0 +1,17 @@
1// Copyright 2024 The Gitea Authors. All rights reserved.
2// SPDX-License-Identifier: MIT
3
4//go:build !gogit
5
6package gitrepo
7
8import (
9 "context"
10
11 "code.gitea.io/gitea/modules/git"
12)
13
14// WalkReferences walks all the references from the repository
15func 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)
17}
modules/indexer/stats/db.go+2−1
@@ -8,6 +8,7 @@ import ( · 7 unmodified lines
8 8
9 repo_model "code.gitea.io/gitea/models/repo"9 repo_model "code.gitea.io/gitea/models/repo"
10 "code.gitea.io/gitea/modules/git"10 "code.gitea.io/gitea/modules/git"
11 "code.gitea.io/gitea/modules/gitrepo"
11 "code.gitea.io/gitea/modules/graceful"12 "code.gitea.io/gitea/modules/graceful"
12 "code.gitea.io/gitea/modules/log"13 "code.gitea.io/gitea/modules/log"
13 "code.gitea.io/gitea/modules/process"14 "code.gitea.io/gitea/modules/process"
@@ -35,7 +36,7 @@ func (db *DBIndexer) Index(id int64) error { · 21 unmodified lines
35 return err36 return err
36 }37 }
37 38
38 gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())39 gitRepo, err := gitrepo.OpenRepository(ctx, repo)
39 if err != nil {40 if err != nil {
40 if err.Error() == "no such file or directory" {41 if err.Error() == "no such file or directory" {
41 return nil42 return nil
modules/repository/branch.go+3−2
@@ -11,6 +11,7 @@ import ( · 10 unmodified lines
11 repo_model "code.gitea.io/gitea/models/repo"11 repo_model "code.gitea.io/gitea/models/repo"
12 "code.gitea.io/gitea/modules/container"12 "code.gitea.io/gitea/modules/container"
13 "code.gitea.io/gitea/modules/git"13 "code.gitea.io/gitea/modules/git"
14 "code.gitea.io/gitea/modules/gitrepo"
14 "code.gitea.io/gitea/modules/log"15 "code.gitea.io/gitea/modules/log"
15 "code.gitea.io/gitea/modules/timeutil"16 "code.gitea.io/gitea/modules/timeutil"
16)17)
@@ -24,9 +25,9 @@ func SyncRepoBranches(ctx context.Context, repoID, doerID int64) (int64, error) · 7 unmodified lines
24 25
25 log.Debug("SyncRepoBranches: in Repo[%d:%s]", repo.ID, repo.FullName())26 log.Debug("SyncRepoBranches: in Repo[%d:%s]", repo.ID, repo.FullName())
26 27
27 gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())28 gitRepo, err := gitrepo.OpenRepository(ctx, repo)
28 if err != nil {29 if err != nil {
29 log.Error("OpenRepository[%s]: %w", repo.RepoPath(), err)30 log.Error("OpenRepository[%s]: %w", repo.FullName(), err)
30 return 0, err31 return 0, err
31 }32 }
32 defer gitRepo.Close()33 defer gitRepo.Close()
modules/repository/generate.go+2−1
@@ -19,6 +19,7 @@ import ( · 18 unmodified lines
19 repo_model "code.gitea.io/gitea/models/repo"19 repo_model "code.gitea.io/gitea/models/repo"
20 user_model "code.gitea.io/gitea/models/user"20 user_model "code.gitea.io/gitea/models/user"
21 "code.gitea.io/gitea/modules/git"21 "code.gitea.io/gitea/modules/git"
22 "code.gitea.io/gitea/modules/gitrepo"
22 "code.gitea.io/gitea/modules/log"23 "code.gitea.io/gitea/modules/log"
23 "code.gitea.io/gitea/modules/util"24 "code.gitea.io/gitea/modules/util"
24 25
@@ -271,7 +272,7 @@ func generateGitContent(ctx context.Context, repo, templateRepo, generateRepo *r · 246 unmodified lines
271 repo.DefaultBranch = templateRepo.DefaultBranch272 repo.DefaultBranch = templateRepo.DefaultBranch
272 }273 }
273 274
274 gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())275 gitRepo, err := gitrepo.OpenRepository(ctx, repo)
275 if err != nil {276 if err != nil {
276 return fmt.Errorf("openRepository: %w", err)277 return fmt.Errorf("openRepository: %w", err)
277 }278 }
modules/repository/repo.go+2−1
@@ -19,6 +19,7 @@ import ( · 18 unmodified lines
19 user_model "code.gitea.io/gitea/models/user"19 user_model "code.gitea.io/gitea/models/user"
20 "code.gitea.io/gitea/modules/container"20 "code.gitea.io/gitea/modules/container"
21 "code.gitea.io/gitea/modules/git"21 "code.gitea.io/gitea/modules/git"
22 "code.gitea.io/gitea/modules/gitrepo"
22 "code.gitea.io/gitea/modules/lfs"23 "code.gitea.io/gitea/modules/lfs"
23 "code.gitea.io/gitea/modules/log"24 "code.gitea.io/gitea/modules/log"
24 "code.gitea.io/gitea/modules/migration"25 "code.gitea.io/gitea/modules/migration"
@@ -291,7 +292,7 @@ func SyncRepoTags(ctx context.Context, repoID int64) error { · 266 unmodified lines
291 return err292 return err
292 }293 }
293 294
294 gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())295 gitRepo, err := gitrepo.OpenRepository(ctx, repo)
295 if err != nil {296 if err != nil {
296 return err297 return err
297 }298 }