Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd2e994d

Browse files
lunnywxiaoguang
andauthored
Move git config/remote to gitrepo package and add global lock to resolve possible conflict when updating repository git config file (#35151)
Partiallyfix#32018 `git config` and `git remote` write operations create a temporary filenamed `config.lock`. Since these operations are not atomic, they mustnot be run in parallel. If two requests attempt to modify the samerepository concurrently—such as during a compare operation—one may faildue to the presence of an existing `config.lock` file.In cases where `config.lock` is left behind due to an unexpected programexit, a global lock mechanism could allow us to safely remove the stalelock file when a related error is detected. While this behavior is notyet implemented in this PR, it is planned for a future enhancement.---------Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
1 parent4e1b8db commitd2e994d

File tree

19 files changed

+312
-225
lines changed

19 files changed

+312
-225
lines changed

‎modules/git/remote.go‎

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"net/url"
1010
"strings"
1111

12-
giturl"code.gitea.io/gitea/modules/git/url"
1312
"code.gitea.io/gitea/modules/util"
1413
)
1514

@@ -33,15 +32,6 @@ func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string,
3332
returnresult,nil
3433
}
3534

36-
// GetRemoteURL returns the url of a specific remote of the repository.
37-
funcGetRemoteURL(ctx context.Context,repoPath,remoteNamestring) (*giturl.GitURL,error) {
38-
addr,err:=GetRemoteAddress(ctx,repoPath,remoteName)
39-
iferr!=nil {
40-
returnnil,err
41-
}
42-
returngiturl.ParseGitURL(addr)
43-
}
44-
4535
// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
4636
typeErrInvalidCloneAddrstruct {
4737
Hoststring

‎modules/git/repo.go‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ func (repo *Repository) GetAllCommitsCount() (int64, error) {
3838
returnAllCommitsCount(repo.Ctx,repo.Path,false)
3939
}
4040

41+
func (repo*Repository)ShowPrettyFormatLogToList(ctx context.Context,revisionRangestring) ([]*Commit,error) {
42+
// avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git <command> [<revision>...] -- [<file>...]'
43+
logs,_,err:=NewCommand("log").AddArguments(prettyLogFormat).
44+
AddDynamicArguments(revisionRange).AddArguments("--").
45+
RunStdBytes(ctx,&RunOpts{Dir:repo.Path})
46+
iferr!=nil {
47+
returnnil,err
48+
}
49+
returnrepo.parsePrettyFormatLogToList(logs)
50+
}
51+
4152
func (repo*Repository)parsePrettyFormatLogToList(logs []byte) ([]*Commit,error) {
4253
varcommits []*Commit
4354
iflen(logs)==0 {

‎modules/git/repo_branch.go‎

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,6 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error {
7979
returnerr
8080
}
8181

82-
// RemoveRemote removes a remote from repository.
83-
func (repo*Repository)RemoveRemote(namestring)error {
84-
_,_,err:=NewCommand("remote","rm").AddDynamicArguments(name).RunStdString(repo.Ctx,&RunOpts{Dir:repo.Path})
85-
returnerr
86-
}
87-
8882
// RenameBranch rename a branch
8983
func (repo*Repository)RenameBranch(from,tostring)error {
9084
_,_,err:=NewCommand("branch","-m").AddDynamicArguments(from,to).RunStdString(repo.Ctx,&RunOpts{Dir:repo.Path})

‎modules/git/repo_compare.go‎

Lines changed: 0 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,8 @@ import (
1616
"regexp"
1717
"strconv"
1818
"strings"
19-
"time"
20-
21-
logger"code.gitea.io/gitea/modules/log"
2219
)
2320

24-
// CompareInfo represents needed information for comparing references.
25-
typeCompareInfostruct {
26-
MergeBasestring
27-
BaseCommitIDstring
28-
HeadCommitIDstring
29-
Commits []*Commit
30-
NumFilesint
31-
}
32-
3321
// GetMergeBase checks and returns merge base of two branches and the reference used as base.
3422
func (repo*Repository)GetMergeBase(tmpRemote,base,headstring) (string,string,error) {
3523
iftmpRemote=="" {
@@ -49,83 +37,6 @@ func (repo *Repository) GetMergeBase(tmpRemote, base, head string) (string, stri
4937
returnstrings.TrimSpace(stdout),base,err
5038
}
5139

52-
// GetCompareInfo generates and returns compare information between base and head branches of repositories.
53-
func (repo*Repository)GetCompareInfo(basePath,baseBranch,headBranchstring,directComparison,fileOnlybool) (_*CompareInfo,errerror) {
54-
var (
55-
remoteBranchstring
56-
tmpRemotestring
57-
)
58-
59-
// We don't need a temporary remote for same repository.
60-
ifrepo.Path!=basePath {
61-
// Add a temporary remote
62-
tmpRemote=strconv.FormatInt(time.Now().UnixNano(),10)
63-
iferr=repo.AddRemote(tmpRemote,basePath,false);err!=nil {
64-
returnnil,fmt.Errorf("AddRemote: %w",err)
65-
}
66-
deferfunc() {
67-
iferr:=repo.RemoveRemote(tmpRemote);err!=nil {
68-
logger.Error("GetPullRequestInfo: RemoveRemote: %v",err)
69-
}
70-
}()
71-
}
72-
73-
compareInfo:=new(CompareInfo)
74-
75-
compareInfo.HeadCommitID,err=GetFullCommitID(repo.Ctx,repo.Path,headBranch)
76-
iferr!=nil {
77-
compareInfo.HeadCommitID=headBranch
78-
}
79-
80-
compareInfo.MergeBase,remoteBranch,err=repo.GetMergeBase(tmpRemote,baseBranch,headBranch)
81-
iferr==nil {
82-
compareInfo.BaseCommitID,err=GetFullCommitID(repo.Ctx,repo.Path,remoteBranch)
83-
iferr!=nil {
84-
compareInfo.BaseCommitID=remoteBranch
85-
}
86-
separator:="..."
87-
baseCommitID:=compareInfo.MergeBase
88-
ifdirectComparison {
89-
separator=".."
90-
baseCommitID=compareInfo.BaseCommitID
91-
}
92-
93-
// We have a common base - therefore we know that ... should work
94-
if!fileOnly {
95-
// avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git <command> [<revision>...] -- [<file>...]'
96-
varlogs []byte
97-
logs,_,err=NewCommand("log").AddArguments(prettyLogFormat).
98-
AddDynamicArguments(baseCommitID+separator+headBranch).AddArguments("--").
99-
RunStdBytes(repo.Ctx,&RunOpts{Dir:repo.Path})
100-
iferr!=nil {
101-
returnnil,err
102-
}
103-
compareInfo.Commits,err=repo.parsePrettyFormatLogToList(logs)
104-
iferr!=nil {
105-
returnnil,fmt.Errorf("parsePrettyFormatLogToList: %w",err)
106-
}
107-
}else {
108-
compareInfo.Commits= []*Commit{}
109-
}
110-
}else {
111-
compareInfo.Commits= []*Commit{}
112-
compareInfo.MergeBase,err=GetFullCommitID(repo.Ctx,repo.Path,remoteBranch)
113-
iferr!=nil {
114-
compareInfo.MergeBase=remoteBranch
115-
}
116-
compareInfo.BaseCommitID=compareInfo.MergeBase
117-
}
118-
119-
// Count number of changed files.
120-
// This probably should be removed as we need to use shortstat elsewhere
121-
// Now there is git diff --shortstat but this appears to be slower than simply iterating with --nameonly
122-
compareInfo.NumFiles,err=repo.GetDiffNumChangedFiles(remoteBranch,headBranch,directComparison)
123-
iferr!=nil {
124-
returnnil,err
125-
}
126-
returncompareInfo,nil
127-
}
128-
12940
typelineCountWriterstruct {
13041
numLinesint
13142
}

‎modules/gitrepo/config.go‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package gitrepo
5+
6+
import (
7+
"context"
8+
"strings"
9+
10+
"code.gitea.io/gitea/modules/git"
11+
"code.gitea.io/gitea/modules/globallock"
12+
)
13+
14+
funcGitConfigGet(ctx context.Context,repoRepository,keystring) (string,error) {
15+
result,_,err:=git.NewCommand("config","--get").
16+
AddDynamicArguments(key).
17+
RunStdString(ctx,&git.RunOpts{Dir:repoPath(repo)})
18+
iferr!=nil {
19+
return"",err
20+
}
21+
returnstrings.TrimSpace(result),nil
22+
}
23+
24+
funcgetRepoConfigLockKey(repoStoragePathstring)string {
25+
return"repo-config:"+repoStoragePath
26+
}
27+
28+
// GitConfigAdd add a git configuration key to a specific value for the given repository.
29+
funcGitConfigAdd(ctx context.Context,repoRepository,key,valuestring)error {
30+
returngloballock.LockAndDo(ctx,getRepoConfigLockKey(repo.RelativePath()),func(ctx context.Context)error {
31+
_,_,err:=git.NewCommand("config","--add").
32+
AddDynamicArguments(key,value).
33+
RunStdString(ctx,&git.RunOpts{Dir:repoPath(repo)})
34+
returnerr
35+
})
36+
}
37+
38+
// GitConfigSet updates a git configuration key to a specific value for the given repository.
39+
// If the key does not exist, it will be created.
40+
// If the key exists, it will be updated to the new value.
41+
funcGitConfigSet(ctx context.Context,repoRepository,key,valuestring)error {
42+
returngloballock.LockAndDo(ctx,getRepoConfigLockKey(repo.RelativePath()),func(ctx context.Context)error {
43+
_,_,err:=git.NewCommand("config").
44+
AddDynamicArguments(key,value).
45+
RunStdString(ctx,&git.RunOpts{Dir:repoPath(repo)})
46+
returnerr
47+
})
48+
}

‎modules/gitrepo/remote.go‎

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package gitrepo
5+
6+
import (
7+
"context"
8+
"errors"
9+
"io"
10+
"time"
11+
12+
"code.gitea.io/gitea/modules/git"
13+
giturl"code.gitea.io/gitea/modules/git/url"
14+
"code.gitea.io/gitea/modules/globallock"
15+
"code.gitea.io/gitea/modules/util"
16+
)
17+
18+
typeRemoteOptionstring
19+
20+
const (
21+
RemoteOptionMirrorPushRemoteOption="--mirror=push"
22+
RemoteOptionMirrorFetchRemoteOption="--mirror=fetch"
23+
)
24+
25+
funcGitRemoteAdd(ctx context.Context,repoRepository,remoteName,remoteURLstring,options...RemoteOption)error {
26+
returngloballock.LockAndDo(ctx,getRepoConfigLockKey(repo.RelativePath()),func(ctx context.Context)error {
27+
cmd:=git.NewCommand("remote","add")
28+
iflen(options)>0 {
29+
switchoptions[0] {
30+
caseRemoteOptionMirrorPush:
31+
cmd.AddArguments("--mirror=push")
32+
caseRemoteOptionMirrorFetch:
33+
cmd.AddArguments("--mirror=fetch")
34+
default:
35+
returnerrors.New("unknown remote option: "+string(options[0]))
36+
}
37+
}
38+
_,_,err:=cmd.
39+
AddDynamicArguments(remoteName,remoteURL).
40+
RunStdString(ctx,&git.RunOpts{Dir:repoPath(repo)})
41+
returnerr
42+
})
43+
}
44+
45+
funcGitRemoteRemove(ctx context.Context,repoRepository,remoteNamestring)error {
46+
returngloballock.LockAndDo(ctx,getRepoConfigLockKey(repo.RelativePath()),func(ctx context.Context)error {
47+
cmd:=git.NewCommand("remote","rm").AddDynamicArguments(remoteName)
48+
_,_,err:=cmd.RunStdString(ctx,&git.RunOpts{Dir:repoPath(repo)})
49+
returnerr
50+
})
51+
}
52+
53+
// GitRemoteGetURL returns the url of a specific remote of the repository.
54+
funcGitRemoteGetURL(ctx context.Context,repoRepository,remoteNamestring) (*giturl.GitURL,error) {
55+
addr,err:=git.GetRemoteAddress(ctx,repoPath(repo),remoteName)
56+
iferr!=nil {
57+
returnnil,err
58+
}
59+
ifaddr=="" {
60+
returnnil,util.NewNotExistErrorf("remote '%s' does not exist",remoteName)
61+
}
62+
returngiturl.ParseGitURL(addr)
63+
}
64+
65+
// GitRemotePrune prunes the remote branches that no longer exist in the remote repository.
66+
funcGitRemotePrune(ctx context.Context,repoRepository,remoteNamestring,timeout time.Duration,stdout,stderr io.Writer)error {
67+
returngit.NewCommand("remote","prune").AddDynamicArguments(remoteName).
68+
Run(ctx,&git.RunOpts{
69+
Timeout:timeout,
70+
Dir:repoPath(repo),
71+
Stdout:stdout,
72+
Stderr:stderr,
73+
})
74+
}
75+
76+
// GitRemoteUpdatePrune updates the remote branches and prunes the ones that no longer exist in the remote repository.
77+
funcGitRemoteUpdatePrune(ctx context.Context,repoRepository,remoteNamestring,timeout time.Duration,stdout,stderr io.Writer)error {
78+
returngit.NewCommand("remote","update","--prune").AddDynamicArguments(remoteName).
79+
Run(ctx,&git.RunOpts{
80+
Timeout:timeout,
81+
Dir:repoPath(repo),
82+
Stdout:stdout,
83+
Stderr:stderr,
84+
})
85+
}

‎modules/templates/util_misc.go‎

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ import (
1414

1515
activities_model"code.gitea.io/gitea/models/activities"
1616
repo_model"code.gitea.io/gitea/models/repo"
17-
"code.gitea.io/gitea/modules/git"
18-
giturl"code.gitea.io/gitea/modules/git/url"
17+
"code.gitea.io/gitea/modules/gitrepo"
1918
"code.gitea.io/gitea/modules/json"
2019
"code.gitea.io/gitea/modules/log"
2120
"code.gitea.io/gitea/modules/repository"
@@ -145,18 +144,12 @@ type remoteAddress struct {
145144

146145
funcmirrorRemoteAddress(ctx context.Context,m*repo_model.Repository,remoteNamestring)remoteAddress {
147146
ret:=remoteAddress{}
148-
remoteURL,err:=git.GetRemoteAddress(ctx,m.RepoPath(),remoteName)
147+
u,err:=gitrepo.GitRemoteGetURL(ctx,m,remoteName)
149148
iferr!=nil {
150149
log.Error("GetRemoteURL %v",err)
151150
returnret
152151
}
153152

154-
u,err:=giturl.ParseGitURL(remoteURL)
155-
iferr!=nil {
156-
log.Error("giturl.Parse %v",err)
157-
returnret
158-
}
159-
160153
ifu.Scheme!="ssh"&&u.Scheme!="file" {
161154
ifu.User!=nil {
162155
ret.Username=u.User.Username()

‎routers/api/v1/repo/pull.go‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,7 +1085,7 @@ func MergePullRequest(ctx *context.APIContext) {
10851085
typeparseCompareInfoResultstruct {
10861086
headRepo*repo_model.Repository
10871087
headGitRepo*git.Repository
1088-
compareInfo*git.CompareInfo
1088+
compareInfo*pull_service.CompareInfo
10891089
baseRef git.RefName
10901090
headRef git.RefName
10911091
}
@@ -1201,7 +1201,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
12011201
returnnil,nil
12021202
}
12031203

1204-
compareInfo,err:=headGitRepo.GetCompareInfo(repo_model.RepoPath(baseRepo.Owner.Name,baseRepo.Name),baseRef.ShortName(),headRef.ShortName(),false,false)
1204+
compareInfo,err:=pull_service.GetCompareInfo(ctx,baseRepo,headRepo,headGitRepo,baseRef.ShortName(),headRef.ShortName(),false,false)
12051205
iferr!=nil {
12061206
ctx.APIErrorInternal(err)
12071207
returnnil,nil
@@ -1452,7 +1452,7 @@ func GetPullRequestCommits(ctx *context.APIContext) {
14521452
return
14531453
}
14541454

1455-
varprInfo*git.CompareInfo
1455+
varprInfo*pull_service.CompareInfo
14561456
baseGitRepo,closer,err:=gitrepo.RepositoryFromContextOrOpen(ctx,pr.BaseRepo)
14571457
iferr!=nil {
14581458
ctx.APIErrorInternal(err)
@@ -1461,9 +1461,9 @@ func GetPullRequestCommits(ctx *context.APIContext) {
14611461
defercloser.Close()
14621462

14631463
ifpr.HasMerged {
1464-
prInfo,err=baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(),pr.MergeBase,pr.GetGitHeadRefName(),false,false)
1464+
prInfo,err=pull_service.GetCompareInfo(ctx,pr.BaseRepo,pr.BaseRepo,baseGitRepo,pr.MergeBase,pr.GetGitHeadRefName(),false,false)
14651465
}else {
1466-
prInfo,err=baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(),pr.BaseBranch,pr.GetGitHeadRefName(),false,false)
1466+
prInfo,err=pull_service.GetCompareInfo(ctx,pr.BaseRepo,pr.BaseRepo,baseGitRepo,pr.BaseBranch,pr.GetGitHeadRefName(),false,false)
14671467
}
14681468
iferr!=nil {
14691469
ctx.APIErrorInternal(err)
@@ -1582,11 +1582,11 @@ func GetPullRequestFiles(ctx *context.APIContext) {
15821582

15831583
baseGitRepo:=ctx.Repo.GitRepo
15841584

1585-
varprInfo*git.CompareInfo
1585+
varprInfo*pull_service.CompareInfo
15861586
ifpr.HasMerged {
1587-
prInfo,err=baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(),pr.MergeBase,pr.GetGitHeadRefName(),true,false)
1587+
prInfo,err=pull_service.GetCompareInfo(ctx,pr.BaseRepo,pr.BaseRepo,baseGitRepo,pr.MergeBase,pr.GetGitHeadRefName(),true,false)
15881588
}else {
1589-
prInfo,err=baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(),pr.BaseBranch,pr.GetGitHeadRefName(),true,false)
1589+
prInfo,err=pull_service.GetCompareInfo(ctx,pr.BaseRepo,pr.BaseRepo,baseGitRepo,pr.BaseBranch,pr.GetGitHeadRefName(),true,false)
15901590
}
15911591
iferr!=nil {
15921592
ctx.APIErrorInternal(err)

‎routers/common/compare.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ import (
77
repo_model"code.gitea.io/gitea/models/repo"
88
user_model"code.gitea.io/gitea/models/user"
99
"code.gitea.io/gitea/modules/git"
10+
pull_service"code.gitea.io/gitea/services/pull"
1011
)
1112

1213
// CompareInfo represents the collected results from ParseCompareInfo
1314
typeCompareInfostruct {
1415
HeadUser*user_model.User
1516
HeadRepo*repo_model.Repository
1617
HeadGitRepo*git.Repository
17-
CompareInfo*git.CompareInfo
18+
CompareInfo*pull_service.CompareInfo
1819
BaseBranchstring
1920
HeadBranchstring
2021
DirectComparisonbool

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp