Skip to content

Commit a38ff9a

Browse files
committed
Move git config/remote to gitrepo package and add global lock to resolve possible conflict when updating repository git config file
1 parent c10c420 commit a38ff9a

File tree

19 files changed

+312
-217
lines changed

19 files changed

+312
-217
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
return result, nil
3433
}
3534

36-
// GetRemoteURL returns the url of a specific remote of the repository.
37-
func GetRemoteURL(ctx context.Context, repoPath, remoteName string) (*giturl.GitURL, error) {
38-
addr, err := GetRemoteAddress(ctx, repoPath, remoteName)
39-
if err != nil {
40-
return nil, err
41-
}
42-
return giturl.ParseGitURL(addr)
43-
}
44-
4535
// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
4636
type ErrInvalidCloneAddr struct {
4737
Host string

modules/git/repo.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,17 @@ type GPGSettings struct {
3131
Format string
3232
}
3333

34-
const prettyLogFormat = `--pretty=format:%H`
34+
const PrettyLogFormat = `--pretty=format:%H`
3535

3636
// GetAllCommitsCount returns count of all commits in repository
3737
func (repo *Repository) GetAllCommitsCount() (int64, error) {
3838
return AllCommitsCount(repo.Ctx, repo.Path, false)
3939
}
4040

41+
func (repo *Repository) ParsePrettyFormatLogToList(logs []byte) ([]*Commit, error) {
42+
return repo.parsePrettyFormatLogToList(logs)
43+
}
44+
4145
func (repo *Repository) parsePrettyFormatLogToList(logs []byte) ([]*Commit, error) {
4246
var commits []*Commit
4347
if len(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
return err
8080
}
8181

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

modules/git/repo_commit.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func (repo *Repository) getCommitByPathWithID(id ObjectID, relpath string) (*Com
5959
relpath = `\` + relpath
6060
}
6161

62-
stdout, _, runErr := NewCommand("log", "-1", prettyLogFormat).AddDynamicArguments(id.String()).AddDashesAndList(relpath).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
62+
stdout, _, runErr := NewCommand("log", "-1", PrettyLogFormat).AddDynamicArguments(id.String()).AddDashesAndList(relpath).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
6363
if runErr != nil {
6464
return nil, runErr
6565
}
@@ -74,7 +74,7 @@ func (repo *Repository) getCommitByPathWithID(id ObjectID, relpath string) (*Com
7474

7575
// GetCommitByPath returns the last commit of relative path.
7676
func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
77-
stdout, _, runErr := NewCommand("log", "-1", prettyLogFormat).AddDashesAndList(relpath).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
77+
stdout, _, runErr := NewCommand("log", "-1", PrettyLogFormat).AddDashesAndList(relpath).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
7878
if runErr != nil {
7979
return nil, runErr
8080
}
@@ -94,7 +94,7 @@ func (repo *Repository) commitsByRangeWithTime(id ObjectID, page, pageSize int,
9494
cmd := NewCommand("log").
9595
AddOptionFormat("--skip=%d", (page-1)*pageSize).
9696
AddOptionFormat("--max-count=%d", pageSize).
97-
AddArguments(prettyLogFormat).
97+
AddArguments(PrettyLogFormat).
9898
AddDynamicArguments(id.String())
9999

100100
if not != "" {
@@ -141,7 +141,7 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([
141141
}
142142

143143
// create new git log command with limit of 100 commits
144-
cmd := NewCommand("log", "-100", prettyLogFormat).AddDynamicArguments(id.String())
144+
cmd := NewCommand("log", "-100", PrettyLogFormat).AddDynamicArguments(id.String())
145145

146146
// pretend that all refs along with HEAD were listed on command line as <commis>
147147
// https://git-scm.com/docs/git-log#Documentation/git-log.txt---all
@@ -175,7 +175,7 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([
175175
// ignore anything not matching a valid sha pattern
176176
if id.Type().IsValid(v) {
177177
// create new git log command with 1 commit limit
178-
hashCmd := NewCommand("log", "-1", prettyLogFormat)
178+
hashCmd := NewCommand("log", "-1", PrettyLogFormat)
179179
// add previous arguments except for --grep and --all
180180
addCommonSearchArgs(hashCmd)
181181
// add keyword as <commit>
@@ -410,7 +410,7 @@ func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) {
410410

411411
// commitsBefore the limit is depth, not total number of returned commits.
412412
func (repo *Repository) commitsBefore(id ObjectID, limit int) ([]*Commit, error) {
413-
cmd := NewCommand("log", prettyLogFormat)
413+
cmd := NewCommand("log", PrettyLogFormat)
414414
if limit > 0 {
415415
cmd.AddOptionFormat("-%d", limit)
416416
}
@@ -536,7 +536,7 @@ func (repo *Repository) AddLastCommitCache(cacheKey, fullName, sha string) error
536536

537537
// GetCommitBranchStart returns the commit where the branch diverged
538538
func (repo *Repository) GetCommitBranchStart(env []string, branch, endCommitID string) (string, error) {
539-
cmd := NewCommand("log", prettyLogFormat)
539+
cmd := NewCommand("log", PrettyLogFormat)
540540
cmd.AddDynamicArguments(endCommitID)
541541

542542
stdout, _, runErr := cmd.RunStdBytes(repo.Ctx, &RunOpts{

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-
type CompareInfo struct {
26-
MergeBase string
27-
BaseCommitID string
28-
HeadCommitID string
29-
Commits []*Commit
30-
NumFiles int
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, head string) (string, string, error) {
3523
if tmpRemote == "" {
@@ -49,83 +37,6 @@ func (repo *Repository) GetMergeBase(tmpRemote, base, head string) (string, stri
4937
return strings.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, headBranch string, directComparison, fileOnly bool) (_ *CompareInfo, err error) {
54-
var (
55-
remoteBranch string
56-
tmpRemote string
57-
)
58-
59-
// We don't need a temporary remote for same repository.
60-
if repo.Path != basePath {
61-
// Add a temporary remote
62-
tmpRemote = strconv.FormatInt(time.Now().UnixNano(), 10)
63-
if err = repo.AddRemote(tmpRemote, basePath, false); err != nil {
64-
return nil, fmt.Errorf("AddRemote: %w", err)
65-
}
66-
defer func() {
67-
if err := 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-
if err != nil {
77-
compareInfo.HeadCommitID = headBranch
78-
}
79-
80-
compareInfo.MergeBase, remoteBranch, err = repo.GetMergeBase(tmpRemote, baseBranch, headBranch)
81-
if err == nil {
82-
compareInfo.BaseCommitID, err = GetFullCommitID(repo.Ctx, repo.Path, remoteBranch)
83-
if err != nil {
84-
compareInfo.BaseCommitID = remoteBranch
85-
}
86-
separator := "..."
87-
baseCommitID := compareInfo.MergeBase
88-
if directComparison {
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-
var logs []byte
97-
logs, _, err = NewCommand("log").AddArguments(prettyLogFormat).
98-
AddDynamicArguments(baseCommitID+separator+headBranch).AddArguments("--").
99-
RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
100-
if err != nil {
101-
return nil, err
102-
}
103-
compareInfo.Commits, err = repo.parsePrettyFormatLogToList(logs)
104-
if err != nil {
105-
return nil, 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-
if err != 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-
if err != nil {
124-
return nil, err
125-
}
126-
return compareInfo, nil
127-
}
128-
12940
type lineCountWriter struct {
13041
numLines int
13142
}

modules/gitrepo/config.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package gitrepo
5+
6+
import (
7+
"context"
8+
"io"
9+
"time"
10+
11+
"code.gitea.io/gitea/modules/git"
12+
giturl "code.gitea.io/gitea/modules/git/url"
13+
"code.gitea.io/gitea/modules/globallock"
14+
)
15+
16+
func GetGitConfig(ctx context.Context, repo Repository, key string) (string, error) {
17+
result, _, err := git.NewCommand("config", "--get").
18+
AddDynamicArguments(key).
19+
RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
20+
if err != nil {
21+
return "", err
22+
}
23+
if len(result) > 0 {
24+
result = result[:len(result)-1] // remove trailing newline
25+
}
26+
return result, nil
27+
}
28+
29+
func getRepoConfigLockKey(repoStoragePath string) string {
30+
return "repo-config:" + repoStoragePath
31+
}
32+
33+
// AddGitConfig add a git configuration key to a specific value for the given repository.
34+
func AddGitConfig(ctx context.Context, repo Repository, key, value string) error {
35+
releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath()))
36+
if err != nil {
37+
return err
38+
}
39+
defer releaser()
40+
41+
_, _, err = git.NewCommand("config", "--add").
42+
AddDynamicArguments(key, value).
43+
RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
44+
return err
45+
}
46+
47+
// UpdateGitConfig updates a git configuration key to a specific value for the given repository.
48+
// If the key does not exist, it will be created.
49+
// If the key exists, it will be updated to the new value.
50+
func UpdateGitConfig(ctx context.Context, repo Repository, key, value string) (string, error) {
51+
releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath()))
52+
if err != nil {
53+
return "", err
54+
}
55+
defer releaser()
56+
57+
value, _, err1 := git.NewCommand("config").
58+
AddDynamicArguments(key, value).
59+
RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
60+
return value, err1
61+
}
62+
63+
func AddGitRemote(ctx context.Context, repo Repository, remoteName, remoteURL string, options ...string) error {
64+
releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath()))
65+
if err != nil {
66+
return err
67+
}
68+
defer releaser()
69+
70+
cmd := git.NewCommand("remote", "add")
71+
if len(options) > 0 {
72+
cmd.AddDynamicArguments(options...)
73+
}
74+
_, _, err = cmd.
75+
AddDynamicArguments(remoteName, remoteURL).
76+
RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
77+
return err
78+
}
79+
80+
func RemoveGitRemote(ctx context.Context, repo Repository, remoteName string) error {
81+
releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath()))
82+
if err != nil {
83+
return err
84+
}
85+
defer releaser()
86+
87+
cmd := git.NewCommand("remote", "rm").AddDynamicArguments(remoteName)
88+
_, _, err = cmd.RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
89+
return err
90+
}
91+
92+
// GetRemoteURL returns the url of a specific remote of the repository.
93+
func GetRemoteURL(ctx context.Context, repo Repository, remoteName string) (*giturl.GitURL, error) {
94+
addr, err := git.GetRemoteAddress(ctx, repoPath(repo), remoteName)
95+
if err != nil {
96+
return nil, err
97+
}
98+
return giturl.ParseGitURL(addr)
99+
}
100+
101+
// PruneRemote prunes the remote branches that no longer exist in the remote repository.
102+
func PruneRemote(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error {
103+
releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath()))
104+
if err != nil {
105+
return err
106+
}
107+
defer releaser()
108+
109+
return git.NewCommand("remote", "prune").AddDynamicArguments(remoteName).
110+
Run(ctx, &git.RunOpts{
111+
Timeout: timeout,
112+
Dir: repoPath(repo),
113+
Stdout: stdout,
114+
Stderr: stderr,
115+
})
116+
}
117+
118+
func UpdateRemotePrune(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error {
119+
releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath()))
120+
if err != nil {
121+
return err
122+
}
123+
defer releaser()
124+
125+
return git.NewCommand("remote", "update", "--prune").AddDynamicArguments(remoteName).
126+
Run(ctx, &git.RunOpts{
127+
Timeout: timeout,
128+
Dir: repoPath(repo),
129+
Stdout: stdout,
130+
Stderr: stderr,
131+
})
132+
}

0 commit comments

Comments
 (0)