Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 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
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
48 changes: 48 additions & 0 deletions modules/gitrepo/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package gitrepo

import (
"context"
"strings"

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

func GitConfigGet(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
}
return strings.TrimSpace(result), nil
}

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

// GitConfigAdd add a git configuration key to a specific value for the given repository.
func GitConfigAdd(ctx context.Context, repo Repository, key, value string) error {
return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error {
_, _, err := git.NewCommand("config", "--add").
AddDynamicArguments(key, value).
RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
return err
})
}

// GitConfigSet 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 GitConfigSet(ctx context.Context, repo Repository, key, value string) error {
return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error {
_, _, err := git.NewCommand("config").
AddDynamicArguments(key, value).
RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
return err
})
}
85 changes: 85 additions & 0 deletions modules/gitrepo/remote.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package gitrepo

import (
"context"
"errors"
"io"
"time"

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

type RemoteOption string

const (
RemoteOptionMirrorPush RemoteOption = "--mirror=push"
RemoteOptionMirrorFetch RemoteOption = "--mirror=fetch"
)

func GitRemoteAdd(ctx context.Context, repo Repository, remoteName, remoteURL string, options ...RemoteOption) error {
return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error {
cmd := git.NewCommand("remote", "add")
if len(options) > 0 {
switch options[0] {
case RemoteOptionMirrorPush:
cmd.AddArguments("--mirror=push")
case RemoteOptionMirrorFetch:
cmd.AddArguments("--mirror=fetch")
default:
return errors.New("unknown remote option: " + string(options[0]))
}
}
_, _, err := cmd.
AddDynamicArguments(remoteName, remoteURL).
RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
return err
})
}

func GitRemoteRemove(ctx context.Context, repo Repository, remoteName string) error {
return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error {
cmd := git.NewCommand("remote", "rm").AddDynamicArguments(remoteName)
_, _, err := cmd.RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
return err
})
}

// GitRemoteGetURL returns the url of a specific remote of the repository.
func GitRemoteGetURL(ctx context.Context, repo Repository, remoteName string) (*giturl.GitURL, error) {
addr, err := git.GetRemoteAddress(ctx, repoPath(repo), remoteName)
if err != nil {
return nil, err
}
if addr == "" {
return nil, util.NewNotExistErrorf("remote '%s' does not exist", remoteName)
}
return giturl.ParseGitURL(addr)
}

// GitRemotePrune prunes the remote branches that no longer exist in the remote repository.
func GitRemotePrune(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error {
return git.NewCommand("remote", "prune").AddDynamicArguments(remoteName).
Run(ctx, &git.RunOpts{
Timeout: timeout,
Dir: repoPath(repo),
Stdout: stdout,
Stderr: stderr,
})
}

// GitRemoteUpdatePrune updates the remote branches and prunes the ones that no longer exist in the remote repository.
func GitRemoteUpdatePrune(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error {
return git.NewCommand("remote", "update", "--prune").AddDynamicArguments(remoteName).
Run(ctx, &git.RunOpts{
Timeout: timeout,
Dir: repoPath(repo),
Stdout: stdout,
Stderr: stderr,
})
}
11 changes: 2 additions & 9 deletions modules/templates/util_misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ import (

activities_model "code.gitea.io/gitea/models/activities"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/repository"
Expand Down Expand Up @@ -145,18 +144,12 @@ type remoteAddress struct {

func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string) remoteAddress {
ret := remoteAddress{}
remoteURL, err := git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
u, err := gitrepo.GitRemoteGetURL(ctx, m, remoteName)
if err != nil {
log.Error("GetRemoteURL %v", err)
return ret
}

u, err := giturl.ParseGitURL(remoteURL)
if err != nil {
log.Error("giturl.Parse %v", err)
return ret
}

if u.Scheme != "ssh" && u.Scheme != "file" {
if u.User != nil {
ret.Username = u.User.Username()
Expand Down
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
Loading