2018-01-05 13:56:52 +03:00
// Copyright 2018 The Gitea Authors.
// Copyright 2014 The Gogs Authors.
// All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2014-03-24 14:25:15 +04:00
package repo
import (
2021-06-07 17:52:59 +03:00
"errors"
2016-10-12 16:28:51 +03:00
"fmt"
2021-11-16 21:18:25 +03:00
"html"
2019-12-16 09:20:25 +03:00
"net/http"
2021-11-16 21:18:25 +03:00
"strconv"
2015-08-08 17:43:14 +03:00
"strings"
2020-01-17 09:03:40 +03:00
"time"
2015-08-08 17:43:14 +03:00
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/models"
2022-08-25 05:31:57 +03:00
activities_model "code.gitea.io/gitea/models/activities"
2021-09-24 14:32:56 +03:00
"code.gitea.io/gitea/models/db"
2022-06-12 18:51:54 +03:00
git_model "code.gitea.io/gitea/models/git"
2022-06-13 12:37:59 +03:00
issues_model "code.gitea.io/gitea/models/issues"
2022-05-11 13:09:36 +03:00
access_model "code.gitea.io/gitea/models/perm/access"
2022-06-11 17:44:20 +03:00
pull_model "code.gitea.io/gitea/models/pull"
2021-12-10 04:27:50 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-09 22:57:58 +03:00
"code.gitea.io/gitea/models/unit"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/base"
2024-02-25 01:34:51 +03:00
"code.gitea.io/gitea/modules/emoji"
2019-03-27 12:33:00 +03:00
"code.gitea.io/gitea/modules/git"
Simplify how git repositories are opened (#28937)
## Purpose
This is a refactor toward building an abstraction over managing git
repositories.
Afterwards, it does not matter anymore if they are stored on the local
disk or somewhere remote.
## What this PR changes
We used `git.OpenRepository` everywhere previously.
Now, we should split them into two distinct functions:
Firstly, there are temporary repositories which do not change:
```go
git.OpenRepository(ctx, diskPath)
```
Gitea managed repositories having a record in the database in the
`repository` table are moved into the new package `gitrepo`:
```go
gitrepo.OpenRepository(ctx, repo_model.Repo)
```
Why is `repo_model.Repository` the second parameter instead of file
path?
Because then we can easily adapt our repository storage strategy.
The repositories can be stored locally, however, they could just as well
be stored on a remote server.
## Further changes in other PRs
- A Git Command wrapper on package `gitrepo` could be created. i.e.
`NewCommand(ctx, repo_model.Repository, commands...)`. `git.RunOpts{Dir:
repo.RepoPath()}`, the directory should be empty before invoking this
method and it can be filled in the function only. #28940
- Remove the `RepoPath()`/`WikiPath()` functions to reduce the
possibility of mistakes.
---------
Co-authored-by: delvh <dev.lh@web.de>
2024-01-27 23:09:51 +03:00
"code.gitea.io/gitea/modules/gitrepo"
2022-09-02 10:58:49 +03:00
issue_template "code.gitea.io/gitea/modules/issue/template"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
2019-01-21 14:45:32 +03:00
"code.gitea.io/gitea/modules/util"
2021-01-26 18:36:53 +03:00
"code.gitea.io/gitea/modules/web"
2020-02-22 16:08:48 +03:00
"code.gitea.io/gitea/routers/utils"
2022-03-31 17:53:08 +03:00
asymkey_service "code.gitea.io/gitea/services/asymkey"
2022-06-11 17:44:20 +03:00
"code.gitea.io/gitea/services/automerge"
2024-02-27 10:12:22 +03:00
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/context/upload"
2021-04-06 22:44:05 +03:00
"code.gitea.io/gitea/services/forms"
2019-09-06 05:20:09 +03:00
"code.gitea.io/gitea/services/gitdiff"
2023-09-05 21:37:47 +03:00
notify_service "code.gitea.io/gitea/services/notify"
2019-09-27 03:22:36 +03:00
pull_service "code.gitea.io/gitea/services/pull"
2019-10-26 09:54:11 +03:00
repo_service "code.gitea.io/gitea/services/repository"
2024-03-04 11:16:03 +03:00
user_service "code.gitea.io/gitea/services/user"
2023-05-17 11:11:13 +03:00
"github.com/gobwas/glob"
2014-03-24 14:25:15 +04:00
)
2014-06-23 07:11:12 +04:00
const (
2019-06-07 23:29:29 +03:00
tplCompareDiff base . TplName = "repo/diff/compare"
2016-11-24 10:04:31 +03:00
tplPullCommits base . TplName = "repo/pulls/commits"
tplPullFiles base . TplName = "repo/pulls/files"
2016-02-18 01:21:31 +03:00
2016-11-24 10:04:31 +03:00
pullRequestTemplateKey = "PullRequestTemplate"
2016-02-18 01:21:31 +03:00
)
2022-01-20 20:46:10 +03:00
var pullRequestTemplateCandidates = [ ] string {
"PULL_REQUEST_TEMPLATE.md" ,
2022-09-02 10:58:49 +03:00
"PULL_REQUEST_TEMPLATE.yaml" ,
"PULL_REQUEST_TEMPLATE.yml" ,
2022-01-20 20:46:10 +03:00
"pull_request_template.md" ,
2022-09-02 10:58:49 +03:00
"pull_request_template.yaml" ,
"pull_request_template.yml" ,
2022-01-20 20:46:10 +03:00
".gitea/PULL_REQUEST_TEMPLATE.md" ,
2022-09-02 10:58:49 +03:00
".gitea/PULL_REQUEST_TEMPLATE.yaml" ,
".gitea/PULL_REQUEST_TEMPLATE.yml" ,
2022-01-20 20:46:10 +03:00
".gitea/pull_request_template.md" ,
2022-09-02 10:58:49 +03:00
".gitea/pull_request_template.yaml" ,
".gitea/pull_request_template.yml" ,
2022-01-20 20:46:10 +03:00
".github/PULL_REQUEST_TEMPLATE.md" ,
2022-09-02 10:58:49 +03:00
".github/PULL_REQUEST_TEMPLATE.yaml" ,
".github/PULL_REQUEST_TEMPLATE.yml" ,
2022-01-20 20:46:10 +03:00
".github/pull_request_template.md" ,
2022-09-02 10:58:49 +03:00
".github/pull_request_template.yaml" ,
".github/pull_request_template.yml" ,
2022-01-20 20:46:10 +03:00
}
2014-06-23 07:11:12 +04:00
2021-12-10 04:27:50 +03:00
func getRepository ( ctx * context . Context , repoID int64 ) * repo_model . Repository {
2022-12-03 05:48:26 +03:00
repo , err := repo_model . GetRepositoryByID ( ctx , repoID )
2015-08-08 12:10:34 +03:00
if err != nil {
2021-12-10 04:27:50 +03:00
if repo_model . IsErrRepoNotExist ( err ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "GetRepositoryByID" , nil )
2015-08-08 12:10:34 +03:00
} else {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetRepositoryByID" , err )
2015-08-08 12:10:34 +03:00
}
return nil
}
2015-09-01 18:57:02 +03:00
2022-05-11 13:09:36 +03:00
perm , err := access_model . GetUserRepoPermission ( ctx , repo , ctx . Doer )
2018-11-28 14:26:14 +03:00
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return nil
}
2021-11-09 22:57:58 +03:00
if ! perm . CanRead ( unit . TypeCode ) {
2019-11-11 18:15:29 +03:00
log . Trace ( "Permission Denied: User %-v cannot read %-v of repo %-v\n" +
"User in repo has Permissions: %-+v" ,
2022-03-22 10:03:22 +03:00
ctx . Doer ,
2021-11-09 22:57:58 +03:00
unit . TypeCode ,
2019-11-11 18:15:29 +03:00
ctx . Repo ,
perm )
ctx . NotFound ( "getRepository" , nil )
return nil
}
return repo
}
2023-08-07 06:43:18 +03:00
func getPullInfo ( ctx * context . Context ) ( issue * issues_model . Issue , ok bool ) {
2023-07-22 17:14:27 +03:00
issue , err := issues_model . GetIssueByIndex ( ctx , ctx . Repo . Repository . ID , ctx . ParamsInt64 ( ":index" ) )
2015-09-02 11:08:05 +03:00
if err != nil {
2022-06-13 12:37:59 +03:00
if issues_model . IsErrIssueNotExist ( err ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "GetIssueByIndex" , err )
2015-09-02 11:08:05 +03:00
} else {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetIssueByIndex" , err )
2015-09-02 11:08:05 +03:00
}
2023-08-07 06:43:18 +03:00
return nil , false
2015-09-02 11:08:05 +03:00
}
2022-11-19 11:12:33 +03:00
if err = issue . LoadPoster ( ctx ) ; err != nil {
2018-12-13 18:55:43 +03:00
ctx . ServerError ( "LoadPoster" , err )
2023-08-07 06:43:18 +03:00
return nil , false
2018-12-13 18:55:43 +03:00
}
2022-04-08 12:11:15 +03:00
if err := issue . LoadRepo ( ctx ) ; err != nil {
2019-10-23 20:54:13 +03:00
ctx . ServerError ( "LoadRepo" , err )
2023-08-07 06:43:18 +03:00
return nil , false
2019-10-23 20:54:13 +03:00
}
2024-02-25 01:34:51 +03:00
ctx . Data [ "Title" ] = fmt . Sprintf ( "#%d - %s" , issue . Index , emoji . ReplaceAliases ( issue . Title ) )
2015-10-19 02:30:39 +03:00
ctx . Data [ "Issue" ] = issue
2015-09-02 11:08:05 +03:00
2015-10-19 02:30:39 +03:00
if ! issue . IsPull {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "ViewPullCommits" , nil )
2023-08-07 06:43:18 +03:00
return nil , false
2015-09-02 11:08:05 +03:00
}
2022-11-19 11:12:33 +03:00
if err = issue . LoadPullRequest ( ctx ) ; err != nil {
2018-12-13 18:55:43 +03:00
ctx . ServerError ( "LoadPullRequest" , err )
2023-08-07 06:43:18 +03:00
return nil , false
2018-12-13 18:55:43 +03:00
}
2022-11-19 11:12:33 +03:00
if err = issue . PullRequest . LoadHeadRepo ( ctx ) ; err != nil {
2020-03-03 01:31:55 +03:00
ctx . ServerError ( "LoadHeadRepo" , err )
2023-08-07 06:43:18 +03:00
return nil , false
2015-09-02 11:08:05 +03:00
}
if ctx . IsSigned {
// Update issue-user.
2022-08-25 05:31:57 +03:00
if err = activities_model . SetIssueReadBy ( ctx , issue . ID , ctx . Doer . ID ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "ReadBy" , err )
2023-08-07 06:43:18 +03:00
return nil , false
2015-09-02 11:08:05 +03:00
}
}
2023-08-07 06:43:18 +03:00
return issue , true
2015-09-02 11:08:05 +03:00
}
2022-06-13 12:37:59 +03:00
func setMergeTarget ( ctx * context . Context , pull * issues_model . PullRequest ) {
2022-11-19 11:12:33 +03:00
if ctx . Repo . Owner . Name == pull . MustHeadUserName ( ctx ) {
2017-10-04 20:35:01 +03:00
ctx . Data [ "HeadTarget" ] = pull . HeadBranch
} else if pull . HeadRepo == nil {
2022-11-19 11:12:33 +03:00
ctx . Data [ "HeadTarget" ] = pull . MustHeadUserName ( ctx ) + ":" + pull . HeadBranch
2017-10-04 20:35:01 +03:00
} else {
2022-11-19 11:12:33 +03:00
ctx . Data [ "HeadTarget" ] = pull . MustHeadUserName ( ctx ) + "/" + pull . HeadRepo . Name + ":" + pull . HeadBranch
2017-10-04 20:35:01 +03:00
}
ctx . Data [ "BaseTarget" ] = pull . BaseBranch
2023-10-11 07:24:07 +03:00
ctx . Data [ "HeadBranchLink" ] = pull . GetHeadBranchLink ( ctx )
ctx . Data [ "BaseBranchLink" ] = pull . GetBaseBranchLink ( ctx )
2017-10-04 20:35:01 +03:00
}
2023-07-03 04:00:28 +03:00
// GetPullDiffStats get Pull Requests diff stats
func GetPullDiffStats ( ctx * context . Context ) {
2023-08-07 06:43:18 +03:00
issue , ok := getPullInfo ( ctx )
if ! ok {
return
}
2016-08-16 20:19:09 +03:00
pull := issue . PullRequest
2015-09-02 16:26:56 +03:00
2023-07-03 04:00:28 +03:00
mergeBaseCommitID := GetMergedBaseCommitID ( ctx , issue )
2023-08-07 06:43:18 +03:00
if mergeBaseCommitID == "" {
2023-07-03 04:00:28 +03:00
ctx . NotFound ( "PullFiles" , nil )
return
}
headCommitID , err := ctx . Repo . GitRepo . GetRefCommitID ( pull . GetGitRefName ( ) )
if err != nil {
ctx . ServerError ( "GetRefCommitID" , err )
return
}
diffOptions := & gitdiff . DiffOptions {
BeforeCommitID : mergeBaseCommitID ,
AfterCommitID : headCommitID ,
MaxLines : setting . Git . MaxGitDiffLines ,
MaxLineCharacters : setting . Git . MaxGitDiffLineCharacters ,
MaxFiles : setting . Git . MaxGitDiffFiles ,
WhitespaceBehavior : gitdiff . GetWhitespaceFlag ( ctx . Data [ "WhitespaceBehavior" ] . ( string ) ) ,
}
diff , err := gitdiff . GetPullDiffStats ( ctx . Repo . GitRepo , diffOptions )
if err != nil {
ctx . ServerError ( "GetPullDiffStats" , err )
return
}
ctx . Data [ "Diff" ] = diff
}
func GetMergedBaseCommitID ( ctx * context . Context , issue * issues_model . Issue ) string {
pull := issue . PullRequest
2017-10-04 20:35:01 +03:00
2021-12-23 11:32:29 +03:00
var baseCommit string
// Some migrated PR won't have any Base SHA and lose history, try to get one
if pull . MergeBase == "" {
var commitSHA , parentCommit string
// If there is a head or a patch file, and it is readable, grab info
2021-12-23 16:44:00 +03:00
commitSHA , err := ctx . Repo . GitRepo . GetRefCommitID ( pull . GetGitRefName ( ) )
2021-12-23 11:32:29 +03:00
if err != nil {
// Head File does not exist, try the patch
commitSHA , err = ctx . Repo . GitRepo . ReadPatchCommit ( pull . Index )
if err == nil {
// Recreate pull head in files for next time
2021-12-23 16:44:00 +03:00
if err := ctx . Repo . GitRepo . SetReference ( pull . GetGitRefName ( ) , commitSHA ) ; err != nil {
2021-12-23 11:32:29 +03:00
log . Error ( "Could not write head file" , err )
}
} else {
// There is no history available
log . Trace ( "No history file available for PR %d" , pull . Index )
}
}
if commitSHA != "" {
// Get immediate parent of the first commit in the patch, grab history back
2022-10-23 17:44:45 +03:00
parentCommit , _ , err = git . NewCommand ( ctx , "rev-list" , "-1" , "--skip=1" ) . AddDynamicArguments ( commitSHA ) . RunStdString ( & git . RunOpts { Dir : ctx . Repo . GitRepo . Path } )
2021-12-23 11:32:29 +03:00
if err == nil {
parentCommit = strings . TrimSpace ( parentCommit )
}
// Special case on Git < 2.25 that doesn't fail on immediate empty history
if err != nil || parentCommit == "" {
log . Info ( "No known parent commit for PR %d, error: %v" , pull . Index , err )
// bring at least partial history if it can work
parentCommit = commitSHA
}
}
baseCommit = parentCommit
} else {
// Keep an empty history or original commit
baseCommit = pull . MergeBase
}
2023-07-03 04:00:28 +03:00
return baseCommit
}
// PrepareMergedViewPullInfo show meta information for a merged pull request view page
func PrepareMergedViewPullInfo ( ctx * context . Context , issue * issues_model . Issue ) * git . CompareInfo {
pull := issue . PullRequest
setMergeTarget ( ctx , pull )
ctx . Data [ "HasMerged" ] = true
baseCommit := GetMergedBaseCommitID ( ctx , issue )
2019-06-12 02:32:08 +03:00
compareInfo , err := ctx . Repo . GitRepo . GetCompareInfo ( ctx . Repo . Repository . RepoPath ( ) ,
2022-01-18 10:45:43 +03:00
baseCommit , pull . GetGitRefName ( ) , false , false )
2015-09-02 16:26:56 +03:00
if err != nil {
2020-10-20 15:52:54 +03:00
if strings . Contains ( err . Error ( ) , "fatal: Not a valid object name" ) || strings . Contains ( err . Error ( ) , "unknown revision or path not in the working tree" ) {
2018-08-01 06:00:35 +03:00
ctx . Data [ "IsPullRequestBroken" ] = true
2020-03-03 01:31:55 +03:00
ctx . Data [ "BaseTarget" ] = pull . BaseBranch
2018-01-19 09:18:51 +03:00
ctx . Data [ "NumCommits" ] = 0
ctx . Data [ "NumFiles" ] = 0
return nil
}
2019-06-07 23:29:29 +03:00
ctx . ServerError ( "GetCompareInfo" , err )
2018-01-19 09:18:51 +03:00
return nil
2015-09-02 16:26:56 +03:00
}
2021-08-09 21:08:51 +03:00
ctx . Data [ "NumCommits" ] = len ( compareInfo . Commits )
2019-06-12 02:32:08 +03:00
ctx . Data [ "NumFiles" ] = compareInfo . NumFiles
2020-12-18 15:37:55 +03:00
2021-08-09 21:08:51 +03:00
if len ( compareInfo . Commits ) != 0 {
sha := compareInfo . Commits [ 0 ] . ID . String ( )
2024-03-22 15:53:52 +03:00
commitStatuses , _ , err := git_model . GetLatestCommitStatus ( ctx , ctx . Repo . Repository . ID , sha , db . ListOptionsAll )
2020-12-18 15:37:55 +03:00
if err != nil {
ctx . ServerError ( "GetLatestCommitStatus" , err )
return nil
}
if len ( commitStatuses ) != 0 {
ctx . Data [ "LatestCommitStatuses" ] = commitStatuses
2022-06-12 18:51:54 +03:00
ctx . Data [ "LatestCommitStatus" ] = git_model . CalcCommitStatus ( commitStatuses )
2020-12-18 15:37:55 +03:00
}
}
2019-06-12 02:32:08 +03:00
return compareInfo
2015-09-02 16:26:56 +03:00
}
2016-11-24 10:04:31 +03:00
// PrepareViewPullInfo show meta information for a pull request preview page
2022-06-13 12:37:59 +03:00
func PrepareViewPullInfo ( ctx * context . Context , issue * issues_model . Issue ) * git . CompareInfo {
2022-02-11 11:02:53 +03:00
ctx . Data [ "PullRequestWorkInProgressPrefixes" ] = setting . Repository . PullRequest . WorkInProgressPrefixes
2015-09-02 11:08:05 +03:00
repo := ctx . Repo . Repository
2016-08-16 20:19:09 +03:00
pull := issue . PullRequest
2015-09-02 11:08:05 +03:00
2022-11-19 11:12:33 +03:00
if err := pull . LoadHeadRepo ( ctx ) ; err != nil {
2020-03-03 01:31:55 +03:00
ctx . ServerError ( "LoadHeadRepo" , err )
2015-10-24 10:36:47 +03:00
return nil
}
2022-11-19 11:12:33 +03:00
if err := pull . LoadBaseRepo ( ctx ) ; err != nil {
2020-03-03 01:31:55 +03:00
ctx . ServerError ( "LoadBaseRepo" , err )
2020-01-07 20:06:14 +03:00
return nil
}
2017-10-04 20:35:01 +03:00
setMergeTarget ( ctx , pull )
2023-01-16 11:00:22 +03:00
pb , err := git_model . GetFirstMatchProtectedBranchRule ( ctx , repo . ID , pull . BaseBranch )
if err != nil {
2020-01-17 09:03:40 +03:00
ctx . ServerError ( "LoadProtectedBranch" , err )
2019-09-18 08:39:45 +03:00
return nil
}
2023-01-16 11:00:22 +03:00
ctx . Data [ "EnableStatusCheck" ] = pb != nil && pb . EnableStatusCheck
2019-09-18 08:39:45 +03:00
2022-01-20 02:26:57 +03:00
var baseGitRepo * git . Repository
if pull . BaseRepoID == ctx . Repo . Repository . ID && ctx . Repo . GitRepo != nil {
baseGitRepo = ctx . Repo . GitRepo
} else {
Simplify how git repositories are opened (#28937)
## Purpose
This is a refactor toward building an abstraction over managing git
repositories.
Afterwards, it does not matter anymore if they are stored on the local
disk or somewhere remote.
## What this PR changes
We used `git.OpenRepository` everywhere previously.
Now, we should split them into two distinct functions:
Firstly, there are temporary repositories which do not change:
```go
git.OpenRepository(ctx, diskPath)
```
Gitea managed repositories having a record in the database in the
`repository` table are moved into the new package `gitrepo`:
```go
gitrepo.OpenRepository(ctx, repo_model.Repo)
```
Why is `repo_model.Repository` the second parameter instead of file
path?
Because then we can easily adapt our repository storage strategy.
The repositories can be stored locally, however, they could just as well
be stored on a remote server.
## Further changes in other PRs
- A Git Command wrapper on package `gitrepo` could be created. i.e.
`NewCommand(ctx, repo_model.Repository, commands...)`. `git.RunOpts{Dir:
repo.RepoPath()}`, the directory should be empty before invoking this
method and it can be filled in the function only. #28940
- Remove the `RepoPath()`/`WikiPath()` functions to reduce the
possibility of mistakes.
---------
Co-authored-by: delvh <dev.lh@web.de>
2024-01-27 23:09:51 +03:00
baseGitRepo , err := gitrepo . OpenRepository ( ctx , pull . BaseRepo )
2022-01-20 02:26:57 +03:00
if err != nil {
ctx . ServerError ( "OpenRepository" , err )
return nil
}
defer baseGitRepo . Close ( )
2020-01-07 20:06:14 +03:00
}
2020-03-05 21:51:21 +03:00
if ! baseGitRepo . IsBranchExist ( pull . BaseBranch ) {
ctx . Data [ "IsPullRequestBroken" ] = true
ctx . Data [ "BaseTarget" ] = pull . BaseBranch
ctx . Data [ "HeadTarget" ] = pull . HeadBranch
2020-03-31 16:42:44 +03:00
sha , err := baseGitRepo . GetRefCommitID ( pull . GetGitRefName ( ) )
if err != nil {
ctx . ServerError ( fmt . Sprintf ( "GetRefCommitID(%s)" , pull . GetGitRefName ( ) ) , err )
return nil
}
2024-03-22 15:53:52 +03:00
commitStatuses , _ , err := git_model . GetLatestCommitStatus ( ctx , repo . ID , sha , db . ListOptionsAll )
2020-03-31 16:42:44 +03:00
if err != nil {
ctx . ServerError ( "GetLatestCommitStatus" , err )
return nil
}
if len ( commitStatuses ) > 0 {
ctx . Data [ "LatestCommitStatuses" ] = commitStatuses
2022-06-12 18:51:54 +03:00
ctx . Data [ "LatestCommitStatus" ] = git_model . CalcCommitStatus ( commitStatuses )
2020-03-31 16:42:44 +03:00
}
compareInfo , err := baseGitRepo . GetCompareInfo ( pull . BaseRepo . RepoPath ( ) ,
2022-01-18 10:45:43 +03:00
pull . MergeBase , pull . GetGitRefName ( ) , false , false )
2020-03-31 16:42:44 +03:00
if err != nil {
if strings . Contains ( err . Error ( ) , "fatal: Not a valid object name" ) {
ctx . Data [ "IsPullRequestBroken" ] = true
ctx . Data [ "BaseTarget" ] = pull . BaseBranch
ctx . Data [ "NumCommits" ] = 0
ctx . Data [ "NumFiles" ] = 0
return nil
}
ctx . ServerError ( "GetCompareInfo" , err )
return nil
}
2021-08-09 21:08:51 +03:00
ctx . Data [ "NumCommits" ] = len ( compareInfo . Commits )
2020-03-31 16:42:44 +03:00
ctx . Data [ "NumFiles" ] = compareInfo . NumFiles
return compareInfo
2020-03-05 21:51:21 +03:00
}
2019-06-30 10:57:59 +03:00
var headBranchExist bool
2020-01-07 20:06:14 +03:00
var headBranchSha string
2019-06-30 10:57:59 +03:00
// HeadRepo may be missing
2015-10-05 03:54:06 +03:00
if pull . HeadRepo != nil {
Simplify how git repositories are opened (#28937)
## Purpose
This is a refactor toward building an abstraction over managing git
repositories.
Afterwards, it does not matter anymore if they are stored on the local
disk or somewhere remote.
## What this PR changes
We used `git.OpenRepository` everywhere previously.
Now, we should split them into two distinct functions:
Firstly, there are temporary repositories which do not change:
```go
git.OpenRepository(ctx, diskPath)
```
Gitea managed repositories having a record in the database in the
`repository` table are moved into the new package `gitrepo`:
```go
gitrepo.OpenRepository(ctx, repo_model.Repo)
```
Why is `repo_model.Repository` the second parameter instead of file
path?
Because then we can easily adapt our repository storage strategy.
The repositories can be stored locally, however, they could just as well
be stored on a remote server.
## Further changes in other PRs
- A Git Command wrapper on package `gitrepo` could be created. i.e.
`NewCommand(ctx, repo_model.Repository, commands...)`. `git.RunOpts{Dir:
repo.RepoPath()}`, the directory should be empty before invoking this
method and it can be filled in the function only. #28940
- Remove the `RepoPath()`/`WikiPath()` functions to reduce the
possibility of mistakes.
---------
Co-authored-by: delvh <dev.lh@web.de>
2024-01-27 23:09:51 +03:00
headGitRepo , err := gitrepo . OpenRepository ( ctx , pull . HeadRepo )
2015-10-05 03:54:06 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "OpenRepository" , err )
2015-10-05 03:54:06 +03:00
return nil
}
2019-11-13 10:01:19 +03:00
defer headGitRepo . Close ( )
2019-06-30 10:57:59 +03:00
2022-06-13 12:37:59 +03:00
if pull . Flow == issues_model . PullRequestFlowGithub {
2021-07-28 12:42:56 +03:00
headBranchExist = headGitRepo . IsBranchExist ( pull . HeadBranch )
} else {
2021-11-30 23:06:32 +03:00
headBranchExist = git . IsReferenceExist ( ctx , baseGitRepo . Path , pull . GetGitRefName ( ) )
2021-07-28 12:42:56 +03:00
}
2019-06-30 10:57:59 +03:00
if headBranchExist {
2022-06-13 12:37:59 +03:00
if pull . Flow != issues_model . PullRequestFlowGithub {
2021-07-28 12:42:56 +03:00
headBranchSha , err = baseGitRepo . GetRefCommitID ( pull . GetGitRefName ( ) )
} else {
headBranchSha , err = headGitRepo . GetBranchCommitID ( pull . HeadBranch )
}
2019-06-30 10:57:59 +03:00
if err != nil {
ctx . ServerError ( "GetBranchCommitID" , err )
return nil
}
2020-01-07 20:06:14 +03:00
}
}
2019-06-30 10:57:59 +03:00
2020-01-25 05:48:22 +03:00
if headBranchExist {
2022-01-20 02:26:57 +03:00
var err error
2022-04-28 14:48:48 +03:00
ctx . Data [ "UpdateAllowed" ] , ctx . Data [ "UpdateByRebaseAllowed" ] , err = pull_service . IsUserAllowedToUpdate ( ctx , pull , ctx . Doer )
2020-01-25 05:48:22 +03:00
if err != nil {
ctx . ServerError ( "IsUserAllowedToUpdate" , err )
return nil
}
2022-01-20 02:26:57 +03:00
ctx . Data [ "GetCommitMessages" ] = pull_service . GetSquashMergeCommitMessages ( ctx , pull )
2022-08-03 07:56:59 +03:00
} else {
ctx . Data [ "GetCommitMessages" ] = ""
2020-01-25 05:48:22 +03:00
}
2020-01-07 20:06:14 +03:00
sha , err := baseGitRepo . GetRefCommitID ( pull . GetGitRefName ( ) )
if err != nil {
2020-06-08 21:07:41 +03:00
if git . IsErrNotExist ( err ) {
ctx . Data [ "IsPullRequestBroken" ] = true
if pull . IsSameRepo ( ) {
ctx . Data [ "HeadTarget" ] = pull . HeadBranch
} else if pull . HeadRepo == nil {
2023-08-04 01:07:15 +03:00
ctx . Data [ "HeadTarget" ] = ctx . Locale . Tr ( "repo.pull.deleted_branch" , pull . HeadBranch )
2020-06-08 21:07:41 +03:00
} else {
ctx . Data [ "HeadTarget" ] = pull . HeadRepo . OwnerName + ":" + pull . HeadBranch
}
ctx . Data [ "BaseTarget" ] = pull . BaseBranch
ctx . Data [ "NumCommits" ] = 0
ctx . Data [ "NumFiles" ] = 0
return nil
}
2020-01-07 20:06:14 +03:00
ctx . ServerError ( fmt . Sprintf ( "GetRefCommitID(%s)" , pull . GetGitRefName ( ) ) , err )
return nil
}
2024-03-22 15:53:52 +03:00
commitStatuses , _ , err := git_model . GetLatestCommitStatus ( ctx , repo . ID , sha , db . ListOptionsAll )
2020-01-07 20:06:14 +03:00
if err != nil {
ctx . ServerError ( "GetLatestCommitStatus" , err )
return nil
}
if len ( commitStatuses ) > 0 {
ctx . Data [ "LatestCommitStatuses" ] = commitStatuses
2022-06-12 18:51:54 +03:00
ctx . Data [ "LatestCommitStatus" ] = git_model . CalcCommitStatus ( commitStatuses )
2020-01-07 20:06:14 +03:00
}
2019-09-18 08:39:45 +03:00
2023-01-16 11:00:22 +03:00
if pb != nil && pb . EnableStatusCheck {
2024-02-19 12:57:08 +03:00
var missingRequiredChecks [ ] string
for _ , requiredContext := range pb . StatusCheckContexts {
contextFound := false
matchesRequiredContext := createRequiredContextMatcher ( requiredContext )
for _ , presentStatus := range commitStatuses {
if matchesRequiredContext ( presentStatus . Context ) {
contextFound = true
break
}
}
if ! contextFound {
missingRequiredChecks = append ( missingRequiredChecks , requiredContext )
}
}
ctx . Data [ "MissingRequiredChecks" ] = missingRequiredChecks
2020-01-07 20:06:14 +03:00
ctx . Data [ "is_context_required" ] = func ( context string ) bool {
2023-01-16 11:00:22 +03:00
for _ , c := range pb . StatusCheckContexts {
2023-12-22 16:29:50 +03:00
if c == context {
return true
}
if gp , err := glob . Compile ( c ) ; err != nil {
// All newly created status_check_contexts are checked to ensure they are valid glob expressions before being stored in the database.
// But some old status_check_context created before glob was introduced may be invalid glob expressions.
// So log the error here for debugging.
log . Error ( "compile glob %q: %v" , c , err )
} else if gp . Match ( context ) {
2020-01-07 20:06:14 +03:00
return true
2019-09-18 08:39:45 +03:00
}
}
2020-01-07 20:06:14 +03:00
return false
2019-06-30 10:57:59 +03:00
}
2023-01-16 11:00:22 +03:00
ctx . Data [ "RequiredStatusCheckState" ] = pull_service . MergeRequiredContextsCommitStatus ( commitStatuses , pb . StatusCheckContexts )
2015-09-02 11:08:05 +03:00
}
2020-01-07 20:06:14 +03:00
ctx . Data [ "HeadBranchMovedOn" ] = headBranchSha != sha
ctx . Data [ "HeadBranchCommitID" ] = headBranchSha
ctx . Data [ "PullHeadCommitID" ] = sha
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 16:16:58 +03:00
if pull . HeadRepo == nil || ! headBranchExist || ( ! pull . Issue . IsClosed && ( headBranchSha != sha ) ) {
2018-08-01 06:00:35 +03:00
ctx . Data [ "IsPullRequestBroken" ] = true
2020-03-03 01:31:55 +03:00
if pull . IsSameRepo ( ) {
ctx . Data [ "HeadTarget" ] = pull . HeadBranch
2020-06-08 21:07:41 +03:00
} else if pull . HeadRepo == nil {
2023-08-04 01:07:15 +03:00
ctx . Data [ "HeadTarget" ] = ctx . Locale . Tr ( "repo.pull.deleted_branch" , pull . HeadBranch )
2020-03-03 01:31:55 +03:00
} else {
2020-06-08 21:07:41 +03:00
ctx . Data [ "HeadTarget" ] = pull . HeadRepo . OwnerName + ":" + pull . HeadBranch
2020-03-03 01:31:55 +03:00
}
2015-09-02 16:26:56 +03:00
}
2020-01-07 20:06:14 +03:00
compareInfo , err := baseGitRepo . GetCompareInfo ( pull . BaseRepo . RepoPath ( ) ,
2022-01-18 10:45:43 +03:00
git . BranchPrefix + pull . BaseBranch , pull . GetGitRefName ( ) , false , false )
2015-09-02 11:08:05 +03:00
if err != nil {
2016-07-23 13:35:16 +03:00
if strings . Contains ( err . Error ( ) , "fatal: Not a valid object name" ) {
2018-08-01 06:00:35 +03:00
ctx . Data [ "IsPullRequestBroken" ] = true
2020-03-03 01:31:55 +03:00
ctx . Data [ "BaseTarget" ] = pull . BaseBranch
2016-07-23 13:35:16 +03:00
ctx . Data [ "NumCommits" ] = 0
ctx . Data [ "NumFiles" ] = 0
return nil
}
2019-06-07 23:29:29 +03:00
ctx . ServerError ( "GetCompareInfo" , err )
2015-09-02 11:08:05 +03:00
return nil
}
2018-08-13 22:04:39 +03:00
2021-07-29 05:32:48 +03:00
if compareInfo . HeadCommitID == compareInfo . MergeBase {
ctx . Data [ "IsNothingToCompare" ] = true
}
2023-10-11 07:24:07 +03:00
if pull . IsWorkInProgress ( ctx ) {
2018-08-13 22:04:39 +03:00
ctx . Data [ "IsPullWorkInProgress" ] = true
2022-11-19 11:12:33 +03:00
ctx . Data [ "WorkInProgressPrefix" ] = pull . GetWorkInProgressPrefix ( ctx )
2018-08-13 22:04:39 +03:00
}
2019-02-05 14:54:49 +03:00
if pull . IsFilesConflicted ( ) {
ctx . Data [ "IsPullFilesConflicted" ] = true
ctx . Data [ "ConflictedFiles" ] = pull . ConflictedFiles
}
2021-08-09 21:08:51 +03:00
ctx . Data [ "NumCommits" ] = len ( compareInfo . Commits )
2019-06-12 02:32:08 +03:00
ctx . Data [ "NumFiles" ] = compareInfo . NumFiles
return compareInfo
2015-09-02 11:08:05 +03:00
}
2024-02-19 12:57:08 +03:00
func createRequiredContextMatcher ( requiredContext string ) func ( string ) bool {
if gp , err := glob . Compile ( requiredContext ) ; err == nil {
return func ( contextToCheck string ) bool {
return gp . Match ( contextToCheck )
}
}
return func ( contextToCheck string ) bool {
return requiredContext == contextToCheck
}
}
2023-07-28 22:18:12 +03:00
type pullCommitList struct {
Commits [ ] pull_service . CommitInfo ` json:"commits" `
LastReviewCommitSha string ` json:"last_review_commit_sha" `
2024-02-15 00:48:45 +03:00
Locale map [ string ] any ` json:"locale" `
2023-07-28 22:18:12 +03:00
}
// GetPullCommits get all commits for given pull request
func GetPullCommits ( ctx * context . Context ) {
2023-08-07 06:43:18 +03:00
issue , ok := getPullInfo ( ctx )
if ! ok {
2023-07-28 22:18:12 +03:00
return
}
resp := & pullCommitList { }
commits , lastReviewCommitSha , err := pull_service . GetPullCommits ( ctx , issue )
if err != nil {
ctx . JSON ( http . StatusInternalServerError , err )
return
}
// Get the needed locale
2024-02-15 00:48:45 +03:00
resp . Locale = map [ string ] any {
2023-07-28 22:18:12 +03:00
"lang" : ctx . Locale . Language ( ) ,
"show_all_commits" : ctx . Tr ( "repo.pulls.show_all_commits" ) ,
"stats_num_commits" : ctx . TrN ( len ( commits ) , "repo.activity.git_stats_commit_1" , "repo.activity.git_stats_commit_n" , len ( commits ) ) ,
"show_changes_since_your_last_review" : ctx . Tr ( "repo.pulls.show_changes_since_your_last_review" ) ,
"select_commit_hold_shift_for_range" : ctx . Tr ( "repo.pulls.select_commit_hold_shift_for_range" ) ,
}
resp . Commits = commits
resp . LastReviewCommitSha = lastReviewCommitSha
ctx . JSON ( http . StatusOK , resp )
}
2016-11-24 10:04:31 +03:00
// ViewPullCommits show commits for a pull request
2016-03-11 19:56:52 +03:00
func ViewPullCommits ( ctx * context . Context ) {
2016-08-14 13:32:24 +03:00
ctx . Data [ "PageIsPullList" ] = true
2015-09-02 11:08:05 +03:00
ctx . Data [ "PageIsPullCommits" ] = true
2023-08-07 06:43:18 +03:00
issue , ok := getPullInfo ( ctx )
if ! ok {
2015-09-02 11:08:05 +03:00
return
}
2016-08-16 20:19:09 +03:00
pull := issue . PullRequest
2015-09-02 11:08:05 +03:00
2019-12-18 21:37:44 +03:00
var prInfo * git . CompareInfo
2015-09-02 16:26:56 +03:00
if pull . HasMerged {
2019-12-18 21:37:44 +03:00
prInfo = PrepareMergedViewPullInfo ( ctx , issue )
2015-09-02 16:26:56 +03:00
} else {
2019-12-18 21:37:44 +03:00
prInfo = PrepareViewPullInfo ( ctx , issue )
2015-09-02 11:08:05 +03:00
}
2019-12-18 21:37:44 +03:00
if ctx . Written ( ) {
return
} else if prInfo == nil {
ctx . NotFound ( "ViewPullCommits" , nil )
return
}
ctx . Data [ "Username" ] = ctx . Repo . Owner . Name
ctx . Data [ "Reponame" ] = ctx . Repo . Repository . Name
2021-08-09 21:08:51 +03:00
2023-01-09 06:50:54 +03:00
commits := git_model . ConvertFromGitCommit ( ctx , prInfo . Commits , ctx . Repo . Repository )
2015-09-02 16:26:56 +03:00
ctx . Data [ "Commits" ] = commits
2021-08-09 21:08:51 +03:00
ctx . Data [ "CommitCount" ] = len ( commits )
2015-09-02 16:26:56 +03:00
2023-07-10 10:05:59 +03:00
ctx . Data [ "HasIssuesOrPullsWritePermission" ] = ctx . Repo . CanWriteIssuesOrPulls ( issue . IsPull )
ctx . Data [ "IsIssuePoster" ] = ctx . IsSigned && issue . IsPoster ( ctx . Doer . ID )
2023-07-21 14:20:04 +03:00
// For PR commits page
PrepareBranchList ( ctx )
if ctx . Written ( ) {
return
}
2019-12-16 09:20:25 +03:00
getBranchData ( ctx , issue )
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplPullCommits )
2015-09-02 11:08:05 +03:00
}
2016-11-24 10:04:31 +03:00
// ViewPullFiles render pull request changed files list page
2023-07-28 22:18:12 +03:00
func viewPullFiles ( ctx * context . Context , specifiedStartCommit , specifiedEndCommit string , willShowSpecifiedCommitRange , willShowSpecifiedCommit bool ) {
2016-08-14 13:32:24 +03:00
ctx . Data [ "PageIsPullList" ] = true
2015-09-02 11:08:05 +03:00
ctx . Data [ "PageIsPullFiles" ] = true
2023-08-07 06:43:18 +03:00
issue , ok := getPullInfo ( ctx )
if ! ok {
2015-09-02 11:08:05 +03:00
return
}
2016-08-16 20:19:09 +03:00
pull := issue . PullRequest
2015-09-02 11:08:05 +03:00
2015-09-02 16:26:56 +03:00
var (
startCommitID string
endCommitID string
2021-08-31 07:16:23 +03:00
gitRepo = ctx . Repo . GitRepo
2015-09-02 16:26:56 +03:00
)
2019-12-18 21:37:44 +03:00
var prInfo * git . CompareInfo
2015-09-02 16:26:56 +03:00
if pull . HasMerged {
2019-12-18 21:37:44 +03:00
prInfo = PrepareMergedViewPullInfo ( ctx , issue )
2015-09-02 16:26:56 +03:00
} else {
2019-12-18 21:37:44 +03:00
prInfo = PrepareViewPullInfo ( ctx , issue )
}
2015-09-02 11:08:05 +03:00
2023-07-28 22:18:12 +03:00
// Validate the given commit sha to show (if any passed)
if willShowSpecifiedCommit || willShowSpecifiedCommitRange {
foundStartCommit := len ( specifiedStartCommit ) == 0
foundEndCommit := len ( specifiedEndCommit ) == 0
if ! ( foundStartCommit && foundEndCommit ) {
for _ , commit := range prInfo . Commits {
if commit . ID . String ( ) == specifiedStartCommit {
foundStartCommit = true
}
if commit . ID . String ( ) == specifiedEndCommit {
foundEndCommit = true
}
if foundStartCommit && foundEndCommit {
break
}
}
}
if ! ( foundStartCommit && foundEndCommit ) {
ctx . NotFound ( "Given SHA1 not found for this PR" , nil )
return
}
}
2019-12-18 21:37:44 +03:00
if ctx . Written ( ) {
return
} else if prInfo == nil {
ctx . NotFound ( "ViewPullFiles" , nil )
return
}
2015-09-02 11:08:05 +03:00
2019-12-18 21:37:44 +03:00
headCommitID , err := gitRepo . GetRefCommitID ( pull . GetGitRefName ( ) )
if err != nil {
ctx . ServerError ( "GetRefCommitID" , err )
return
}
2015-09-02 16:26:56 +03:00
2023-07-28 22:18:12 +03:00
ctx . Data [ "IsShowingOnlySingleCommit" ] = willShowSpecifiedCommit
if willShowSpecifiedCommit || willShowSpecifiedCommitRange {
if len ( specifiedEndCommit ) > 0 {
endCommitID = specifiedEndCommit
} else {
endCommitID = headCommitID
}
if len ( specifiedStartCommit ) > 0 {
startCommitID = specifiedStartCommit
} else {
startCommitID = prInfo . MergeBase
}
ctx . Data [ "IsShowingAllCommits" ] = false
} else {
endCommitID = headCommitID
startCommitID = prInfo . MergeBase
ctx . Data [ "IsShowingAllCommits" ] = true
}
2017-06-21 01:25:38 +03:00
2019-12-18 21:37:44 +03:00
ctx . Data [ "Username" ] = ctx . Repo . Owner . Name
ctx . Data [ "Reponame" ] = ctx . Repo . Repository . Name
2019-11-15 05:52:59 +03:00
ctx . Data [ "AfterCommitID" ] = endCommitID
2023-07-28 22:18:12 +03:00
ctx . Data [ "BeforeCommitID" ] = startCommitID
2015-09-02 11:08:05 +03:00
2021-11-21 19:51:08 +03:00
fileOnly := ctx . FormBool ( "file-only" )
maxLines , maxFiles := setting . Git . MaxGitDiffLines , setting . Git . MaxGitDiffFiles
files := ctx . FormStrings ( "files" )
if fileOnly && ( len ( files ) == 2 || len ( files ) == 1 ) {
maxLines , maxFiles = - 1 , - 1
}
2023-07-28 22:18:12 +03:00
2022-05-07 21:28:10 +03:00
diffOptions := & gitdiff . DiffOptions {
AfterCommitID : endCommitID ,
SkipTo : ctx . FormString ( "skip-to" ) ,
MaxLines : maxLines ,
MaxLineCharacters : setting . Git . MaxGitDiffLineCharacters ,
MaxFiles : maxFiles ,
WhitespaceBehavior : gitdiff . GetWhitespaceFlag ( ctx . Data [ "WhitespaceBehavior" ] . ( string ) ) ,
}
2023-07-28 22:18:12 +03:00
if ! willShowSpecifiedCommit {
diffOptions . BeforeCommitID = startCommitID
}
2022-05-07 21:28:10 +03:00
var methodWithError string
var diff * gitdiff . Diff
2023-07-28 22:18:12 +03:00
// if we're not logged in or only a single commit (or commit range) is shown we
// have to load only the diff and not get the viewed information
// as the viewed information is designed to be loaded only on latest PR
// diff and if you're signed in.
if ! ctx . IsSigned || willShowSpecifiedCommit || willShowSpecifiedCommitRange {
2023-10-03 13:30:41 +03:00
diff , err = gitdiff . GetDiff ( ctx , gitRepo , diffOptions , files ... )
2022-05-07 21:28:10 +03:00
methodWithError = "GetDiff"
} else {
diff , err = gitdiff . SyncAndGetUserSpecificDiff ( ctx , ctx . Doer . ID , pull , gitRepo , diffOptions , files ... )
methodWithError = "SyncAndGetUserSpecificDiff"
}
2015-09-02 11:08:05 +03:00
if err != nil {
2022-05-07 21:28:10 +03:00
ctx . ServerError ( methodWithError , err )
2015-09-02 11:08:05 +03:00
return
}
2018-08-06 07:43:22 +03:00
2023-07-04 21:36:08 +03:00
ctx . PageData [ "prReview" ] = map [ string ] any {
2022-05-07 21:28:10 +03:00
"numberOfFiles" : diff . NumFiles ,
"numberOfViewedFiles" : diff . NumViewedFiles ,
}
2023-06-21 19:08:12 +03:00
if err = diff . LoadComments ( ctx , issue , ctx . Doer , ctx . Data [ "ShowOutdatedComments" ] . ( bool ) ) ; err != nil {
2018-08-06 07:43:22 +03:00
ctx . ServerError ( "LoadComments" , err )
return
}
2024-02-25 09:00:55 +03:00
for _ , file := range diff . Files {
for _ , section := range file . Sections {
for _ , line := range section . Lines {
for _ , comment := range line . Comments {
if err := comment . LoadAttachments ( ctx ) ; err != nil {
ctx . ServerError ( "LoadAttachments" , err )
return
}
}
}
}
}
2023-01-16 11:00:22 +03:00
pb , err := git_model . GetFirstMatchProtectedBranchRule ( ctx , pull . BaseRepoID , pull . BaseBranch )
if err != nil {
2020-10-13 21:50:57 +03:00
ctx . ServerError ( "LoadProtectedBranch" , err )
return
}
2023-01-16 11:00:22 +03:00
if pb != nil {
glob := pb . GetProtectedFilePatterns ( )
2020-10-13 21:50:57 +03:00
if len ( glob ) != 0 {
for _ , file := range diff . Files {
2023-01-16 11:00:22 +03:00
file . IsProtected = pb . IsProtectedFile ( glob , file . Name )
2020-10-13 21:50:57 +03:00
}
}
}
2015-09-02 11:08:05 +03:00
ctx . Data [ "Diff" ] = diff
2020-05-26 08:58:07 +03:00
ctx . Data [ "DiffNotAvailable" ] = diff . NumFiles == 0
2015-09-02 11:08:05 +03:00
2019-09-16 12:03:22 +03:00
baseCommit , err := ctx . Repo . GitRepo . GetCommit ( startCommitID )
if err != nil {
ctx . ServerError ( "GetCommit" , err )
return
}
2015-09-02 16:26:56 +03:00
commit , err := gitRepo . GetCommit ( endCommitID )
2015-09-02 11:08:05 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetCommit" , err )
2015-09-02 11:08:05 +03:00
return
}
2022-03-22 10:03:22 +03:00
if ctx . IsSigned && ctx . Doer != nil {
2023-09-29 15:12:54 +03:00
if ctx . Data [ "CanMarkConversation" ] , err = issues_model . CanMarkConversation ( ctx , issue , ctx . Doer ) ; err != nil {
2020-04-18 16:50:25 +03:00
ctx . ServerError ( "CanMarkConversation" , err )
return
}
}
2021-11-16 21:18:25 +03:00
setCompareContext ( ctx , baseCommit , commit , ctx . Repo . Owner . Name , ctx . Repo . Repository . Name )
2019-09-16 12:03:22 +03:00
2023-04-07 03:11:02 +03:00
assigneeUsers , err := repo_model . GetRepoAssignees ( ctx , ctx . Repo . Repository )
if err != nil {
ctx . ServerError ( "GetRepoAssignees" , err )
2018-08-06 07:43:22 +03:00
return
}
2023-08-25 14:07:42 +03:00
ctx . Data [ "Assignees" ] = MakeSelfOnTop ( ctx . Doer , assigneeUsers )
2023-04-07 03:11:02 +03:00
2020-12-21 18:39:28 +03:00
handleTeamMentions ( ctx )
if ctx . Written ( ) {
return
}
2022-05-07 08:35:12 +03:00
2022-06-13 12:37:59 +03:00
currentReview , err := issues_model . GetCurrentReview ( ctx , ctx . Doer , issue )
if err != nil && ! issues_model . IsErrReviewNotExist ( err ) {
2018-08-06 07:43:22 +03:00
ctx . ServerError ( "GetCurrentReview" , err )
return
}
2022-05-07 08:35:12 +03:00
numPendingCodeComments := int64 ( 0 )
if currentReview != nil {
2023-09-29 15:12:54 +03:00
numPendingCodeComments , err = issues_model . CountComments ( ctx , & issues_model . FindCommentsOptions {
2022-06-13 12:37:59 +03:00
Type : issues_model . CommentTypeCode ,
2022-05-07 08:35:12 +03:00
ReviewID : currentReview . ID ,
IssueID : issue . ID ,
} )
if err != nil {
ctx . ServerError ( "CountComments" , err )
return
}
}
ctx . Data [ "CurrentReview" ] = currentReview
ctx . Data [ "PendingCodeCommentNumber" ] = numPendingCodeComments
2019-12-16 09:20:25 +03:00
getBranchData ( ctx , issue )
2022-03-22 10:03:22 +03:00
ctx . Data [ "IsIssuePoster" ] = ctx . IsSigned && issue . IsPoster ( ctx . Doer . ID )
2020-04-04 08:39:48 +03:00
ctx . Data [ "HasIssuesOrPullsWritePermission" ] = ctx . Repo . CanWriteIssuesOrPulls ( issue . IsPull )
2021-06-15 04:12:33 +03:00
ctx . Data [ "IsAttachmentEnabled" ] = setting . Attachment . Enabled
2023-07-21 14:20:04 +03:00
// For files changed page
PrepareBranchList ( ctx )
if ctx . Written ( ) {
return
}
2021-06-15 04:12:33 +03:00
upload . AddUploadContext ( ctx , "comment" )
2024-03-04 11:16:03 +03:00
ctx . Data [ "CanBlockUser" ] = func ( blocker , blockee * user_model . User ) bool {
return user_service . CanBlockUser ( ctx , ctx . Doer , blocker , blockee )
}
2024-03-27 05:34:10 +03:00
if ! willShowSpecifiedCommit && ! willShowSpecifiedCommitRange && pull . Flow == issues_model . PullRequestFlowGithub {
if err := pull . LoadHeadRepo ( ctx ) ; err != nil {
ctx . ServerError ( "LoadHeadRepo" , err )
return
}
if pull . HeadRepo != nil {
2024-05-25 17:02:07 +03:00
ctx . Data [ "SourcePath" ] = pull . HeadRepo . Link ( ) + "/src/commit/" + endCommitID
2024-03-27 05:34:10 +03:00
2024-04-24 08:26:50 +03:00
if ! pull . HasMerged && ctx . Doer != nil {
perm , err := access_model . GetUserRepoPermission ( ctx , pull . HeadRepo , ctx . Doer )
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return
}
2024-03-27 05:34:10 +03:00
2024-04-24 08:26:50 +03:00
if perm . CanWrite ( unit . TypeCode ) || issues_model . CanMaintainerWriteToBranch ( ctx , perm , pull . HeadBranch , ctx . Doer ) {
ctx . Data [ "CanEditFile" ] = true
ctx . Data [ "EditFileTooltip" ] = ctx . Tr ( "repo.editor.edit_this_file" )
ctx . Data [ "HeadRepoLink" ] = pull . HeadRepo . Link ( )
ctx . Data [ "HeadBranchName" ] = pull . HeadBranch
ctx . Data [ "BackToLink" ] = setting . AppSubURL + ctx . Req . URL . RequestURI ( )
}
2024-03-27 05:34:10 +03:00
}
}
}
2024-03-04 11:16:03 +03:00
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplPullFiles )
2015-09-02 11:08:05 +03:00
}
2015-08-31 10:24:28 +03:00
2023-07-28 22:18:12 +03:00
func ViewPullFilesForSingleCommit ( ctx * context . Context ) {
viewPullFiles ( ctx , "" , ctx . Params ( "sha" ) , true , true )
}
func ViewPullFilesForRange ( ctx * context . Context ) {
viewPullFiles ( ctx , ctx . Params ( "shaFrom" ) , ctx . Params ( "shaTo" ) , true , false )
}
func ViewPullFilesStartingFromCommit ( ctx * context . Context ) {
viewPullFiles ( ctx , "" , ctx . Params ( "sha" ) , true , false )
}
func ViewPullFilesForAllCommitsOfPr ( ctx * context . Context ) {
viewPullFiles ( ctx , "" , "" , false , false )
}
2020-08-04 23:55:22 +03:00
// UpdatePullRequest merge PR's baseBranch into headBranch
2020-01-17 09:03:40 +03:00
func UpdatePullRequest ( ctx * context . Context ) {
2023-08-07 06:43:18 +03:00
issue , ok := getPullInfo ( ctx )
if ! ok {
2020-01-17 09:03:40 +03:00
return
}
if issue . IsClosed {
ctx . NotFound ( "MergePullRequest" , nil )
return
}
if issue . PullRequest . HasMerged {
ctx . NotFound ( "MergePullRequest" , nil )
return
}
2021-08-31 17:03:45 +03:00
rebase := ctx . FormString ( "style" ) == "rebase"
2022-11-19 11:12:33 +03:00
if err := issue . PullRequest . LoadBaseRepo ( ctx ) ; err != nil {
2021-01-14 23:27:22 +03:00
ctx . ServerError ( "LoadBaseRepo" , err )
2020-01-17 09:03:40 +03:00
return
}
2022-11-19 11:12:33 +03:00
if err := issue . PullRequest . LoadHeadRepo ( ctx ) ; err != nil {
2021-01-14 23:27:22 +03:00
ctx . ServerError ( "LoadHeadRepo" , err )
2020-01-17 09:03:40 +03:00
return
}
2022-04-28 14:48:48 +03:00
allowedUpdateByMerge , allowedUpdateByRebase , err := pull_service . IsUserAllowedToUpdate ( ctx , issue . PullRequest , ctx . Doer )
2020-01-17 09:03:40 +03:00
if err != nil {
ctx . ServerError ( "IsUserAllowedToMerge" , err )
return
}
// ToDo: add check if maintainers are allowed to change branch ... (need migration & co)
2021-08-31 17:03:45 +03:00
if ( ! allowedUpdateByMerge && ! rebase ) || ( rebase && ! allowedUpdateByRebase ) {
2020-01-17 09:03:40 +03:00
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.update_not_allowed" ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2020-01-17 09:03:40 +03:00
return
}
// default merge commit message
message := fmt . Sprintf ( "Merge branch '%s' into %s" , issue . PullRequest . BaseBranch , issue . PullRequest . HeadBranch )
2022-03-22 10:03:22 +03:00
if err = pull_service . Update ( ctx , issue . PullRequest , ctx . Doer , message , rebase ) ; err != nil {
2020-01-17 09:03:40 +03:00
if models . IsErrMergeConflicts ( err ) {
conflictError := err . ( models . ErrMergeConflicts )
2024-03-02 18:05:07 +03:00
flashError , err := ctx . RenderToHTML ( tplAlertDetails , map [ string ] any {
2020-10-21 02:50:10 +03:00
"Message" : ctx . Tr ( "repo.pulls.merge_conflict" ) ,
"Summary" : ctx . Tr ( "repo.pulls.merge_conflict_summary" ) ,
"Details" : utils . SanitizeFlashErrorString ( conflictError . StdErr ) + "<br>" + utils . SanitizeFlashErrorString ( conflictError . StdOut ) ,
} )
if err != nil {
ctx . ServerError ( "UpdatePullRequest.HTMLString" , err )
return
}
ctx . Flash . Error ( flashError )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2020-01-17 09:03:40 +03:00
return
2021-09-05 12:30:40 +03:00
} else if models . IsErrRebaseConflicts ( err ) {
conflictError := err . ( models . ErrRebaseConflicts )
2024-03-02 18:05:07 +03:00
flashError , err := ctx . RenderToHTML ( tplAlertDetails , map [ string ] any {
2021-09-05 12:30:40 +03:00
"Message" : ctx . Tr ( "repo.pulls.rebase_conflict" , utils . SanitizeFlashErrorString ( conflictError . CommitSHA ) ) ,
"Summary" : ctx . Tr ( "repo.pulls.rebase_conflict_summary" ) ,
"Details" : utils . SanitizeFlashErrorString ( conflictError . StdErr ) + "<br>" + utils . SanitizeFlashErrorString ( conflictError . StdOut ) ,
} )
if err != nil {
ctx . ServerError ( "UpdatePullRequest.HTMLString" , err )
return
}
ctx . Flash . Error ( flashError )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2021-09-05 12:30:40 +03:00
return
2020-01-17 09:03:40 +03:00
}
ctx . Flash . Error ( err . Error ( ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2020-08-03 23:50:29 +03:00
return
2020-01-17 09:03:40 +03:00
}
time . Sleep ( 1 * time . Second )
ctx . Flash . Success ( ctx . Tr ( "repo.pulls.update_branch_success" ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2020-01-17 09:03:40 +03:00
}
2016-11-24 10:04:31 +03:00
// MergePullRequest response for merging pull request
2021-01-26 18:36:53 +03:00
func MergePullRequest ( ctx * context . Context ) {
2021-04-06 22:44:05 +03:00
form := web . GetForm ( ctx ) . ( * forms . MergePullRequestForm )
2023-08-07 06:43:18 +03:00
issue , ok := getPullInfo ( ctx )
if ! ok {
2015-09-02 16:26:56 +03:00
return
}
2018-12-13 18:55:43 +03:00
pr := issue . PullRequest
2022-03-31 17:53:08 +03:00
pr . Issue = issue
pr . Issue . Repo = ctx . Repo . Repository
2023-02-21 17:42:07 +03:00
manuallyMerged := repo_model . MergeStyle ( form . Do ) == repo_model . MergeStyleManuallyMerged
mergeCheckType := pull_service . MergeCheckTypeGeneral
if form . MergeWhenChecksSucceed {
mergeCheckType = pull_service . MergeCheckTypeAuto
}
if manuallyMerged {
mergeCheckType = pull_service . MergeCheckTypeManually
}
2015-09-02 16:26:56 +03:00
2022-05-03 22:46:28 +03:00
// start with merging by checking
2024-05-08 19:11:43 +03:00
if err := pull_service . CheckPullMergeable ( ctx , ctx . Doer , & ctx . Repo . Permission , pr , mergeCheckType , form . ForceMerge ) ; err != nil {
2023-02-04 02:11:48 +03:00
switch {
case errors . Is ( err , pull_service . ErrIsClosed ) :
2022-03-31 17:53:08 +03:00
if issue . IsPull {
2024-01-15 01:00:47 +03:00
ctx . JSONError ( ctx . Tr ( "repo.pulls.is_closed" ) )
2022-03-31 17:53:08 +03:00
} else {
2024-01-15 01:00:47 +03:00
ctx . JSONError ( ctx . Tr ( "repo.issues.closed_title" ) )
2022-03-31 17:53:08 +03:00
}
2023-02-04 02:11:48 +03:00
case errors . Is ( err , pull_service . ErrUserNotAllowedToMerge ) :
2024-01-15 01:00:47 +03:00
ctx . JSONError ( ctx . Tr ( "repo.pulls.update_not_allowed" ) )
2023-02-04 02:11:48 +03:00
case errors . Is ( err , pull_service . ErrHasMerged ) :
2024-01-15 01:00:47 +03:00
ctx . JSONError ( ctx . Tr ( "repo.pulls.has_merged" ) )
2023-02-04 02:11:48 +03:00
case errors . Is ( err , pull_service . ErrIsWorkInProgress ) :
2024-01-15 01:00:47 +03:00
ctx . JSONError ( ctx . Tr ( "repo.pulls.no_merge_wip" ) )
2024-05-08 19:11:43 +03:00
case errors . Is ( err , pull_service . ErrNotMergeableState ) :
2024-01-15 01:00:47 +03:00
ctx . JSONError ( ctx . Tr ( "repo.pulls.no_merge_not_ready" ) )
2023-02-04 02:11:48 +03:00
case models . IsErrDisallowedToMerge ( err ) :
2024-01-15 01:00:47 +03:00
ctx . JSONError ( ctx . Tr ( "repo.pulls.no_merge_not_ready" ) )
2023-02-04 02:11:48 +03:00
case asymkey_service . IsErrWontSign ( err ) :
2024-01-15 01:00:47 +03:00
ctx . JSONError ( err . Error ( ) ) // has no translation ...
2023-02-04 02:11:48 +03:00
case errors . Is ( err , pull_service . ErrDependenciesLeft ) :
2024-01-15 01:00:47 +03:00
ctx . JSONError ( ctx . Tr ( "repo.issues.dependency.pr_close_blocked" ) )
2023-02-04 02:11:48 +03:00
default :
2022-03-31 17:53:08 +03:00
ctx . ServerError ( "WebCheck" , err )
}
2023-02-04 02:11:48 +03:00
2020-01-27 13:26:53 +03:00
return
}
2021-03-04 06:41:23 +03:00
// handle manually-merged mark
2023-02-21 17:42:07 +03:00
if manuallyMerged {
2023-10-14 11:37:24 +03:00
if err := pull_service . MergedManually ( ctx , pr , ctx . Doer , ctx . Repo . GitRepo , form . MergeCommitID ) ; err != nil {
2023-02-04 02:11:48 +03:00
switch {
case models . IsErrInvalidMergeStyle ( err ) :
2024-01-15 01:00:47 +03:00
ctx . JSONError ( ctx . Tr ( "repo.pulls.invalid_merge_option" ) )
2023-02-04 02:11:48 +03:00
case strings . Contains ( err . Error ( ) , "Wrong commit ID" ) :
2024-01-15 01:00:47 +03:00
ctx . JSONError ( ctx . Tr ( "repo.pulls.wrong_commit_id" ) )
2023-02-04 02:11:48 +03:00
default :
2022-05-03 22:46:28 +03:00
ctx . ServerError ( "MergedManually" , err )
2021-03-04 06:41:23 +03:00
}
2024-01-15 01:00:47 +03:00
return
2021-03-04 06:41:23 +03:00
}
2024-01-15 01:00:47 +03:00
ctx . JSONRedirect ( issue . Link ( ) )
2021-03-04 06:41:23 +03:00
return
}
2022-05-08 15:32:45 +03:00
message := strings . TrimSpace ( form . MergeTitleField )
if len ( message ) == 0 {
var err error
2022-12-29 15:40:20 +03:00
message , _ , err = pull_service . GetDefaultMergeMessage ( ctx , ctx . Repo . GitRepo , pr , repo_model . MergeStyle ( form . Do ) )
2022-05-08 15:32:45 +03:00
if err != nil {
ctx . ServerError ( "GetDefaultMergeMessage" , err )
return
}
}
form . MergeMessageField = strings . TrimSpace ( form . MergeMessageField )
if len ( form . MergeMessageField ) > 0 {
message += "\n\n" + form . MergeMessageField
2018-07-18 00:23:58 +03:00
}
2022-06-11 17:44:20 +03:00
if form . MergeWhenChecksSucceed {
// delete all scheduled auto merges
_ = pull_model . DeleteScheduledAutoMerge ( ctx , pr . ID )
// schedule auto merge
scheduled , err := automerge . ScheduleAutoMerge ( ctx , ctx . Doer , pr , repo_model . MergeStyle ( form . Do ) , message )
if err != nil {
ctx . ServerError ( "ScheduleAutoMerge" , err )
return
} else if scheduled {
// nothing more to do ...
ctx . Flash . Success ( ctx . Tr ( "repo.pulls.auto_merge_newly_scheduled" ) )
2024-01-15 01:00:47 +03:00
ctx . JSONRedirect ( fmt . Sprintf ( "%s/pulls/%d" , ctx . Repo . RepoLink , pr . Index ) )
2022-06-11 17:44:20 +03:00
return
}
}
2022-11-03 18:49:00 +03:00
if err := pull_service . Merge ( ctx , pr , ctx . Doer , ctx . Repo . GitRepo , repo_model . MergeStyle ( form . Do ) , form . HeadCommitID , message , false ) ; err != nil {
2018-01-05 21:56:50 +03:00
if models . IsErrInvalidMergeStyle ( err ) {
2024-01-15 01:00:47 +03:00
ctx . JSONError ( ctx . Tr ( "repo.pulls.invalid_merge_option" ) )
2019-11-10 11:42:51 +03:00
} else if models . IsErrMergeConflicts ( err ) {
conflictError := err . ( models . ErrMergeConflicts )
2024-03-02 18:05:07 +03:00
flashError , err := ctx . RenderToHTML ( tplAlertDetails , map [ string ] any {
2020-10-21 02:50:10 +03:00
"Message" : ctx . Tr ( "repo.editor.merge_conflict" ) ,
"Summary" : ctx . Tr ( "repo.editor.merge_conflict_summary" ) ,
"Details" : utils . SanitizeFlashErrorString ( conflictError . StdErr ) + "<br>" + utils . SanitizeFlashErrorString ( conflictError . StdOut ) ,
} )
if err != nil {
ctx . ServerError ( "MergePullRequest.HTMLString" , err )
return
}
ctx . Flash . Error ( flashError )
2024-01-15 01:00:47 +03:00
ctx . JSONRedirect ( issue . Link ( ) )
2019-11-10 11:42:51 +03:00
} else if models . IsErrRebaseConflicts ( err ) {
conflictError := err . ( models . ErrRebaseConflicts )
2024-03-02 18:05:07 +03:00
flashError , err := ctx . RenderToHTML ( tplAlertDetails , map [ string ] any {
2020-10-21 22:59:12 +03:00
"Message" : ctx . Tr ( "repo.pulls.rebase_conflict" , utils . SanitizeFlashErrorString ( conflictError . CommitSHA ) ) ,
"Summary" : ctx . Tr ( "repo.pulls.rebase_conflict_summary" ) ,
2020-10-21 02:50:10 +03:00
"Details" : utils . SanitizeFlashErrorString ( conflictError . StdErr ) + "<br>" + utils . SanitizeFlashErrorString ( conflictError . StdOut ) ,
} )
if err != nil {
ctx . ServerError ( "MergePullRequest.HTMLString" , err )
return
}
ctx . Flash . Error ( flashError )
2024-02-21 14:40:46 +03:00
ctx . JSONRedirect ( issue . Link ( ) )
2019-11-10 11:42:51 +03:00
} else if models . IsErrMergeUnrelatedHistories ( err ) {
log . Debug ( "MergeUnrelatedHistories error: %v" , err )
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.unrelated_histories" ) )
2024-02-21 14:40:46 +03:00
ctx . JSONRedirect ( issue . Link ( ) )
2020-03-28 07:13:18 +03:00
} else if git . IsErrPushOutOfDate ( err ) {
2019-11-10 11:42:51 +03:00
log . Debug ( "MergePushOutOfDate error: %v" , err )
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.merge_out_of_date" ) )
2024-02-21 14:40:46 +03:00
ctx . JSONRedirect ( issue . Link ( ) )
2021-12-20 03:32:54 +03:00
} else if models . IsErrSHADoesNotMatch ( err ) {
log . Debug ( "MergeHeadOutOfDate error: %v" , err )
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.head_out_of_date" ) )
2024-02-21 14:40:46 +03:00
ctx . JSONRedirect ( issue . Link ( ) )
2020-03-28 07:13:18 +03:00
} else if git . IsErrPushRejected ( err ) {
2020-02-22 16:08:48 +03:00
log . Debug ( "MergePushRejected error: %v" , err )
2020-03-28 07:13:18 +03:00
pushrejErr := err . ( * git . ErrPushRejected )
2020-02-22 16:08:48 +03:00
message := pushrejErr . Message
if len ( message ) == 0 {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.push_rejected_no_message" ) )
} else {
2024-03-02 18:05:07 +03:00
flashError , err := ctx . RenderToHTML ( tplAlertDetails , map [ string ] any {
2020-10-21 02:50:10 +03:00
"Message" : ctx . Tr ( "repo.pulls.push_rejected" ) ,
"Summary" : ctx . Tr ( "repo.pulls.push_rejected_summary" ) ,
"Details" : utils . SanitizeFlashErrorString ( pushrejErr . Message ) ,
} )
if err != nil {
ctx . ServerError ( "MergePullRequest.HTMLString" , err )
return
}
ctx . Flash . Error ( flashError )
2020-02-22 16:08:48 +03:00
}
2024-01-15 01:00:47 +03:00
ctx . JSONRedirect ( issue . Link ( ) )
2022-05-03 22:46:28 +03:00
} else {
ctx . ServerError ( "Merge" , err )
2018-01-05 21:56:50 +03:00
}
2015-09-02 16:26:56 +03:00
return
}
2022-05-03 22:46:28 +03:00
log . Trace ( "Pull request merged: %d" , pr . ID )
2015-09-02 16:26:56 +03:00
2023-09-16 17:39:12 +03:00
if err := stopTimerIfAvailable ( ctx , ctx . Doer , issue ) ; err != nil {
2024-01-15 01:00:47 +03:00
ctx . ServerError ( "stopTimerIfAvailable" , err )
2019-02-05 14:38:11 +03:00
return
}
2015-09-02 16:26:56 +03:00
log . Trace ( "Pull request merged: %d" , pr . ID )
2021-07-13 02:26:25 +03:00
if form . DeleteBranchAfterMerge {
2022-01-03 22:45:58 +03:00
// Don't cleanup when other pr use this branch as head branch
2022-06-13 12:37:59 +03:00
exist , err := issues_model . HasUnmergedPullRequestsByHeadInfo ( ctx , pr . HeadRepoID , pr . HeadBranch )
2022-01-03 22:45:58 +03:00
if err != nil {
ctx . ServerError ( "HasUnmergedPullRequestsByHeadInfo" , err )
return
}
if exist {
2024-01-15 01:00:47 +03:00
ctx . JSONRedirect ( issue . Link ( ) )
2022-01-03 22:45:58 +03:00
return
}
2021-07-13 02:26:25 +03:00
var headRepo * git . Repository
if ctx . Repo != nil && ctx . Repo . Repository != nil && pr . HeadRepoID == ctx . Repo . Repository . ID && ctx . Repo . GitRepo != nil {
headRepo = ctx . Repo . GitRepo
} else {
Simplify how git repositories are opened (#28937)
## Purpose
This is a refactor toward building an abstraction over managing git
repositories.
Afterwards, it does not matter anymore if they are stored on the local
disk or somewhere remote.
## What this PR changes
We used `git.OpenRepository` everywhere previously.
Now, we should split them into two distinct functions:
Firstly, there are temporary repositories which do not change:
```go
git.OpenRepository(ctx, diskPath)
```
Gitea managed repositories having a record in the database in the
`repository` table are moved into the new package `gitrepo`:
```go
gitrepo.OpenRepository(ctx, repo_model.Repo)
```
Why is `repo_model.Repository` the second parameter instead of file
path?
Because then we can easily adapt our repository storage strategy.
The repositories can be stored locally, however, they could just as well
be stored on a remote server.
## Further changes in other PRs
- A Git Command wrapper on package `gitrepo` could be created. i.e.
`NewCommand(ctx, repo_model.Repository, commands...)`. `git.RunOpts{Dir:
repo.RepoPath()}`, the directory should be empty before invoking this
method and it can be filled in the function only. #28940
- Remove the `RepoPath()`/`WikiPath()` functions to reduce the
possibility of mistakes.
---------
Co-authored-by: delvh <dev.lh@web.de>
2024-01-27 23:09:51 +03:00
headRepo , err = gitrepo . OpenRepository ( ctx , pr . HeadRepo )
2021-07-13 02:26:25 +03:00
if err != nil {
Simplify how git repositories are opened (#28937)
## Purpose
This is a refactor toward building an abstraction over managing git
repositories.
Afterwards, it does not matter anymore if they are stored on the local
disk or somewhere remote.
## What this PR changes
We used `git.OpenRepository` everywhere previously.
Now, we should split them into two distinct functions:
Firstly, there are temporary repositories which do not change:
```go
git.OpenRepository(ctx, diskPath)
```
Gitea managed repositories having a record in the database in the
`repository` table are moved into the new package `gitrepo`:
```go
gitrepo.OpenRepository(ctx, repo_model.Repo)
```
Why is `repo_model.Repository` the second parameter instead of file
path?
Because then we can easily adapt our repository storage strategy.
The repositories can be stored locally, however, they could just as well
be stored on a remote server.
## Further changes in other PRs
- A Git Command wrapper on package `gitrepo` could be created. i.e.
`NewCommand(ctx, repo_model.Repository, commands...)`. `git.RunOpts{Dir:
repo.RepoPath()}`, the directory should be empty before invoking this
method and it can be filled in the function only. #28940
- Remove the `RepoPath()`/`WikiPath()` functions to reduce the
possibility of mistakes.
---------
Co-authored-by: delvh <dev.lh@web.de>
2024-01-27 23:09:51 +03:00
ctx . ServerError ( fmt . Sprintf ( "OpenRepository[%s]" , pr . HeadRepo . FullName ( ) ) , err )
2021-07-13 02:26:25 +03:00
return
}
defer headRepo . Close ( )
}
deleteBranch ( ctx , pr , headRepo )
}
2024-01-15 01:00:47 +03:00
ctx . JSONRedirect ( issue . Link ( ) )
2022-06-11 17:44:20 +03:00
}
// CancelAutoMergePullRequest cancels a scheduled pr
func CancelAutoMergePullRequest ( ctx * context . Context ) {
2023-08-07 06:43:18 +03:00
issue , ok := getPullInfo ( ctx )
if ! ok {
2022-06-11 17:44:20 +03:00
return
}
if err := automerge . RemoveScheduledAutoMerge ( ctx , ctx . Doer , issue . PullRequest ) ; err != nil {
if db . IsErrNotExist ( err ) {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.auto_merge_not_scheduled" ) )
ctx . Redirect ( fmt . Sprintf ( "%s/pulls/%d" , ctx . Repo . RepoLink , issue . Index ) )
return
}
ctx . ServerError ( "RemoveScheduledAutoMerge" , err )
return
}
ctx . Flash . Success ( ctx . Tr ( "repo.pulls.auto_merge_canceled_schedule" ) )
ctx . Redirect ( fmt . Sprintf ( "%s/pulls/%d" , ctx . Repo . RepoLink , issue . Index ) )
2015-09-02 16:26:56 +03:00
}
2023-09-16 17:39:12 +03:00
func stopTimerIfAvailable ( ctx * context . Context , user * user_model . User , issue * issues_model . Issue ) error {
if issues_model . StopwatchExists ( ctx , user . ID , issue . ID ) {
if err := issues_model . CreateOrStopIssueStopwatch ( ctx , user , issue ) ; err != nil {
2019-02-05 14:38:11 +03:00
return err
}
}
return nil
}
2016-11-24 10:04:31 +03:00
// CompareAndPullRequestPost response for creating pull request
2021-01-26 18:36:53 +03:00
func CompareAndPullRequestPost ( ctx * context . Context ) {
2021-04-06 22:44:05 +03:00
form := web . GetForm ( ctx ) . ( * forms . CreateIssueForm )
2015-09-02 02:07:02 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "repo.pulls.compare_changes" )
ctx . Data [ "PageIsComparePull" ] = true
ctx . Data [ "IsDiffCompare" ] = true
2018-08-13 22:04:39 +03:00
ctx . Data [ "PullRequestWorkInProgressPrefixes" ] = setting . Repository . PullRequest . WorkInProgressPrefixes
2020-10-05 08:49:33 +03:00
ctx . Data [ "IsAttachmentEnabled" ] = setting . Attachment . Enabled
upload . AddUploadContext ( ctx , "comment" )
2021-11-09 22:57:58 +03:00
ctx . Data [ "HasIssuesOrPullsWritePermission" ] = ctx . Repo . CanWrite ( unit . TypePullRequests )
2015-09-02 02:07:02 +03:00
var (
repo = ctx . Repo . Repository
attachments [ ] string
)
2021-09-27 15:19:34 +03:00
ci := ParseCompareInfo ( ctx )
2021-08-31 10:43:31 +03:00
defer func ( ) {
2021-09-30 22:31:02 +03:00
if ci != nil && ci . HeadGitRepo != nil {
2021-09-27 15:19:34 +03:00
ci . HeadGitRepo . Close ( )
2021-08-31 10:43:31 +03:00
}
} ( )
2015-09-02 02:07:02 +03:00
if ctx . Written ( ) {
return
}
2024-01-12 18:25:15 +03:00
labelIDs , assigneeIDs , milestoneID , projectID := ValidateRepoMetas ( ctx , * form , true )
2015-09-02 02:07:02 +03:00
if ctx . Written ( ) {
return
}
2020-08-18 07:23:45 +03:00
if setting . Attachment . Enabled {
2016-08-11 15:48:08 +03:00
attachments = form . Files
2015-09-02 02:07:02 +03:00
}
if ctx . HasError ( ) {
2023-06-19 11:25:36 +03:00
ctx . JSONError ( ctx . GetErrMsg ( ) )
2015-09-02 02:07:02 +03:00
return
}
2019-01-21 14:45:32 +03:00
if util . IsEmptyString ( form . Title ) {
2023-06-19 11:25:36 +03:00
ctx . JSONError ( ctx . Tr ( "repo.issues.new.title_empty" ) )
2019-01-21 14:45:32 +03:00
return
}
2022-09-02 10:58:49 +03:00
content := form . Content
if filename := ctx . Req . Form . Get ( "template-file" ) ; filename != "" {
if template , err := issue_template . UnmarshalFromRepo ( ctx . Repo . GitRepo , ctx . Repo . Repository . DefaultBranch , filename ) ; err == nil {
content = issue_template . RenderToMarkdown ( template , ctx . Req . Form )
}
}
2022-06-13 12:37:59 +03:00
pullIssue := & issues_model . Issue {
2015-09-02 02:07:02 +03:00
RepoID : repo . ID ,
2021-11-16 21:18:25 +03:00
Repo : repo ,
2016-08-14 13:32:24 +03:00
Title : form . Title ,
2022-03-22 10:03:22 +03:00
PosterID : ctx . Doer . ID ,
Poster : ctx . Doer ,
2015-09-02 02:07:02 +03:00
MilestoneID : milestoneID ,
IsPull : true ,
2022-09-02 10:58:49 +03:00
Content : content ,
2015-09-02 02:07:02 +03:00
}
2022-06-13 12:37:59 +03:00
pullRequest := & issues_model . PullRequest {
2022-04-28 18:45:33 +03:00
HeadRepoID : ci . HeadRepo . ID ,
BaseRepoID : repo . ID ,
HeadBranch : ci . HeadBranch ,
BaseBranch : ci . BaseBranch ,
HeadRepo : ci . HeadRepo ,
BaseRepo : repo ,
MergeBase : ci . CompareInfo . MergeBase ,
2022-06-13 12:37:59 +03:00
Type : issues_model . PullRequestGitea ,
2022-04-28 18:45:33 +03:00
AllowMaintainerEdit : form . AllowMaintainerEdit ,
2016-02-24 15:56:54 +03:00
}
2016-08-16 00:04:44 +03:00
// FIXME: check error in the case two people send pull request at almost same time, give nice error prompt
// instead of 500.
2018-05-09 19:29:04 +03:00
2022-01-20 02:26:57 +03:00
if err := pull_service . NewPullRequest ( ctx , repo , pullIssue , labelIDs , attachments , pullRequest , assigneeIDs ) ; err != nil {
2022-06-13 12:37:59 +03:00
if repo_model . IsErrUserDoesNotHaveAccessToRepo ( err ) {
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusBadRequest , "UserDoesNotHaveAccessToRepo" , err . Error ( ) )
2020-06-08 21:07:41 +03:00
} else if git . IsErrPushRejected ( err ) {
pushrejErr := err . ( * git . ErrPushRejected )
message := pushrejErr . Message
if len ( message ) == 0 {
2023-06-19 11:25:36 +03:00
ctx . JSONError ( ctx . Tr ( "repo.pulls.push_rejected_no_message" ) )
return
2020-06-08 21:07:41 +03:00
}
2024-03-02 18:05:07 +03:00
flashError , err := ctx . RenderToHTML ( tplAlertDetails , map [ string ] any {
2023-06-19 11:25:36 +03:00
"Message" : ctx . Tr ( "repo.pulls.push_rejected" ) ,
"Summary" : ctx . Tr ( "repo.pulls.push_rejected_summary" ) ,
"Details" : utils . SanitizeFlashErrorString ( pushrejErr . Message ) ,
} )
if err != nil {
ctx . ServerError ( "CompareAndPullRequest.HTMLString" , err )
return
}
2024-03-02 18:05:07 +03:00
ctx . JSONError ( flashError )
2024-03-04 11:16:03 +03:00
} else if errors . Is ( err , user_model . ErrBlockedUser ) {
flashError , err := ctx . RenderToHTML ( tplAlertDetails , map [ string ] any {
"Message" : ctx . Tr ( "repo.pulls.push_rejected" ) ,
"Summary" : ctx . Tr ( "repo.pulls.new.blocked_user" ) ,
} )
if err != nil {
ctx . ServerError ( "CompareAndPullRequest.HTMLString" , err )
return
}
ctx . JSONError ( flashError )
2018-05-09 19:29:04 +03:00
}
2015-09-02 02:07:02 +03:00
return
2015-12-10 19:18:56 +03:00
}
2024-05-08 16:44:57 +03:00
if projectID > 0 && ctx . Repo . CanWrite ( unit . TypeProjects ) {
if err := issues_model . IssueAssignOrRemoveProject ( ctx , pullIssue , ctx . Doer , projectID , 0 ) ; err != nil {
if ! errors . Is ( err , util . ErrPermissionDenied ) {
ctx . ServerError ( "IssueAssignOrRemoveProject" , err )
return
}
2024-01-12 18:25:15 +03:00
}
}
2016-02-24 15:56:54 +03:00
log . Trace ( "Pull request created: %d/%d" , repo . ID , pullIssue . ID )
2023-06-19 11:25:36 +03:00
ctx . JSONRedirect ( pullIssue . Link ( ) )
2014-03-24 14:25:15 +04:00
}
2015-10-24 10:36:47 +03:00
2017-06-21 04:00:03 +03:00
// CleanUpPullRequest responses for delete merged branch when PR has been merged
func CleanUpPullRequest ( ctx * context . Context ) {
2023-08-07 06:43:18 +03:00
issue , ok := getPullInfo ( ctx )
if ! ok {
2017-06-21 04:00:03 +03:00
return
}
2018-12-13 18:55:43 +03:00
pr := issue . PullRequest
2017-06-21 04:00:03 +03:00
2019-04-20 23:50:34 +03:00
// Don't cleanup unmerged and unclosed PRs
if ! pr . HasMerged && ! issue . IsClosed {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "CleanUpPullRequest" , nil )
2017-06-21 04:00:03 +03:00
return
}
2022-01-03 22:45:58 +03:00
// Don't cleanup when there are other PR's that use this branch as head branch.
2022-06-13 12:37:59 +03:00
exist , err := issues_model . HasUnmergedPullRequestsByHeadInfo ( ctx , pr . HeadRepoID , pr . HeadBranch )
2022-01-03 22:45:58 +03:00
if err != nil {
ctx . ServerError ( "HasUnmergedPullRequestsByHeadInfo" , err )
return
}
if exist {
ctx . NotFound ( "CleanUpPullRequest" , nil )
return
}
2022-11-19 11:12:33 +03:00
if err := pr . LoadHeadRepo ( ctx ) ; err != nil {
2020-03-03 01:31:55 +03:00
ctx . ServerError ( "LoadHeadRepo" , err )
2017-06-21 04:00:03 +03:00
return
} else if pr . HeadRepo == nil {
// Forked repository has already been deleted
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "CleanUpPullRequest" , nil )
2017-06-21 04:00:03 +03:00
return
2022-11-19 11:12:33 +03:00
} else if err = pr . LoadBaseRepo ( ctx ) ; err != nil {
2020-03-03 01:31:55 +03:00
ctx . ServerError ( "LoadBaseRepo" , err )
2017-06-21 04:00:03 +03:00
return
2023-02-18 15:11:03 +03:00
} else if err = pr . HeadRepo . LoadOwner ( ctx ) ; err != nil {
ctx . ServerError ( "HeadRepo.LoadOwner" , err )
2017-06-21 04:00:03 +03:00
return
}
2022-05-11 13:09:36 +03:00
perm , err := access_model . GetUserRepoPermission ( ctx , pr . HeadRepo , ctx . Doer )
2018-11-28 14:26:14 +03:00
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return
}
2021-11-09 22:57:58 +03:00
if ! perm . CanWrite ( unit . TypeCode ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "CleanUpPullRequest" , nil )
2017-06-21 04:00:03 +03:00
return
}
fullBranchName := pr . HeadRepo . Owner . Name + "/" + pr . HeadBranch
2021-07-13 02:26:25 +03:00
var gitBaseRepo * git . Repository
// Assume that the base repo is the current context (almost certainly)
if ctx . Repo != nil && ctx . Repo . Repository != nil && ctx . Repo . Repository . ID == pr . BaseRepoID && ctx . Repo . GitRepo != nil {
gitBaseRepo = ctx . Repo . GitRepo
} else {
// If not just open it
Simplify how git repositories are opened (#28937)
## Purpose
This is a refactor toward building an abstraction over managing git
repositories.
Afterwards, it does not matter anymore if they are stored on the local
disk or somewhere remote.
## What this PR changes
We used `git.OpenRepository` everywhere previously.
Now, we should split them into two distinct functions:
Firstly, there are temporary repositories which do not change:
```go
git.OpenRepository(ctx, diskPath)
```
Gitea managed repositories having a record in the database in the
`repository` table are moved into the new package `gitrepo`:
```go
gitrepo.OpenRepository(ctx, repo_model.Repo)
```
Why is `repo_model.Repository` the second parameter instead of file
path?
Because then we can easily adapt our repository storage strategy.
The repositories can be stored locally, however, they could just as well
be stored on a remote server.
## Further changes in other PRs
- A Git Command wrapper on package `gitrepo` could be created. i.e.
`NewCommand(ctx, repo_model.Repository, commands...)`. `git.RunOpts{Dir:
repo.RepoPath()}`, the directory should be empty before invoking this
method and it can be filled in the function only. #28940
- Remove the `RepoPath()`/`WikiPath()` functions to reduce the
possibility of mistakes.
---------
Co-authored-by: delvh <dev.lh@web.de>
2024-01-27 23:09:51 +03:00
gitBaseRepo , err = gitrepo . OpenRepository ( ctx , pr . BaseRepo )
2021-07-13 02:26:25 +03:00
if err != nil {
Simplify how git repositories are opened (#28937)
## Purpose
This is a refactor toward building an abstraction over managing git
repositories.
Afterwards, it does not matter anymore if they are stored on the local
disk or somewhere remote.
## What this PR changes
We used `git.OpenRepository` everywhere previously.
Now, we should split them into two distinct functions:
Firstly, there are temporary repositories which do not change:
```go
git.OpenRepository(ctx, diskPath)
```
Gitea managed repositories having a record in the database in the
`repository` table are moved into the new package `gitrepo`:
```go
gitrepo.OpenRepository(ctx, repo_model.Repo)
```
Why is `repo_model.Repository` the second parameter instead of file
path?
Because then we can easily adapt our repository storage strategy.
The repositories can be stored locally, however, they could just as well
be stored on a remote server.
## Further changes in other PRs
- A Git Command wrapper on package `gitrepo` could be created. i.e.
`NewCommand(ctx, repo_model.Repository, commands...)`. `git.RunOpts{Dir:
repo.RepoPath()}`, the directory should be empty before invoking this
method and it can be filled in the function only. #28940
- Remove the `RepoPath()`/`WikiPath()` functions to reduce the
possibility of mistakes.
---------
Co-authored-by: delvh <dev.lh@web.de>
2024-01-27 23:09:51 +03:00
ctx . ServerError ( fmt . Sprintf ( "OpenRepository[%s]" , pr . BaseRepo . FullName ( ) ) , err )
2021-07-13 02:26:25 +03:00
return
}
defer gitBaseRepo . Close ( )
2017-06-21 04:00:03 +03:00
}
2021-07-13 02:26:25 +03:00
// Now assume that the head repo is the same as the base repo (reasonable chance)
gitRepo := gitBaseRepo
// But if not: is it the same as the context?
if pr . BaseRepoID != pr . HeadRepoID && ctx . Repo != nil && ctx . Repo . Repository != nil && ctx . Repo . Repository . ID == pr . HeadRepoID && ctx . Repo . GitRepo != nil {
gitRepo = ctx . Repo . GitRepo
} else if pr . BaseRepoID != pr . HeadRepoID {
// Otherwise just load it up
Simplify how git repositories are opened (#28937)
## Purpose
This is a refactor toward building an abstraction over managing git
repositories.
Afterwards, it does not matter anymore if they are stored on the local
disk or somewhere remote.
## What this PR changes
We used `git.OpenRepository` everywhere previously.
Now, we should split them into two distinct functions:
Firstly, there are temporary repositories which do not change:
```go
git.OpenRepository(ctx, diskPath)
```
Gitea managed repositories having a record in the database in the
`repository` table are moved into the new package `gitrepo`:
```go
gitrepo.OpenRepository(ctx, repo_model.Repo)
```
Why is `repo_model.Repository` the second parameter instead of file
path?
Because then we can easily adapt our repository storage strategy.
The repositories can be stored locally, however, they could just as well
be stored on a remote server.
## Further changes in other PRs
- A Git Command wrapper on package `gitrepo` could be created. i.e.
`NewCommand(ctx, repo_model.Repository, commands...)`. `git.RunOpts{Dir:
repo.RepoPath()}`, the directory should be empty before invoking this
method and it can be filled in the function only. #28940
- Remove the `RepoPath()`/`WikiPath()` functions to reduce the
possibility of mistakes.
---------
Co-authored-by: delvh <dev.lh@web.de>
2024-01-27 23:09:51 +03:00
gitRepo , err = gitrepo . OpenRepository ( ctx , pr . HeadRepo )
2021-07-13 02:26:25 +03:00
if err != nil {
Simplify how git repositories are opened (#28937)
## Purpose
This is a refactor toward building an abstraction over managing git
repositories.
Afterwards, it does not matter anymore if they are stored on the local
disk or somewhere remote.
## What this PR changes
We used `git.OpenRepository` everywhere previously.
Now, we should split them into two distinct functions:
Firstly, there are temporary repositories which do not change:
```go
git.OpenRepository(ctx, diskPath)
```
Gitea managed repositories having a record in the database in the
`repository` table are moved into the new package `gitrepo`:
```go
gitrepo.OpenRepository(ctx, repo_model.Repo)
```
Why is `repo_model.Repository` the second parameter instead of file
path?
Because then we can easily adapt our repository storage strategy.
The repositories can be stored locally, however, they could just as well
be stored on a remote server.
## Further changes in other PRs
- A Git Command wrapper on package `gitrepo` could be created. i.e.
`NewCommand(ctx, repo_model.Repository, commands...)`. `git.RunOpts{Dir:
repo.RepoPath()}`, the directory should be empty before invoking this
method and it can be filled in the function only. #28940
- Remove the `RepoPath()`/`WikiPath()` functions to reduce the
possibility of mistakes.
---------
Co-authored-by: delvh <dev.lh@web.de>
2024-01-27 23:09:51 +03:00
ctx . ServerError ( fmt . Sprintf ( "OpenRepository[%s]" , pr . HeadRepo . FullName ( ) ) , err )
2021-07-13 02:26:25 +03:00
return
}
defer gitRepo . Close ( )
2017-06-21 04:00:03 +03:00
}
defer func ( ) {
2023-07-26 09:04:01 +03:00
ctx . JSONRedirect ( issue . Link ( ) )
2017-06-21 04:00:03 +03:00
} ( )
// Check if branch has no new commits
2018-01-30 15:29:39 +03:00
headCommitID , err := gitBaseRepo . GetRefCommitID ( pr . GetGitRefName ( ) )
if err != nil {
2019-04-02 10:48:31 +03:00
log . Error ( "GetRefCommitID: %v" , err )
2018-01-30 15:29:39 +03:00
ctx . Flash . Error ( ctx . Tr ( "repo.branch.deletion_failed" , fullBranchName ) )
return
}
branchCommitID , err := gitRepo . GetBranchCommitID ( pr . HeadBranch )
if err != nil {
2019-04-02 10:48:31 +03:00
log . Error ( "GetBranchCommitID: %v" , err )
2018-01-30 15:29:39 +03:00
ctx . Flash . Error ( ctx . Tr ( "repo.branch.deletion_failed" , fullBranchName ) )
return
}
if headCommitID != branchCommitID {
ctx . Flash . Error ( ctx . Tr ( "repo.branch.delete_branch_has_new_commits" , fullBranchName ) )
return
2017-06-21 04:00:03 +03:00
}
2021-07-13 02:26:25 +03:00
deleteBranch ( ctx , pr , gitRepo )
}
2022-06-13 12:37:59 +03:00
func deleteBranch ( ctx * context . Context , pr * issues_model . PullRequest , gitRepo * git . Repository ) {
2023-02-01 01:11:48 +03:00
fullBranchName := pr . HeadRepo . FullName ( ) + ":" + pr . HeadBranch
2024-01-17 03:44:56 +03:00
if err := pull_service . RetargetChildrenOnMerge ( ctx , ctx . Doer , pr ) ; err != nil {
ctx . Flash . Error ( ctx . Tr ( "repo.branch.deletion_failed" , fullBranchName ) )
return
}
2023-03-01 01:17:51 +03:00
if err := repo_service . DeleteBranch ( ctx , ctx . Doer , pr . HeadRepo , gitRepo , pr . HeadBranch ) ; err != nil {
2021-06-07 17:52:59 +03:00
switch {
case git . IsErrBranchNotExist ( err ) :
ctx . Flash . Error ( ctx . Tr ( "repo.branch.deletion_failed" , fullBranchName ) )
case errors . Is ( err , repo_service . ErrBranchIsDefault ) :
ctx . Flash . Error ( ctx . Tr ( "repo.branch.deletion_failed" , fullBranchName ) )
2023-01-16 11:00:22 +03:00
case errors . Is ( err , git_model . ErrBranchIsProtected ) :
2021-06-07 17:52:59 +03:00
ctx . Flash . Error ( ctx . Tr ( "repo.branch.deletion_failed" , fullBranchName ) )
default :
log . Error ( "DeleteBranch: %v" , err )
ctx . Flash . Error ( ctx . Tr ( "repo.branch.deletion_failed" , fullBranchName ) )
}
2017-06-21 04:00:03 +03:00
return
}
2022-06-13 12:37:59 +03:00
if err := issues_model . AddDeletePRBranchComment ( ctx , ctx . Doer , pr . BaseRepo , pr . IssueID , pr . HeadBranch ) ; err != nil {
2017-06-21 04:00:03 +03:00
// Do not fail here as branch has already been deleted
2019-04-02 10:48:31 +03:00
log . Error ( "DeleteBranch: %v" , err )
2017-06-21 04:00:03 +03:00
}
ctx . Flash . Success ( ctx . Tr ( "repo.branch.deletion_success" , fullBranchName ) )
}
2018-01-05 13:56:52 +03:00
// DownloadPullDiff render a pull's raw diff
func DownloadPullDiff ( ctx * context . Context ) {
2019-12-14 01:21:06 +03:00
DownloadPullDiffOrPatch ( ctx , false )
2018-01-05 13:56:52 +03:00
}
2018-01-07 16:10:20 +03:00
// DownloadPullPatch render a pull's raw patch
func DownloadPullPatch ( ctx * context . Context ) {
2019-12-14 01:21:06 +03:00
DownloadPullDiffOrPatch ( ctx , true )
}
// DownloadPullDiffOrPatch render a pull's raw diff or patch
func DownloadPullDiffOrPatch ( ctx * context . Context , patch bool ) {
2022-06-13 12:37:59 +03:00
pr , err := issues_model . GetPullRequestByIndex ( ctx , ctx . Repo . Repository . ID , ctx . ParamsInt64 ( ":index" ) )
2018-01-07 16:10:20 +03:00
if err != nil {
2022-06-13 12:37:59 +03:00
if issues_model . IsErrPullRequestNotExist ( err ) {
2021-10-05 17:41:48 +03:00
ctx . NotFound ( "GetPullRequestByIndex" , err )
2018-01-07 16:10:20 +03:00
} else {
2021-10-05 17:41:48 +03:00
ctx . ServerError ( "GetPullRequestByIndex" , err )
2018-01-07 16:10:20 +03:00
}
return
}
2021-09-28 00:09:49 +03:00
binary := ctx . FormBool ( "binary" )
2018-01-07 16:10:20 +03:00
2022-01-20 02:26:57 +03:00
if err := pull_service . DownloadDiffOrPatch ( ctx , pr , ctx , patch , binary ) ; err != nil {
2019-12-14 01:21:06 +03:00
ctx . ServerError ( "DownloadDiffOrPatch" , err )
2018-01-07 16:10:20 +03:00
return
}
}
2019-12-16 09:20:25 +03:00
// UpdatePullRequestTarget change pull request's target branch
func UpdatePullRequestTarget ( ctx * context . Context ) {
issue := GetActionIssue ( ctx )
if ctx . Written ( ) {
return
}
2023-07-05 21:52:12 +03:00
pr := issue . PullRequest
2019-12-16 09:20:25 +03:00
if ! issue . IsPull {
ctx . Error ( http . StatusNotFound )
return
}
2022-03-22 10:03:22 +03:00
if ! ctx . IsSigned || ( ! issue . IsPoster ( ctx . Doer . ID ) && ! ctx . Repo . CanWriteIssuesOrPulls ( issue . IsPull ) ) {
2019-12-16 09:20:25 +03:00
ctx . Error ( http . StatusForbidden )
return
}
2021-07-29 04:42:15 +03:00
targetBranch := ctx . FormTrim ( "target_branch" )
2019-12-16 09:20:25 +03:00
if len ( targetBranch ) == 0 {
ctx . Error ( http . StatusNoContent )
return
}
2022-03-22 10:03:22 +03:00
if err := pull_service . ChangeTargetBranch ( ctx , pr , ctx . Doer , targetBranch ) ; err != nil {
2022-06-13 12:37:59 +03:00
if issues_model . IsErrPullRequestAlreadyExists ( err ) {
err := err . ( issues_model . ErrPullRequestAlreadyExists )
2019-12-16 09:20:25 +03:00
RepoRelPath := ctx . Repo . Owner . Name + "/" + ctx . Repo . Repository . Name
Fix various typos (#20338)
* Fix various typos
Found via `codespell -q 3 -S ./options/locale,./options/license,./public/vendor -L actived,allways,attachements,ba,befores,commiter,pullrequest,pullrequests,readby,splitted,te,unknwon`
Co-authored-by: zeripath <art27@cantab.net>
2022-07-13 00:32:37 +03:00
errorMessage := ctx . Tr ( "repo.pulls.has_pull_request" , html . EscapeString ( ctx . Repo . RepoLink + "/pulls/" + strconv . FormatInt ( err . IssueID , 10 ) ) , html . EscapeString ( RepoRelPath ) , err . IssueID ) // FIXME: Creates url inside locale string
2019-12-16 09:20:25 +03:00
ctx . Flash . Error ( errorMessage )
2023-07-04 21:36:08 +03:00
ctx . JSON ( http . StatusConflict , map [ string ] any {
2019-12-16 09:20:25 +03:00
"error" : err . Error ( ) ,
"user_error" : errorMessage ,
} )
2022-06-13 12:37:59 +03:00
} else if issues_model . IsErrIssueIsClosed ( err ) {
2019-12-16 09:20:25 +03:00
errorMessage := ctx . Tr ( "repo.pulls.is_closed" )
ctx . Flash . Error ( errorMessage )
2023-07-04 21:36:08 +03:00
ctx . JSON ( http . StatusConflict , map [ string ] any {
2019-12-16 09:20:25 +03:00
"error" : err . Error ( ) ,
"user_error" : errorMessage ,
} )
} else if models . IsErrPullRequestHasMerged ( err ) {
errorMessage := ctx . Tr ( "repo.pulls.has_merged" )
ctx . Flash . Error ( errorMessage )
2023-07-04 21:36:08 +03:00
ctx . JSON ( http . StatusConflict , map [ string ] any {
2019-12-16 09:20:25 +03:00
"error" : err . Error ( ) ,
"user_error" : errorMessage ,
} )
2023-06-29 13:03:20 +03:00
} else if git_model . IsErrBranchesEqual ( err ) {
2019-12-16 09:20:25 +03:00
errorMessage := ctx . Tr ( "repo.pulls.nothing_to_compare" )
ctx . Flash . Error ( errorMessage )
2023-07-04 21:36:08 +03:00
ctx . JSON ( http . StatusBadRequest , map [ string ] any {
2019-12-16 09:20:25 +03:00
"error" : err . Error ( ) ,
"user_error" : errorMessage ,
} )
} else {
ctx . ServerError ( "UpdatePullRequestTarget" , err )
}
return
}
2023-09-05 21:37:47 +03:00
notify_service . PullRequestChangeTargetBranch ( ctx , ctx . Doer , pr , targetBranch )
2019-12-16 09:20:25 +03:00
2023-07-04 21:36:08 +03:00
ctx . JSON ( http . StatusOK , map [ string ] any {
2019-12-16 09:20:25 +03:00
"base_branch" : pr . BaseBranch ,
} )
}
2022-04-28 18:45:33 +03:00
// SetAllowEdits allow edits from maintainers to PRs
func SetAllowEdits ( ctx * context . Context ) {
form := web . GetForm ( ctx ) . ( * forms . UpdateAllowEditsForm )
2022-06-13 12:37:59 +03:00
pr , err := issues_model . GetPullRequestByIndex ( ctx , ctx . Repo . Repository . ID , ctx . ParamsInt64 ( ":index" ) )
2022-04-28 18:45:33 +03:00
if err != nil {
2022-06-13 12:37:59 +03:00
if issues_model . IsErrPullRequestNotExist ( err ) {
2022-04-28 18:45:33 +03:00
ctx . NotFound ( "GetPullRequestByIndex" , err )
} else {
ctx . ServerError ( "GetPullRequestByIndex" , err )
}
return
}
if err := pull_service . SetAllowEdits ( ctx , ctx . Doer , pr , form . AllowMaintainerEdit ) ; err != nil {
if errors . Is ( pull_service . ErrUserHasNoPermissionForAction , err ) {
ctx . Error ( http . StatusForbidden )
return
}
ctx . ServerError ( "SetAllowEdits" , err )
return
}
2023-07-04 21:36:08 +03:00
ctx . JSON ( http . StatusOK , map [ string ] any {
2022-04-28 18:45:33 +03:00
"allow_maintainer_edit" : pr . AllowMaintainerEdit ,
} )
}