diff options
Diffstat (limited to 'services/pull')
-rw-r--r-- | services/pull/check.go | 58 | ||||
-rw-r--r-- | services/pull/commit_status.go | 12 | ||||
-rw-r--r-- | services/pull/lfs.go | 9 | ||||
-rw-r--r-- | services/pull/merge.go | 71 | ||||
-rw-r--r-- | services/pull/patch.go | 30 | ||||
-rw-r--r-- | services/pull/pull.go | 111 | ||||
-rw-r--r-- | services/pull/review.go | 25 | ||||
-rw-r--r-- | services/pull/temp_repo.go | 17 | ||||
-rw-r--r-- | services/pull/update.go | 13 |
9 files changed, 185 insertions, 161 deletions
diff --git a/services/pull/check.go b/services/pull/check.go index 363a716b28..3615c6c654 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -21,6 +21,7 @@ import ( "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" + "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/queue" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -69,7 +70,7 @@ func checkAndUpdateStatus(pr *models.PullRequest) { // getMergeCommit checks if a pull request got merged // Returns the git.Commit of the pull request if merged -func getMergeCommit(pr *models.PullRequest) (*git.Commit, error) { +func getMergeCommit(ctx context.Context, pr *models.PullRequest) (*git.Commit, error) { if pr.BaseRepo == nil { var err error pr.BaseRepo, err = repo_model.GetRepositoryByID(pr.BaseRepoID) @@ -91,7 +92,7 @@ func getMergeCommit(pr *models.PullRequest) (*git.Commit, error) { headFile := pr.GetGitRefName() // Check if a pull request is merged into BaseBranch - _, err = git.NewCommand("merge-base", "--is-ancestor", headFile, pr.BaseBranch). + _, err = git.NewCommandContext(ctx, "merge-base", "--is-ancestor", headFile, pr.BaseBranch). RunInDirWithEnv(pr.BaseRepo.RepoPath(), []string{"GIT_INDEX_FILE=" + indexTmpPath, "GIT_DIR=" + pr.BaseRepo.RepoPath()}) if err != nil { // Errors are signaled by a non-zero status that is not 1 @@ -112,7 +113,7 @@ func getMergeCommit(pr *models.PullRequest) (*git.Commit, error) { cmd := commitID[:40] + ".." + pr.BaseBranch // Get the commit from BaseBranch where the pull request got merged - mergeCommit, err := git.NewCommand("rev-list", "--ancestry-path", "--merges", "--reverse", cmd). + mergeCommit, err := git.NewCommandContext(ctx, "rev-list", "--ancestry-path", "--merges", "--reverse", cmd). RunInDirWithEnv("", []string{"GIT_INDEX_FILE=" + indexTmpPath, "GIT_DIR=" + pr.BaseRepo.RepoPath()}) if err != nil { return nil, fmt.Errorf("git rev-list --ancestry-path --merges --reverse: %v", err) @@ -121,7 +122,7 @@ func getMergeCommit(pr *models.PullRequest) (*git.Commit, error) { mergeCommit = commitID[:40] } - gitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) + gitRepo, err := git.OpenRepositoryCtx(ctx, pr.BaseRepo.RepoPath()) if err != nil { return nil, fmt.Errorf("OpenRepository: %v", err) } @@ -137,7 +138,7 @@ func getMergeCommit(pr *models.PullRequest) (*git.Commit, error) { // manuallyMerged checks if a pull request got manually merged // When a pull request got manually merged mark the pull request as merged -func manuallyMerged(pr *models.PullRequest) bool { +func manuallyMerged(ctx context.Context, pr *models.PullRequest) bool { if err := pr.LoadBaseRepo(); err != nil { log.Error("PullRequest[%d].LoadBaseRepo: %v", pr.ID, err) return false @@ -153,7 +154,7 @@ func manuallyMerged(pr *models.PullRequest) bool { return false } - commit, err := getMergeCommit(pr) + commit, err := getMergeCommit(ctx, pr) if err != nil { log.Error("PullRequest[%d].getMergeCommit: %v", pr.ID, err) return false @@ -219,26 +220,37 @@ func handle(data ...queue.Data) { for _, datum := range data { id, _ := strconv.ParseInt(datum.(string), 10, 64) - log.Trace("Testing PR ID %d from the pull requests patch checking queue", id) + testPR(id) + } +} - pr, err := models.GetPullRequestByID(id) - if err != nil { - log.Error("GetPullRequestByID[%s]: %v", datum, err) - continue - } else if pr.HasMerged { - continue - } else if manuallyMerged(pr) { - continue - } else if err = TestPatch(pr); err != nil { - log.Error("testPatch[%d]: %v", pr.ID, err) - pr.Status = models.PullRequestStatusError - if err := pr.UpdateCols("status"); err != nil { - log.Error("update pr [%d] status to PullRequestStatusError failed: %v", pr.ID, err) - } - continue +func testPR(id int64) { + ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("Test PR[%d] from patch checking queue", id)) + defer finished() + + pr, err := models.GetPullRequestByID(id) + if err != nil { + log.Error("GetPullRequestByID[%d]: %v", id, err) + return + } + + if pr.HasMerged { + return + } + + if manuallyMerged(ctx, pr) { + return + } + + if err := TestPatch(pr); err != nil { + log.Error("testPatch[%d]: %v", pr.ID, err) + pr.Status = models.PullRequestStatusError + if err := pr.UpdateCols("status"); err != nil { + log.Error("update pr [%d] status to PullRequestStatusError failed: %v", pr.ID, err) } - checkAndUpdateStatus(pr) + return } + checkAndUpdateStatus(pr) } // CheckPrsForBaseBranch check all pulls with bseBrannch diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go index 5324b49fa9..d605f8e301 100644 --- a/services/pull/commit_status.go +++ b/services/pull/commit_status.go @@ -6,6 +6,8 @@ package pull import ( + "context" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/git" @@ -80,7 +82,7 @@ func IsCommitStatusContextSuccess(commitStatuses []*models.CommitStatus, require } // IsPullCommitStatusPass returns if all required status checks PASS -func IsPullCommitStatusPass(pr *models.PullRequest) (bool, error) { +func IsPullCommitStatusPass(ctx context.Context, pr *models.PullRequest) (bool, error) { if err := pr.LoadProtectedBranch(); err != nil { return false, errors.Wrap(err, "GetLatestCommitStatus") } @@ -88,7 +90,7 @@ func IsPullCommitStatusPass(pr *models.PullRequest) (bool, error) { return true, nil } - state, err := GetPullRequestCommitStatusState(pr) + state, err := GetPullRequestCommitStatusState(ctx, pr) if err != nil { return false, err } @@ -96,18 +98,18 @@ func IsPullCommitStatusPass(pr *models.PullRequest) (bool, error) { } // GetPullRequestCommitStatusState returns pull request merged commit status state -func GetPullRequestCommitStatusState(pr *models.PullRequest) (structs.CommitStatusState, error) { +func GetPullRequestCommitStatusState(ctx context.Context, pr *models.PullRequest) (structs.CommitStatusState, error) { // Ensure HeadRepo is loaded if err := pr.LoadHeadRepo(); err != nil { return "", errors.Wrap(err, "LoadHeadRepo") } // check if all required status checks are successful - headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) + headGitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, pr.HeadRepo.RepoPath()) if err != nil { return "", errors.Wrap(err, "OpenRepository") } - defer headGitRepo.Close() + defer closer.Close() if pr.Flow == models.PullRequestFlowGithub && !headGitRepo.IsBranchExist(pr.HeadBranch) { return "", errors.New("Head branch does not exist, can not merge") diff --git a/services/pull/lfs.go b/services/pull/lfs.go index c9ec415cb1..fada9b6121 100644 --- a/services/pull/lfs.go +++ b/services/pull/lfs.go @@ -7,6 +7,7 @@ package pull import ( "bufio" + "context" "io" "strconv" "sync" @@ -18,7 +19,7 @@ import ( ) // LFSPush pushes lfs objects referred to in new commits in the head repository from the base repository -func LFSPush(tmpBasePath, mergeHeadSHA, mergeBaseSHA string, pr *models.PullRequest) error { +func LFSPush(ctx context.Context, tmpBasePath, mergeHeadSHA, mergeBaseSHA string, pr *models.PullRequest) error { // Now we have to implement git lfs push // git rev-list --objects --filter=blob:limit=1k HEAD --not base // pass blob shas in to git cat-file --batch-check (possibly unnecessary) @@ -41,19 +42,19 @@ func LFSPush(tmpBasePath, mergeHeadSHA, mergeBaseSHA string, pr *models.PullRequ go createLFSMetaObjectsFromCatFileBatch(catFileBatchReader, &wg, pr) // 5. Take the shas of the blobs and batch read them - go pipeline.CatFileBatch(shasToBatchReader, catFileBatchWriter, &wg, tmpBasePath) + go pipeline.CatFileBatch(ctx, shasToBatchReader, catFileBatchWriter, &wg, tmpBasePath) // 4. From the provided objects restrict to blobs <=1k go pipeline.BlobsLessThan1024FromCatFileBatchCheck(catFileCheckReader, shasToBatchWriter, &wg) // 3. Run batch-check on the objects retrieved from rev-list - go pipeline.CatFileBatchCheck(shasToCheckReader, catFileCheckWriter, &wg, tmpBasePath) + go pipeline.CatFileBatchCheck(ctx, shasToCheckReader, catFileCheckWriter, &wg, tmpBasePath) // 2. Check each object retrieved rejecting those without names as they will be commits or trees go pipeline.BlobsFromRevListObjects(revListReader, shasToCheckWriter, &wg) // 1. Run rev-list objects from mergeHead to mergeBase - go pipeline.RevListObjects(revListWriter, &wg, tmpBasePath, mergeHeadSHA, mergeBaseSHA, errChan) + go pipeline.RevListObjects(ctx, revListWriter, &wg, tmpBasePath, mergeHeadSHA, mergeBaseSHA, errChan) wg.Wait() select { diff --git a/services/pull/merge.go b/services/pull/merge.go index f6a6415bff..2a67507a87 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -8,6 +8,7 @@ package pull import ( "bufio" "bytes" + "context" "fmt" "os" "path/filepath" @@ -34,7 +35,7 @@ import ( // Merge merges pull request to base repository. // Caller should check PR is ready to be merged (review and status checks) // FIXME: add repoWorkingPull make sure two merges does not happen at same time. -func Merge(pr *models.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string) (err error) { +func Merge(ctx context.Context, pr *models.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string) (err error) { if err = pr.LoadHeadRepo(); err != nil { log.Error("LoadHeadRepo: %v", err) return fmt.Errorf("LoadHeadRepo: %v", err) @@ -59,7 +60,7 @@ func Merge(pr *models.PullRequest, doer *user_model.User, baseGitRepo *git.Repos go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "") }() - pr.MergedCommitID, err = rawMerge(pr, doer, mergeStyle, expectedHeadCommitID, message) + pr.MergedCommitID, err = rawMerge(ctx, pr, doer, mergeStyle, expectedHeadCommitID, message) if err != nil { return err } @@ -117,7 +118,7 @@ func Merge(pr *models.PullRequest, doer *user_model.User, baseGitRepo *git.Repos } // rawMerge perform the merge operation without changing any pull information in database -func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string) (string, error) { +func rawMerge(ctx context.Context, pr *models.PullRequest, doer *user_model.User, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string) (string, error) { err := git.LoadGitVersion() if err != nil { log.Error("git.LoadGitVersion: %v", err) @@ -125,7 +126,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod } // Clone base repo. - tmpBasePath, err := createTemporaryRepo(pr) + tmpBasePath, err := createTemporaryRepo(ctx, pr) if err != nil { log.Error("CreateTemporaryPath: %v", err) return "", err @@ -141,7 +142,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod stagingBranch := "staging" if expectedHeadCommitID != "" { - trackingCommitID, err := git.NewCommand("show-ref", "--hash", git.BranchPrefix+trackingBranch).RunInDir(tmpBasePath) + trackingCommitID, err := git.NewCommandContext(ctx, "show-ref", "--hash", git.BranchPrefix+trackingBranch).RunInDir(tmpBasePath) if err != nil { log.Error("show-ref[%s] --hash refs/heads/trackingn: %v", tmpBasePath, git.BranchPrefix+trackingBranch, err) return "", fmt.Errorf("getDiffTree: %v", err) @@ -157,7 +158,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod var outbuf, errbuf strings.Builder // Enable sparse-checkout - sparseCheckoutList, err := getDiffTree(tmpBasePath, baseBranch, trackingBranch) + sparseCheckoutList, err := getDiffTree(ctx, tmpBasePath, baseBranch, trackingBranch) if err != nil { log.Error("getDiffTree(%s, %s, %s): %v", tmpBasePath, baseBranch, trackingBranch, err) return "", fmt.Errorf("getDiffTree: %v", err) @@ -178,11 +179,11 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod var gitConfigCommand func() *git.Command if git.CheckGitVersionAtLeast("1.8.0") == nil { gitConfigCommand = func() *git.Command { - return git.NewCommand("config", "--local") + return git.NewCommandContext(ctx, "config", "--local") } } else { gitConfigCommand = func() *git.Command { - return git.NewCommand("config") + return git.NewCommandContext(ctx, "config") } } @@ -223,7 +224,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod errbuf.Reset() // Read base branch index - if err := git.NewCommand("read-tree", "HEAD").RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "read-tree", "HEAD").RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("git read-tree HEAD: %v\n%s\n%s", err, outbuf.String(), errbuf.String()) return "", fmt.Errorf("Unable to read base branch in to the index: %v\n%s\n%s", err, outbuf.String(), errbuf.String()) } @@ -236,7 +237,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod // Determine if we should sign signArg := "" if git.CheckGitVersionAtLeast("1.7.9") == nil { - sign, keyID, signer, _ := asymkey_service.SignMerge(pr, doer, tmpBasePath, "HEAD", trackingBranch) + sign, keyID, signer, _ := asymkey_service.SignMerge(ctx, pr, doer, tmpBasePath, "HEAD", trackingBranch) if sign { signArg = "-S" + keyID if pr.BaseRepo.GetTrustModel() == repo_model.CommitterTrustModel || pr.BaseRepo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel { @@ -262,13 +263,13 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod // Merge commits. switch mergeStyle { case repo_model.MergeStyleMerge: - cmd := git.NewCommand("merge", "--no-ff", "--no-commit", trackingBranch) + cmd := git.NewCommandContext(ctx, "merge", "--no-ff", "--no-commit", trackingBranch) if err := runMergeCommand(pr, mergeStyle, cmd, tmpBasePath); err != nil { log.Error("Unable to merge tracking into base: %v", err) return "", err } - if err := commitAndSignNoAuthor(pr, message, signArg, tmpBasePath, env); err != nil { + if err := commitAndSignNoAuthor(ctx, pr, message, signArg, tmpBasePath, env); err != nil { log.Error("Unable to make final commit: %v", err) return "", err } @@ -278,7 +279,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod fallthrough case repo_model.MergeStyleRebaseMerge: // Checkout head branch - if err := git.NewCommand("checkout", "-b", stagingBranch, trackingBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "checkout", "-b", stagingBranch, trackingBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("git checkout base prior to merge post staging rebase [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) return "", fmt.Errorf("git checkout base prior to merge post staging rebase [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) } @@ -286,7 +287,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod errbuf.Reset() // Rebase before merging - if err := git.NewCommand("rebase", baseBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "rebase", baseBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { // Rebase will leave a REBASE_HEAD file in .git if there is a conflict if _, statErr := os.Stat(filepath.Join(tmpBasePath, ".git", "REBASE_HEAD")); statErr == nil { var commitSha string @@ -334,14 +335,14 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod } // Checkout base branch again - if err := git.NewCommand("checkout", baseBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "checkout", baseBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("git checkout base prior to merge post staging rebase [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) return "", fmt.Errorf("git checkout base prior to merge post staging rebase [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) } outbuf.Reset() errbuf.Reset() - cmd := git.NewCommand("merge") + cmd := git.NewCommandContext(ctx, "merge") if mergeStyle == repo_model.MergeStyleRebase { cmd.AddArguments("--ff-only") } else { @@ -355,14 +356,14 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod return "", err } if mergeStyle == repo_model.MergeStyleRebaseMerge { - if err := commitAndSignNoAuthor(pr, message, signArg, tmpBasePath, env); err != nil { + if err := commitAndSignNoAuthor(ctx, pr, message, signArg, tmpBasePath, env); err != nil { log.Error("Unable to make final commit: %v", err) return "", err } } case repo_model.MergeStyleSquash: // Merge with squash - cmd := git.NewCommand("merge", "--squash", trackingBranch) + cmd := git.NewCommandContext(ctx, "merge", "--squash", trackingBranch) if err := runMergeCommand(pr, mergeStyle, cmd, tmpBasePath); err != nil { log.Error("Unable to merge --squash tracking into base: %v", err) return "", err @@ -374,7 +375,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod } sig := pr.Issue.Poster.NewGitSig() if signArg == "" { - if err := git.NewCommand("commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) return "", fmt.Errorf("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) } @@ -383,7 +384,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod // add trailer message += fmt.Sprintf("\nCo-authored-by: %s\nCo-committed-by: %s\n", sig.String(), sig.String()) } - if err := git.NewCommand("commit", signArg, fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "commit", signArg, fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) return "", fmt.Errorf("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) } @@ -395,15 +396,15 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod } // OK we should cache our current head and origin/headbranch - mergeHeadSHA, err := git.GetFullCommitID(tmpBasePath, "HEAD") + mergeHeadSHA, err := git.GetFullCommitID(ctx, tmpBasePath, "HEAD") if err != nil { return "", fmt.Errorf("Failed to get full commit id for HEAD: %v", err) } - mergeBaseSHA, err := git.GetFullCommitID(tmpBasePath, "original_"+baseBranch) + mergeBaseSHA, err := git.GetFullCommitID(ctx, tmpBasePath, "original_"+baseBranch) if err != nil { return "", fmt.Errorf("Failed to get full commit id for origin/%s: %v", pr.BaseBranch, err) } - mergeCommitID, err := git.GetFullCommitID(tmpBasePath, baseBranch) + mergeCommitID, err := git.GetFullCommitID(ctx, tmpBasePath, baseBranch) if err != nil { return "", fmt.Errorf("Failed to get full commit id for the new merge: %v", err) } @@ -412,7 +413,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod // I think in the interests of data safety - failures to push to the lfs should prevent // the merge as you can always remerge. if setting.LFS.StartServer { - if err := LFSPush(tmpBasePath, mergeHeadSHA, mergeBaseSHA, pr); err != nil { + if err := LFSPush(ctx, tmpBasePath, mergeHeadSHA, mergeBaseSHA, pr); err != nil { return "", err } } @@ -441,9 +442,9 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod var pushCmd *git.Command if mergeStyle == repo_model.MergeStyleRebaseUpdate { // force push the rebase result to head branch - pushCmd = git.NewCommand("push", "-f", "head_repo", stagingBranch+":"+git.BranchPrefix+pr.HeadBranch) + pushCmd = git.NewCommandContext(ctx, "push", "-f", "head_repo", stagingBranch+":"+git.BranchPrefix+pr.HeadBranch) } else { - pushCmd = git.NewCommand("push", "origin", baseBranch+":"+git.BranchPrefix+pr.BaseBranch) + pushCmd = git.NewCommandContext(ctx, "push", "origin", baseBranch+":"+git.BranchPrefix+pr.BaseBranch) } // Push back to upstream. @@ -471,15 +472,15 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod return mergeCommitID, nil } -func commitAndSignNoAuthor(pr *models.PullRequest, message, signArg, tmpBasePath string, env []string) error { +func commitAndSignNoAuthor(ctx context.Context, pr *models.PullRequest, message, signArg, tmpBasePath string, env []string) error { var outbuf, errbuf strings.Builder if signArg == "" { - if err := git.NewCommand("commit", "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "commit", "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) return fmt.Errorf("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) } } else { - if err := git.NewCommand("commit", signArg, "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "commit", signArg, "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) return fmt.Errorf("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) } @@ -518,11 +519,11 @@ func runMergeCommand(pr *models.PullRequest, mergeStyle repo_model.MergeStyle, c var escapedSymbols = regexp.MustCompile(`([*[?! \\])`) -func getDiffTree(repoPath, baseBranch, headBranch string) (string, error) { +func getDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string) (string, error) { getDiffTreeFromBranch := func(repoPath, baseBranch, headBranch string) (string, error) { var outbuf, errbuf strings.Builder // Compute the diff-tree for sparse-checkout - if err := git.NewCommand("diff-tree", "--no-commit-id", "--name-only", "-r", "-z", "--root", baseBranch, headBranch, "--").RunInDirPipeline(repoPath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "diff-tree", "--no-commit-id", "--name-only", "-r", "-z", "--root", baseBranch, headBranch, "--").RunInDirPipeline(repoPath, &outbuf, &errbuf); err != nil { return "", fmt.Errorf("git diff-tree [%s base:%s head:%s]: %s", repoPath, baseBranch, headBranch, errbuf.String()) } return outbuf.String(), nil @@ -562,7 +563,7 @@ func getDiffTree(repoPath, baseBranch, headBranch string) (string, error) { } // IsSignedIfRequired check if merge will be signed if required -func IsSignedIfRequired(pr *models.PullRequest, doer *user_model.User) (bool, error) { +func IsSignedIfRequired(ctx context.Context, pr *models.PullRequest, doer *user_model.User) (bool, error) { if err := pr.LoadProtectedBranch(); err != nil { return false, err } @@ -571,7 +572,7 @@ func IsSignedIfRequired(pr *models.PullRequest, doer *user_model.User) (bool, er return true, nil } - sign, _, _, err := asymkey_service.SignMerge(pr, doer, pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName()) + sign, _, _, err := asymkey_service.SignMerge(ctx, pr, doer, pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName()) return sign, err } @@ -595,7 +596,7 @@ func IsUserAllowedToMerge(pr *models.PullRequest, p models.Permission, user *use } // CheckPRReadyToMerge checks whether the PR is ready to be merged (reviews and status checks) -func CheckPRReadyToMerge(pr *models.PullRequest, skipProtectedFilesCheck bool) (err error) { +func CheckPRReadyToMerge(ctx context.Context, pr *models.PullRequest, skipProtectedFilesCheck bool) (err error) { if err = pr.LoadBaseRepo(); err != nil { return fmt.Errorf("LoadBaseRepo: %v", err) } @@ -607,7 +608,7 @@ func CheckPRReadyToMerge(pr *models.PullRequest, skipProtectedFilesCheck bool) ( return nil } - isPass, err := IsPullCommitStatusPass(pr) + isPass, err := IsPullCommitStatusPass(ctx, pr) if err != nil { return err } diff --git a/services/pull/patch.go b/services/pull/patch.go index 0eba3f86ed..a632167916 100644 --- a/services/pull/patch.go +++ b/services/pull/patch.go @@ -26,17 +26,18 @@ import ( ) // DownloadDiffOrPatch will write the patch for the pr to the writer -func DownloadDiffOrPatch(pr *models.PullRequest, w io.Writer, patch, binary bool) error { +func DownloadDiffOrPatch(ctx context.Context, pr *models.PullRequest, w io.Writer, patch, binary bool) error { if err := pr.LoadBaseRepo(); err != nil { log.Error("Unable to load base repository ID %d for pr #%d [%d]", pr.BaseRepoID, pr.Index, pr.ID) return err } - gitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) + gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, pr.BaseRepo.RepoPath()) if err != nil { return fmt.Errorf("OpenRepository: %v", err) } - defer gitRepo.Close() + defer closer.Close() + if err := gitRepo.GetDiffOrPatch(pr.MergeBase, pr.GetGitRefName(), w, patch, binary); err != nil { log.Error("Unable to get patch file from %s to %s in %s Error: %v", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.FullName(), err) return fmt.Errorf("Unable to get patch file from %s to %s in %s Error: %v", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.FullName(), err) @@ -53,8 +54,11 @@ var patchErrorSuffices = []string{ // TestPatch will test whether a simple patch will apply func TestPatch(pr *models.PullRequest) error { + ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("TestPatch: Repo[%d]#%d", pr.BaseRepoID, pr.Index)) + defer finished() + // Clone base repo. - tmpBasePath, err := createTemporaryRepo(pr) + tmpBasePath, err := createTemporaryRepo(ctx, pr) if err != nil { log.Error("CreateTemporaryPath: %v", err) return err @@ -65,14 +69,14 @@ func TestPatch(pr *models.PullRequest) error { } }() - gitRepo, err := git.OpenRepository(tmpBasePath) + gitRepo, err := git.OpenRepositoryCtx(ctx, tmpBasePath) if err != nil { return fmt.Errorf("OpenRepository: %v", err) } defer gitRepo.Close() // 1. update merge base - pr.MergeBase, err = git.NewCommand("merge-base", "--", "base", "tracking").RunInDir(tmpBasePath) + pr.MergeBase, err = git.NewCommandContext(ctx, "merge-base", "--", "base", "tracking").RunInDir(tmpBasePath) if err != nil { var err2 error pr.MergeBase, err2 = gitRepo.GetRefCommitID(git.BranchPrefix + "base") @@ -322,7 +326,7 @@ func checkConflicts(pr *models.PullRequest, gitRepo *git.Repository, tmpBasePath pr.Status = models.PullRequestStatusChecking // 3. Read the base branch in to the index of the temporary repository - _, err = git.NewCommand("read-tree", "base").RunInDir(tmpBasePath) + _, err = git.NewCommandContext(gitRepo.Ctx, "read-tree", "base").RunInDir(tmpBasePath) if err != nil { return false, fmt.Errorf("git read-tree %s: %v", pr.BaseBranch, err) } @@ -365,7 +369,7 @@ func checkConflicts(pr *models.PullRequest, gitRepo *git.Repository, tmpBasePath // 7. Run the check command conflict = false - err = git.NewCommand(args...). + err = git.NewCommandContext(gitRepo.Ctx, args...). RunInDirTimeoutEnvFullPipelineFunc( nil, -1, tmpBasePath, nil, stderrWriter, nil, @@ -432,11 +436,11 @@ func checkConflicts(pr *models.PullRequest, gitRepo *git.Repository, tmpBasePath } // CheckFileProtection check file Protection -func CheckFileProtection(oldCommitID, newCommitID string, patterns []glob.Glob, limit int, env []string, repo *git.Repository) ([]string, error) { +func CheckFileProtection(repo *git.Repository, oldCommitID, newCommitID string, patterns []glob.Glob, limit int, env []string) ([]string, error) { if len(patterns) == 0 { return nil, nil } - affectedFiles, err := git.GetAffectedFiles(oldCommitID, newCommitID, env, repo) + affectedFiles, err := git.GetAffectedFiles(repo, oldCommitID, newCommitID, env) if err != nil { return nil, err } @@ -462,11 +466,11 @@ func CheckFileProtection(oldCommitID, newCommitID string, patterns []glob.Glob, } // CheckUnprotectedFiles check if the commit only touches unprotected files -func CheckUnprotectedFiles(oldCommitID, newCommitID string, patterns []glob.Glob, env []string, repo *git.Repository) (bool, error) { +func CheckUnprotectedFiles(repo *git.Repository, oldCommitID, newCommitID string, patterns []glob.Glob, env []string) (bool, error) { if len(patterns) == 0 { return false, nil } - affectedFiles, err := git.GetAffectedFiles(oldCommitID, newCommitID, env, repo) + affectedFiles, err := git.GetAffectedFiles(repo, oldCommitID, newCommitID, env) if err != nil { return false, err } @@ -498,7 +502,7 @@ func checkPullFilesProtection(pr *models.PullRequest, gitRepo *git.Repository) e } var err error - pr.ChangedProtectedFiles, err = CheckFileProtection(pr.MergeBase, "tracking", pr.ProtectedBranch.GetProtectedFilePatterns(), 10, os.Environ(), gitRepo) + pr.ChangedProtectedFiles, err = CheckFileProtection(gitRepo, pr.MergeBase, "tracking", pr.ProtectedBranch.GetProtectedFilePatterns(), 10, os.Environ()) if err != nil && !models.IsErrFilePathProtected(err) { return err } diff --git a/services/pull/pull.go b/services/pull/pull.go index db3533ebe4..4f691c0eba 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -9,6 +9,7 @@ import ( "bytes" "context" "fmt" + "io" "regexp" "strings" "time" @@ -22,24 +23,25 @@ import ( "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" + "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/setting" issue_service "code.gitea.io/gitea/services/issue" ) // NewPullRequest creates new pull request with labels for repository. -func NewPullRequest(repo *repo_model.Repository, pull *models.Issue, labelIDs []int64, uuids []string, pr *models.PullRequest, assigneeIDs []int64) error { +func NewPullRequest(ctx context.Context, repo *repo_model.Repository, pull *models.Issue, labelIDs []int64, uuids []string, pr *models.PullRequest, assigneeIDs []int64) error { if err := TestPatch(pr); err != nil { return err } - divergence, err := GetDiverging(pr) + divergence, err := GetDiverging(ctx, pr) if err != nil { return err } pr.CommitsAhead = divergence.Ahead pr.CommitsBehind = divergence.Behind - if err := models.NewPullRequest(repo, pull, labelIDs, uuids, pr); err != nil { + if err := models.NewPullRequest(ctx, repo, pull, labelIDs, uuids, pr); err != nil { return err } @@ -52,10 +54,16 @@ func NewPullRequest(repo *repo_model.Repository, pull *models.Issue, labelIDs [] pr.Issue = pull pull.PullRequest = pr + // Now - even if the request context has been cancelled as the PR has been created + // in the db and there is no way to cancel that transaction we have to proceed - therefore + // create new context and work from there + prCtx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("NewPullRequest: %s:%d", repo.FullName(), pr.Index)) + defer finished() + if pr.Flow == models.PullRequestFlowGithub { - err = PushToBaseRepo(pr) + err = PushToBaseRepo(prCtx, pr) } else { - err = UpdateRef(pr) + err = UpdateRef(prCtx, pr) } if err != nil { return err @@ -75,7 +83,7 @@ func NewPullRequest(repo *repo_model.Repository, pull *models.Issue, labelIDs [] } // add first push codes comment - baseGitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) + baseGitRepo, err := git.OpenRepositoryCtx(prCtx, pr.BaseRepo.RepoPath()) if err != nil { return err } @@ -115,7 +123,7 @@ func NewPullRequest(repo *repo_model.Repository, pull *models.Issue, labelIDs [] } // ChangeTargetBranch changes the target branch of this pull request, as the given user. -func ChangeTargetBranch(pr *models.PullRequest, doer *user_model.User, targetBranch string) (err error) { +func ChangeTargetBranch(ctx context.Context, pr *models.PullRequest, doer *user_model.User, targetBranch string) (err error) { // Current target branch is already the same if pr.BaseBranch == targetBranch { return nil @@ -141,7 +149,7 @@ func ChangeTargetBranch(pr *models.PullRequest, doer *user_model.User, targetBra } // Check if branches are equal - branchesEqual, err := IsHeadEqualWithBranch(pr, targetBranch) + branchesEqual, err := IsHeadEqualWithBranch(ctx, pr, targetBranch) if err != nil { return err } @@ -184,7 +192,7 @@ func ChangeTargetBranch(pr *models.PullRequest, doer *user_model.User, targetBra } // Update Commit Divergence - divergence, err := GetDiverging(pr) + divergence, err := GetDiverging(ctx, pr) if err != nil { return err } @@ -211,12 +219,12 @@ func ChangeTargetBranch(pr *models.PullRequest, doer *user_model.User, targetBra return nil } -func checkForInvalidation(requests models.PullRequestList, repoID int64, doer *user_model.User, branch string) error { +func checkForInvalidation(ctx context.Context, requests models.PullRequestList, repoID int64, doer *user_model.User, branch string) error { repo, err := repo_model.GetRepositoryByID(repoID) if err != nil { return fmt.Errorf("GetRepositoryByID: %v", err) } - gitRepo, err := git.OpenRepository(repo.RepoPath()) + gitRepo, err := git.OpenRepositoryCtx(ctx, repo.RepoPath()) if err != nil { return fmt.Errorf("git.OpenRepository: %v", err) } @@ -251,13 +259,13 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, if err = requests.LoadAttributes(); err != nil { log.Error("PullRequestList.LoadAttributes: %v", err) } - if invalidationErr := checkForInvalidation(requests, repoID, doer, branch); invalidationErr != nil { + if invalidationErr := checkForInvalidation(ctx, requests, repoID, doer, branch); invalidationErr != nil { log.Error("checkForInvalidation: %v", invalidationErr) } if err == nil { for _, pr := range prs { if newCommitID != "" && newCommitID != git.EmptySHA { - changed, err := checkIfPRContentChanged(pr, oldCommitID, newCommitID) + changed, err := checkIfPRContentChanged(ctx, pr, oldCommitID, newCommitID) if err != nil { log.Error("checkIfPRContentChanged: %v", err) } @@ -270,7 +278,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, if err := models.MarkReviewsAsNotStale(pr.IssueID, newCommitID); err != nil { log.Error("MarkReviewsAsNotStale: %v", err) } - divergence, err := GetDiverging(pr) + divergence, err := GetDiverging(ctx, pr) if err != nil { log.Error("GetDiverging: %v", err) } else { @@ -290,7 +298,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, for _, pr := range prs { log.Trace("Updating PR[%d]: composing new test task", pr.ID) if pr.Flow == models.PullRequestFlowGithub { - if err := PushToBaseRepo(pr); err != nil { + if err := PushToBaseRepo(ctx, pr); err != nil { log.Error("PushToBaseRepo: %v", err) continue } @@ -299,7 +307,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, } AddToTaskQueue(pr) - comment, err := models.CreatePushPullComment(doer, pr, oldCommitID, newCommitID) + comment, err := models.CreatePushPullComment(ctx, doer, pr, oldCommitID, newCommitID) if err == nil && comment != nil { notification.NotifyPullRequestPushCommits(doer, pr, comment) } @@ -312,7 +320,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, return } for _, pr := range prs { - divergence, err := GetDiverging(pr) + divergence, err := GetDiverging(ctx, pr) if err != nil { if models.IsErrBranchDoesNotExist(err) && !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) { log.Warn("Cannot test PR %s/%d: head_branch %s no longer exists", pr.BaseRepo.Name, pr.IssueID, pr.HeadBranch) @@ -332,7 +340,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, // checkIfPRContentChanged checks if diff to target branch has changed by push // A commit can be considered to leave the PR untouched if the patch/diff with its merge base is unchanged -func checkIfPRContentChanged(pr *models.PullRequest, oldCommitID, newCommitID string) (hasChanged bool, err error) { +func checkIfPRContentChanged(ctx context.Context, pr *models.PullRequest, oldCommitID, newCommitID string) (hasChanged bool, err error) { if err = pr.LoadHeadRepo(); err != nil { return false, fmt.Errorf("LoadHeadRepo: %v", err) } else if pr.HeadRepo == nil { @@ -344,7 +352,7 @@ func checkIfPRContentChanged(pr *models.PullRequest, oldCommitID, newCommitID st return false, fmt.Errorf("LoadBaseRepo: %v", err) } - headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) + headGitRepo, err := git.OpenRepositoryCtx(ctx, pr.HeadRepo.RepoPath()) if err != nil { return false, fmt.Errorf("OpenRepository: %v", err) } @@ -404,11 +412,11 @@ func checkIfPRContentChanged(pr *models.PullRequest, oldCommitID, newCommitID st // PushToBaseRepo pushes commits from branches of head repository to // corresponding branches of base repository. // FIXME: Only push branches that are actually updates? -func PushToBaseRepo(pr *models.PullRequest) (err error) { - return pushToBaseRepoHelper(pr, "") +func PushToBaseRepo(ctx context.Context, pr *models.PullRequest) (err error) { + return pushToBaseRepoHelper(ctx, pr, "") } -func pushToBaseRepoHelper(pr *models.PullRequest, prefixHeadBranch string) (err error) { +func pushToBaseRepoHelper(ctx context.Context, pr *models.PullRequest, prefixHeadBranch string) (err error) { log.Trace("PushToBaseRepo[%d]: pushing commits to base repo '%s'", pr.BaseRepoID, pr.GetGitRefName()) if err := pr.LoadHeadRepo(); err != nil { @@ -432,7 +440,7 @@ func pushToBaseRepoHelper(pr *models.PullRequest, prefixHeadBranch string) (err gitRefName := pr.GetGitRefName() - if err := git.Push(git.DefaultContext, headRepoPath, git.PushOptions{ + if err := git.Push(ctx, headRepoPath, git.PushOptions{ Remote: baseRepoPath, Branch: prefixHeadBranch + pr.HeadBranch + ":" + gitRefName, Force: true, @@ -452,8 +460,8 @@ func pushToBaseRepoHelper(pr *models.PullRequest, prefixHeadBranch string) (err log.Info("Can't push with %s%s", prefixHeadBranch, pr.HeadBranch) return err } - log.Info("Retrying to push with "+git.BranchPrefix+"%s", pr.HeadBranch) - err = pushToBaseRepoHelper(pr, git.BranchPrefix) + log.Info("Retrying to push with %s%s", git.BranchPrefix, pr.HeadBranch) + err = pushToBaseRepoHelper(ctx, pr, git.BranchPrefix) return err } log.Error("Unable to push PR head for %s#%d (%-v:%s) due to Error: %v", pr.BaseRepo.FullName(), pr.Index, pr.BaseRepo, gitRefName, err) @@ -464,14 +472,14 @@ func pushToBaseRepoHelper(pr *models.PullRequest, prefixHeadBranch string) (err } // UpdateRef update refs/pull/id/head directly for agit flow pull request -func UpdateRef(pr *models.PullRequest) (err error) { +func UpdateRef(ctx context.Context, pr *models.PullRequest) (err error) { log.Trace("UpdateRef[%d]: upgate pull request ref in base repo '%s'", pr.ID, pr.GetGitRefName()) if err := pr.LoadBaseRepo(); err != nil { log.Error("Unable to load base repository for PR[%d] Error: %v", pr.ID, err) return err } - _, err = git.NewCommand("update-ref", pr.GetGitRefName(), pr.HeadCommitID).RunInDir(pr.BaseRepo.RepoPath()) + _, err = git.NewCommandContext(ctx, "update-ref", pr.GetGitRefName(), pr.HeadCommitID).RunInDir(pr.BaseRepo.RepoPath()) if err != nil { log.Error("Unable to update ref in base repository for PR[%d] Error: %v", pr.ID, err) } @@ -525,8 +533,8 @@ func CloseBranchPulls(doer *user_model.User, repoID int64, branch string) error } // CloseRepoBranchesPulls close all pull requests which head branches are in the given repository, but only whose base repo is not in the given repository -func CloseRepoBranchesPulls(doer *user_model.User, repo *repo_model.Repository) error { - branches, _, err := git.GetBranchesByPath(repo.RepoPath(), 0, 0) +func CloseRepoBranchesPulls(ctx context.Context, doer *user_model.User, repo *repo_model.Repository) error { + branches, _, err := git.GetBranchesByPath(ctx, repo.RepoPath(), 0, 0) if err != nil { return err } @@ -563,7 +571,7 @@ func CloseRepoBranchesPulls(doer *user_model.User, repo *repo_model.Repository) var commitMessageTrailersPattern = regexp.MustCompile(`(?:^|\n\n)(?:[\w-]+[ \t]*:[^\n]+\n*(?:[ \t]+[^\n]+\n*)*)+$`) // GetSquashMergeCommitMessages returns the commit messages between head and merge base (if there is one) -func GetSquashMergeCommitMessages(pr *models.PullRequest) string { +func GetSquashMergeCommitMessages(ctx context.Context, pr *models.PullRequest) string { if err := pr.LoadIssue(); err != nil { log.Error("Cannot load issue %d for PR id %d: Error: %v", pr.IssueID, pr.ID, err) return "" @@ -583,12 +591,12 @@ func GetSquashMergeCommitMessages(pr *models.PullRequest) string { } } - gitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) + gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, pr.HeadRepo.RepoPath()) if err != nil { log.Error("Unable to open head repository: Error: %v", err) return "" } - defer gitRepo.Close() + defer closer.Close() var headCommit *git.Commit if pr.Flow == models.PullRequestFlowGithub { @@ -719,7 +727,7 @@ func GetSquashMergeCommitMessages(pr *models.PullRequest) string { } // GetIssuesLastCommitStatus returns a map -func GetIssuesLastCommitStatus(issues models.IssueList) (map[int64]*models.CommitStatus, error) { +func GetIssuesLastCommitStatus(ctx context.Context, issues models.IssueList) (map[int64]*models.CommitStatus, error) { if err := issues.LoadPullRequests(); err != nil { return nil, err } @@ -744,7 +752,7 @@ func GetIssuesLastCommitStatus(issues models.IssueList) (map[int64]*models.Commi } gitRepo, ok := gitRepos[issue.RepoID] if !ok { - gitRepo, err = git.OpenRepository(issue.Repo.RepoPath()) + gitRepo, err = git.OpenRepositoryCtx(ctx, issue.Repo.RepoPath()) if err != nil { log.Error("Cannot open git repository %-v for issue #%d[%d]. Error: %v", issue.Repo, issue.Index, issue.ID, err) continue @@ -762,20 +770,6 @@ func GetIssuesLastCommitStatus(issues models.IssueList) (map[int64]*models.Commi return res, nil } -// GetLastCommitStatus returns list of commit statuses for latest commit on this pull request. -func GetLastCommitStatus(pr *models.PullRequest) (status *models.CommitStatus, err error) { - if err = pr.LoadBaseRepo(); err != nil { - return nil, err - } - gitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) - if err != nil { - return nil, err - } - defer gitRepo.Close() - - return getLastCommitStatus(gitRepo, pr) -} - // getLastCommitStatus get pr's last commit status. PR's last commit status is the head commit id's last commit status func getLastCommitStatus(gitRepo *git.Repository, pr *models.PullRequest) (status *models.CommitStatus, err error) { sha, err := gitRepo.GetRefCommitID(pr.GetGitRefName()) @@ -791,16 +785,16 @@ func getLastCommitStatus(gitRepo *git.Repository, pr *models.PullRequest) (statu } // IsHeadEqualWithBranch returns if the commits of branchName are available in pull request head -func IsHeadEqualWithBranch(pr *models.PullRequest, branchName string) (bool, error) { +func IsHeadEqualWithBranch(ctx context.Context, pr *models.PullRequest, branchName string) (bool, error) { var err error if err = pr.LoadBaseRepo(); err != nil { return false, err } - baseGitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) + baseGitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, pr.BaseRepo.RepoPath()) if err != nil { return false, err } - defer baseGitRepo.Close() + defer closer.Close() baseCommit, err := baseGitRepo.GetBranchCommit(branchName) if err != nil { @@ -810,11 +804,18 @@ func IsHeadEqualWithBranch(pr *models.PullRequest, branchName string) (bool, err if err = pr.LoadHeadRepo(); err != nil { return false, err } - headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) - if err != nil { - return false, err + var headGitRepo *git.Repository + if pr.HeadRepoID == pr.BaseRepoID { + headGitRepo = baseGitRepo + } else { + var closer io.Closer + + headGitRepo, closer, err = git.RepositoryFromContextOrOpen(ctx, pr.HeadRepo.RepoPath()) + if err != nil { + return false, err + } + defer closer.Close() } - defer headGitRepo.Close() var headCommit *git.Commit if pr.Flow == models.PullRequestFlowGithub { diff --git a/services/pull/review.go b/services/pull/review.go index 42292ac209..0db3168895 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -6,6 +6,7 @@ package pull import ( + "context" "fmt" "io" "regexp" @@ -22,7 +23,8 @@ import ( ) // CreateCodeComment creates a comment on the code line -func CreateCodeComment(doer *user_model.User, gitRepo *git.Repository, issue *models.Issue, line int64, content, treePath string, isReview bool, replyReviewID int64, latestCommitID string) (*models.Comment, error) { +func CreateCodeComment(ctx context.Context, doer *user_model.User, gitRepo *git.Repository, issue *models.Issue, line int64, content, treePath string, isReview bool, replyReviewID int64, latestCommitID string) (*models.Comment, error) { + var ( existsReview bool err error @@ -47,7 +49,7 @@ func CreateCodeComment(doer *user_model.User, gitRepo *git.Repository, issue *mo return nil, err } - comment, err := createCodeComment( + comment, err := createCodeComment(ctx, doer, issue.Repo, issue, @@ -87,7 +89,7 @@ func CreateCodeComment(doer *user_model.User, gitRepo *git.Repository, issue *mo } } - comment, err := createCodeComment( + comment, err := createCodeComment(ctx, doer, issue.Repo, issue, @@ -102,7 +104,7 @@ func CreateCodeComment(doer *user_model.User, gitRepo *git.Repository, issue *mo if !isReview && !existsReview { // Submit the review we've just created so the comment shows up in the issue view - if _, _, err = SubmitReview(doer, gitRepo, issue, models.ReviewTypeComment, "", latestCommitID, nil); err != nil { + if _, _, err = SubmitReview(ctx, doer, gitRepo, issue, models.ReviewTypeComment, "", latestCommitID, nil); err != nil { return nil, err } } @@ -115,7 +117,7 @@ func CreateCodeComment(doer *user_model.User, gitRepo *git.Repository, issue *mo var notEnoughLines = regexp.MustCompile(`exit status 128 - fatal: file .* has only \d+ lines?`) // createCodeComment creates a plain code comment at the specified line / path -func createCodeComment(doer *user_model.User, repo *repo_model.Repository, issue *models.Issue, content, treePath string, line, reviewID int64) (*models.Comment, error) { +func createCodeComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issue *models.Issue, content, treePath string, line, reviewID int64) (*models.Comment, error) { var commitID, patch string if err := issue.LoadPullRequest(); err != nil { return nil, fmt.Errorf("GetPullRequestByIssueID: %v", err) @@ -124,11 +126,11 @@ func createCodeComment(doer *user_model.User, repo *repo_model.Repository, issue if err := pr.LoadBaseRepo(); err != nil { return nil, fmt.Errorf("LoadHeadRepo: %v", err) } - gitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) + gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, pr.BaseRepo.RepoPath()) if err != nil { return nil, fmt.Errorf("OpenRepository: %v", err) } - defer gitRepo.Close() + defer closer.Close() invalidated := false head := pr.GetGitRefName() @@ -217,7 +219,7 @@ func createCodeComment(doer *user_model.User, repo *repo_model.Repository, issue } // SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist -func SubmitReview(doer *user_model.User, gitRepo *git.Repository, issue *models.Issue, reviewType models.ReviewType, content, commitID string, attachmentUUIDs []string) (*models.Review, *models.Comment, error) { +func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repository, issue *models.Issue, reviewType models.ReviewType, content, commitID string, attachmentUUIDs []string) (*models.Review, *models.Comment, error) { pr, err := issue.GetPullRequest() if err != nil { return nil, nil, err @@ -235,7 +237,7 @@ func SubmitReview(doer *user_model.User, gitRepo *git.Repository, issue *models. if headCommitID == commitID { stale = false } else { - stale, err = checkIfPRContentChanged(pr, commitID, headCommitID) + stale, err = checkIfPRContentChanged(ctx, pr, commitID, headCommitID) if err != nil { return nil, nil, err } @@ -247,7 +249,6 @@ func SubmitReview(doer *user_model.User, gitRepo *git.Repository, issue *models. return nil, nil, err } - ctx := db.DefaultContext mentions, err := issue.FindAndUpdateIssueMentions(ctx, doer, comm.Content) if err != nil { return nil, nil, err @@ -271,7 +272,7 @@ func SubmitReview(doer *user_model.User, gitRepo *git.Repository, issue *models. } // DismissReview dismissing stale review by repo admin -func DismissReview(reviewID int64, message string, doer *user_model.User, isDismiss bool) (comment *models.Comment, err error) { +func DismissReview(ctx context.Context, reviewID int64, message string, doer *user_model.User, isDismiss bool) (comment *models.Comment, err error) { review, err := models.GetReviewByID(reviewID) if err != nil { return @@ -290,7 +291,7 @@ func DismissReview(reviewID int64, message string, doer *user_model.User, isDism } // load data for notify - if err = review.LoadAttributes(); err != nil { + if err = review.LoadAttributes(ctx); err != nil { return } if err = review.Issue.LoadPullRequest(); err != nil { diff --git a/services/pull/temp_repo.go b/services/pull/temp_repo.go index d5dc8a5c4b..140403172c 100644 --- a/services/pull/temp_repo.go +++ b/services/pull/temp_repo.go @@ -6,6 +6,7 @@ package pull import ( + "context" "fmt" "os" "path/filepath" @@ -20,7 +21,7 @@ import ( // createTemporaryRepo creates a temporary repo with "base" for pr.BaseBranch and "tracking" for pr.HeadBranch // it also create a second base branch called "original_base" -func createTemporaryRepo(pr *models.PullRequest) (string, error) { +func createTemporaryRepo(ctx context.Context, pr *models.PullRequest) (string, error) { if err := pr.LoadHeadRepo(); err != nil { log.Error("LoadHeadRepo: %v", err) return "", fmt.Errorf("LoadHeadRepo: %v", err) @@ -55,7 +56,7 @@ func createTemporaryRepo(pr *models.PullRequest) (string, error) { baseRepoPath := pr.BaseRepo.RepoPath() headRepoPath := pr.HeadRepo.RepoPath() - if err := git.InitRepository(tmpBasePath, false); err != nil { + if err := git.InitRepository(ctx, tmpBasePath, false); err != nil { log.Error("git init tmpBasePath: %v", err) if err := models.RemoveTemporaryPath(tmpBasePath); err != nil { log.Error("CreateTempRepo: RemoveTemporaryPath: %s", err) @@ -92,7 +93,7 @@ func createTemporaryRepo(pr *models.PullRequest) (string, error) { } var outbuf, errbuf strings.Builder - if err := git.NewCommand("remote", "add", "-t", pr.BaseBranch, "-m", pr.BaseBranch, "origin", baseRepoPath).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "remote", "add", "-t", pr.BaseBranch, "-m", pr.BaseBranch, "origin", baseRepoPath).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("Unable to add base repository as origin [%s -> %s]: %v\n%s\n%s", pr.BaseRepo.FullName(), tmpBasePath, err, outbuf.String(), errbuf.String()) if err := models.RemoveTemporaryPath(tmpBasePath); err != nil { log.Error("CreateTempRepo: RemoveTemporaryPath: %s", err) @@ -102,7 +103,7 @@ func createTemporaryRepo(pr *models.PullRequest) (string, error) { outbuf.Reset() errbuf.Reset() - if err := git.NewCommand("fetch", "origin", "--no-tags", "--", pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "fetch", "origin", "--no-tags", "--", pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("Unable to fetch origin base branch [%s:%s -> base, original_base in %s]: %v:\n%s\n%s", pr.BaseRepo.FullName(), pr.BaseBranch, tmpBasePath, err, outbuf.String(), errbuf.String()) if err := models.RemoveTemporaryPath(tmpBasePath); err != nil { log.Error("CreateTempRepo: RemoveTemporaryPath: %s", err) @@ -112,7 +113,7 @@ func createTemporaryRepo(pr *models.PullRequest) (string, error) { outbuf.Reset() errbuf.Reset() - if err := git.NewCommand("symbolic-ref", "HEAD", git.BranchPrefix+baseBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "symbolic-ref", "HEAD", git.BranchPrefix+baseBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("Unable to set HEAD as base branch [%s]: %v\n%s\n%s", tmpBasePath, err, outbuf.String(), errbuf.String()) if err := models.RemoveTemporaryPath(tmpBasePath); err != nil { log.Error("CreateTempRepo: RemoveTemporaryPath: %s", err) @@ -130,7 +131,7 @@ func createTemporaryRepo(pr *models.PullRequest) (string, error) { return "", fmt.Errorf("Unable to head base repository to temporary repo [%s -> tmpBasePath]: %v", pr.HeadRepo.FullName(), err) } - if err := git.NewCommand("remote", "add", remoteRepoName, headRepoPath).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "remote", "add", remoteRepoName, headRepoPath).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("Unable to add head repository as head_repo [%s -> %s]: %v\n%s\n%s", pr.HeadRepo.FullName(), tmpBasePath, err, outbuf.String(), errbuf.String()) if err := models.RemoveTemporaryPath(tmpBasePath); err != nil { log.Error("CreateTempRepo: RemoveTemporaryPath: %s", err) @@ -150,11 +151,11 @@ func createTemporaryRepo(pr *models.PullRequest) (string, error) { } else { headBranch = pr.GetGitRefName() } - if err := git.NewCommand("fetch", "--no-tags", remoteRepoName, headBranch+":"+trackingBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "fetch", "--no-tags", remoteRepoName, headBranch+":"+trackingBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { if err := models.RemoveTemporaryPath(tmpBasePath); err != nil { log.Error("CreateTempRepo: RemoveTemporaryPath: %s", err) } - if !git.IsBranchExist(git.DefaultContext, pr.HeadRepo.RepoPath(), pr.HeadBranch) { + if !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) { return "", models.ErrBranchDoesNotExist{ BranchName: pr.HeadBranch, } diff --git a/services/pull/update.go b/services/pull/update.go index 8ca7e4cee7..09cf1ce52e 100644 --- a/services/pull/update.go +++ b/services/pull/update.go @@ -5,6 +5,7 @@ package pull import ( + "context" "fmt" "code.gitea.io/gitea/models" @@ -15,7 +16,7 @@ import ( ) // Update updates pull request with base branch. -func Update(pull *models.PullRequest, doer *user_model.User, message string, rebase bool) error { +func Update(ctx context.Context, pull *models.PullRequest, doer *user_model.User, message string, rebase bool) error { var ( pr *models.PullRequest style repo_model.MergeStyle @@ -48,14 +49,14 @@ func Update(pull *models.PullRequest, doer *user_model.User, message string, reb return fmt.Errorf("LoadBaseRepo: %v", err) } - diffCount, err := GetDiverging(pull) + diffCount, err := GetDiverging(ctx, pull) if err != nil { return err } else if diffCount.Behind == 0 { return fmt.Errorf("HeadBranch of PR %d is up to date", pull.Index) } - _, err = rawMerge(pr, doer, style, "", message) + _, err = rawMerge(ctx, pr, doer, style, "", message) defer func() { if rebase { @@ -113,7 +114,7 @@ func IsUserAllowedToUpdate(pull *models.PullRequest, user *user_model.User) (mer } // GetDiverging determines how many commits a PR is ahead or behind the PR base branch -func GetDiverging(pr *models.PullRequest) (*git.DivergeObject, error) { +func GetDiverging(ctx context.Context, pr *models.PullRequest) (*git.DivergeObject, error) { log.Trace("GetDiverging[%d]: compare commits", pr.ID) if err := pr.LoadBaseRepo(); err != nil { return nil, err @@ -122,7 +123,7 @@ func GetDiverging(pr *models.PullRequest) (*git.DivergeObject, error) { return nil, err } - tmpRepo, err := createTemporaryRepo(pr) + tmpRepo, err := createTemporaryRepo(ctx, pr) if err != nil { if !models.IsErrBranchDoesNotExist(err) { log.Error("CreateTemporaryRepo: %v", err) @@ -135,6 +136,6 @@ func GetDiverging(pr *models.PullRequest) (*git.DivergeObject, error) { } }() - diff, err := git.GetDivergingCommits(tmpRepo, "base", "tracking") + diff, err := git.GetDivergingCommits(ctx, tmpRepo, "base", "tracking") return &diff, err } |