2021-07-28 17:42:56 +08:00
// Copyright 2021 The Gitea Authors. All rights reserved.
2022-11-27 13:20:29 -05:00
// SPDX-License-Identifier: MIT
2021-07-28 17:42:56 +08:00
package agit
import (
2022-06-18 02:17:12 +08:00
"context"
2021-07-28 17:42:56 +08:00
"fmt"
"os"
"strings"
2022-06-13 17:37:59 +08:00
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
2021-11-24 17:49:20 +08:00
user_model "code.gitea.io/gitea/models/user"
2021-07-28 17:42:56 +08:00
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
2024-04-09 11:43:17 +08:00
"code.gitea.io/gitea/modules/setting"
2023-09-06 02:37:47 +08:00
notify_service "code.gitea.io/gitea/services/notify"
2021-07-28 17:42:56 +08:00
pull_service "code.gitea.io/gitea/services/pull"
)
2022-06-01 04:06:31 +01:00
// ProcReceive handle proc receive work
2022-06-18 02:17:12 +08:00
func ProcReceive ( ctx context . Context , repo * repo_model . Repository , gitRepo * git . Repository , opts * private . HookOptions ) ( [ ] private . HookProcReceiveRefResult , error ) {
2021-08-14 19:17:10 +08:00
results := make ( [ ] private . HookProcReceiveRefResult , 0 , len ( opts . OldCommitIDs ) )
2024-10-12 13:42:10 +08:00
forcePush := opts . GitPushOptions . Bool ( private . GitPushOptionForcePush )
2024-03-06 13:52:48 +08:00
topicBranch := opts . GitPushOptions [ "topic" ]
title := strings . TrimSpace ( opts . GitPushOptions [ "title" ] )
2024-10-12 13:42:10 +08:00
description := strings . TrimSpace ( opts . GitPushOptions [ "description" ] )
2024-02-24 14:55:19 +08:00
objectFormat := git . ObjectFormatFromName ( repo . ObjectFormatName )
2024-03-06 13:52:48 +08:00
userName := strings . ToLower ( opts . UserName )
2021-07-28 17:42:56 +08:00
2024-02-22 13:31:37 +08:00
pusher , err := user_model . GetUserByID ( ctx , opts . UserID )
if err != nil {
2024-03-06 13:52:48 +08:00
return nil , fmt . Errorf ( "failed to get user. Error: %w" , err )
2024-02-22 13:31:37 +08:00
}
2021-07-28 17:42:56 +08:00
for i := range opts . OldCommitIDs {
2023-12-17 19:56:08 +08:00
if opts . NewCommitIDs [ i ] == objectFormat . EmptyObjectID ( ) . String ( ) {
2021-08-14 19:17:10 +08:00
results = append ( results , private . HookProcReceiveRefResult {
2021-07-28 17:42:56 +08:00
OriginalRef : opts . RefFullNames [ i ] ,
OldOID : opts . OldCommitIDs [ i ] ,
NewOID : opts . NewCommitIDs [ i ] ,
Err : "Can't delete not exist branch" ,
} )
continue
}
2023-05-26 09:04:48 +08:00
if ! opts . RefFullNames [ i ] . IsFor ( ) {
2021-08-14 19:17:10 +08:00
results = append ( results , private . HookProcReceiveRefResult {
2021-07-28 17:42:56 +08:00
IsNotMatched : true ,
OriginalRef : opts . RefFullNames [ i ] ,
} )
continue
}
2023-05-26 09:04:48 +08:00
baseBranchName := opts . RefFullNames [ i ] . ForBranchName ( )
2024-10-12 13:42:10 +08:00
currentTopicBranch := ""
2021-07-28 17:42:56 +08:00
if ! gitRepo . IsBranchExist ( baseBranchName ) {
// try match refs/for/<target-branch>/<topic-branch>
for p , v := range baseBranchName {
if v == '/' && gitRepo . IsBranchExist ( baseBranchName [ : p ] ) && p != len ( baseBranchName ) - 1 {
2024-10-12 13:42:10 +08:00
currentTopicBranch = baseBranchName [ p + 1 : ]
2021-07-28 17:42:56 +08:00
baseBranchName = baseBranchName [ : p ]
break
}
}
}
2024-10-12 13:42:10 +08:00
if len ( topicBranch ) == 0 && len ( currentTopicBranch ) == 0 {
2021-08-14 19:17:10 +08:00
results = append ( results , private . HookProcReceiveRefResult {
2021-07-28 17:42:56 +08:00
OriginalRef : opts . RefFullNames [ i ] ,
OldOID : opts . OldCommitIDs [ i ] ,
NewOID : opts . NewCommitIDs [ i ] ,
Err : "topic-branch is not set" ,
} )
continue
}
2024-10-12 13:42:10 +08:00
if len ( currentTopicBranch ) == 0 {
currentTopicBranch = topicBranch
2021-07-28 17:42:56 +08:00
}
// because different user maybe want to use same topic,
// So it's better to make sure the topic branch name
2024-10-12 13:42:10 +08:00
// has username prefix
2024-03-06 13:52:48 +08:00
var headBranch string
2024-10-12 13:42:10 +08:00
if ! strings . HasPrefix ( currentTopicBranch , userName + "/" ) {
headBranch = userName + "/" + currentTopicBranch
2021-07-28 17:42:56 +08:00
} else {
2024-10-12 13:42:10 +08:00
headBranch = currentTopicBranch
2021-07-28 17:42:56 +08:00
}
2022-11-19 09:12:33 +01:00
pr , err := issues_model . GetUnmergedPullRequest ( ctx , repo . ID , repo . ID , headBranch , baseBranchName , issues_model . PullRequestFlowAGit )
2021-07-28 17:42:56 +08:00
if err != nil {
2022-06-13 17:37:59 +08:00
if ! issues_model . IsErrPullRequestNotExist ( err ) {
2024-03-06 13:52:48 +08:00
return nil , fmt . Errorf ( "failed to get unmerged agit flow pull request in repository: %s Error: %w" , repo . FullName ( ) , err )
2021-07-28 17:42:56 +08:00
}
2024-03-06 13:52:48 +08:00
var commit * git . Commit
if title == "" || description == "" {
commit , err = gitRepo . GetCommit ( opts . NewCommitIDs [ i ] )
if err != nil {
return nil , fmt . Errorf ( "failed to get commit %s in repository: %s Error: %w" , opts . NewCommitIDs [ i ] , repo . FullName ( ) , err )
2021-07-28 17:42:56 +08:00
}
2024-03-06 13:52:48 +08:00
}
// create a new pull request
if title == "" {
title = strings . Split ( commit . CommitMessage , "\n" ) [ 0 ]
}
if description == "" {
_ , description , _ = strings . Cut ( commit . CommitMessage , "\n\n" )
}
if description == "" {
description = title
2021-07-28 17:42:56 +08:00
}
2022-06-13 17:37:59 +08:00
prIssue := & issues_model . Issue {
2021-07-28 17:42:56 +08:00
RepoID : repo . ID ,
Title : title ,
PosterID : pusher . ID ,
Poster : pusher ,
IsPull : true ,
Content : description ,
}
2022-06-13 17:37:59 +08:00
pr := & issues_model . PullRequest {
2021-07-28 17:42:56 +08:00
HeadRepoID : repo . ID ,
BaseRepoID : repo . ID ,
HeadBranch : headBranch ,
HeadCommitID : opts . NewCommitIDs [ i ] ,
BaseBranch : baseBranchName ,
HeadRepo : repo ,
BaseRepo : repo ,
MergeBase : "" ,
2022-06-13 17:37:59 +08:00
Type : issues_model . PullRequestGitea ,
Flow : issues_model . PullRequestFlowAGit ,
2021-07-28 17:42:56 +08:00
}
2022-01-19 23:26:57 +00:00
if err := pull_service . NewPullRequest ( ctx , repo , prIssue , [ ] int64 { } , [ ] string { } , pr , [ ] int64 { } ) ; err != nil {
2022-06-18 02:17:12 +08:00
return nil , err
2021-07-28 17:42:56 +08:00
}
log . Trace ( "Pull request created: %d/%d" , repo . ID , prIssue . ID )
2021-08-14 19:17:10 +08:00
results = append ( results , private . HookProcReceiveRefResult {
2024-04-09 11:43:17 +08:00
Ref : pr . GetGitRefName ( ) ,
OriginalRef : opts . RefFullNames [ i ] ,
OldOID : objectFormat . EmptyObjectID ( ) . String ( ) ,
NewOID : opts . NewCommitIDs [ i ] ,
IsCreatePR : true ,
URL : fmt . Sprintf ( "%s/pulls/%d" , repo . HTMLURL ( ) , pr . Index ) ,
ShouldShowMessage : setting . Git . PullRequestPushMessage && repo . AllowsPulls ( ctx ) ,
HeadBranch : headBranch ,
2021-07-28 17:42:56 +08:00
} )
continue
}
// update exist pull request
2022-11-19 09:12:33 +01:00
if err := pr . LoadBaseRepo ( ctx ) ; err != nil {
2024-03-06 13:52:48 +08:00
return nil , fmt . Errorf ( "unable to load base repository for PR[%d] Error: %w" , pr . ID , err )
2021-07-28 17:42:56 +08:00
}
oldCommitID , err := gitRepo . GetRefCommitID ( pr . GetGitRefName ( ) )
if err != nil {
2024-03-06 13:52:48 +08:00
return nil , fmt . Errorf ( "unable to get ref commit id in base repository for PR[%d] Error: %w" , pr . ID , err )
2021-07-28 17:42:56 +08:00
}
if oldCommitID == opts . NewCommitIDs [ i ] {
2021-08-14 19:17:10 +08:00
results = append ( results , private . HookProcReceiveRefResult {
2021-07-28 17:42:56 +08:00
OriginalRef : opts . RefFullNames [ i ] ,
OldOID : opts . OldCommitIDs [ i ] ,
NewOID : opts . NewCommitIDs [ i ] ,
Err : "new commit is same with old commit" ,
} )
continue
}
2024-10-12 13:42:10 +08:00
if ! forcePush . Value ( ) {
2024-03-06 13:52:48 +08:00
output , _ , err := git . NewCommand ( ctx , "rev-list" , "--max-count=1" ) .
AddDynamicArguments ( oldCommitID , "^" + opts . NewCommitIDs [ i ] ) .
RunStdString ( & git . RunOpts { Dir : repo . RepoPath ( ) , Env : os . Environ ( ) } )
2021-07-28 17:42:56 +08:00
if err != nil {
2024-03-06 13:52:48 +08:00
return nil , fmt . Errorf ( "failed to detect force push: %w" , err )
2021-07-28 17:42:56 +08:00
} else if len ( output ) > 0 {
2021-08-14 19:17:10 +08:00
results = append ( results , private . HookProcReceiveRefResult {
2021-09-09 04:20:55 +08:00
OriginalRef : opts . RefFullNames [ i ] ,
2021-07-28 17:42:56 +08:00
OldOID : opts . OldCommitIDs [ i ] ,
NewOID : opts . NewCommitIDs [ i ] ,
Err : "request `force-push` push option" ,
} )
continue
}
}
pr . HeadCommitID = opts . NewCommitIDs [ i ]
2022-01-19 23:26:57 +00:00
if err = pull_service . UpdateRef ( ctx , pr ) ; err != nil {
2024-03-06 13:52:48 +08:00
return nil , fmt . Errorf ( "failed to update pull ref. Error: %w" , err )
2021-07-28 17:42:56 +08:00
}
2023-07-22 22:14:27 +08:00
pull_service . AddToTaskQueue ( ctx , pr )
2022-11-19 09:12:33 +01:00
err = pr . LoadIssue ( ctx )
2021-07-28 17:42:56 +08:00
if err != nil {
2024-03-06 13:52:48 +08:00
return nil , fmt . Errorf ( "failed to load pull issue. Error: %w" , err )
2021-07-28 17:42:56 +08:00
}
2022-12-10 10:46:31 +08:00
comment , err := pull_service . CreatePushPullComment ( ctx , pusher , pr , oldCommitID , opts . NewCommitIDs [ i ] )
2021-07-28 17:42:56 +08:00
if err == nil && comment != nil {
2023-09-06 02:37:47 +08:00
notify_service . PullRequestPushCommits ( ctx , pusher , pr , comment )
2021-07-28 17:42:56 +08:00
}
2023-09-06 02:37:47 +08:00
notify_service . PullRequestSynchronized ( ctx , pusher , pr )
2021-07-28 17:42:56 +08:00
isForcePush := comment != nil && comment . IsForcePush
2021-08-14 19:17:10 +08:00
results = append ( results , private . HookProcReceiveRefResult {
2024-04-09 11:43:17 +08:00
OldOID : oldCommitID ,
NewOID : opts . NewCommitIDs [ i ] ,
Ref : pr . GetGitRefName ( ) ,
OriginalRef : opts . RefFullNames [ i ] ,
IsForcePush : isForcePush ,
IsCreatePR : false ,
URL : fmt . Sprintf ( "%s/pulls/%d" , repo . HTMLURL ( ) , pr . Index ) ,
ShouldShowMessage : setting . Git . PullRequestPushMessage && repo . AllowsPulls ( ctx ) ,
2021-07-28 17:42:56 +08:00
} )
}
2022-06-18 02:17:12 +08:00
return results , nil
2021-07-28 17:42:56 +08:00
}
2022-01-10 04:32:37 -05:00
// UserNameChanged handle user name change for agit flow pull
2023-03-14 03:45:21 -04:00
func UserNameChanged ( ctx context . Context , user * user_model . User , newName string ) error {
pulls , err := issues_model . GetAllUnmergedAgitPullRequestByPoster ( ctx , user . ID )
2021-07-28 17:42:56 +08:00
if err != nil {
return err
}
newName = strings . ToLower ( newName )
for _ , pull := range pulls {
pull . HeadBranch = strings . TrimPrefix ( pull . HeadBranch , user . LowerName + "/" )
pull . HeadBranch = newName + "/" + pull . HeadBranch
2023-10-11 06:24:07 +02:00
if err = pull . UpdateCols ( ctx , "head_branch" ) ; err != nil {
2021-07-28 17:42:56 +08:00
return err
}
}
return nil
}