This commit is contained in:
Lunny Xiao 2025-07-26 11:06:32 -07:00 committed by GitHub
commit a2e6f1ca0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 360 additions and 221 deletions

View File

@ -9,7 +9,6 @@ import (
"net/url" "net/url"
"strings" "strings"
giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
) )
@ -33,15 +32,6 @@ func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string,
return result, nil 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. // ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
type ErrInvalidCloneAddr struct { type ErrInvalidCloneAddr struct {
Host string Host string

View File

@ -38,6 +38,17 @@ func (repo *Repository) GetAllCommitsCount() (int64, error) {
return AllCommitsCount(repo.Ctx, repo.Path, false) 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) { func (repo *Repository) parsePrettyFormatLogToList(logs []byte) ([]*Commit, error) {
var commits []*Commit var commits []*Commit
if len(logs) == 0 { if len(logs) == 0 {

View File

@ -79,12 +79,6 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error {
return err 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 // RenameBranch rename a branch
func (repo *Repository) RenameBranch(from, to string) error { func (repo *Repository) RenameBranch(from, to string) error {
_, _, err := NewCommand("branch", "-m").AddDynamicArguments(from, to).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) _, _, err := NewCommand("branch", "-m").AddDynamicArguments(from, to).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})

View File

@ -16,20 +16,8 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"strings" "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. // 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) { func (repo *Repository) GetMergeBase(tmpRemote, base, head string) (string, string, error) {
if tmpRemote == "" { if tmpRemote == "" {
@ -49,83 +37,6 @@ func (repo *Repository) GetMergeBase(tmpRemote, base, head string) (string, stri
return strings.TrimSpace(stdout), base, err 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 { type lineCountWriter struct {
numLines int numLines int
} }

155
modules/gitrepo/config.go Normal file
View File

@ -0,0 +1,155 @@
// 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"
)
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
}
if len(result) > 0 {
result = result[:len(result)-1] // remove trailing newline
}
return 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 {
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
}
// 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 {
releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath()))
if err != nil {
return err
}
defer releaser()
_, _, err = git.NewCommand("config").
AddDynamicArguments(key, value).
RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
return err
}
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 {
releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath()))
if err != nil {
return err
}
defer releaser()
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 {
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
}
// 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, nil
}
return giturl.ParseGitURL(addr)
}
func SetRemoteURL(ctx context.Context, repo Repository, remoteName, remoteURL string) error {
releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath()))
if err != nil {
return err
}
defer releaser()
cmd := git.NewCommand("remote", "set-url").AddDynamicArguments(remoteName, remoteURL)
_, _, err = cmd.RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
return err
}
// GitRemotePrune prunes the remote branches that no longer exist in the remote repository.
// No lock is needed because the remote remoteName will be checked before invoking this function.
// Then it will not update the remote automatically if the remote does not exist.
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.
// No lock is needed because the remote remoteName will be checked before invoking this function.
// Then it will not update the remote automatically if the remote does not exist.
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,
})
}

View File

@ -14,8 +14,7 @@ import (
activities_model "code.gitea.io/gitea/models/activities" activities_model "code.gitea.io/gitea/models/activities"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo"
giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/repository"
@ -145,15 +144,13 @@ type remoteAddress struct {
func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string) remoteAddress { func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string) remoteAddress {
ret := remoteAddress{} ret := remoteAddress{}
remoteURL, err := git.GetRemoteAddress(ctx, m.RepoPath(), remoteName) u, err := gitrepo.GitRemoteGetURL(ctx, m, remoteName)
if err != nil { if err != nil {
log.Error("GetRemoteURL %v", err) log.Error("GetRemoteURL %v", err)
return ret return ret
} }
if u == nil {
u, err := giturl.ParseGitURL(remoteURL) log.Error("GetRemoteURL %s returned nil", remoteName)
if err != nil {
log.Error("giturl.Parse %v", err)
return ret return ret
} }

View File

@ -1085,7 +1085,7 @@ func MergePullRequest(ctx *context.APIContext) {
type parseCompareInfoResult struct { type parseCompareInfoResult struct {
headRepo *repo_model.Repository headRepo *repo_model.Repository
headGitRepo *git.Repository headGitRepo *git.Repository
compareInfo *git.CompareInfo compareInfo *pull_service.CompareInfo
baseRef git.RefName baseRef git.RefName
headRef git.RefName headRef git.RefName
} }
@ -1201,7 +1201,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
return nil, nil 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 { if err != nil {
ctx.APIErrorInternal(err) ctx.APIErrorInternal(err)
return nil, nil return nil, nil
@ -1452,7 +1452,7 @@ func GetPullRequestCommits(ctx *context.APIContext) {
return return
} }
var prInfo *git.CompareInfo var prInfo *pull_service.CompareInfo
baseGitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, pr.BaseRepo) baseGitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, pr.BaseRepo)
if err != nil { if err != nil {
ctx.APIErrorInternal(err) ctx.APIErrorInternal(err)
@ -1461,9 +1461,9 @@ func GetPullRequestCommits(ctx *context.APIContext) {
defer closer.Close() defer closer.Close()
if pr.HasMerged { 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 { } 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 { if err != nil {
ctx.APIErrorInternal(err) ctx.APIErrorInternal(err)
@ -1582,11 +1582,11 @@ func GetPullRequestFiles(ctx *context.APIContext) {
baseGitRepo := ctx.Repo.GitRepo baseGitRepo := ctx.Repo.GitRepo
var prInfo *git.CompareInfo var prInfo *pull_service.CompareInfo
if pr.HasMerged { 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 { } 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 { if err != nil {
ctx.APIErrorInternal(err) ctx.APIErrorInternal(err)

View File

@ -7,6 +7,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
pull_service "code.gitea.io/gitea/services/pull"
) )
// CompareInfo represents the collected results from ParseCompareInfo // CompareInfo represents the collected results from ParseCompareInfo
@ -14,7 +15,7 @@ type CompareInfo struct {
HeadUser *user_model.User HeadUser *user_model.User
HeadRepo *repo_model.Repository HeadRepo *repo_model.Repository
HeadGitRepo *git.Repository HeadGitRepo *git.Repository
CompareInfo *git.CompareInfo CompareInfo *pull_service.CompareInfo
BaseBranch string BaseBranch string
HeadBranch string HeadBranch string
DirectComparison bool DirectComparison bool

View File

@ -41,6 +41,7 @@ import (
"code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/context/upload"
"code.gitea.io/gitea/services/gitdiff" "code.gitea.io/gitea/services/gitdiff"
pull_service "code.gitea.io/gitea/services/pull"
) )
const ( const (
@ -550,7 +551,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
headBranchRef = git.TagPrefix + ci.HeadBranch 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 { if err != nil {
ctx.ServerError("GetCompareInfo", err) ctx.ServerError("GetCompareInfo", err)
return nil return nil

View File

@ -254,7 +254,7 @@ func GetMergedBaseCommitID(ctx *context.Context, issue *issues_model.Issue) stri
return baseCommit return baseCommit
} }
func preparePullViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.CompareInfo { func preparePullViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *pull_service.CompareInfo {
if !issue.IsPull { if !issue.IsPull {
return nil return nil
} }
@ -265,7 +265,7 @@ func preparePullViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *g
} }
// prepareMergedViewPullInfo show meta information for a merged pull request view page // prepareMergedViewPullInfo show meta information for a merged pull request view page
func prepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.CompareInfo { func prepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *pull_service.CompareInfo {
pull := issue.PullRequest pull := issue.PullRequest
setMergeTarget(ctx, pull) setMergeTarget(ctx, pull)
@ -273,7 +273,7 @@ func prepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue)
baseCommit := GetMergedBaseCommitID(ctx, issue) baseCommit := GetMergedBaseCommitID(ctx, issue)
compareInfo, err := ctx.Repo.GitRepo.GetCompareInfo(ctx.Repo.Repository.RepoPath(), compareInfo, err := pull_service.GetCompareInfo(ctx, ctx.Repo.Repository, ctx.Repo.Repository, ctx.Repo.GitRepo,
baseCommit, pull.GetGitHeadRefName(), false, false) baseCommit, pull.GetGitHeadRefName(), false, false)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "fatal: Not a valid object name") || strings.Contains(err.Error(), "unknown revision or path not in the working tree") { if strings.Contains(err.Error(), "fatal: Not a valid object name") || strings.Contains(err.Error(), "unknown revision or path not in the working tree") {
@ -311,7 +311,7 @@ func prepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue)
} }
// prepareViewPullInfo show meta information for a pull request preview page // prepareViewPullInfo show meta information for a pull request preview page
func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.CompareInfo { func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *pull_service.CompareInfo {
ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
repo := ctx.Repo.Repository repo := ctx.Repo.Repository
@ -373,7 +373,7 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(commitStatuses) ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(commitStatuses)
} }
compareInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(), compareInfo, err := pull_service.GetCompareInfo(ctx, pull.BaseRepo, pull.BaseRepo, baseGitRepo,
pull.MergeBase, pull.GetGitHeadRefName(), false, false) pull.MergeBase, pull.GetGitHeadRefName(), false, false)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "fatal: Not a valid object name") { if strings.Contains(err.Error(), "fatal: Not a valid object name") {
@ -521,7 +521,7 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
} }
} }
compareInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(), compareInfo, err := pull_service.GetCompareInfo(ctx, pull.BaseRepo, pull.BaseRepo, baseGitRepo,
git.BranchPrefix+pull.BaseBranch, pull.GetGitHeadRefName(), false, false) git.BranchPrefix+pull.BaseBranch, pull.GetGitHeadRefName(), false, false)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "fatal: Not a valid object name") { if strings.Contains(err.Error(), "fatal: Not a valid object name") {

View File

@ -16,6 +16,7 @@ import (
unit_model "code.gitea.io/gitea/models/unit" unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/indexer/code" "code.gitea.io/gitea/modules/indexer/code"
issue_indexer "code.gitea.io/gitea/modules/indexer/issues" issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
"code.gitea.io/gitea/modules/indexer/stats" "code.gitea.io/gitea/modules/indexer/stats"
@ -258,12 +259,17 @@ func handleSettingsPostMirror(ctx *context.Context) {
return return
} }
u, err := git.GetRemoteURL(ctx, ctx.Repo.Repository.RepoPath(), pullMirror.GetRemoteName()) u, err := gitrepo.GitRemoteGetURL(ctx, ctx.Repo.Repository, pullMirror.GetRemoteName())
if err != nil { if err != nil {
ctx.Data["Err_MirrorAddress"] = true ctx.Data["Err_MirrorAddress"] = true
handleSettingRemoteAddrError(ctx, err, form) handleSettingRemoteAddrError(ctx, err, form)
return return
} }
if u == nil {
ctx.Data["Err_MirrorAddress"] = true
handleSettingRemoteAddrError(ctx, err, form)
return
}
if u.User != nil && form.MirrorPassword == "" && form.MirrorUsername == u.User.Username() { if u.User != nil && form.MirrorPassword == "" && form.MirrorUsername == u.User.Username() {
form.MirrorPassword, _ = u.User.Password() form.MirrorPassword, _ = u.User.Password()
} }

View File

@ -91,18 +91,12 @@ func checkEnablePushOptions(ctx context.Context, logger log.Logger, autofix bool
if err := iterateRepositories(ctx, func(repo *repo_model.Repository) error { if err := iterateRepositories(ctx, func(repo *repo_model.Repository) error {
numRepos++ numRepos++
r, err := gitrepo.OpenRepository(ctx, repo)
if err != nil {
return err
}
defer r.Close()
if autofix { if autofix {
_, _, err := git.NewCommand("config", "receive.advertisePushOptions", "true").RunStdString(ctx, &git.RunOpts{Dir: r.Path}) return gitrepo.GitConfigSet(ctx, repo, "receive.advertisePushOptions", "true")
return err
} }
value, _, err := git.NewCommand("config", "receive.advertisePushOptions").RunStdString(ctx, &git.RunOpts{Dir: r.Path}) value, err := gitrepo.GitConfigGet(ctx, repo, "receive.advertisePushOptions")
if err != nil { if err != nil {
return err return err
} }

View File

@ -11,6 +11,7 @@ import (
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
org_model "code.gitea.io/gitea/models/organization" org_model "code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/gitrepo"
@ -18,19 +19,19 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
) )
func getMergeBase(repo *git.Repository, pr *issues_model.PullRequest, baseBranch, headBranch string) (string, error) { func getMergeBase(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, pr *issues_model.PullRequest, baseBranch, headBranch string) (string, error) {
// Add a temporary remote // Add a temporary remote
tmpRemote := fmt.Sprintf("mergebase-%d-%d", pr.ID, time.Now().UnixNano()) tmpRemote := fmt.Sprintf("mergebase-%d-%d", pr.ID, time.Now().UnixNano())
if err := repo.AddRemote(tmpRemote, repo.Path, false); err != nil { if err := gitrepo.GitRemoteAdd(ctx, repo, tmpRemote, gitRepo.Path); err != nil {
return "", fmt.Errorf("AddRemote: %w", err) return "", fmt.Errorf("GitRemoteAdd: %w", err)
} }
defer func() { defer func() {
if err := repo.RemoveRemote(tmpRemote); err != nil { if err := gitrepo.GitRemoteRemove(ctx, repo, tmpRemote); err != nil {
log.Error("getMergeBase: RemoveRemote: %v", err) log.Error("getMergeBase: GitRemoteRemove: %v", err)
} }
}() }()
mergeBase, _, err := repo.GetMergeBase(tmpRemote, baseBranch, headBranch) mergeBase, _, err := gitRepo.GetMergeBase(tmpRemote, baseBranch, headBranch)
return mergeBase, err return mergeBase, err
} }
@ -97,7 +98,7 @@ func PullRequestCodeOwnersReview(ctx context.Context, pr *issues_model.PullReque
} }
// get the mergebase // get the mergebase
mergeBase, err := getMergeBase(repo, pr, git.BranchPrefix+pr.BaseBranch, pr.GetGitHeadRefName()) mergeBase, err := getMergeBase(ctx, pr.BaseRepo, repo, pr, git.BranchPrefix+pr.BaseBranch, pr.GetGitHeadRefName())
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -39,30 +39,27 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
} }
remoteName := m.GetRemoteName() remoteName := m.GetRemoteName()
repoPath := m.GetRepository(ctx).RepoPath() repo := m.GetRepository(ctx)
// Remove old remote // Remove old remote
_, _, err = git.NewCommand("remote", "rm").AddDynamicArguments(remoteName).RunStdString(ctx, &git.RunOpts{Dir: repoPath}) err = gitrepo.GitRemoteRemove(ctx, repo, remoteName)
if err != nil && !git.IsRemoteNotExistError(err) { if err != nil && !git.IsRemoteNotExistError(err) {
return err return err
} }
cmd := git.NewCommand("remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(addr) err = gitrepo.GitRemoteAdd(ctx, repo, remoteName, addr, "--mirror=fetch")
_, _, err = cmd.RunStdString(ctx, &git.RunOpts{Dir: repoPath})
if err != nil && !git.IsRemoteNotExistError(err) { if err != nil && !git.IsRemoteNotExistError(err) {
return err return err
} }
if m.Repo.HasWiki() { if m.Repo.HasWiki() {
wikiPath := m.Repo.WikiPath()
wikiRemotePath := repo_module.WikiRemoteURL(ctx, addr) wikiRemotePath := repo_module.WikiRemoteURL(ctx, addr)
// Remove old remote of wiki // Remove old remote of wiki
_, _, err = git.NewCommand("remote", "rm").AddDynamicArguments(remoteName).RunStdString(ctx, &git.RunOpts{Dir: wikiPath}) err = gitrepo.GitRemoteRemove(ctx, repo.WikiStorageRepo(), remoteName)
if err != nil && !git.IsRemoteNotExistError(err) { if err != nil && !git.IsRemoteNotExistError(err) {
return err return err
} }
cmd = git.NewCommand("remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(wikiRemotePath) err = gitrepo.GitRemoteAdd(ctx, repo.WikiStorageRepo(), remoteName, wikiRemotePath, "--mirror=fetch")
_, _, err = cmd.RunStdString(ctx, &git.RunOpts{Dir: wikiPath})
if err != nil && !git.IsRemoteNotExistError(err) { if err != nil && !git.IsRemoteNotExistError(err) {
return err return err
} }
@ -197,25 +194,21 @@ func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult {
func pruneBrokenReferences(ctx context.Context, func pruneBrokenReferences(ctx context.Context,
m *repo_model.Mirror, m *repo_model.Mirror,
repoPath string,
timeout time.Duration, timeout time.Duration,
stdoutBuilder, stderrBuilder *strings.Builder, stdoutBuilder, stderrBuilder *strings.Builder,
isWiki bool, isWiki bool,
) error { ) error {
wiki := "" wiki := ""
var storageRepo gitrepo.Repository = m.Repo
if isWiki { if isWiki {
wiki = "Wiki " wiki = "Wiki "
storageRepo = m.Repo.WikiStorageRepo()
} }
stderrBuilder.Reset() stderrBuilder.Reset()
stdoutBuilder.Reset() stdoutBuilder.Reset()
pruneErr := git.NewCommand("remote", "prune").AddDynamicArguments(m.GetRemoteName()).
Run(ctx, &git.RunOpts{ pruneErr := gitrepo.GitRemotePrune(ctx, storageRepo, m.GetRemoteName(), timeout, stdoutBuilder, stderrBuilder)
Timeout: timeout,
Dir: repoPath,
Stdout: stdoutBuilder,
Stderr: stderrBuilder,
})
if pruneErr != nil { if pruneErr != nil {
stdout := stdoutBuilder.String() stdout := stdoutBuilder.String()
stderr := stderrBuilder.String() stderr := stderrBuilder.String()
@ -226,7 +219,7 @@ func pruneBrokenReferences(ctx context.Context,
stdoutMessage := util.SanitizeCredentialURLs(stdout) stdoutMessage := util.SanitizeCredentialURLs(stdout)
log.Error("Failed to prune mirror repository %s%-v references:\nStdout: %s\nStderr: %s\nErr: %v", wiki, m.Repo, stdoutMessage, stderrMessage, pruneErr) log.Error("Failed to prune mirror repository %s%-v references:\nStdout: %s\nStderr: %s\nErr: %v", wiki, m.Repo, stdoutMessage, stderrMessage, pruneErr)
desc := fmt.Sprintf("Failed to prune mirror repository %s'%s' references: %s", wiki, repoPath, stderrMessage) desc := fmt.Sprintf("Failed to prune mirror repository %s'%s' references: %s", wiki, storageRepo.RelativePath(), stderrMessage)
if err := system_model.CreateRepositoryNotice(desc); err != nil { if err := system_model.CreateRepositoryNotice(desc); err != nil {
log.Error("CreateRepositoryNotice: %v", err) log.Error("CreateRepositoryNotice: %v", err)
} }
@ -268,9 +261,9 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
} }
cmd.AddArguments("--tags").AddDynamicArguments(m.GetRemoteName()) cmd.AddArguments("--tags").AddDynamicArguments(m.GetRemoteName())
remoteURL, remoteErr := git.GetRemoteURL(ctx, repoPath, m.GetRemoteName()) remoteURL, remoteErr := gitrepo.GitRemoteGetURL(ctx, m.Repo, m.GetRemoteName())
if remoteErr != nil { if remoteErr != nil {
log.Error("SyncMirrors [repo: %-v]: GetRemoteAddress Error %v", m.Repo, remoteErr) log.Error("SyncMirrors [repo: %-v]: GetRemoteURL Error %v", m.Repo, remoteErr)
return nil, false return nil, false
} }
@ -298,7 +291,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
err = nil err = nil
// Attempt prune // Attempt prune
pruneErr := pruneBrokenReferences(ctx, m, repoPath, timeout, &stdoutBuilder, &stderrBuilder, false) pruneErr := pruneBrokenReferences(ctx, m, timeout, &stdoutBuilder, &stderrBuilder, false)
if pruneErr == nil { if pruneErr == nil {
// Successful prune - reattempt mirror // Successful prune - reattempt mirror
stderrBuilder.Reset() stderrBuilder.Reset()
@ -371,13 +364,31 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
log.Trace("SyncMirrors [repo: %-v Wiki]: running git remote update...", m.Repo) log.Trace("SyncMirrors [repo: %-v Wiki]: running git remote update...", m.Repo)
stderrBuilder.Reset() stderrBuilder.Reset()
stdoutBuilder.Reset() stdoutBuilder.Reset()
if err := git.NewCommand("remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()).
Run(ctx, &git.RunOpts{ // check whether the remote still exists before pruning to avoid prune creating a new remote
Timeout: timeout, // this is needed because prune will not fail if the remote does not exist
Dir: wikiPath, u, err := gitrepo.GitRemoteGetURL(ctx, m.Repo.WikiStorageRepo(), m.GetRemoteName())
Stdout: &stdoutBuilder, if err != nil {
Stderr: &stderrBuilder, log.Error("SyncMirrors [repo: %-v Wiki]: GetRemoteURL Error %v", m.Repo, err)
}); err != nil { return nil, false
}
if u == nil {
log.Error("remote %s does not exist for repository %s", m.GetRemoteName(), m.Repo.WikiStorageRepo().RelativePath())
return nil, false
}
fetchConfig, err := gitrepo.GitConfigGet(ctx, m.Repo.WikiStorageRepo(), "remote.origin.fetch")
if err != nil {
log.Error("SyncMirrors [repo: %-v Wiki]: GetGitConfig Error %v", m.Repo, err)
return nil, false
}
if fetchConfig == "" {
log.Error("remote %s has no fetch config for repository %s", m.GetRemoteName(), m.Repo.WikiStorageRepo().RelativePath())
return nil, false
}
if err := gitrepo.GitRemoteUpdatePrune(ctx, m.Repo.WikiStorageRepo(), m.GetRemoteName(),
timeout, &stdoutBuilder, &stderrBuilder); err != nil {
stdout := stdoutBuilder.String() stdout := stdoutBuilder.String()
stderr := stderrBuilder.String() stderr := stderrBuilder.String()
@ -391,19 +402,14 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
err = nil err = nil
// Attempt prune // Attempt prune
pruneErr := pruneBrokenReferences(ctx, m, repoPath, timeout, &stdoutBuilder, &stderrBuilder, true) pruneErr := pruneBrokenReferences(ctx, m, timeout, &stdoutBuilder, &stderrBuilder, true)
if pruneErr == nil { if pruneErr == nil {
// Successful prune - reattempt mirror // Successful prune - reattempt mirror
stderrBuilder.Reset() stderrBuilder.Reset()
stdoutBuilder.Reset() stdoutBuilder.Reset()
if err = git.NewCommand("remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()). if err = gitrepo.GitRemoteUpdatePrune(ctx, m.Repo.WikiStorageRepo(), m.GetRemoteName(),
Run(ctx, &git.RunOpts{ timeout, &stdoutBuilder, &stderrBuilder); err != nil {
Timeout: timeout,
Dir: wikiPath,
Stdout: &stdoutBuilder,
Stderr: &stderrBuilder,
}); err != nil {
stdout := stdoutBuilder.String() stdout := stdoutBuilder.String()
stderr := stderrBuilder.String() stderr := stderrBuilder.String()
stderrMessage = util.SanitizeCredentialURLs(stderr) stderrMessage = util.SanitizeCredentialURLs(stderr)

View File

@ -29,28 +29,24 @@ var stripExitStatus = regexp.MustCompile(`exit status \d+ - `)
// AddPushMirrorRemote registers the push mirror remote. // AddPushMirrorRemote registers the push mirror remote.
func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error { func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error {
addRemoteAndConfig := func(addr, path string) error { addRemoteAndConfig := func(storageRepo gitrepo.Repository, addr string) error {
cmd := git.NewCommand("remote", "add", "--mirror=push").AddDynamicArguments(m.RemoteName, addr) if err := gitrepo.GitRemoteAdd(ctx, storageRepo, m.RemoteName, addr, "--mirror=push"); err != nil {
if _, _, err := cmd.RunStdString(ctx, &git.RunOpts{Dir: path}); err != nil {
return err return err
} }
if _, _, err := git.NewCommand("config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunStdString(ctx, &git.RunOpts{Dir: path}); err != nil { if err := gitrepo.GitConfigAdd(ctx, storageRepo, "remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*"); err != nil {
return err return err
} }
if _, _, err := git.NewCommand("config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*").RunStdString(ctx, &git.RunOpts{Dir: path}); err != nil { return gitrepo.GitConfigAdd(ctx, storageRepo, "remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*")
return err
}
return nil
} }
if err := addRemoteAndConfig(addr, m.Repo.RepoPath()); err != nil { if err := addRemoteAndConfig(m.Repo, addr); err != nil {
return err return err
} }
if m.Repo.HasWiki() { if m.Repo.HasWiki() {
wikiRemoteURL := repository.WikiRemoteURL(ctx, addr) wikiRemoteURL := repository.WikiRemoteURL(ctx, addr)
if len(wikiRemoteURL) > 0 { if len(wikiRemoteURL) > 0 {
if err := addRemoteAndConfig(wikiRemoteURL, m.Repo.WikiPath()); err != nil { if err := addRemoteAndConfig(m.Repo.WikiStorageRepo(), wikiRemoteURL); err != nil {
return err return err
} }
} }
@ -61,15 +57,13 @@ func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr str
// RemovePushMirrorRemote removes the push mirror remote. // RemovePushMirrorRemote removes the push mirror remote.
func RemovePushMirrorRemote(ctx context.Context, m *repo_model.PushMirror) error { func RemovePushMirrorRemote(ctx context.Context, m *repo_model.PushMirror) error {
cmd := git.NewCommand("remote", "rm").AddDynamicArguments(m.RemoteName)
_ = m.GetRepository(ctx) _ = m.GetRepository(ctx)
if err := gitrepo.GitRemoteRemove(ctx, m.Repo, m.RemoteName); err != nil {
if _, _, err := cmd.RunStdString(ctx, &git.RunOpts{Dir: m.Repo.RepoPath()}); err != nil {
return err return err
} }
if m.Repo.HasWiki() { if m.Repo.HasWiki() {
if _, _, err := cmd.RunStdString(ctx, &git.RunOpts{Dir: m.Repo.WikiPath()}); err != nil { if err := gitrepo.GitRemoteRemove(ctx, m.Repo.WikiStorageRepo(), m.RemoteName); err != nil {
// The wiki remote may not exist // The wiki remote may not exist
log.Warn("Wiki Remote[%d] could not be removed: %v", m.ID, err) log.Warn("Wiki Remote[%d] could not be removed: %v", m.ID, err)
} }
@ -128,25 +122,22 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error {
timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second
performPush := func(repo *repo_model.Repository, isWiki bool) error { performPush := func(repo *repo_model.Repository, isWiki bool) error {
var storageRepo gitrepo.Repository = repo
path := repo.RepoPath() path := repo.RepoPath()
if isWiki { if isWiki {
storageRepo = repo.WikiStorageRepo()
path = repo.WikiPath() path = repo.WikiPath()
} }
remoteURL, err := git.GetRemoteURL(ctx, path, m.RemoteName) remoteURL, err := gitrepo.GitRemoteGetURL(ctx, storageRepo, m.RemoteName)
if err != nil { if err != nil {
log.Error("GetRemoteAddress(%s) Error %v", path, err) log.Error("GetRemoteURL(%s) Error %v", path, err)
return errors.New("Unexpected error") return errors.New("Unexpected error")
} }
if setting.LFS.StartServer { if setting.LFS.StartServer {
log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo) log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo)
var gitRepo *git.Repository gitRepo, err := gitrepo.OpenRepository(ctx, storageRepo)
if isWiki {
gitRepo, err = gitrepo.OpenRepository(ctx, repo.WikiStorageRepo())
} else {
gitRepo, err = gitrepo.OpenRepository(ctx, repo)
}
if err != nil { if err != nil {
log.Error("OpenRepository: %v", err) log.Error("OpenRepository: %v", err)
return errors.New("Unexpected error") return errors.New("Unexpected error")
@ -184,8 +175,8 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error {
} }
if m.Repo.HasWiki() { if m.Repo.HasWiki() {
_, err := git.GetRemoteAddress(ctx, m.Repo.WikiPath(), m.RemoteName) u, err := gitrepo.GitRemoteGetURL(ctx, m.Repo.WikiStorageRepo(), m.RemoteName)
if err == nil { if err == nil && u != nil {
err := performPush(m.Repo, true) err := performPush(m.Repo, true)
if err != nil { if err != nil {
return err return err

94
services/pull/compare.go Normal file
View File

@ -0,0 +1,94 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package pull
import (
"context"
"fmt"
"strconv"
"time"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
logger "code.gitea.io/gitea/modules/log"
)
// CompareInfo represents needed information for comparing references.
type CompareInfo struct {
MergeBase string
BaseCommitID string
HeadCommitID string
Commits []*git.Commit
NumFiles int
}
// GetCompareInfo generates and returns compare information between base and head branches of repositories.
func GetCompareInfo(ctx context.Context, baseRepo, headRepo *repo_model.Repository, headGitRepo *git.Repository, 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 headGitRepo.Path != baseRepo.RepoPath() {
// Add a temporary remote
tmpRemote = strconv.FormatInt(time.Now().UnixNano(), 10)
if err = gitrepo.GitRemoteAdd(ctx, headRepo, tmpRemote, baseRepo.RepoPath()); err != nil {
return nil, fmt.Errorf("AddRemote: %w", err)
}
defer func() {
if err := gitrepo.GitRemoteRemove(ctx, headRepo, tmpRemote); err != nil {
logger.Error("GetPullRequestInfo: GitRemoteRemove: %v", err)
}
}()
}
compareInfo := new(CompareInfo)
compareInfo.HeadCommitID, err = git.GetFullCommitID(ctx, headGitRepo.Path, headBranch)
if err != nil {
compareInfo.HeadCommitID = headBranch
}
compareInfo.MergeBase, remoteBranch, err = headGitRepo.GetMergeBase(tmpRemote, baseBranch, headBranch)
if err == nil {
compareInfo.BaseCommitID, err = git.GetFullCommitID(ctx, headGitRepo.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 {
compareInfo.Commits, err = headGitRepo.ShowPrettyFormatLogToList(ctx, baseCommitID+separator+headBranch)
if err != nil {
return nil, fmt.Errorf("ShowPrettyFormatLogToList: %w", err)
}
} else {
compareInfo.Commits = []*git.Commit{}
}
} else {
compareInfo.Commits = []*git.Commit{}
compareInfo.MergeBase, err = git.GetFullCommitID(ctx, headGitRepo.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 = headGitRepo.GetDiffNumChangedFiles(remoteBranch, headBranch, directComparison)
if err != nil {
return nil, err
}
return compareInfo, nil
}

View File

@ -142,7 +142,7 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error {
return err return err
} }
compareInfo, err := baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), compareInfo, err := GetCompareInfo(ctx, pr.BaseRepo, pr.BaseRepo, baseGitRepo,
git.BranchPrefix+pr.BaseBranch, pr.GetGitHeadRefName(), false, false) git.BranchPrefix+pr.BaseBranch, pr.GetGitHeadRefName(), false, false)
if err != nil { if err != nil {
return err return err
@ -1077,7 +1077,7 @@ func GetPullCommits(ctx *gitea_context.Context, issue *issues_model.Issue) ([]Co
if pull.HasMerged { if pull.HasMerged {
baseBranch = pull.MergeBase baseBranch = pull.MergeBase
} }
prInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(), baseBranch, pull.GetGitHeadRefName(), true, false) prInfo, err := GetCompareInfo(ctx, pull.BaseRepo, pull.BaseRepo, baseGitRepo, baseBranch, pull.GetGitHeadRefName(), true, false)
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }

View File

@ -261,20 +261,6 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
return repo, committer.Commit() return repo, committer.Commit()
} }
// cleanUpMigrateGitConfig removes mirror info which prevents "push --all".
// This also removes possible user credentials.
func cleanUpMigrateGitConfig(ctx context.Context, repoPath string) error {
cmd := git.NewCommand("remote", "rm", "origin")
// if the origin does not exist
_, _, err := cmd.RunStdString(ctx, &git.RunOpts{
Dir: repoPath,
})
if err != nil && !git.IsRemoteNotExistError(err) {
return err
}
return nil
}
// CleanUpMigrateInfo finishes migrating repository and/or wiki with things that don't need to be done for mirrors. // CleanUpMigrateInfo finishes migrating repository and/or wiki with things that don't need to be done for mirrors.
func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo_model.Repository, error) { func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo_model.Repository, error) {
if err := gitrepo.CreateDelegateHooks(ctx, repo); err != nil { if err := gitrepo.CreateDelegateHooks(ctx, repo); err != nil {
@ -286,13 +272,14 @@ func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo
} }
} }
_, _, err := git.NewCommand("remote", "rm", "origin").RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}) err := gitrepo.GitRemoteRemove(ctx, repo, "origin")
if err != nil && !git.IsRemoteNotExistError(err) { if err != nil && !git.IsRemoteNotExistError(err) {
return repo, fmt.Errorf("CleanUpMigrateInfo: %w", err) return repo, fmt.Errorf("CleanUpMigrateInfo: %w", err)
} }
if repo.HasWiki() { if repo.HasWiki() {
if err := cleanUpMigrateGitConfig(ctx, repo.WikiPath()); err != nil { err = gitrepo.GitRemoteRemove(ctx, repo.WikiStorageRepo(), "origin")
if err != nil && !git.IsRemoteNotExistError(err) {
return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %w", err) return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %w", err)
} }
} }