Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a38ff9a
Move git config/remote to gitrepo package and add global lock to reso…
lunny Jul 24, 2025
2e69ad3
remove unnecessary change
lunny Jul 24, 2025
2f89a04
Fix bug
lunny Jul 24, 2025
183cde5
fix
wxiaoguang Jul 25, 2025
70ef126
fix
wxiaoguang Jul 25, 2025
98bc0df
some improvements
lunny Jul 25, 2025
976df17
Merge branch 'lunny/fix_git_config_conflict' of github.com:lunny/gite…
lunny Jul 25, 2025
7a8fdc2
improvements
lunny Jul 25, 2025
869f3ae
improvements
lunny Jul 25, 2025
6501cff
Merge branch 'main' into lunny/fix_git_config_conflict
lunny Aug 3, 2025
ae3c338
Follow @wxiaoguang's suggestion
lunny Aug 19, 2025
00666bd
Merge branch 'lunny/fix_git_config_conflict' of github.com:lunny/gite…
lunny Aug 19, 2025
38c19d2
update comment
lunny Aug 20, 2025
a9633fd
follow wxiaoguang's suggestion
lunny Aug 20, 2025
578d82d
Merge branch 'main' into lunny/fix_git_config_conflict
lunny Aug 21, 2025
8fffb1b
Merge branch 'main' into lunny/fix_git_config_conflict
lunny Aug 23, 2025
661fa4f
Merge branch 'lunny/fix_git_config_conflict' of github.com:lunny/gite…
lunny Aug 23, 2025
0b82431
Merge branch 'main' into lunny/fix_git_config_conflict
lunny Aug 24, 2025
5ec1088
Merge branch 'main' into lunny/fix_git_config_conflict
lunny Aug 27, 2025
da34762
Merge branch 'main' into lunny/fix_git_config_conflict
lunny Aug 28, 2025
2e77047
Merge branch 'main' into lunny/fix_git_config_conflict
6543 Aug 28, 2025
04ca695
Update services/pull/compare.go
wxiaoguang Aug 28, 2025
b40e8fd
rename function
lunny Aug 29, 2025
fb2b02f
follow wxiaoguang's suggestion
lunny Aug 31, 2025
60bcbe8
Merge branch 'main' into lunny/fix_git_config_conflict
lunny Aug 31, 2025
308bae0
Merge branch 'lunny/fix_git_config_conflict' of github.com:lunny/gite…
lunny Aug 31, 2025
cd9d095
Merge branch 'main' into lunny/fix_git_config_conflict
lunny Sep 1, 2025
7a25938
Merge branch 'main' into lunny/fix_git_config_conflict
GiteaBot Sep 1, 2025
0effe51
Merge branch 'main' into lunny/fix_git_config_conflict
lunny Sep 1, 2025
d3dcab6
Merge branch 'lunny/fix_git_config_conflict' of github.com:lunny/gite…
lunny Sep 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions modules/git/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"net/url"
"strings"

giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/util"
)

Expand All @@ -33,15 +32,6 @@ func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string,
return result, nil
}

// GetRemoteURL returns the url of a specific remote of the repository.
func GetRemoteURL(ctx context.Context, repoPath, remoteName string) (*giturl.GitURL, error) {
addr, err := GetRemoteAddress(ctx, repoPath, remoteName)
if err != nil {
return nil, err
}
return giturl.ParseGitURL(addr)
}

// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
type ErrInvalidCloneAddr struct {
Host string
Expand Down
11 changes: 11 additions & 0 deletions modules/git/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ 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 := NewCommand("log").AddArguments(prettyLogFormat).
AddDynamicArguments(revisionRange).AddArguments("--").
RunStdBytes(ctx, &RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
return repo.parsePrettyFormatLogToList(logs)
}

func (repo *Repository) parsePrettyFormatLogToList(logs []byte) ([]*Commit, error) {
var commits []*Commit
if len(logs) == 0 {
Expand Down
6 changes: 0 additions & 6 deletions modules/git/repo_branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,6 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error {
return err
}

// RemoveRemote removes a remote from repository.
func (repo *Repository) RemoveRemote(name string) error {
_, _, err := NewCommand("remote", "rm").AddDynamicArguments(name).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
return err
}

// RenameBranch rename a branch
func (repo *Repository) RenameBranch(from, to string) error {
_, _, err := NewCommand("branch", "-m").AddDynamicArguments(from, to).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
Expand Down
89 changes: 0 additions & 89 deletions modules/git/repo_compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,8 @@ import (
"regexp"
"strconv"
"strings"
"time"

logger "code.gitea.io/gitea/modules/log"
)

// CompareInfo represents needed information for comparing references.
type CompareInfo struct {
MergeBase string
BaseCommitID string
HeadCommitID string
Commits []*Commit
NumFiles int
}

// GetMergeBase checks and returns merge base of two branches and the reference used as base.
func (repo *Repository) GetMergeBase(tmpRemote, base, head string) (string, string, error) {
if tmpRemote == "" {
Expand All @@ -49,83 +37,6 @@ func (repo *Repository) GetMergeBase(tmpRemote, base, head string) (string, stri
return strings.TrimSpace(stdout), base, err
}

// GetCompareInfo generates and returns compare information between base and head branches of repositories.
func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string, directComparison, fileOnly bool) (_ *CompareInfo, err error) {
var (
remoteBranch string
tmpRemote string
)

// We don't need a temporary remote for same repository.
if repo.Path != basePath {
// Add a temporary remote
tmpRemote = strconv.FormatInt(time.Now().UnixNano(), 10)
if err = repo.AddRemote(tmpRemote, basePath, false); err != nil {
return nil, fmt.Errorf("AddRemote: %w", err)
}
defer func() {
if err := repo.RemoveRemote(tmpRemote); err != nil {
logger.Error("GetPullRequestInfo: RemoveRemote: %v", err)
}
}()
}

compareInfo := new(CompareInfo)

compareInfo.HeadCommitID, err = GetFullCommitID(repo.Ctx, repo.Path, headBranch)
if err != nil {
compareInfo.HeadCommitID = headBranch
}

compareInfo.MergeBase, remoteBranch, err = repo.GetMergeBase(tmpRemote, baseBranch, headBranch)
if err == nil {
compareInfo.BaseCommitID, err = GetFullCommitID(repo.Ctx, repo.Path, remoteBranch)
if err != nil {
compareInfo.BaseCommitID = remoteBranch
}
separator := "..."
baseCommitID := compareInfo.MergeBase
if directComparison {
separator = ".."
baseCommitID = compareInfo.BaseCommitID
}

// We have a common base - therefore we know that ... should work
if !fileOnly {
// avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git <command> [<revision>...] -- [<file>...]'
var logs []byte
logs, _, err = NewCommand("log").AddArguments(prettyLogFormat).
AddDynamicArguments(baseCommitID+separator+headBranch).AddArguments("--").
RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
compareInfo.Commits, err = repo.parsePrettyFormatLogToList(logs)
if err != nil {
return nil, fmt.Errorf("parsePrettyFormatLogToList: %w", err)
}
} else {
compareInfo.Commits = []*Commit{}
}
} else {
compareInfo.Commits = []*Commit{}
compareInfo.MergeBase, err = GetFullCommitID(repo.Ctx, repo.Path, remoteBranch)
if err != nil {
compareInfo.MergeBase = remoteBranch
}
compareInfo.BaseCommitID = compareInfo.MergeBase
}

// Count number of changed files.
// This probably should be removed as we need to use shortstat elsewhere
// Now there is git diff --shortstat but this appears to be slower than simply iterating with --nameonly
compareInfo.NumFiles, err = repo.GetDiffNumChangedFiles(remoteBranch, headBranch, directComparison)
if err != nil {
return nil, err
}
return compareInfo, nil
}

type lineCountWriter struct {
numLines int
}
Expand Down
132 changes: 132 additions & 0 deletions modules/gitrepo/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package gitrepo

import (
"context"
"io"
"time"

"code.gitea.io/gitea/modules/git"
giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/globallock"
)

func GetGitConfig(ctx context.Context, repo Repository, key string) (string, error) {
result, _, err := git.NewCommand("config", "--get").
AddDynamicArguments(key).
RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
if err != nil {
return "", err
}
if len(result) > 0 {
result = result[:len(result)-1] // remove trailing newline
}
return result, nil
}

func getRepoConfigLockKey(repoStoragePath string) string {
return "repo-config:" + repoStoragePath
}

// AddGitConfig add a git configuration key to a specific value for the given repository.
func AddGitConfig(ctx context.Context, repo Repository, key, value string) error {
releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath()))
if err != nil {
return err
}
defer releaser()

_, _, err = git.NewCommand("config", "--add").
AddDynamicArguments(key, value).
RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
return err
}

// UpdateGitConfig updates a git configuration key to a specific value for the given repository.
// If the key does not exist, it will be created.
// If the key exists, it will be updated to the new value.
func UpdateGitConfig(ctx context.Context, repo Repository, key, value string) (string, error) {
releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath()))
if err != nil {
return "", err
}
defer releaser()

value, _, err1 := git.NewCommand("config").
AddDynamicArguments(key, value).
RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
return value, err1
}

func AddGitRemote(ctx context.Context, repo Repository, remoteName, remoteURL string, options ...string) error {
releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath()))
if err != nil {
return err
}
defer releaser()

cmd := git.NewCommand("remote", "add")
if len(options) > 0 {
cmd.AddDynamicArguments(options...)
}
_, _, err = cmd.
AddDynamicArguments(remoteName, remoteURL).
RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
return err
}

func RemoveGitRemote(ctx context.Context, repo Repository, remoteName string) error {
releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath()))
if err != nil {
return err
}
defer releaser()

cmd := git.NewCommand("remote", "rm").AddDynamicArguments(remoteName)
_, _, err = cmd.RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
return err
}

// GetRemoteURL returns the url of a specific remote of the repository.
func GetRemoteURL(ctx context.Context, repo Repository, remoteName string) (*giturl.GitURL, error) {
addr, err := git.GetRemoteAddress(ctx, repoPath(repo), remoteName)
if err != nil {
return nil, err
}
return giturl.ParseGitURL(addr)
}

// PruneRemote prunes the remote branches that no longer exist in the remote repository.
func PruneRemote(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error {
releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath()))
if err != nil {
return err
}
defer releaser()

return git.NewCommand("remote", "prune").AddDynamicArguments(remoteName).
Run(ctx, &git.RunOpts{
Timeout: timeout,
Dir: repoPath(repo),
Stdout: stdout,
Stderr: stderr,
})
}

func UpdateRemotePrune(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error {
releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath()))
if err != nil {
return err
}
defer releaser()

return git.NewCommand("remote", "update", "--prune").AddDynamicArguments(remoteName).
Run(ctx, &git.RunOpts{
Timeout: timeout,
Dir: repoPath(repo),
Stdout: stdout,
Stderr: stderr,
})
}
16 changes: 8 additions & 8 deletions routers/api/v1/repo/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -1085,7 +1085,7 @@ func MergePullRequest(ctx *context.APIContext) {
type parseCompareInfoResult struct {
headRepo *repo_model.Repository
headGitRepo *git.Repository
compareInfo *git.CompareInfo
compareInfo *pull_service.CompareInfo
baseRef git.RefName
headRef git.RefName
}
Expand Down Expand Up @@ -1201,7 +1201,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
return nil, nil
}

compareInfo, err := headGitRepo.GetCompareInfo(repo_model.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseRef.ShortName(), headRef.ShortName(), false, false)
compareInfo, err := pull_service.GetCompareInfo(ctx, baseRepo, headRepo, headGitRepo, baseRef.ShortName(), headRef.ShortName(), false, false)
if err != nil {
ctx.APIErrorInternal(err)
return nil, nil
Expand Down Expand Up @@ -1452,7 +1452,7 @@ func GetPullRequestCommits(ctx *context.APIContext) {
return
}

var prInfo *git.CompareInfo
var prInfo *pull_service.CompareInfo
baseGitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, pr.BaseRepo)
if err != nil {
ctx.APIErrorInternal(err)
Expand All @@ -1461,9 +1461,9 @@ func GetPullRequestCommits(ctx *context.APIContext) {
defer closer.Close()

if pr.HasMerged {
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.MergeBase, pr.GetGitHeadRefName(), false, false)
prInfo, err = pull_service.GetCompareInfo(ctx, pr.BaseRepo, pr.BaseRepo, baseGitRepo, pr.MergeBase, pr.GetGitHeadRefName(), false, false)
} else {
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitHeadRefName(), false, false)
prInfo, err = pull_service.GetCompareInfo(ctx, pr.BaseRepo, pr.BaseRepo, baseGitRepo, pr.BaseBranch, pr.GetGitHeadRefName(), false, false)
}
if err != nil {
ctx.APIErrorInternal(err)
Expand Down Expand Up @@ -1582,11 +1582,11 @@ func GetPullRequestFiles(ctx *context.APIContext) {

baseGitRepo := ctx.Repo.GitRepo

var prInfo *git.CompareInfo
var prInfo *pull_service.CompareInfo
if pr.HasMerged {
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.MergeBase, pr.GetGitHeadRefName(), true, false)
prInfo, err = pull_service.GetCompareInfo(ctx, pr.BaseRepo, pr.BaseRepo, baseGitRepo, pr.MergeBase, pr.GetGitHeadRefName(), true, false)
} else {
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitHeadRefName(), true, false)
prInfo, err = pull_service.GetCompareInfo(ctx, pr.BaseRepo, pr.BaseRepo, baseGitRepo, pr.BaseBranch, pr.GetGitHeadRefName(), true, false)
}
if err != nil {
ctx.APIErrorInternal(err)
Expand Down
3 changes: 2 additions & 1 deletion routers/common/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
pull_service "code.gitea.io/gitea/services/pull"
)

// CompareInfo represents the collected results from ParseCompareInfo
type CompareInfo struct {
HeadUser *user_model.User
HeadRepo *repo_model.Repository
HeadGitRepo *git.Repository
CompareInfo *git.CompareInfo
CompareInfo *pull_service.CompareInfo
BaseBranch string
HeadBranch string
DirectComparison bool
Expand Down
3 changes: 2 additions & 1 deletion routers/web/repo/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/context/upload"
"code.gitea.io/gitea/services/gitdiff"
pull_service "code.gitea.io/gitea/services/pull"
)

const (
Expand Down Expand Up @@ -550,7 +551,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
headBranchRef = git.TagPrefix + ci.HeadBranch
}

ci.CompareInfo, err = ci.HeadGitRepo.GetCompareInfo(baseRepo.RepoPath(), baseBranchRef, headBranchRef, ci.DirectComparison, fileOnly)
ci.CompareInfo, err = pull_service.GetCompareInfo(ctx, baseRepo, ci.HeadRepo, ci.HeadGitRepo, baseBranchRef, headBranchRef, ci.DirectComparison, fileOnly)
if err != nil {
ctx.ServerError("GetCompareInfo", err)
return nil
Expand Down
Loading
Loading