mirror of
https://github.com/go-gitea/gitea
synced 2025-09-10 10:48:28 +00:00
Move git config/remote to gitrepo package and add global lock to resolve possible conflict when updating repository git config file (#35151)
Partially fix #32018 `git config` and `git remote` write operations create a temporary file named `config.lock`. Since these operations are not atomic, they must not be run in parallel. If two requests attempt to modify the same repository concurrently—such as during a compare operation—one may fail due to the presence of an existing `config.lock` file. In cases where `config.lock` is left behind due to an unexpected program exit, a global lock mechanism could allow us to safely remove the stale lock file when a related error is detected. While this behavior is not yet implemented in this PR, it is planned for a future enhancement. --------- Signed-off-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -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
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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})
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
48
modules/gitrepo/config.go
Normal file
48
modules/gitrepo/config.go
Normal file
@@ -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
modules/gitrepo/remote.go
Normal file
85
modules/gitrepo/remote.go
Normal file
@@ -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,
|
||||||
|
})
|
||||||
|
}
|
@@ -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,18 +144,12 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
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.Scheme != "ssh" && u.Scheme != "file" {
|
||||||
if u.User != nil {
|
if u.User != nil {
|
||||||
ret.Username = u.User.Username()
|
ret.Username = u.User.Username()
|
||||||
|
@@ -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)
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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") {
|
||||||
|
@@ -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,7 +259,7 @@ 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)
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -11,26 +11,28 @@ 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"
|
||||||
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"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(graceful.GetManager().ShutdownContext(), 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 +99,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
|
||||||
}
|
}
|
||||||
|
@@ -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, gitrepo.RemoteOptionMirrorFetch)
|
||||||
_, _, 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 repo_service.HasWiki(ctx, m.Repo) {
|
if repo_service.HasWiki(ctx, m.Repo) {
|
||||||
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, gitrepo.RemoteOptionMirrorFetch)
|
||||||
_, _, 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,9 @@ 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{
|
if err := gitrepo.GitRemoteUpdatePrune(ctx, m.Repo.WikiStorageRepo(), m.GetRemoteName(),
|
||||||
Timeout: timeout,
|
timeout, &stdoutBuilder, &stderrBuilder); err != nil {
|
||||||
Dir: wikiPath,
|
|
||||||
Stdout: &stdoutBuilder,
|
|
||||||
Stderr: &stderrBuilder,
|
|
||||||
}); err != nil {
|
|
||||||
stdout := stdoutBuilder.String()
|
stdout := stdoutBuilder.String()
|
||||||
stderr := stderrBuilder.String()
|
stderr := stderrBuilder.String()
|
||||||
|
|
||||||
@@ -391,19 +380,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)
|
||||||
|
@@ -30,28 +30,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, gitrepo.RemoteOptionMirrorPush); 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 repo_service.HasWiki(ctx, m.Repo) {
|
if repo_service.HasWiki(ctx, m.Repo) {
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,15 +58,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 repo_service.HasWiki(ctx, m.Repo) {
|
if repo_service.HasWiki(ctx, m.Repo) {
|
||||||
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)
|
||||||
}
|
}
|
||||||
@@ -129,25 +123,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")
|
||||||
@@ -185,14 +176,13 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if repo_service.HasWiki(ctx, m.Repo) {
|
if repo_service.HasWiki(ctx, m.Repo) {
|
||||||
_, err := git.GetRemoteAddress(ctx, m.Repo.WikiPath(), m.RemoteName)
|
if _, err := gitrepo.GitRemoteGetURL(ctx, m.Repo.WikiStorageRepo(), m.RemoteName); err == nil {
|
||||||
if err == nil {
|
|
||||||
err := performPush(m.Repo, true)
|
err := performPush(m.Repo, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else if !errors.Is(err, util.ErrNotExist) {
|
||||||
log.Trace("Skipping wiki: No remote configured")
|
log.Error("GetRemote of wiki failed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
95
services/pull/compare.go
Normal file
95
services/pull/compare.go
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
// 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"
|
||||||
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
|
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("GitRemoteAdd: %w", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := gitrepo.GitRemoteRemove(graceful.GetManager().ShutdownContext(), 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
|
||||||
|
}
|
@@ -141,7 +141,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
|
||||||
@@ -1074,7 +1074,7 @@ func GetPullCommits(ctx context.Context, baseGitRepo *git.Repository, doer *user
|
|||||||
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
|
||||||
}
|
}
|
||||||
|
@@ -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 {
|
||||||
@@ -288,13 +274,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 hasWiki {
|
if 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user