2019-09-26 18:22:36 -06:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2022-11-27 11:20:29 -07:00
// SPDX-License-Identifier: MIT
2019-09-26 18:22:36 -06:00
package pull
import (
2019-12-15 02:51:28 -07:00
"context"
2019-09-26 18:22:36 -06:00
"fmt"
2022-01-19 16:26:57 -07:00
"io"
2023-01-28 08:54:40 -07:00
"os"
2021-06-25 11:01:43 -06:00
"regexp"
2020-01-08 18:47:45 -07:00
"strings"
2019-09-26 18:22:36 -06:00
"code.gitea.io/gitea/models"
2021-09-19 05:49:59 -06:00
"code.gitea.io/gitea/models/db"
2022-06-12 09:51:54 -06:00
git_model "code.gitea.io/gitea/models/git"
2022-06-13 03:37:59 -06:00
issues_model "code.gitea.io/gitea/models/issues"
2021-12-09 18:27:50 -07:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-24 02:49:20 -07:00
user_model "code.gitea.io/gitea/models/user"
2022-10-11 23:18:26 -06:00
"code.gitea.io/gitea/modules/container"
2019-10-14 21:28:40 -06:00
"code.gitea.io/gitea/modules/git"
2019-12-15 02:51:28 -07:00
"code.gitea.io/gitea/modules/graceful"
2021-07-24 10:03:58 -06:00
"code.gitea.io/gitea/modules/json"
2019-09-26 18:22:36 -06:00
"code.gitea.io/gitea/modules/log"
2019-11-03 13:59:09 -07:00
"code.gitea.io/gitea/modules/notification"
2022-01-19 16:26:57 -07:00
"code.gitea.io/gitea/modules/process"
2022-05-08 10:46:32 -06:00
repo_module "code.gitea.io/gitea/modules/repository"
2020-04-10 05:26:37 -06:00
"code.gitea.io/gitea/modules/setting"
2022-05-04 10:06:23 -06:00
"code.gitea.io/gitea/modules/sync"
2023-01-28 08:54:40 -07:00
"code.gitea.io/gitea/modules/util"
2019-10-28 10:45:43 -06:00
issue_service "code.gitea.io/gitea/services/issue"
2019-09-26 18:22:36 -06:00
)
2022-05-04 10:06:23 -06:00
// TODO: use clustered lock (unique queue? or *abuse* cache)
var pullWorkingPool = sync . NewExclusivePool ( )
2019-09-26 18:22:36 -06:00
// NewPullRequest creates new pull request with labels for repository.
2022-06-13 03:37:59 -06:00
func NewPullRequest ( ctx context . Context , repo * repo_model . Repository , pull * issues_model . Issue , labelIDs [ ] int64 , uuids [ ] string , pr * issues_model . PullRequest , assigneeIDs [ ] int64 ) error {
2019-12-13 15:21:06 -07:00
if err := TestPatch ( pr ) ; err != nil {
return err
}
2022-01-19 16:26:57 -07:00
divergence , err := GetDiverging ( ctx , pr )
2020-04-14 07:53:34 -06:00
if err != nil {
return err
}
pr . CommitsAhead = divergence . Ahead
pr . CommitsBehind = divergence . Behind
2022-06-13 03:37:59 -06:00
if err := issues_model . NewPullRequest ( ctx , repo , pull , labelIDs , uuids , pr ) ; err != nil {
2019-09-26 18:22:36 -06:00
return err
}
2019-10-28 10:45:43 -06:00
for _ , assigneeID := range assigneeIDs {
if err := issue_service . AddAssigneeIfNotAssigned ( pull , pull . Poster , assigneeID ) ; err != nil {
return err
}
}
2019-09-26 18:22:36 -06:00
pr . Issue = pull
pull . PullRequest = pr
2019-11-03 13:59:09 -07:00
2022-01-19 16:26:57 -07:00
// 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 ( )
2022-06-13 03:37:59 -06:00
if pr . Flow == issues_model . PullRequestFlowGithub {
2022-01-19 16:26:57 -07:00
err = PushToBaseRepo ( prCtx , pr )
2021-07-28 03:42:56 -06:00
} else {
2022-01-19 16:26:57 -07:00
err = UpdateRef ( prCtx , pr )
2021-07-28 03:42:56 -06:00
}
if err != nil {
2019-12-14 20:28:51 -07:00
return err
}
2022-06-13 03:37:59 -06:00
mentions , err := issues_model . FindAndUpdateIssueMentions ( ctx , pull , pull . Poster , pull . Content )
2021-01-02 10:04:02 -07:00
if err != nil {
return err
}
2022-11-19 01:12:33 -07:00
notification . NotifyNewPullRequest ( prCtx , pr , mentions )
2021-01-17 07:15:57 -07:00
if len ( pull . Labels ) > 0 {
2022-11-19 01:12:33 -07:00
notification . NotifyIssueChangeLabels ( prCtx , pull . Poster , pull , pull . Labels , nil )
2021-01-17 07:15:57 -07:00
}
if pull . Milestone != nil {
2022-11-19 01:12:33 -07:00
notification . NotifyIssueChangeMilestone ( prCtx , pull . Poster , pull , 0 )
2021-01-17 07:15:57 -07:00
}
2019-09-26 18:22:36 -06:00
2020-05-20 06:47:24 -06:00
// add first push codes comment
2022-03-29 13:13:41 -06:00
baseGitRepo , err := git . OpenRepository ( prCtx , pr . BaseRepo . RepoPath ( ) )
2020-05-20 06:47:24 -06:00
if err != nil {
return err
}
defer baseGitRepo . Close ( )
compareInfo , err := baseGitRepo . GetCompareInfo ( pr . BaseRepo . RepoPath ( ) ,
2022-01-18 00:45:43 -07:00
git . BranchPrefix + pr . BaseBranch , pr . GetGitRefName ( ) , false , false )
2020-05-20 06:47:24 -06:00
if err != nil {
return err
}
2021-08-09 12:08:51 -06:00
if len ( compareInfo . Commits ) > 0 {
2022-06-13 03:37:59 -06:00
data := issues_model . PushActionContent { IsForcePush : false }
2021-08-09 12:08:51 -06:00
data . CommitIDs = make ( [ ] string , 0 , len ( compareInfo . Commits ) )
for i := len ( compareInfo . Commits ) - 1 ; i >= 0 ; i -- {
data . CommitIDs = append ( data . CommitIDs , compareInfo . Commits [ i ] . ID . String ( ) )
2020-05-20 06:47:24 -06:00
}
dataJSON , err := json . Marshal ( data )
if err != nil {
return err
}
2022-06-13 03:37:59 -06:00
ops := & issues_model . CreateCommentOptions {
Type : issues_model . CommentTypePullRequestPush ,
2020-05-20 06:47:24 -06:00
Doer : pull . Poster ,
Repo : repo ,
Issue : pr . Issue ,
IsForcePush : false ,
Content : string ( dataJSON ) ,
}
2022-12-09 19:46:31 -07:00
_ , _ = issue_service . CreateComment ( ops )
2020-05-20 06:47:24 -06:00
}
2019-09-26 18:22:36 -06:00
return nil
}
2019-10-14 21:28:40 -06:00
2019-12-15 23:20:25 -07:00
// ChangeTargetBranch changes the target branch of this pull request, as the given user.
2022-06-13 03:37:59 -06:00
func ChangeTargetBranch ( ctx context . Context , pr * issues_model . PullRequest , doer * user_model . User , targetBranch string ) ( err error ) {
2022-05-04 10:06:23 -06:00
pullWorkingPool . CheckIn ( fmt . Sprint ( pr . ID ) )
defer pullWorkingPool . CheckOut ( fmt . Sprint ( pr . ID ) )
2019-12-15 23:20:25 -07:00
// Current target branch is already the same
if pr . BaseBranch == targetBranch {
return nil
}
if pr . Issue . IsClosed {
2022-06-13 03:37:59 -06:00
return issues_model . ErrIssueIsClosed {
2019-12-15 23:20:25 -07:00
ID : pr . Issue . ID ,
RepoID : pr . Issue . RepoID ,
Index : pr . Issue . Index ,
}
}
if pr . HasMerged {
return models . ErrPullRequestHasMerged {
ID : pr . ID ,
IssueID : pr . Index ,
HeadRepoID : pr . HeadRepoID ,
BaseRepoID : pr . BaseRepoID ,
HeadBranch : pr . HeadBranch ,
BaseBranch : pr . BaseBranch ,
}
}
// Check if branches are equal
2022-01-19 16:26:57 -07:00
branchesEqual , err := IsHeadEqualWithBranch ( ctx , pr , targetBranch )
2019-12-15 23:20:25 -07:00
if err != nil {
return err
}
if branchesEqual {
return models . ErrBranchesEqual {
HeadBranchName : pr . HeadBranch ,
BaseBranchName : targetBranch ,
}
}
// Check if pull request for the new target branch already exists
2022-11-19 01:12:33 -07:00
existingPr , err := issues_model . GetUnmergedPullRequest ( ctx , pr . HeadRepoID , pr . BaseRepoID , pr . HeadBranch , targetBranch , issues_model . PullRequestFlowGithub )
2019-12-15 23:20:25 -07:00
if existingPr != nil {
2022-06-13 03:37:59 -06:00
return issues_model . ErrPullRequestAlreadyExists {
2019-12-15 23:20:25 -07:00
ID : existingPr . ID ,
IssueID : existingPr . Index ,
HeadRepoID : existingPr . HeadRepoID ,
BaseRepoID : existingPr . BaseRepoID ,
HeadBranch : existingPr . HeadBranch ,
BaseBranch : existingPr . BaseBranch ,
}
}
2022-06-13 03:37:59 -06:00
if err != nil && ! issues_model . IsErrPullRequestNotExist ( err ) {
2019-12-15 23:20:25 -07:00
return err
}
// Set new target branch
oldBranch := pr . BaseBranch
pr . BaseBranch = targetBranch
// Refresh patch
if err := TestPatch ( pr ) ; err != nil {
return err
}
// Update target branch, PR diff and status
// This is the same as checkAndUpdateStatus in check service, but also updates base_branch
2022-06-13 03:37:59 -06:00
if pr . Status == issues_model . PullRequestStatusChecking {
pr . Status = issues_model . PullRequestStatusMergeable
2019-12-15 23:20:25 -07:00
}
2020-06-16 11:52:33 -06:00
// Update Commit Divergence
2022-01-19 16:26:57 -07:00
divergence , err := GetDiverging ( ctx , pr )
2020-06-16 11:52:33 -06:00
if err != nil {
return err
}
pr . CommitsAhead = divergence . Ahead
pr . CommitsBehind = divergence . Behind
2022-11-19 01:12:33 -07:00
if err := pr . UpdateColsIfNotMerged ( ctx , "merge_base" , "status" , "conflicted_files" , "changed_protected_files" , "base_branch" , "commits_ahead" , "commits_behind" ) ; err != nil {
2019-12-15 23:20:25 -07:00
return err
}
// Create comment
2022-06-13 03:37:59 -06:00
options := & issues_model . CreateCommentOptions {
Type : issues_model . CommentTypeChangeTargetBranch ,
2019-12-15 23:20:25 -07:00
Doer : doer ,
Repo : pr . Issue . Repo ,
Issue : pr . Issue ,
OldRef : oldBranch ,
NewRef : targetBranch ,
}
2022-12-09 19:46:31 -07:00
if _ , err = issue_service . CreateComment ( options ) ; err != nil {
2022-10-24 13:29:17 -06:00
return fmt . Errorf ( "CreateChangeTargetBranchComment: %w" , err )
2019-12-15 23:20:25 -07:00
}
return nil
}
2022-06-13 03:37:59 -06:00
func checkForInvalidation ( ctx context . Context , requests issues_model . PullRequestList , repoID int64 , doer * user_model . User , branch string ) error {
2022-12-02 19:48:26 -07:00
repo , err := repo_model . GetRepositoryByID ( ctx , repoID )
2019-10-14 21:28:40 -06:00
if err != nil {
2022-11-19 01:12:33 -07:00
return fmt . Errorf ( "GetRepositoryByIDCtx: %w" , err )
2019-10-14 21:28:40 -06:00
}
2022-03-29 13:13:41 -06:00
gitRepo , err := git . OpenRepository ( ctx , repo . RepoPath ( ) )
2019-10-14 21:28:40 -06:00
if err != nil {
2022-10-24 13:29:17 -06:00
return fmt . Errorf ( "git.OpenRepository: %w" , err )
2019-10-14 21:28:40 -06:00
}
go func ( ) {
2019-12-15 02:51:28 -07:00
// FIXME: graceful: We need to tell the manager we're doing something...
2023-01-17 14:03:44 -07:00
err := InvalidateCodeComments ( ctx , requests , doer , gitRepo , branch )
2019-10-14 21:28:40 -06:00
if err != nil {
log . Error ( "PullRequestList.InvalidateCodeComments: %v" , err )
}
2019-11-13 00:01:19 -07:00
gitRepo . Close ( )
2019-10-14 21:28:40 -06:00
} ( )
return nil
}
// AddTestPullRequestTask adds new test tasks by given head/base repository and head/base branch,
// and generate new patch for testing as needed.
2021-11-24 02:49:20 -07:00
func AddTestPullRequestTask ( doer * user_model . User , repoID int64 , branch string , isSync bool , oldCommitID , newCommitID string ) {
2019-10-14 21:28:40 -06:00
log . Trace ( "AddTestPullRequestTask [head_repo_id: %d, head_branch: %s]: finding pull requests" , repoID , branch )
2019-12-15 02:51:28 -07:00
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
2022-05-07 11:05:52 -06:00
// TODO: graceful: AddTestPullRequestTask needs to become a queue!
2019-10-14 21:28:40 -06:00
Fix cannot reopen after pushing commits to a closed PR (#23189)
Close: #22784
1. On GH, we can reopen a PR which was closed before after pushing
commits. After reopening PR, we can see the commits that were pushed
after closing PR in the time line. So the case of
[issue](https://github.com/go-gitea/gitea/issues/22784) is a bug which
needs to be fixed.
2. After closing a PR and pushing commits, `headBranchSha` is not equal
to `sha`(which is the last commit ID string of reference). If the
judgement exists, the button of reopen will not display. So, skip the
judgement if the status of PR is closed.
![image](https://user-images.githubusercontent.com/33891828/222037529-651fccf9-0bba-433e-b2f0-79c17e0cc812.png)
3. Even if PR is already close, we should still insert comment record
into DB when we push commits.
So we should still call function `CreatePushPullComment()`.
https://github.com/go-gitea/gitea/blob/067b0c2664d127c552ccdfd264257caca4907a77/services/pull/pull.go#L260-L282
So, I add a switch(`includeClosed`) to the
`GetUnmergedPullRequestsByHeadInfo` func to control whether the status
of PR must be open. In this case, by setting `includeClosed` to `true`,
we can query the closed PR.
![image](https://user-images.githubusercontent.com/33891828/222621045-bb80987c-10c5-4eac-aa0c-1fb9c6aefb51.png)
4. In the loop of comments, I use the`latestCloseCommentID` variable to
record the last occurrence of the close comment.
In the go template, if the status of PR is closed, the comments whose
type is `CommentTypePullRequestPush(29)` after `latestCloseCommentID`
won't be rendered.
![image](https://user-images.githubusercontent.com/33891828/222058913-c91cf3e3-819b-40c5-8015-654b31eeccff.png)
e.g.
1). The initial status of the PR is opened.
![image](https://user-images.githubusercontent.com/33891828/222453617-33c5093e-f712-4cd6-8489-9f87e2075869.png)
2). Then I click the button of `Close`. PR is closed now.
![image](https://user-images.githubusercontent.com/33891828/222453694-25c588a9-c121-4897-9ae5-0b13cf33d20b.png)
3). I try to push a commit to this PR, even though its current status is
closed.
![image](https://user-images.githubusercontent.com/33891828/222453916-361678fb-7321-410d-9e37-5a26e8095638.png)
But in comments list, this commit do not display.This is as expected :)
![image](https://user-images.githubusercontent.com/33891828/222454169-7617a791-78d2-404e-be5e-77d555f93313.png)
4). Click the `Reopen` button, the commit which is pushed after closing
PR display now.
![image](https://user-images.githubusercontent.com/33891828/222454533-897893b6-b96e-4701-b5cb-b1800f382b8f.png)
---------
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-03-03 06:16:58 -07:00
prs , err := issues_model . GetUnmergedPullRequestsByHeadInfo ( repoID , branch , true )
2019-12-15 02:51:28 -07:00
if err != nil {
log . Error ( "Find pull requests [head_repo_id: %d, head_branch: %s]: %v" , repoID , branch , err )
return
2019-10-14 21:28:40 -06:00
}
2019-12-15 02:51:28 -07:00
2023-02-05 04:57:38 -07:00
for _ , pr := range prs {
log . Trace ( "Updating PR[%d]: composing new test task" , pr . ID )
if pr . Flow == issues_model . PullRequestFlowGithub {
if err := PushToBaseRepo ( ctx , pr ) ; err != nil {
log . Error ( "PushToBaseRepo: %v" , err )
continue
}
} else {
continue
}
2023-03-15 00:42:21 -06:00
// If the PR is closed, someone still push some commits to the PR,
// 1. We will insert comments of commits, but hidden until the PR is reopened.
// 2. We won't send any notification.
2023-02-05 04:57:38 -07:00
AddToTaskQueue ( pr )
comment , err := CreatePushPullComment ( ctx , doer , pr , oldCommitID , newCommitID )
2023-03-15 00:42:21 -06:00
if err == nil && comment != nil && ! pr . Issue . IsClosed {
2023-02-05 04:57:38 -07:00
notification . NotifyPullRequestPushCommits ( ctx , doer , pr , comment )
}
}
2019-12-15 02:51:28 -07:00
if isSync {
2022-06-13 03:37:59 -06:00
requests := issues_model . PullRequestList ( prs )
2019-12-15 02:51:28 -07:00
if err = requests . LoadAttributes ( ) ; err != nil {
log . Error ( "PullRequestList.LoadAttributes: %v" , err )
}
2022-01-19 16:26:57 -07:00
if invalidationErr := checkForInvalidation ( ctx , requests , repoID , doer , branch ) ; invalidationErr != nil {
2019-12-15 02:51:28 -07:00
log . Error ( "checkForInvalidation: %v" , invalidationErr )
}
if err == nil {
for _ , pr := range prs {
2020-01-08 18:47:45 -07:00
if newCommitID != "" && newCommitID != git . EmptySHA {
2022-01-19 16:26:57 -07:00
changed , err := checkIfPRContentChanged ( ctx , pr , oldCommitID , newCommitID )
2020-01-08 18:47:45 -07:00
if err != nil {
log . Error ( "checkIfPRContentChanged: %v" , err )
}
if changed {
// Mark old reviews as stale if diff to mergebase has changed
2022-06-13 03:37:59 -06:00
if err := issues_model . MarkReviewsAsStale ( pr . IssueID ) ; err != nil {
2020-01-08 18:47:45 -07:00
log . Error ( "MarkReviewsAsStale: %v" , err )
}
}
2022-06-13 03:37:59 -06:00
if err := issues_model . MarkReviewsAsNotStale ( pr . IssueID , newCommitID ) ; err != nil {
2020-01-08 18:47:45 -07:00
log . Error ( "MarkReviewsAsNotStale: %v" , err )
}
2022-01-19 16:26:57 -07:00
divergence , err := GetDiverging ( ctx , pr )
2020-04-14 07:53:34 -06:00
if err != nil {
log . Error ( "GetDiverging: %v" , err )
} else {
2022-05-20 08:08:52 -06:00
err = pr . UpdateCommitDivergence ( ctx , divergence . Ahead , divergence . Behind )
2020-04-14 07:53:34 -06:00
if err != nil {
log . Error ( "UpdateCommitDivergence: %v" , err )
}
}
2020-01-08 18:47:45 -07:00
}
2022-11-19 01:12:33 -07:00
notification . NotifyPullRequestSynchronized ( ctx , doer , pr )
2019-12-15 02:51:28 -07:00
}
2019-10-14 21:28:40 -06:00
}
}
2019-12-15 02:51:28 -07:00
log . Trace ( "AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests" , repoID , branch )
2022-06-13 03:37:59 -06:00
prs , err = issues_model . GetUnmergedPullRequestsByBaseInfo ( repoID , branch )
2019-12-15 02:51:28 -07:00
if err != nil {
log . Error ( "Find pull requests [base_repo_id: %d, base_branch: %s]: %v" , repoID , branch , err )
return
}
for _ , pr := range prs {
2022-01-19 16:26:57 -07:00
divergence , err := GetDiverging ( ctx , pr )
2020-04-14 07:53:34 -06:00
if err != nil {
2021-11-30 13:06:32 -07:00
if models . IsErrBranchDoesNotExist ( err ) && ! git . IsBranchExist ( ctx , pr . HeadRepo . RepoPath ( ) , pr . HeadBranch ) {
2021-07-12 17:26:25 -06:00
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 )
}
2020-04-14 07:53:34 -06:00
} else {
2022-05-20 08:08:52 -06:00
err = pr . UpdateCommitDivergence ( ctx , divergence . Ahead , divergence . Behind )
2020-04-14 07:53:34 -06:00
if err != nil {
log . Error ( "UpdateCommitDivergence: %v" , err )
}
}
2019-12-15 02:51:28 -07:00
AddToTaskQueue ( pr )
}
} )
2019-10-14 21:28:40 -06:00
}
2019-12-14 20:28:51 -07:00
2020-01-08 18:47:45 -07:00
// 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
2022-06-13 03:37:59 -06:00
func checkIfPRContentChanged ( ctx context . Context , pr * issues_model . PullRequest , oldCommitID , newCommitID string ) ( hasChanged bool , err error ) {
2023-03-07 13:07:35 -07:00
prCtx , cancel , err := createTemporaryRepoForPR ( ctx , pr )
2020-01-08 18:47:45 -07:00
if err != nil {
2023-03-07 13:07:35 -07:00
log . Error ( "CreateTemporaryRepoForPR %-v: %v" , pr , err )
2023-01-28 08:54:40 -07:00
return false , err
2020-01-08 18:47:45 -07:00
}
2023-03-07 13:07:35 -07:00
defer cancel ( )
2023-01-28 08:54:40 -07:00
2023-03-07 13:07:35 -07:00
tmpRepo , err := git . OpenRepository ( ctx , prCtx . tmpBasePath )
2020-01-08 18:47:45 -07:00
if err != nil {
2023-01-28 08:54:40 -07:00
return false , fmt . Errorf ( "OpenRepository: %w" , err )
2020-01-08 18:47:45 -07:00
}
2023-01-28 08:54:40 -07:00
defer tmpRepo . Close ( )
2020-01-08 18:47:45 -07:00
2023-01-28 08:54:40 -07:00
// Find the merge-base
_ , base , err := tmpRepo . GetMergeBase ( "" , "base" , "tracking" )
if err != nil {
return false , fmt . Errorf ( "GetMergeBase: %w" , err )
2020-01-08 18:47:45 -07:00
}
2023-01-28 08:54:40 -07:00
cmd := git . NewCommand ( ctx , "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 )
}
if err := cmd . Run ( & git . RunOpts {
2023-03-07 13:07:35 -07:00
Dir : prCtx . tmpBasePath ,
2023-01-28 08:54:40 -07:00
Stdout : stdoutWriter ,
PipelineFunc : func ( ctx context . Context , cancel context . CancelFunc ) error {
_ = stdoutWriter . Close ( )
defer func ( ) {
_ = stdoutReader . Close ( )
} ( )
return util . IsEmptyReader ( stdoutReader )
} ,
} ) ; err != nil {
if err == util . ErrNotEmpty {
2020-01-08 18:47:45 -07:00
return true , nil
}
2023-01-28 08:54:40 -07:00
log . Error ( "Unable to run diff on %s %s %s in tempRepo for PR[%d]%s/%s...%s/%s: Error: %v" ,
newCommitID , oldCommitID , base ,
pr . ID , pr . BaseRepo . FullName ( ) , pr . BaseBranch , pr . HeadRepo . FullName ( ) , pr . HeadBranch ,
err )
return false , fmt . Errorf ( "Unable to run git diff --name-only -z %s %s %s: %w" , newCommitID , oldCommitID , base , err )
2020-01-08 18:47:45 -07:00
}
return false , nil
}
2019-12-14 20:28:51 -07:00
// PushToBaseRepo pushes commits from branches of head repository to
// corresponding branches of base repository.
// FIXME: Only push branches that are actually updates?
2022-06-13 03:37:59 -06:00
func PushToBaseRepo ( ctx context . Context , pr * issues_model . PullRequest ) ( err error ) {
2022-01-19 16:26:57 -07:00
return pushToBaseRepoHelper ( ctx , pr , "" )
2021-06-23 15:08:26 -06:00
}
2022-06-13 03:37:59 -06:00
func pushToBaseRepoHelper ( ctx context . Context , pr * issues_model . PullRequest , prefixHeadBranch string ) ( err error ) {
2019-12-14 20:28:51 -07:00
log . Trace ( "PushToBaseRepo[%d]: pushing commits to base repo '%s'" , pr . BaseRepoID , pr . GetGitRefName ( ) )
2022-11-19 01:12:33 -07:00
if err := pr . LoadHeadRepo ( ctx ) ; err != nil {
2020-02-21 11:18:13 -07:00
log . Error ( "Unable to load head repository for PR[%d] Error: %v" , pr . ID , err )
return err
}
2019-12-14 20:28:51 -07:00
headRepoPath := pr . HeadRepo . RepoPath ( )
2020-01-28 03:23:58 -07:00
2022-11-19 01:12:33 -07:00
if err := pr . LoadBaseRepo ( ctx ) ; err != nil {
2020-02-21 11:18:13 -07:00
log . Error ( "Unable to load base repository for PR[%d] Error: %v" , pr . ID , err )
return err
}
2020-09-14 21:32:31 -06:00
baseRepoPath := pr . BaseRepo . RepoPath ( )
2019-12-14 20:28:51 -07:00
2022-11-19 01:12:33 -07:00
if err = pr . LoadIssue ( ctx ) ; err != nil {
2022-10-24 13:29:17 -06:00
return fmt . Errorf ( "unable to load issue %d for pr %d: %w" , pr . IssueID , pr . ID , err )
2019-12-27 14:15:04 -07:00
}
2022-11-19 01:12:33 -07:00
if err = pr . Issue . LoadPoster ( ctx ) ; err != nil {
2022-10-24 13:29:17 -06:00
return fmt . Errorf ( "unable to load poster %d for pr %d: %w" , pr . Issue . PosterID , pr . ID , err )
2019-12-27 14:15:04 -07:00
}
2020-09-14 21:32:31 -06:00
gitRefName := pr . GetGitRefName ( )
2022-01-19 16:26:57 -07:00
if err := git . Push ( ctx , headRepoPath , git . PushOptions {
2020-09-14 21:32:31 -06:00
Remote : baseRepoPath ,
2021-06-23 15:08:26 -06:00
Branch : prefixHeadBranch + pr . HeadBranch + ":" + gitRefName ,
2019-12-14 20:28:51 -07:00
Force : true ,
2019-12-27 14:15:04 -07:00
// Use InternalPushingEnvironment here because we know that pre-receive and post-receive do not run on a refs/pulls/...
2022-05-08 10:46:32 -06:00
Env : repo_module . InternalPushingEnvironment ( pr . Issue . Poster , pr . BaseRepo ) ,
2019-12-14 20:28:51 -07:00
} ) ; err != nil {
2020-06-08 12:07:41 -06:00
if git . IsErrPushOutOfDate ( err ) {
// This should not happen as we're using force!
2020-09-14 21:32:31 -06:00
log . Error ( "Unable to push PR head for %s#%d (%-v:%s) due to ErrPushOfDate: %v" , pr . BaseRepo . FullName ( ) , pr . Index , pr . BaseRepo , gitRefName , err )
2020-06-08 12:07:41 -06:00
return err
} else if git . IsErrPushRejected ( err ) {
rejectErr := err . ( * git . ErrPushRejected )
2020-09-14 21:32:31 -06:00
log . Info ( "Unable to push PR head for %s#%d (%-v:%s) due to rejection:\nStdout: %s\nStderr: %s\nError: %v" , pr . BaseRepo . FullName ( ) , pr . Index , pr . BaseRepo , gitRefName , rejectErr . StdOut , rejectErr . StdErr , rejectErr . Err )
2020-06-08 12:07:41 -06:00
return err
2021-06-23 15:08:26 -06:00
} else if git . IsErrMoreThanOne ( err ) {
if prefixHeadBranch != "" {
log . Info ( "Can't push with %s%s" , prefixHeadBranch , pr . HeadBranch )
return err
}
2022-01-19 16:26:57 -07:00
log . Info ( "Retrying to push with %s%s" , git . BranchPrefix , pr . HeadBranch )
err = pushToBaseRepoHelper ( ctx , pr , git . BranchPrefix )
2021-06-23 15:08:26 -06:00
return err
2020-06-08 12:07:41 -06:00
}
2020-09-14 21:32:31 -06:00
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 )
2022-10-24 13:29:17 -06:00
return fmt . Errorf ( "Push: %s:%s %s:%s %w" , pr . HeadRepo . FullName ( ) , pr . HeadBranch , pr . BaseRepo . FullName ( ) , gitRefName , err )
2019-12-14 20:28:51 -07:00
}
return nil
}
2020-01-24 19:48:22 -07:00
2021-07-28 03:42:56 -06:00
// UpdateRef update refs/pull/id/head directly for agit flow pull request
2022-06-13 03:37:59 -06:00
func UpdateRef ( ctx context . Context , pr * issues_model . PullRequest ) ( err error ) {
2021-07-28 03:42:56 -06:00
log . Trace ( "UpdateRef[%d]: upgate pull request ref in base repo '%s'" , pr . ID , pr . GetGitRefName ( ) )
2022-11-19 01:12:33 -07:00
if err := pr . LoadBaseRepo ( ctx ) ; err != nil {
2021-07-28 03:42:56 -06:00
log . Error ( "Unable to load base repository for PR[%d] Error: %v" , pr . ID , err )
return err
}
2022-10-23 08:44:45 -06:00
_ , _ , err = git . NewCommand ( ctx , "update-ref" ) . AddDynamicArguments ( pr . GetGitRefName ( ) , pr . HeadCommitID ) . RunStdString ( & git . RunOpts { Dir : pr . BaseRepo . RepoPath ( ) } )
2021-07-28 03:42:56 -06:00
if err != nil {
log . Error ( "Unable to update ref in base repository for PR[%d] Error: %v" , pr . ID , err )
}
return err
}
2020-01-24 19:48:22 -07:00
type errlist [ ] error
func ( errs errlist ) Error ( ) string {
if len ( errs ) > 0 {
var buf strings . Builder
for i , err := range errs {
if i > 0 {
buf . WriteString ( ", " )
}
buf . WriteString ( err . Error ( ) )
}
return buf . String ( )
}
return ""
}
// CloseBranchPulls close all the pull requests who's head branch is the branch
2021-11-24 02:49:20 -07:00
func CloseBranchPulls ( doer * user_model . User , repoID int64 , branch string ) error {
Fix cannot reopen after pushing commits to a closed PR (#23189)
Close: #22784
1. On GH, we can reopen a PR which was closed before after pushing
commits. After reopening PR, we can see the commits that were pushed
after closing PR in the time line. So the case of
[issue](https://github.com/go-gitea/gitea/issues/22784) is a bug which
needs to be fixed.
2. After closing a PR and pushing commits, `headBranchSha` is not equal
to `sha`(which is the last commit ID string of reference). If the
judgement exists, the button of reopen will not display. So, skip the
judgement if the status of PR is closed.
![image](https://user-images.githubusercontent.com/33891828/222037529-651fccf9-0bba-433e-b2f0-79c17e0cc812.png)
3. Even if PR is already close, we should still insert comment record
into DB when we push commits.
So we should still call function `CreatePushPullComment()`.
https://github.com/go-gitea/gitea/blob/067b0c2664d127c552ccdfd264257caca4907a77/services/pull/pull.go#L260-L282
So, I add a switch(`includeClosed`) to the
`GetUnmergedPullRequestsByHeadInfo` func to control whether the status
of PR must be open. In this case, by setting `includeClosed` to `true`,
we can query the closed PR.
![image](https://user-images.githubusercontent.com/33891828/222621045-bb80987c-10c5-4eac-aa0c-1fb9c6aefb51.png)
4. In the loop of comments, I use the`latestCloseCommentID` variable to
record the last occurrence of the close comment.
In the go template, if the status of PR is closed, the comments whose
type is `CommentTypePullRequestPush(29)` after `latestCloseCommentID`
won't be rendered.
![image](https://user-images.githubusercontent.com/33891828/222058913-c91cf3e3-819b-40c5-8015-654b31eeccff.png)
e.g.
1). The initial status of the PR is opened.
![image](https://user-images.githubusercontent.com/33891828/222453617-33c5093e-f712-4cd6-8489-9f87e2075869.png)
2). Then I click the button of `Close`. PR is closed now.
![image](https://user-images.githubusercontent.com/33891828/222453694-25c588a9-c121-4897-9ae5-0b13cf33d20b.png)
3). I try to push a commit to this PR, even though its current status is
closed.
![image](https://user-images.githubusercontent.com/33891828/222453916-361678fb-7321-410d-9e37-5a26e8095638.png)
But in comments list, this commit do not display.This is as expected :)
![image](https://user-images.githubusercontent.com/33891828/222454169-7617a791-78d2-404e-be5e-77d555f93313.png)
4). Click the `Reopen` button, the commit which is pushed after closing
PR display now.
![image](https://user-images.githubusercontent.com/33891828/222454533-897893b6-b96e-4701-b5cb-b1800f382b8f.png)
---------
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-03-03 06:16:58 -07:00
prs , err := issues_model . GetUnmergedPullRequestsByHeadInfo ( repoID , branch , false )
2020-01-24 19:48:22 -07:00
if err != nil {
return err
}
2022-06-13 03:37:59 -06:00
prs2 , err := issues_model . GetUnmergedPullRequestsByBaseInfo ( repoID , branch )
2020-01-24 19:48:22 -07:00
if err != nil {
return err
}
prs = append ( prs , prs2 ... )
2022-06-13 03:37:59 -06:00
if err := issues_model . PullRequestList ( prs ) . LoadAttributes ( ) ; err != nil {
2020-01-24 19:48:22 -07:00
return err
}
var errs errlist
for _ , pr := range prs {
2023-01-24 21:47:53 -07:00
if err = issue_service . ChangeStatus ( pr . Issue , doer , "" , true ) ; err != nil && ! issues_model . IsErrPullWasClosed ( err ) && ! issues_model . IsErrDependenciesLeft ( err ) {
2020-01-24 19:48:22 -07:00
errs = append ( errs , err )
}
}
if len ( errs ) > 0 {
return errs
}
return nil
}
2021-03-01 10:39:44 -07:00
// CloseRepoBranchesPulls close all pull requests which head branches are in the given repository, but only whose base repo is not in the given repository
2022-01-19 16:26:57 -07:00
func CloseRepoBranchesPulls ( ctx context . Context , doer * user_model . User , repo * repo_model . Repository ) error {
branches , _ , err := git . GetBranchesByPath ( ctx , repo . RepoPath ( ) , 0 , 0 )
2020-01-24 19:48:22 -07:00
if err != nil {
return err
}
var errs errlist
for _ , branch := range branches {
Fix cannot reopen after pushing commits to a closed PR (#23189)
Close: #22784
1. On GH, we can reopen a PR which was closed before after pushing
commits. After reopening PR, we can see the commits that were pushed
after closing PR in the time line. So the case of
[issue](https://github.com/go-gitea/gitea/issues/22784) is a bug which
needs to be fixed.
2. After closing a PR and pushing commits, `headBranchSha` is not equal
to `sha`(which is the last commit ID string of reference). If the
judgement exists, the button of reopen will not display. So, skip the
judgement if the status of PR is closed.
![image](https://user-images.githubusercontent.com/33891828/222037529-651fccf9-0bba-433e-b2f0-79c17e0cc812.png)
3. Even if PR is already close, we should still insert comment record
into DB when we push commits.
So we should still call function `CreatePushPullComment()`.
https://github.com/go-gitea/gitea/blob/067b0c2664d127c552ccdfd264257caca4907a77/services/pull/pull.go#L260-L282
So, I add a switch(`includeClosed`) to the
`GetUnmergedPullRequestsByHeadInfo` func to control whether the status
of PR must be open. In this case, by setting `includeClosed` to `true`,
we can query the closed PR.
![image](https://user-images.githubusercontent.com/33891828/222621045-bb80987c-10c5-4eac-aa0c-1fb9c6aefb51.png)
4. In the loop of comments, I use the`latestCloseCommentID` variable to
record the last occurrence of the close comment.
In the go template, if the status of PR is closed, the comments whose
type is `CommentTypePullRequestPush(29)` after `latestCloseCommentID`
won't be rendered.
![image](https://user-images.githubusercontent.com/33891828/222058913-c91cf3e3-819b-40c5-8015-654b31eeccff.png)
e.g.
1). The initial status of the PR is opened.
![image](https://user-images.githubusercontent.com/33891828/222453617-33c5093e-f712-4cd6-8489-9f87e2075869.png)
2). Then I click the button of `Close`. PR is closed now.
![image](https://user-images.githubusercontent.com/33891828/222453694-25c588a9-c121-4897-9ae5-0b13cf33d20b.png)
3). I try to push a commit to this PR, even though its current status is
closed.
![image](https://user-images.githubusercontent.com/33891828/222453916-361678fb-7321-410d-9e37-5a26e8095638.png)
But in comments list, this commit do not display.This is as expected :)
![image](https://user-images.githubusercontent.com/33891828/222454169-7617a791-78d2-404e-be5e-77d555f93313.png)
4). Click the `Reopen` button, the commit which is pushed after closing
PR display now.
![image](https://user-images.githubusercontent.com/33891828/222454533-897893b6-b96e-4701-b5cb-b1800f382b8f.png)
---------
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-03-03 06:16:58 -07:00
prs , err := issues_model . GetUnmergedPullRequestsByHeadInfo ( repo . ID , branch . Name , false )
2020-01-24 19:48:22 -07:00
if err != nil {
return err
}
2022-06-13 03:37:59 -06:00
if err = issues_model . PullRequestList ( prs ) . LoadAttributes ( ) ; err != nil {
2020-01-24 19:48:22 -07:00
return err
}
for _ , pr := range prs {
2021-03-01 10:39:44 -07:00
// If the base repository for this pr is this repository there is no need to close it
// as it is going to be deleted anyway
if pr . BaseRepoID == repo . ID {
continue
}
2023-01-24 21:47:53 -07:00
if err = issue_service . ChangeStatus ( pr . Issue , doer , "" , true ) ; err != nil && ! issues_model . IsErrPullWasClosed ( err ) {
2020-01-24 19:48:22 -07:00
errs = append ( errs , err )
}
}
}
if len ( errs ) > 0 {
return errs
}
return nil
}
2020-04-10 05:26:37 -06:00
2021-06-25 11:01:43 -06:00
var commitMessageTrailersPattern = regexp . MustCompile ( ` (?:^|\n\n)(?:[\w-]+[ \t]*:[^\n]+\n*(?:[ \t]+[^\n]+\n*)*)+$ ` )
2020-12-21 09:46:14 -07:00
// GetSquashMergeCommitMessages returns the commit messages between head and merge base (if there is one)
2022-06-13 03:37:59 -06:00
func GetSquashMergeCommitMessages ( ctx context . Context , pr * issues_model . PullRequest ) string {
2022-11-19 01:12:33 -07:00
if err := pr . LoadIssue ( ctx ) ; err != nil {
2020-04-10 05:26:37 -06:00
log . Error ( "Cannot load issue %d for PR id %d: Error: %v" , pr . IssueID , pr . ID , err )
return ""
}
2022-11-19 01:12:33 -07:00
if err := pr . Issue . LoadPoster ( ctx ) ; err != nil {
2020-04-10 05:26:37 -06:00
log . Error ( "Cannot load poster %d for pr id %d, index %d Error: %v" , pr . Issue . PosterID , pr . ID , pr . Index , err )
return ""
}
if pr . HeadRepo == nil {
var err error
2022-12-02 19:48:26 -07:00
pr . HeadRepo , err = repo_model . GetRepositoryByID ( ctx , pr . HeadRepoID )
2020-04-10 05:26:37 -06:00
if err != nil {
2022-11-19 01:12:33 -07:00
log . Error ( "GetRepositoryByIdCtx[%d]: %v" , pr . HeadRepoID , err )
2020-04-10 05:26:37 -06:00
return ""
}
}
2022-01-19 16:26:57 -07:00
gitRepo , closer , err := git . RepositoryFromContextOrOpen ( ctx , pr . HeadRepo . RepoPath ( ) )
2020-04-10 05:26:37 -06:00
if err != nil {
log . Error ( "Unable to open head repository: Error: %v" , err )
return ""
}
2022-01-19 16:26:57 -07:00
defer closer . Close ( )
2020-04-10 05:26:37 -06:00
2021-07-28 03:42:56 -06:00
var headCommit * git . Commit
2022-06-13 03:37:59 -06:00
if pr . Flow == issues_model . PullRequestFlowGithub {
2021-07-28 03:42:56 -06:00
headCommit , err = gitRepo . GetBranchCommit ( pr . HeadBranch )
} else {
pr . HeadCommitID , err = gitRepo . GetRefCommitID ( pr . GetGitRefName ( ) )
if err != nil {
log . Error ( "Unable to get head commit: %s Error: %v" , pr . GetGitRefName ( ) , err )
return ""
}
headCommit , err = gitRepo . GetCommit ( pr . HeadCommitID )
}
2020-04-10 05:26:37 -06:00
if err != nil {
log . Error ( "Unable to get head commit: %s Error: %v" , pr . HeadBranch , err )
return ""
}
mergeBase , err := gitRepo . GetCommit ( pr . MergeBase )
if err != nil {
log . Error ( "Unable to get merge base commit: %s Error: %v" , pr . MergeBase , err )
return ""
}
limit := setting . Repository . PullRequest . DefaultMergeMessageCommitsLimit
2021-08-09 12:08:51 -06:00
commits , err := gitRepo . CommitsBetweenLimit ( headCommit , mergeBase , limit , 0 )
2020-04-10 05:26:37 -06:00
if err != nil {
log . Error ( "Unable to get commits between: %s %s Error: %v" , pr . HeadBranch , pr . MergeBase , err )
return ""
}
posterSig := pr . Issue . Poster . NewGitSig ( ) . String ( )
2022-10-11 23:18:26 -06:00
uniqueAuthors := make ( container . Set [ string ] )
2021-08-09 12:08:51 -06:00
authors := make ( [ ] string , 0 , len ( commits ) )
2020-04-10 05:26:37 -06:00
stringBuilder := strings . Builder { }
2020-12-21 09:46:14 -07:00
2021-06-18 16:08:22 -06:00
if ! setting . Repository . PullRequest . PopulateSquashCommentWithCommitMessages {
2021-06-25 11:01:43 -06:00
message := strings . TrimSpace ( pr . Issue . Content )
stringBuilder . WriteString ( message )
2021-06-18 16:08:22 -06:00
if stringBuilder . Len ( ) > 0 {
stringBuilder . WriteRune ( '\n' )
2021-06-25 11:01:43 -06:00
if ! commitMessageTrailersPattern . MatchString ( message ) {
stringBuilder . WriteRune ( '\n' )
}
2021-06-18 16:08:22 -06:00
}
2020-12-21 09:46:14 -07:00
}
2020-11-25 13:08:17 -07:00
// commits list is in reverse chronological order
2021-08-09 12:08:51 -06:00
first := true
for i := len ( commits ) - 1 ; i >= 0 ; i -- {
commit := commits [ i ]
2021-06-18 16:08:22 -06:00
if setting . Repository . PullRequest . PopulateSquashCommentWithCommitMessages {
maxSize := setting . Repository . PullRequest . DefaultMergeMessageSize
if maxSize < 0 || stringBuilder . Len ( ) < maxSize {
var toWrite [ ] byte
2021-08-09 12:08:51 -06:00
if first {
first = false
2021-06-18 16:08:22 -06:00
toWrite = [ ] byte ( strings . TrimPrefix ( commit . CommitMessage , pr . Issue . Title ) )
} else {
toWrite = [ ] byte ( commit . CommitMessage )
}
if len ( toWrite ) > maxSize - stringBuilder . Len ( ) && maxSize > - 1 {
toWrite = append ( toWrite [ : maxSize - stringBuilder . Len ( ) ] , "..." ... )
}
if _ , err := stringBuilder . Write ( toWrite ) ; err != nil {
log . Error ( "Unable to write commit message Error: %v" , err )
return ""
}
if _ , err := stringBuilder . WriteRune ( '\n' ) ; err != nil {
log . Error ( "Unable to write commit message Error: %v" , err )
return ""
}
}
}
2020-04-10 05:26:37 -06:00
authorString := commit . Author . String ( )
2022-10-11 23:18:26 -06:00
if uniqueAuthors . Add ( authorString ) && authorString != posterSig {
2023-03-09 20:17:04 -07:00
// Compare use account as well to avoid adding the same author multiple times
// times when email addresses are private or multiple emails are used.
commitUser , _ := user_model . GetUserByEmail ( ctx , commit . Author . Email )
if commitUser == nil || commitUser . ID != pr . Issue . Poster . ID {
authors = append ( authors , authorString )
}
2020-04-10 05:26:37 -06:00
}
}
// Consider collecting the remaining authors
if limit >= 0 && setting . Repository . PullRequest . DefaultMergeMessageAllAuthors {
skip := limit
limit = 30
for {
2021-08-09 12:08:51 -06:00
commits , err := gitRepo . CommitsBetweenLimit ( headCommit , mergeBase , limit , skip )
2020-04-10 05:26:37 -06:00
if err != nil {
log . Error ( "Unable to get commits between: %s %s Error: %v" , pr . HeadBranch , pr . MergeBase , err )
return ""
}
2021-08-09 12:08:51 -06:00
if len ( commits ) == 0 {
2020-04-10 05:26:37 -06:00
break
}
2021-08-09 12:08:51 -06:00
for _ , commit := range commits {
2020-04-10 05:26:37 -06:00
authorString := commit . Author . String ( )
2022-10-11 23:18:26 -06:00
if uniqueAuthors . Add ( authorString ) && authorString != posterSig {
2023-03-09 20:17:04 -07:00
commitUser , _ := user_model . GetUserByEmail ( ctx , commit . Author . Email )
if commitUser == nil || commitUser . ID != pr . Issue . Poster . ID {
authors = append ( authors , authorString )
}
2020-04-10 05:26:37 -06:00
}
}
2020-11-27 13:00:52 -07:00
skip += limit
2020-04-10 05:26:37 -06:00
}
}
for _ , author := range authors {
if _ , err := stringBuilder . Write ( [ ] byte ( "Co-authored-by: " ) ) ; err != nil {
log . Error ( "Unable to write to string builder Error: %v" , err )
return ""
}
if _ , err := stringBuilder . Write ( [ ] byte ( author ) ) ; err != nil {
log . Error ( "Unable to write to string builder Error: %v" , err )
return ""
}
if _ , err := stringBuilder . WriteRune ( '\n' ) ; err != nil {
log . Error ( "Unable to write to string builder Error: %v" , err )
return ""
}
}
return stringBuilder . String ( )
}
2022-04-26 16:40:01 -06:00
// GetIssuesLastCommitStatus returns a map of issue ID to the most recent commit's latest status
2022-06-13 03:37:59 -06:00
func GetIssuesLastCommitStatus ( ctx context . Context , issues issues_model . IssueList ) ( map [ int64 ] * git_model . CommitStatus , error ) {
2022-04-26 16:40:01 -06:00
_ , lastStatus , err := GetIssuesAllCommitStatus ( ctx , issues )
return lastStatus , err
}
// GetIssuesAllCommitStatus returns a map of issue ID to a list of all statuses for the most recent commit as well as a map of issue ID to only the commit's latest status
2022-06-13 03:37:59 -06:00
func GetIssuesAllCommitStatus ( ctx context . Context , issues issues_model . IssueList ) ( map [ int64 ] [ ] * git_model . CommitStatus , map [ int64 ] * git_model . CommitStatus , error ) {
2022-11-19 01:12:33 -07:00
if err := issues . LoadPullRequests ( ctx ) ; err != nil {
2022-04-26 16:40:01 -06:00
return nil , nil , err
2021-04-15 11:34:43 -06:00
}
2022-11-19 01:12:33 -07:00
if _ , err := issues . LoadRepositories ( ctx ) ; err != nil {
2022-04-26 16:40:01 -06:00
return nil , nil , err
2021-04-15 11:34:43 -06:00
}
var (
gitRepos = make ( map [ int64 ] * git . Repository )
2022-06-12 09:51:54 -06:00
res = make ( map [ int64 ] [ ] * git_model . CommitStatus )
lastRes = make ( map [ int64 ] * git_model . CommitStatus )
2021-04-15 11:34:43 -06:00
err error
)
defer func ( ) {
for _ , gitRepo := range gitRepos {
gitRepo . Close ( )
}
} ( )
for _ , issue := range issues {
if ! issue . IsPull {
continue
}
gitRepo , ok := gitRepos [ issue . RepoID ]
if ! ok {
2022-03-29 13:13:41 -06:00
gitRepo , err = git . OpenRepository ( ctx , issue . Repo . RepoPath ( ) )
2021-04-15 11:34:43 -06:00
if err != nil {
2021-12-16 12:01:14 -07:00
log . Error ( "Cannot open git repository %-v for issue #%d[%d]. Error: %v" , issue . Repo , issue . Index , issue . ID , err )
continue
2021-04-15 11:34:43 -06:00
}
gitRepos [ issue . RepoID ] = gitRepo
}
2022-04-26 16:40:01 -06:00
statuses , lastStatus , err := getAllCommitStatus ( gitRepo , issue . PullRequest )
2021-04-15 11:34:43 -06:00
if err != nil {
2022-04-26 16:40:01 -06:00
log . Error ( "getAllCommitStatus: cant get commit statuses of pull [%d]: %v" , issue . PullRequest . ID , err )
2021-05-04 06:03:02 -06:00
continue
2021-04-15 11:34:43 -06:00
}
2022-04-26 16:40:01 -06:00
res [ issue . PullRequest . ID ] = statuses
lastRes [ issue . PullRequest . ID ] = lastStatus
2021-04-15 11:34:43 -06:00
}
2022-04-26 16:40:01 -06:00
return res , lastRes , nil
2021-04-15 11:34:43 -06:00
}
2022-04-26 16:40:01 -06:00
// getAllCommitStatus get pr's commit statuses.
2022-06-13 03:37:59 -06:00
func getAllCommitStatus ( gitRepo * git . Repository , pr * issues_model . PullRequest ) ( statuses [ ] * git_model . CommitStatus , lastStatus * git_model . CommitStatus , err error ) {
2022-04-26 16:40:01 -06:00
sha , shaErr := gitRepo . GetRefCommitID ( pr . GetGitRefName ( ) )
if shaErr != nil {
return nil , nil , shaErr
2020-04-10 05:26:37 -06:00
}
2022-06-12 09:51:54 -06:00
statuses , _ , err = git_model . GetLatestCommitStatus ( db . DefaultContext , pr . BaseRepo . ID , sha , db . ListOptions { } )
lastStatus = git_model . CalcCommitStatus ( statuses )
2022-04-26 16:40:01 -06:00
return statuses , lastStatus , err
2020-04-10 05:26:37 -06:00
}
// IsHeadEqualWithBranch returns if the commits of branchName are available in pull request head
2022-06-13 03:37:59 -06:00
func IsHeadEqualWithBranch ( ctx context . Context , pr * issues_model . PullRequest , branchName string ) ( bool , error ) {
2020-04-10 05:26:37 -06:00
var err error
2022-11-19 01:12:33 -07:00
if err = pr . LoadBaseRepo ( ctx ) ; err != nil {
2020-04-10 05:26:37 -06:00
return false , err
}
2022-01-19 16:26:57 -07:00
baseGitRepo , closer , err := git . RepositoryFromContextOrOpen ( ctx , pr . BaseRepo . RepoPath ( ) )
2020-04-10 05:26:37 -06:00
if err != nil {
return false , err
}
2022-01-19 16:26:57 -07:00
defer closer . Close ( )
2021-01-06 12:23:57 -07:00
2020-04-10 05:26:37 -06:00
baseCommit , err := baseGitRepo . GetBranchCommit ( branchName )
if err != nil {
return false , err
}
2022-11-19 01:12:33 -07:00
if err = pr . LoadHeadRepo ( ctx ) ; err != nil {
2020-04-10 05:26:37 -06:00
return false , err
}
2022-01-19 16:26:57 -07:00
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 ( )
2020-04-10 05:26:37 -06:00
}
2021-01-06 12:23:57 -07:00
2021-07-28 03:42:56 -06:00
var headCommit * git . Commit
2022-06-13 03:37:59 -06:00
if pr . Flow == issues_model . PullRequestFlowGithub {
2021-07-28 03:42:56 -06:00
headCommit , err = headGitRepo . GetBranchCommit ( pr . HeadBranch )
if err != nil {
return false , err
}
} else {
pr . HeadCommitID , err = baseGitRepo . GetRefCommitID ( pr . GetGitRefName ( ) )
if err != nil {
return false , err
}
if headCommit , err = baseGitRepo . GetCommit ( pr . HeadCommitID ) ; err != nil {
return false , err
}
2020-04-10 05:26:37 -06:00
}
return baseCommit . HasPreviousCommit ( headCommit . ID )
}