Merge branch 'main' into lunny/issue_dev

This commit is contained in:
Lunny Xiao
2025-03-27 17:32:20 -07:00
1599 changed files with 37936 additions and 75160 deletions
+5 -5
View File
@@ -206,9 +206,9 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com
prHeadRef := pr.GetGitRefName()
// Check if the pull request is merged into BaseBranch
if _, _, err := git.NewCommand(ctx, "merge-base", "--is-ancestor").
if _, _, err := git.NewCommand("merge-base", "--is-ancestor").
AddDynamicArguments(prHeadRef, pr.BaseBranch).
RunStdString(&git.RunOpts{Dir: pr.BaseRepo.RepoPath()}); err != nil {
RunStdString(ctx, &git.RunOpts{Dir: pr.BaseRepo.RepoPath()}); err != nil {
if strings.Contains(err.Error(), "exit status 1") {
// prHeadRef is not an ancestor of the base branch
return nil, nil
@@ -234,9 +234,9 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com
objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName)
// Get the commit from BaseBranch where the pull request got merged
mergeCommit, _, err := git.NewCommand(ctx, "rev-list", "--ancestry-path", "--merges", "--reverse").
AddDynamicArguments(prHeadCommitID + ".." + pr.BaseBranch).
RunStdString(&git.RunOpts{Dir: pr.BaseRepo.RepoPath()})
mergeCommit, _, err := git.NewCommand("rev-list", "--ancestry-path", "--merges", "--reverse").
AddDynamicArguments(prHeadCommitID+".."+pr.BaseBranch).
RunStdString(ctx, &git.RunOpts{Dir: pr.BaseRepo.RepoPath()})
if err != nil {
return nil, fmt.Errorf("git rev-list --ancestry-path --merges --reverse: %w", err)
} else if len(mergeCommit) < objectFormat.FullLength() {
+1 -2
View File
@@ -5,7 +5,6 @@
package pull
import (
"context"
"strconv"
"testing"
"time"
@@ -33,7 +32,7 @@ func TestPullRequest_AddToTaskQueue(t *testing.T) {
cfg, err := setting.GetQueueSettings(setting.CfgProvider, "pr_patch_checker")
assert.NoError(t, err)
prPatchCheckerQueue, err = queue.NewWorkerPoolQueueWithContext(context.Background(), "pr_patch_checker", cfg, testHandler, true)
prPatchCheckerQueue, err = queue.NewWorkerPoolQueueWithContext(t.Context(), "pr_patch_checker", cfg, testHandler, true)
assert.NoError(t, err)
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
+2 -3
View File
@@ -10,7 +10,6 @@ import (
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/structs"
@@ -131,10 +130,10 @@ func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullR
}
defer closer.Close()
if pr.Flow == issues_model.PullRequestFlowGithub && !headGitRepo.IsBranchExist(pr.HeadBranch) {
if pr.Flow == issues_model.PullRequestFlowGithub && !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) {
return "", errors.New("Head branch does not exist, can not merge")
}
if pr.Flow == issues_model.PullRequestFlowAGit && !git.IsReferenceExist(ctx, headGitRepo.Path, pr.GetGitRefName()) {
if pr.Flow == issues_model.PullRequestFlowAGit && !gitrepo.IsReferenceExist(ctx, pr.HeadRepo, pr.GetGitRefName()) {
return "", errors.New("Head branch does not exist, can not merge")
}
+14 -6
View File
@@ -211,7 +211,15 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
}
defer releaser()
defer func() {
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "")
go AddTestPullRequestTask(TestPullRequestOptions{
RepoID: pr.BaseRepo.ID,
Doer: doer,
Branch: pr.BaseBranch,
IsSync: false,
IsForcePush: false,
OldCommitID: "",
NewCommitID: "",
})
}()
_, err = doMergeAndPush(ctx, pr, doer, mergeStyle, expectedHeadCommitID, message, repo_module.PushTriggerPRMergeToBase)
@@ -356,12 +364,12 @@ func doMergeAndPush(ctx context.Context, pr *issues_model.PullRequest, doer *use
)
mergeCtx.env = append(mergeCtx.env, repo_module.EnvPushTrigger+"="+string(pushTrigger))
pushCmd := git.NewCommand(ctx, "push", "origin").AddDynamicArguments(baseBranch + ":" + git.BranchPrefix + pr.BaseBranch)
pushCmd := git.NewCommand("push", "origin").AddDynamicArguments(baseBranch + ":" + git.BranchPrefix + pr.BaseBranch)
// Push back to upstream.
// This cause an api call to "/api/internal/hook/post-receive/...",
// If it's merge, all db transaction and operations should be there but not here to prevent deadlock.
if err := pushCmd.Run(mergeCtx.RunOpts()); err != nil {
if err := pushCmd.Run(ctx, mergeCtx.RunOpts()); err != nil {
if strings.Contains(mergeCtx.errbuf.String(), "non-fast-forward") {
return "", &git.ErrPushOutOfDate{
StdOut: mergeCtx.outbuf.String(),
@@ -386,13 +394,13 @@ func doMergeAndPush(ctx context.Context, pr *issues_model.PullRequest, doer *use
}
func commitAndSignNoAuthor(ctx *mergeContext, message string) error {
cmdCommit := git.NewCommand(ctx, "commit").AddOptionFormat("--message=%s", message)
cmdCommit := git.NewCommand("commit").AddOptionFormat("--message=%s", message)
if ctx.signKeyID == "" {
cmdCommit.AddArguments("--no-gpg-sign")
} else {
cmdCommit.AddOptionFormat("-S%s", ctx.signKeyID)
}
if err := cmdCommit.Run(ctx.RunOpts()); err != nil {
if err := cmdCommit.Run(ctx, ctx.RunOpts()); err != nil {
log.Error("git commit %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
return fmt.Errorf("git commit %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
}
@@ -453,7 +461,7 @@ func (err ErrMergeDivergingFastForwardOnly) Error() string {
}
func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *git.Command) error {
if err := cmd.Run(ctx.RunOpts()); err != nil {
if err := cmd.Run(ctx, ctx.RunOpts()); err != nil {
// Merge will leave a MERGE_HEAD file in the .git folder if there is a conflict
if _, statErr := os.Stat(filepath.Join(ctx.tmpBasePath, ".git", "MERGE_HEAD")); statErr == nil {
// We have a merge conflict error
+1 -1
View File
@@ -11,7 +11,7 @@ import (
// doMergeStyleFastForwardOnly merges the tracking into the current HEAD - which is assumed to be staging branch (equal to the pr.BaseBranch)
func doMergeStyleFastForwardOnly(ctx *mergeContext) error {
cmd := git.NewCommand(ctx, "merge", "--ff-only").AddDynamicArguments(trackingBranch)
cmd := git.NewCommand("merge", "--ff-only").AddDynamicArguments(trackingBranch)
if err := runMergeCommand(ctx, repo_model.MergeStyleFastForwardOnly, cmd); err != nil {
log.Error("%-v Unable to merge tracking into base: %v", ctx.pr, err)
return err
+1 -1
View File
@@ -11,7 +11,7 @@ import (
// doMergeStyleMerge merges the tracking branch into the current HEAD - which is assumed to be the staging branch (equal to the pr.BaseBranch)
func doMergeStyleMerge(ctx *mergeContext, message string) error {
cmd := git.NewCommand(ctx, "merge", "--no-ff", "--no-commit").AddDynamicArguments(trackingBranch)
cmd := git.NewCommand("merge", "--no-ff", "--no-commit").AddDynamicArguments(trackingBranch)
if err := runMergeCommand(ctx, repo_model.MergeStyleMerge, cmd); err != nil {
log.Error("%-v Unable to merge tracking into base: %v", ctx.pr, err)
return err
+11 -11
View File
@@ -73,7 +73,7 @@ func createTemporaryRepoForMerge(ctx context.Context, pr *issues_model.PullReque
}
if expectedHeadCommitID != "" {
trackingCommitID, _, err := git.NewCommand(ctx, "show-ref", "--hash").AddDynamicArguments(git.BranchPrefix + trackingBranch).RunStdString(&git.RunOpts{Dir: mergeCtx.tmpBasePath})
trackingCommitID, _, err := git.NewCommand("show-ref", "--hash").AddDynamicArguments(git.BranchPrefix+trackingBranch).RunStdString(ctx, &git.RunOpts{Dir: mergeCtx.tmpBasePath})
if err != nil {
defer cancel()
log.Error("failed to get sha of head branch in %-v: show-ref[%s] --hash refs/heads/tracking: %v", mergeCtx.pr, mergeCtx.tmpBasePath, err)
@@ -151,8 +151,8 @@ func prepareTemporaryRepoForMerge(ctx *mergeContext) error {
}
setConfig := func(key, value string) error {
if err := git.NewCommand(ctx, "config", "--local").AddDynamicArguments(key, value).
Run(ctx.RunOpts()); err != nil {
if err := git.NewCommand("config", "--local").AddDynamicArguments(key, value).
Run(ctx, ctx.RunOpts()); err != nil {
log.Error("git config [%s -> %q]: %v\n%s\n%s", key, value, err, ctx.outbuf.String(), ctx.errbuf.String())
return fmt.Errorf("git config [%s -> %q]: %w\n%s\n%s", key, value, err, ctx.outbuf.String(), ctx.errbuf.String())
}
@@ -184,8 +184,8 @@ func prepareTemporaryRepoForMerge(ctx *mergeContext) error {
}
// Read base branch index
if err := git.NewCommand(ctx, "read-tree", "HEAD").
Run(ctx.RunOpts()); err != nil {
if err := git.NewCommand("read-tree", "HEAD").
Run(ctx, ctx.RunOpts()); err != nil {
log.Error("git read-tree HEAD: %v\n%s\n%s", err, ctx.outbuf.String(), ctx.errbuf.String())
return fmt.Errorf("Unable to read base branch in to the index: %w\n%s\n%s", err, ctx.outbuf.String(), ctx.errbuf.String())
}
@@ -221,8 +221,8 @@ func getDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string, o
return 0, nil, nil
}
err = git.NewCommand(ctx, "diff-tree", "--no-commit-id", "--name-only", "-r", "-r", "-z", "--root").AddDynamicArguments(baseBranch, headBranch).
Run(&git.RunOpts{
err = git.NewCommand("diff-tree", "--no-commit-id", "--name-only", "-r", "-r", "-z", "--root").AddDynamicArguments(baseBranch, headBranch).
Run(ctx, &git.RunOpts{
Dir: repoPath,
Stdout: diffOutWriter,
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
@@ -272,16 +272,16 @@ func (err ErrRebaseConflicts) Error() string {
// if there is a conflict it will return an ErrRebaseConflicts
func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle) error {
// Checkout head branch
if err := git.NewCommand(ctx, "checkout", "-b").AddDynamicArguments(stagingBranch, trackingBranch).
Run(ctx.RunOpts()); err != nil {
if err := git.NewCommand("checkout", "-b").AddDynamicArguments(stagingBranch, trackingBranch).
Run(ctx, ctx.RunOpts()); err != nil {
return fmt.Errorf("unable to git checkout tracking as staging in temp repo for %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
}
ctx.outbuf.Reset()
ctx.errbuf.Reset()
// Rebase before merging
if err := git.NewCommand(ctx, "rebase").AddDynamicArguments(baseBranch).
Run(ctx.RunOpts()); err != nil {
if err := git.NewCommand("rebase").AddDynamicArguments(baseBranch).
Run(ctx, ctx.RunOpts()); err != nil {
// Rebase will leave a REBASE_HEAD file in .git if there is a conflict
if _, statErr := os.Stat(filepath.Join(ctx.tmpBasePath, ".git", "REBASE_HEAD")); statErr == nil {
var commitSha string
+6 -6
View File
@@ -16,7 +16,7 @@ import (
// getRebaseAmendMessage composes the message to amend commits in rebase merge of a pull request.
func getRebaseAmendMessage(ctx *mergeContext, baseGitRepo *git.Repository) (message string, err error) {
// Get existing commit message.
commitMessage, _, err := git.NewCommand(ctx, "show", "--format=%B", "-s").RunStdString(&git.RunOpts{Dir: ctx.tmpBasePath})
commitMessage, _, err := git.NewCommand("show", "--format=%B", "-s").RunStdString(ctx, &git.RunOpts{Dir: ctx.tmpBasePath})
if err != nil {
return "", err
}
@@ -42,7 +42,7 @@ func doMergeRebaseFastForward(ctx *mergeContext) error {
return fmt.Errorf("Failed to get full commit id for HEAD: %w", err)
}
cmd := git.NewCommand(ctx, "merge", "--ff-only").AddDynamicArguments(stagingBranch)
cmd := git.NewCommand("merge", "--ff-only").AddDynamicArguments(stagingBranch)
if err := runMergeCommand(ctx, repo_model.MergeStyleRebase, cmd); err != nil {
log.Error("Unable to merge staging into base: %v", err)
return err
@@ -73,7 +73,7 @@ func doMergeRebaseFastForward(ctx *mergeContext) error {
}
if newMessage != "" {
if err := git.NewCommand(ctx, "commit", "--amend").AddOptionFormat("--message=%s", newMessage).Run(&git.RunOpts{Dir: ctx.tmpBasePath}); err != nil {
if err := git.NewCommand("commit", "--amend").AddOptionFormat("--message=%s", newMessage).Run(ctx, &git.RunOpts{Dir: ctx.tmpBasePath}); err != nil {
log.Error("Unable to amend commit message: %v", err)
return err
}
@@ -84,7 +84,7 @@ func doMergeRebaseFastForward(ctx *mergeContext) error {
// Perform rebase merge with merge commit.
func doMergeRebaseMergeCommit(ctx *mergeContext, message string) error {
cmd := git.NewCommand(ctx, "merge").AddArguments("--no-ff", "--no-commit").AddDynamicArguments(stagingBranch)
cmd := git.NewCommand("merge").AddArguments("--no-ff", "--no-commit").AddDynamicArguments(stagingBranch)
if err := runMergeCommand(ctx, repo_model.MergeStyleRebaseMerge, cmd); err != nil {
log.Error("Unable to merge staging into base: %v", err)
@@ -105,8 +105,8 @@ func doMergeStyleRebase(ctx *mergeContext, mergeStyle repo_model.MergeStyle, mes
}
// Checkout base branch again
if err := git.NewCommand(ctx, "checkout").AddDynamicArguments(baseBranch).
Run(ctx.RunOpts()); err != nil {
if err := git.NewCommand("checkout").AddDynamicArguments(baseBranch).
Run(ctx, ctx.RunOpts()); err != nil {
log.Error("git checkout base prior to merge post staging rebase %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
return fmt.Errorf("git checkout base prior to merge post staging rebase %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
}
+3 -3
View File
@@ -58,7 +58,7 @@ func doMergeStyleSquash(ctx *mergeContext, message string) error {
return fmt.Errorf("getAuthorSignatureSquash: %w", err)
}
cmdMerge := git.NewCommand(ctx, "merge", "--squash").AddDynamicArguments(trackingBranch)
cmdMerge := git.NewCommand("merge", "--squash").AddDynamicArguments(trackingBranch)
if err := runMergeCommand(ctx, repo_model.MergeStyleSquash, cmdMerge); err != nil {
log.Error("%-v Unable to merge --squash tracking into base: %v", ctx.pr, err)
return err
@@ -71,7 +71,7 @@ func doMergeStyleSquash(ctx *mergeContext, message string) error {
}
message += fmt.Sprintf("\nCo-committed-by: %s\n", sig.String())
}
cmdCommit := git.NewCommand(ctx, "commit").
cmdCommit := git.NewCommand("commit").
AddOptionFormat("--author='%s <%s>'", sig.Name, sig.Email).
AddOptionFormat("--message=%s", message)
if ctx.signKeyID == "" {
@@ -79,7 +79,7 @@ func doMergeStyleSquash(ctx *mergeContext, message string) error {
} else {
cmdCommit.AddOptionFormat("-S%s", ctx.signKeyID)
}
if err := cmdCommit.Run(ctx.RunOpts()); err != nil {
if err := cmdCommit.Run(ctx, ctx.RunOpts()); err != nil {
log.Error("git commit %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
return fmt.Errorf("git commit [%s:%s -> %s:%s]: %w\n%s\n%s", ctx.pr.HeadRepo.FullName(), ctx.pr.HeadBranch, ctx.pr.BaseRepo.FullName(), ctx.pr.BaseBranch, err, ctx.outbuf.String(), ctx.errbuf.String())
}
+12 -12
View File
@@ -92,7 +92,7 @@ func testPatch(ctx context.Context, prCtx *prContext, pr *issues_model.PullReque
defer gitRepo.Close()
// 1. update merge base
pr.MergeBase, _, err = git.NewCommand(ctx, "merge-base", "--", "base", "tracking").RunStdString(&git.RunOpts{Dir: prCtx.tmpBasePath})
pr.MergeBase, _, err = git.NewCommand("merge-base", "--", "base", "tracking").RunStdString(ctx, &git.RunOpts{Dir: prCtx.tmpBasePath})
if err != nil {
var err2 error
pr.MergeBase, err2 = gitRepo.GetRefCommitID(git.BranchPrefix + "base")
@@ -192,7 +192,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f
}
// Need to get the objects from the object db to attempt to merge
root, _, err := git.NewCommand(ctx, "unpack-file").AddDynamicArguments(file.stage1.sha).RunStdString(&git.RunOpts{Dir: tmpBasePath})
root, _, err := git.NewCommand("unpack-file").AddDynamicArguments(file.stage1.sha).RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath})
if err != nil {
return fmt.Errorf("unable to get root object: %s at path: %s for merging. Error: %w", file.stage1.sha, file.stage1.path, err)
}
@@ -201,7 +201,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f
_ = util.Remove(filepath.Join(tmpBasePath, root))
}()
base, _, err := git.NewCommand(ctx, "unpack-file").AddDynamicArguments(file.stage2.sha).RunStdString(&git.RunOpts{Dir: tmpBasePath})
base, _, err := git.NewCommand("unpack-file").AddDynamicArguments(file.stage2.sha).RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath})
if err != nil {
return fmt.Errorf("unable to get base object: %s at path: %s for merging. Error: %w", file.stage2.sha, file.stage2.path, err)
}
@@ -209,7 +209,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f
defer func() {
_ = util.Remove(base)
}()
head, _, err := git.NewCommand(ctx, "unpack-file").AddDynamicArguments(file.stage3.sha).RunStdString(&git.RunOpts{Dir: tmpBasePath})
head, _, err := git.NewCommand("unpack-file").AddDynamicArguments(file.stage3.sha).RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath})
if err != nil {
return fmt.Errorf("unable to get head object:%s at path: %s for merging. Error: %w", file.stage3.sha, file.stage3.path, err)
}
@@ -219,13 +219,13 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f
}()
// now git merge-file annoyingly takes a different order to the merge-tree ...
_, _, conflictErr := git.NewCommand(ctx, "merge-file").AddDynamicArguments(base, root, head).RunStdString(&git.RunOpts{Dir: tmpBasePath})
_, _, conflictErr := git.NewCommand("merge-file").AddDynamicArguments(base, root, head).RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath})
if conflictErr != nil {
return &errMergeConflict{file.stage2.path}
}
// base now contains the merged data
hash, _, err := git.NewCommand(ctx, "hash-object", "-w", "--path").AddDynamicArguments(file.stage2.path, base).RunStdString(&git.RunOpts{Dir: tmpBasePath})
hash, _, err := git.NewCommand("hash-object", "-w", "--path").AddDynamicArguments(file.stage2.path, base).RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath})
if err != nil {
return err
}
@@ -250,7 +250,7 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo
defer cancel()
// First we use read-tree to do a simple three-way merge
if _, _, err := git.NewCommand(ctx, "read-tree", "-m").AddDynamicArguments(base, ours, theirs).RunStdString(&git.RunOpts{Dir: gitPath}); err != nil {
if _, _, err := git.NewCommand("read-tree", "-m").AddDynamicArguments(base, ours, theirs).RunStdString(ctx, &git.RunOpts{Dir: gitPath}); err != nil {
log.Error("Unable to run read-tree -m! Error: %v", err)
return false, nil, fmt.Errorf("unable to run read-tree -m! Error: %w", err)
}
@@ -324,9 +324,9 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
// No conflicts detected so we need to check if the patch is empty...
// a. Write the newly merged tree and check the new tree-hash
var treeHash string
treeHash, _, err = git.NewCommand(ctx, "write-tree").RunStdString(&git.RunOpts{Dir: tmpBasePath})
treeHash, _, err = git.NewCommand("write-tree").RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath})
if err != nil {
lsfiles, _, _ := git.NewCommand(ctx, "ls-files", "-u").RunStdString(&git.RunOpts{Dir: tmpBasePath})
lsfiles, _, _ := git.NewCommand("ls-files", "-u").RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath})
return false, fmt.Errorf("unable to write unconflicted tree: %w\n`git ls-files -u`:\n%s", err, lsfiles)
}
treeHash = strings.TrimSpace(treeHash)
@@ -387,7 +387,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath)
// 4. Read the base branch in to the index of the temporary repository
_, _, err = git.NewCommand(gitRepo.Ctx, "read-tree", "base").RunStdString(&git.RunOpts{Dir: tmpBasePath})
_, _, err = git.NewCommand("read-tree", "base").RunStdString(gitRepo.Ctx, &git.RunOpts{Dir: tmpBasePath})
if err != nil {
return false, fmt.Errorf("git read-tree %s: %w", pr.BaseBranch, err)
}
@@ -400,7 +400,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
prConfig := prUnit.PullRequestsConfig()
// 6. Prepare the arguments to apply the patch against the index
cmdApply := git.NewCommand(gitRepo.Ctx, "apply", "--check", "--cached")
cmdApply := git.NewCommand("apply", "--check", "--cached")
if prConfig.IgnoreWhitespaceConflicts {
cmdApply.AddArguments("--ignore-whitespace")
}
@@ -431,7 +431,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
// 8. Run the check command
conflict = false
err = cmdApply.Run(&git.RunOpts{
err = cmdApply.Run(gitRepo.Ctx, &git.RunOpts{
Dir: tmpBasePath,
Stderr: stderrWriter,
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
+2 -2
View File
@@ -72,8 +72,8 @@ func readUnmergedLsFileLines(ctx context.Context, tmpBasePath string, outputChan
}()
stderr := &strings.Builder{}
err = git.NewCommand(ctx, "ls-files", "-u", "-z").
Run(&git.RunOpts{
err = git.NewCommand("ls-files", "-u", "-z").
Run(ctx, &git.RunOpts{
Dir: tmpBasePath,
Stdout: lsFilesWriter,
Stderr: stderr,
+49
View File
@@ -0,0 +1,49 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package pull
import (
"context"
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/gitrepo"
)
func CreateOrUpdateProtectedBranch(ctx context.Context, repo *repo_model.Repository,
protectBranch *git_model.ProtectedBranch, whitelistOptions git_model.WhitelistOptions,
) error {
err := git_model.UpdateProtectBranch(ctx, repo, protectBranch, whitelistOptions)
if err != nil {
return err
}
isPlainRule := !git_model.IsRuleNameSpecial(protectBranch.RuleName)
var isBranchExist bool
if isPlainRule {
// TODO: read the database directly to check if the branch exists
isBranchExist = gitrepo.IsBranchExist(ctx, repo, protectBranch.RuleName)
}
if isBranchExist {
if err := CheckPRsForBaseBranch(ctx, repo, protectBranch.RuleName); err != nil {
return err
}
} else {
if !isPlainRule {
// FIXME: since we only need to recheck files protected rules, we could improve this
matchedBranches, err := git_model.FindAllMatchedBranches(ctx, repo.ID, protectBranch.RuleName)
if err != nil {
return err
}
for _, branchName := range matchedBranches {
if err = CheckPRsForBaseBranch(ctx, repo, branchName); err != nil {
return err
}
}
}
}
return nil
}
+55 -31
View File
@@ -194,7 +194,7 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error {
}
if !pr.IsWorkInProgress(ctx) {
reviewNotifiers, err = issue_service.PullRequestCodeOwnersReview(ctx, issue, pr)
reviewNotifiers, err = issue_service.PullRequestCodeOwnersReview(ctx, pr)
if err != nil {
return err
}
@@ -390,19 +390,29 @@ func checkForInvalidation(ctx context.Context, requests issues_model.PullRequest
return nil
}
type TestPullRequestOptions struct {
RepoID int64
Doer *user_model.User
Branch string
IsSync bool // True means it's a pull request synchronization, false means it's triggered for pull request merging or updating
IsForcePush bool
OldCommitID string
NewCommitID string
}
// AddTestPullRequestTask adds new test tasks by given head/base repository and head/base branch,
// and generate new patch for testing as needed.
func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, isSync bool, oldCommitID, newCommitID string) {
log.Trace("AddTestPullRequestTask [head_repo_id: %d, head_branch: %s]: finding pull requests", repoID, branch)
func AddTestPullRequestTask(opts TestPullRequestOptions) {
log.Trace("AddTestPullRequestTask [head_repo_id: %d, head_branch: %s]: finding pull requests", opts.RepoID, opts.Branch)
graceful.GetManager().RunWithShutdownContext(func(ctx context.Context) {
// There is no sensible way to shut this down ":-("
// If you don't let it run all the way then you will lose data
// TODO: graceful: AddTestPullRequestTask needs to become a queue!
// GetUnmergedPullRequestsByHeadInfo() only return open and unmerged PR.
prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(ctx, repoID, branch)
prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(ctx, opts.RepoID, opts.Branch)
if err != nil {
log.Error("Find pull requests [head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err)
log.Error("Find pull requests [head_repo_id: %d, head_branch: %s]: %v", opts.RepoID, opts.Branch, err)
return
}
@@ -418,25 +428,24 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string,
}
AddToTaskQueue(ctx, pr)
comment, err := CreatePushPullComment(ctx, doer, pr, oldCommitID, newCommitID)
comment, err := CreatePushPullComment(ctx, opts.Doer, pr, opts.OldCommitID, opts.NewCommitID)
if err == nil && comment != nil {
notify_service.PullRequestPushCommits(ctx, doer, pr, comment)
notify_service.PullRequestPushCommits(ctx, opts.Doer, pr, comment)
}
}
if isSync {
requests := issues_model.PullRequestList(prs)
if err = requests.LoadAttributes(ctx); err != nil {
if opts.IsSync {
if err = prs.LoadAttributes(ctx); err != nil {
log.Error("PullRequestList.LoadAttributes: %v", err)
}
if invalidationErr := checkForInvalidation(ctx, requests, repoID, doer, branch); invalidationErr != nil {
if invalidationErr := checkForInvalidation(ctx, prs, opts.RepoID, opts.Doer, opts.Branch); invalidationErr != nil {
log.Error("checkForInvalidation: %v", invalidationErr)
}
if err == nil {
for _, pr := range prs {
objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName)
if newCommitID != "" && newCommitID != objectFormat.EmptyObjectID().String() {
changed, err := checkIfPRContentChanged(ctx, pr, oldCommitID, newCommitID)
if opts.NewCommitID != "" && opts.NewCommitID != objectFormat.EmptyObjectID().String() {
changed, err := checkIfPRContentChanged(ctx, pr, opts.OldCommitID, opts.NewCommitID)
if err != nil {
log.Error("checkIfPRContentChanged: %v", err)
}
@@ -452,12 +461,12 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string,
log.Error("GetFirstMatchProtectedBranchRule: %v", err)
}
if pb != nil && pb.DismissStaleApprovals {
if err := DismissApprovalReviews(ctx, doer, pr); err != nil {
if err := DismissApprovalReviews(ctx, opts.Doer, pr); err != nil {
log.Error("DismissApprovalReviews: %v", err)
}
}
}
if err := issues_model.MarkReviewsAsNotStale(ctx, pr.IssueID, newCommitID); err != nil {
if err := issues_model.MarkReviewsAsNotStale(ctx, pr.IssueID, opts.NewCommitID); err != nil {
log.Error("MarkReviewsAsNotStale: %v", err)
}
divergence, err := GetDiverging(ctx, pr)
@@ -471,21 +480,36 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string,
}
}
notify_service.PullRequestSynchronized(ctx, doer, pr)
if !pr.IsWorkInProgress(ctx) {
var reviewNotifiers []*issue_service.ReviewRequestNotifier
if opts.IsForcePush {
reviewNotifiers, err = issue_service.PullRequestCodeOwnersReview(ctx, pr)
} else {
reviewNotifiers, err = issue_service.PullRequestCodeOwnersReviewSpecialCommits(ctx, pr, opts.OldCommitID, opts.NewCommitID)
}
if err != nil {
log.Error("PullRequestCodeOwnersReview: %v", err)
}
if len(reviewNotifiers) > 0 {
issue_service.ReviewRequestNotify(ctx, pr.Issue, opts.Doer, reviewNotifiers)
}
}
notify_service.PullRequestSynchronized(ctx, opts.Doer, pr)
}
}
}
log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch)
prs, err = issues_model.GetUnmergedPullRequestsByBaseInfo(ctx, repoID, branch)
log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", opts.RepoID, opts.Branch)
prs, err = issues_model.GetUnmergedPullRequestsByBaseInfo(ctx, opts.RepoID, opts.Branch)
if err != nil {
log.Error("Find pull requests [base_repo_id: %d, base_branch: %s]: %v", repoID, branch, err)
log.Error("Find pull requests [base_repo_id: %d, base_branch: %s]: %v", opts.RepoID, opts.Branch, err)
return
}
for _, pr := range prs {
divergence, err := GetDiverging(ctx, pr)
if err != nil {
if git_model.IsErrBranchNotExist(err) && !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) {
if git_model.IsErrBranchNotExist(err) && !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) {
log.Warn("Cannot test PR %s/%d: head_branch %s no longer exists", pr.BaseRepo.Name, pr.IssueID, pr.HeadBranch)
} else {
log.Error("GetDiverging: %v", err)
@@ -523,14 +547,14 @@ func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest,
return false, fmt.Errorf("GetMergeBase: %w", err)
}
cmd := git.NewCommand(ctx, "diff", "--name-only", "-z").AddDynamicArguments(newCommitID, oldCommitID, base)
cmd := git.NewCommand("diff", "--name-only", "-z").AddDynamicArguments(newCommitID, oldCommitID, base)
stdoutReader, stdoutWriter, err := os.Pipe()
if err != nil {
return false, fmt.Errorf("unable to open pipe for to run diff: %w", err)
}
stderr := new(bytes.Buffer)
if err := cmd.Run(&git.RunOpts{
if err := cmd.Run(ctx, &git.RunOpts{
Dir: prCtx.tmpBasePath,
Stdout: stdoutWriter,
Stderr: stderr,
@@ -647,7 +671,7 @@ func UpdateRef(ctx context.Context, pr *issues_model.PullRequest) (err error) {
return err
}
_, _, err = git.NewCommand(ctx, "update-ref").AddDynamicArguments(pr.GetGitRefName(), pr.HeadCommitID).RunStdString(&git.RunOpts{Dir: pr.BaseRepo.RepoPath()})
_, _, err = git.NewCommand("update-ref").AddDynamicArguments(pr.GetGitRefName(), pr.HeadCommitID).RunStdString(ctx, &git.RunOpts{Dir: pr.BaseRepo.RepoPath()})
if err != nil {
log.Error("Unable to update ref in base repository for PR[%d] Error: %v", pr.ID, err)
}
@@ -663,7 +687,7 @@ func retargetBranchPulls(ctx context.Context, doer *user_model.User, repoID int6
return err
}
if err := issues_model.PullRequestList(prs).LoadAttributes(ctx); err != nil {
if err := prs.LoadAttributes(ctx); err != nil {
return err
}
@@ -690,11 +714,11 @@ func AdjustPullsCausedByBranchDeleted(ctx context.Context, doer *user_model.User
return err
}
if err := issues_model.PullRequestList(prs).LoadAttributes(ctx); err != nil {
if err := prs.LoadAttributes(ctx); err != nil {
return err
}
issues_model.PullRequestList(prs).SetHeadRepo(repo)
if err := issues_model.PullRequestList(prs).LoadRepositories(ctx); err != nil {
prs.SetHeadRepo(repo)
if err := prs.LoadRepositories(ctx); err != nil {
return err
}
@@ -725,11 +749,11 @@ func AdjustPullsCausedByBranchDeleted(ctx context.Context, doer *user_model.User
return err
}
if err := issues_model.PullRequestList(prs).LoadAttributes(ctx); err != nil {
if err := prs.LoadAttributes(ctx); err != nil {
return err
}
issues_model.PullRequestList(prs).SetBaseRepo(repo)
if err := issues_model.PullRequestList(prs).LoadRepositories(ctx); err != nil {
prs.SetBaseRepo(repo)
if err := prs.LoadRepositories(ctx); err != nil {
return err
}
@@ -762,7 +786,7 @@ func CloseRepoBranchesPulls(ctx context.Context, doer *user_model.User, repo *re
return err
}
if err = issues_model.PullRequestList(prs).LoadAttributes(ctx); err != nil {
if err = prs.LoadAttributes(ctx); err != nil {
return err
}
+12 -11
View File
@@ -15,6 +15,7 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
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/log"
repo_module "code.gitea.io/gitea/modules/repository"
)
@@ -133,22 +134,22 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
return nil, nil, fmt.Errorf("Unable to add base repository to temporary repo [%s -> tmpBasePath]: %w", pr.BaseRepo.FullName(), err)
}
if err := git.NewCommand(ctx, "remote", "add", "-t").AddDynamicArguments(pr.BaseBranch).AddArguments("-m").AddDynamicArguments(pr.BaseBranch).AddDynamicArguments("origin", baseRepoPath).
Run(prCtx.RunOpts()); err != nil {
if err := git.NewCommand("remote", "add", "-t").AddDynamicArguments(pr.BaseBranch).AddArguments("-m").AddDynamicArguments(pr.BaseBranch).AddDynamicArguments("origin", baseRepoPath).
Run(ctx, prCtx.RunOpts()); err != nil {
log.Error("%-v Unable to add base repository as origin [%s -> %s]: %v\n%s\n%s", pr, pr.BaseRepo.FullName(), tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String())
cancel()
return nil, nil, fmt.Errorf("Unable to add base repository as origin [%s -> tmpBasePath]: %w\n%s\n%s", pr.BaseRepo.FullName(), err, prCtx.outbuf.String(), prCtx.errbuf.String())
}
if err := git.NewCommand(ctx, "fetch", "origin").AddArguments(fetchArgs...).AddDashesAndList(pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch).
Run(prCtx.RunOpts()); err != nil {
if err := git.NewCommand("fetch", "origin").AddArguments(fetchArgs...).AddDashesAndList(pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch).
Run(ctx, prCtx.RunOpts()); err != nil {
log.Error("%-v Unable to fetch origin base branch [%s:%s -> base, original_base in %s]: %v:\n%s\n%s", pr, pr.BaseRepo.FullName(), pr.BaseBranch, tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String())
cancel()
return nil, nil, fmt.Errorf("Unable to fetch origin base branch [%s:%s -> base, original_base in tmpBasePath]: %w\n%s\n%s", pr.BaseRepo.FullName(), pr.BaseBranch, err, prCtx.outbuf.String(), prCtx.errbuf.String())
}
if err := git.NewCommand(ctx, "symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseBranch).
Run(prCtx.RunOpts()); err != nil {
if err := git.NewCommand("symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseBranch).
Run(ctx, prCtx.RunOpts()); err != nil {
log.Error("%-v Unable to set HEAD as base branch in [%s]: %v\n%s\n%s", pr, tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String())
cancel()
return nil, nil, fmt.Errorf("Unable to set HEAD as base branch in tmpBasePath: %w\n%s\n%s", err, prCtx.outbuf.String(), prCtx.errbuf.String())
@@ -160,8 +161,8 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
return nil, nil, fmt.Errorf("Unable to add head base repository to temporary repo [%s -> tmpBasePath]: %w", pr.HeadRepo.FullName(), err)
}
if err := git.NewCommand(ctx, "remote", "add").AddDynamicArguments(remoteRepoName, headRepoPath).
Run(prCtx.RunOpts()); err != nil {
if err := git.NewCommand("remote", "add").AddDynamicArguments(remoteRepoName, headRepoPath).
Run(ctx, prCtx.RunOpts()); err != nil {
log.Error("%-v Unable to add head repository as head_repo [%s -> %s]: %v\n%s\n%s", pr, pr.HeadRepo.FullName(), tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String())
cancel()
return nil, nil, fmt.Errorf("Unable to add head repository as head_repo [%s -> tmpBasePath]: %w\n%s\n%s", pr.HeadRepo.FullName(), err, prCtx.outbuf.String(), prCtx.errbuf.String())
@@ -178,10 +179,10 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
} else {
headBranch = pr.GetGitRefName()
}
if err := git.NewCommand(ctx, "fetch").AddArguments(fetchArgs...).AddDynamicArguments(remoteRepoName, headBranch+":"+trackingBranch).
Run(prCtx.RunOpts()); err != nil {
if err := git.NewCommand("fetch").AddArguments(fetchArgs...).AddDynamicArguments(remoteRepoName, headBranch+":"+trackingBranch).
Run(ctx, prCtx.RunOpts()); err != nil {
cancel()
if !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) {
if !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) {
return nil, nil, git_model.ErrBranchNotExist{
BranchName: pr.HeadBranch,
}
+18 -2
View File
@@ -42,7 +42,15 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.
if rebase {
defer func() {
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "")
go AddTestPullRequestTask(TestPullRequestOptions{
RepoID: pr.BaseRepo.ID,
Doer: doer,
Branch: pr.BaseBranch,
IsSync: false,
IsForcePush: false,
OldCommitID: "",
NewCommitID: "",
})
}()
return updateHeadByRebaseOnToBase(ctx, pr, doer)
@@ -83,7 +91,15 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.
_, err = doMergeAndPush(ctx, reversePR, doer, repo_model.MergeStyleMerge, "", message, repository.PushTriggerPRUpdateWithBase)
defer func() {
go AddTestPullRequestTask(doer, reversePR.HeadRepo.ID, reversePR.HeadBranch, false, "", "")
go AddTestPullRequestTask(TestPullRequestOptions{
RepoID: reversePR.HeadRepo.ID,
Doer: doer,
Branch: reversePR.HeadBranch,
IsSync: false,
IsForcePush: false,
OldCommitID: "",
NewCommitID: "",
})
}()
return err
+3 -3
View File
@@ -27,7 +27,7 @@ func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullReques
defer cancel()
// Determine the old merge-base before the rebase - we use this for LFS push later on
oldMergeBase, _, _ := git.NewCommand(ctx, "merge-base").AddDashesAndList(baseBranch, trackingBranch).RunStdString(&git.RunOpts{Dir: mergeCtx.tmpBasePath})
oldMergeBase, _, _ := git.NewCommand("merge-base").AddDashesAndList(baseBranch, trackingBranch).RunStdString(ctx, &git.RunOpts{Dir: mergeCtx.tmpBasePath})
oldMergeBase = strings.TrimSpace(oldMergeBase)
// Rebase the tracking branch on to the base as the staging branch
@@ -62,7 +62,7 @@ func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullReques
headUser = pr.HeadRepo.Owner
}
pushCmd := git.NewCommand(ctx, "push", "-f", "head_repo").
pushCmd := git.NewCommand("push", "-f", "head_repo").
AddDynamicArguments(stagingBranch + ":" + git.BranchPrefix + pr.HeadBranch)
// Push back to the head repository.
@@ -71,7 +71,7 @@ func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullReques
mergeCtx.outbuf.Reset()
mergeCtx.errbuf.Reset()
if err := pushCmd.Run(&git.RunOpts{
if err := pushCmd.Run(ctx, &git.RunOpts{
Env: repo_module.FullPushingEnvironment(
headUser,
doer,