Add merge style fast-forward-only
(#28954)
With this option, it is possible to require a linear commit history with the following benefits over the next best option `Rebase+fast-forward`: The original commits continue existing, with the original signatures continuing to stay valid instead of being rewritten, there is no merge commit, and reverting commits becomes easier. Closes #24906
This commit is contained in:
parent
05eaf1cf3e
commit
83123b493f
|
@ -1061,7 +1061,7 @@ LEVEL = Info
|
||||||
;; List of keywords used in Pull Request comments to automatically reopen a related issue
|
;; List of keywords used in Pull Request comments to automatically reopen a related issue
|
||||||
;REOPEN_KEYWORDS = reopen,reopens,reopened
|
;REOPEN_KEYWORDS = reopen,reopens,reopened
|
||||||
;;
|
;;
|
||||||
;; Set default merge style for repository creating, valid options: merge, rebase, rebase-merge, squash
|
;; Set default merge style for repository creating, valid options: merge, rebase, rebase-merge, squash, fast-forward-only
|
||||||
;DEFAULT_MERGE_STYLE = merge
|
;DEFAULT_MERGE_STYLE = merge
|
||||||
;;
|
;;
|
||||||
;; In the default merge message for squash commits include at most this many commits
|
;; In the default merge message for squash commits include at most this many commits
|
||||||
|
|
|
@ -126,7 +126,7 @@ In addition, there is _`StaticRootPath`_ which can be set as a built-in at build
|
||||||
keywords used in Pull Request comments to automatically close a related issue
|
keywords used in Pull Request comments to automatically close a related issue
|
||||||
- `REOPEN_KEYWORDS`: **reopen**, **reopens**, **reopened**: List of keywords used in Pull Request comments to automatically reopen
|
- `REOPEN_KEYWORDS`: **reopen**, **reopens**, **reopened**: List of keywords used in Pull Request comments to automatically reopen
|
||||||
a related issue
|
a related issue
|
||||||
- `DEFAULT_MERGE_STYLE`: **merge**: Set default merge style for repository creating, valid options: `merge`, `rebase`, `rebase-merge`, `squash`
|
- `DEFAULT_MERGE_STYLE`: **merge**: Set default merge style for repository creating, valid options: `merge`, `rebase`, `rebase-merge`, `squash`, `fast-forward-only`
|
||||||
- `DEFAULT_MERGE_MESSAGE_COMMITS_LIMIT`: **50**: In the default merge message for squash commits include at most this many commits. Set to `-1` to include all commits
|
- `DEFAULT_MERGE_MESSAGE_COMMITS_LIMIT`: **50**: In the default merge message for squash commits include at most this many commits. Set to `-1` to include all commits
|
||||||
- `DEFAULT_MERGE_MESSAGE_SIZE`: **5120**: In the default merge message for squash commits limit the size of the commit messages. Set to `-1` to have no limit. Only used if `POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES` is `true`.
|
- `DEFAULT_MERGE_MESSAGE_SIZE`: **5120**: In the default merge message for squash commits limit the size of the commit messages. Set to `-1` to have no limit. Only used if `POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES` is `true`.
|
||||||
- `DEFAULT_MERGE_MESSAGE_ALL_AUTHORS`: **false**: In the default merge message for squash commits walk all commits to include all authors in the Co-authored-by otherwise just use those in the limited list
|
- `DEFAULT_MERGE_MESSAGE_ALL_AUTHORS`: **false**: In the default merge message for squash commits walk all commits to include all authors in the Co-authored-by otherwise just use those in the limited list
|
||||||
|
|
|
@ -125,7 +125,7 @@ menu:
|
||||||
- `CLOSE_KEYWORDS`: **close**, **closes**, **closed**, **fix**, **fixes**, **fixed**, **resolve**, **resolves**, **resolved**: 在拉取请求评论中用于自动关闭相关问题的关键词列表。
|
- `CLOSE_KEYWORDS`: **close**, **closes**, **closed**, **fix**, **fixes**, **fixed**, **resolve**, **resolves**, **resolved**: 在拉取请求评论中用于自动关闭相关问题的关键词列表。
|
||||||
- `REOPEN_KEYWORDS`: **reopen**, **reopens**, **reopened**: 在拉取请求评论中用于自动重新打开相关问题的
|
- `REOPEN_KEYWORDS`: **reopen**, **reopens**, **reopened**: 在拉取请求评论中用于自动重新打开相关问题的
|
||||||
关键词列表。
|
关键词列表。
|
||||||
- `DEFAULT_MERGE_STYLE`: **merge**: 设置创建仓库的默认合并方式,可选: `merge`, `rebase`, `rebase-merge`, `squash`
|
- `DEFAULT_MERGE_STYLE`: **merge**: 设置创建仓库的默认合并方式,可选: `merge`, `rebase`, `rebase-merge`, `squash`, `fast-forward-only`
|
||||||
- `DEFAULT_MERGE_MESSAGE_COMMITS_LIMIT`: **50**: 在默认合并消息中,对于`squash`提交,最多包括此数量的提交。设置为 -1 以包括所有提交。
|
- `DEFAULT_MERGE_MESSAGE_COMMITS_LIMIT`: **50**: 在默认合并消息中,对于`squash`提交,最多包括此数量的提交。设置为 -1 以包括所有提交。
|
||||||
- `DEFAULT_MERGE_MESSAGE_SIZE`: **5120**: 在默认的合并消息中,对于`squash`提交,限制提交消息的大小。设置为 `-1`以取消限制。仅在`POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES`为`true`时使用。
|
- `DEFAULT_MERGE_MESSAGE_SIZE`: **5120**: 在默认的合并消息中,对于`squash`提交,限制提交消息的大小。设置为 `-1`以取消限制。仅在`POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES`为`true`时使用。
|
||||||
- `DEFAULT_MERGE_MESSAGE_ALL_AUTHORS`: **false**: 在默认合并消息中,对于`squash`提交,遍历所有提交以包括所有作者的`Co-authored-by`,否则仅使用限定列表中的作者。
|
- `DEFAULT_MERGE_MESSAGE_ALL_AUTHORS`: **false**: 在默认合并消息中,对于`squash`提交,遍历所有提交以包括所有作者的`Co-authored-by`,否则仅使用限定列表中的作者。
|
||||||
|
|
|
@ -493,6 +493,23 @@ func (err ErrMergeUnrelatedHistories) Error() string {
|
||||||
return fmt.Sprintf("Merge UnrelatedHistories Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
return fmt.Sprintf("Merge UnrelatedHistories Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrMergeDivergingFastForwardOnly represents an error if a fast-forward-only merge fails because the branches diverge
|
||||||
|
type ErrMergeDivergingFastForwardOnly struct {
|
||||||
|
StdOut string
|
||||||
|
StdErr string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrMergeDivergingFastForwardOnly checks if an error is a ErrMergeDivergingFastForwardOnly.
|
||||||
|
func IsErrMergeDivergingFastForwardOnly(err error) bool {
|
||||||
|
_, ok := err.(ErrMergeDivergingFastForwardOnly)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrMergeDivergingFastForwardOnly) Error() string {
|
||||||
|
return fmt.Sprintf("Merge DivergingFastForwardOnly Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
||||||
|
}
|
||||||
|
|
||||||
// ErrRebaseConflicts represents an error if rebase fails with a conflict
|
// ErrRebaseConflicts represents an error if rebase fails with a conflict
|
||||||
type ErrRebaseConflicts struct {
|
type ErrRebaseConflicts struct {
|
||||||
Style repo_model.MergeStyle
|
Style repo_model.MergeStyle
|
||||||
|
|
|
@ -21,6 +21,8 @@ const (
|
||||||
MergeStyleRebaseMerge MergeStyle = "rebase-merge"
|
MergeStyleRebaseMerge MergeStyle = "rebase-merge"
|
||||||
// MergeStyleSquash squash commits into single commit before merging
|
// MergeStyleSquash squash commits into single commit before merging
|
||||||
MergeStyleSquash MergeStyle = "squash"
|
MergeStyleSquash MergeStyle = "squash"
|
||||||
|
// MergeStyleFastForwardOnly fast-forward merge if possible, otherwise fail
|
||||||
|
MergeStyleFastForwardOnly MergeStyle = "fast-forward-only"
|
||||||
// MergeStyleManuallyMerged pr has been merged manually, just mark it as merged directly
|
// MergeStyleManuallyMerged pr has been merged manually, just mark it as merged directly
|
||||||
MergeStyleManuallyMerged MergeStyle = "manually-merged"
|
MergeStyleManuallyMerged MergeStyle = "manually-merged"
|
||||||
// MergeStyleRebaseUpdate not a merge style, used to update pull head by rebase
|
// MergeStyleRebaseUpdate not a merge style, used to update pull head by rebase
|
||||||
|
|
|
@ -153,6 +153,7 @@ type PullRequestsConfig struct {
|
||||||
AllowRebase bool
|
AllowRebase bool
|
||||||
AllowRebaseMerge bool
|
AllowRebaseMerge bool
|
||||||
AllowSquash bool
|
AllowSquash bool
|
||||||
|
AllowFastForwardOnly bool
|
||||||
AllowManualMerge bool
|
AllowManualMerge bool
|
||||||
AutodetectManualMerge bool
|
AutodetectManualMerge bool
|
||||||
AllowRebaseUpdate bool
|
AllowRebaseUpdate bool
|
||||||
|
@ -179,6 +180,7 @@ func (cfg *PullRequestsConfig) IsMergeStyleAllowed(mergeStyle MergeStyle) bool {
|
||||||
mergeStyle == MergeStyleRebase && cfg.AllowRebase ||
|
mergeStyle == MergeStyleRebase && cfg.AllowRebase ||
|
||||||
mergeStyle == MergeStyleRebaseMerge && cfg.AllowRebaseMerge ||
|
mergeStyle == MergeStyleRebaseMerge && cfg.AllowRebaseMerge ||
|
||||||
mergeStyle == MergeStyleSquash && cfg.AllowSquash ||
|
mergeStyle == MergeStyleSquash && cfg.AllowSquash ||
|
||||||
|
mergeStyle == MergeStyleFastForwardOnly && cfg.AllowFastForwardOnly ||
|
||||||
mergeStyle == MergeStyleManuallyMerged && cfg.AllowManualMerge
|
mergeStyle == MergeStyleManuallyMerged && cfg.AllowManualMerge
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ func (err ErrBranchNotExist) Unwrap() error {
|
||||||
return util.ErrNotExist
|
return util.ErrNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrPushOutOfDate represents an error if merging fails due to unrelated histories
|
// ErrPushOutOfDate represents an error if merging fails due to the base branch being updated
|
||||||
type ErrPushOutOfDate struct {
|
type ErrPushOutOfDate struct {
|
||||||
StdOut string
|
StdOut string
|
||||||
StdErr string
|
StdErr string
|
||||||
|
|
|
@ -87,7 +87,11 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
|
||||||
units = append(units, repo_model.RepoUnit{
|
units = append(units, repo_model.RepoUnit{
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
Type: tp,
|
Type: tp,
|
||||||
Config: &repo_model.PullRequestsConfig{AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle), AllowRebaseUpdate: true},
|
Config: &repo_model.PullRequestsConfig{
|
||||||
|
AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, AllowFastForwardOnly: true,
|
||||||
|
DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle),
|
||||||
|
AllowRebaseUpdate: true,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
units = append(units, repo_model.RepoUnit{
|
units = append(units, repo_model.RepoUnit{
|
||||||
|
|
|
@ -99,6 +99,7 @@ type Repository struct {
|
||||||
AllowRebase bool `json:"allow_rebase"`
|
AllowRebase bool `json:"allow_rebase"`
|
||||||
AllowRebaseMerge bool `json:"allow_rebase_explicit"`
|
AllowRebaseMerge bool `json:"allow_rebase_explicit"`
|
||||||
AllowSquash bool `json:"allow_squash_merge"`
|
AllowSquash bool `json:"allow_squash_merge"`
|
||||||
|
AllowFastForwardOnly bool `json:"allow_fast_forward_only_merge"`
|
||||||
AllowRebaseUpdate bool `json:"allow_rebase_update"`
|
AllowRebaseUpdate bool `json:"allow_rebase_update"`
|
||||||
DefaultDeleteBranchAfterMerge bool `json:"default_delete_branch_after_merge"`
|
DefaultDeleteBranchAfterMerge bool `json:"default_delete_branch_after_merge"`
|
||||||
DefaultMergeStyle string `json:"default_merge_style"`
|
DefaultMergeStyle string `json:"default_merge_style"`
|
||||||
|
@ -198,6 +199,8 @@ type EditRepoOption struct {
|
||||||
AllowRebaseMerge *bool `json:"allow_rebase_explicit,omitempty"`
|
AllowRebaseMerge *bool `json:"allow_rebase_explicit,omitempty"`
|
||||||
// either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging.
|
// either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging.
|
||||||
AllowSquash *bool `json:"allow_squash_merge,omitempty"`
|
AllowSquash *bool `json:"allow_squash_merge,omitempty"`
|
||||||
|
// either `true` to allow fast-forward-only merging pull requests, or `false` to prevent fast-forward-only merging.
|
||||||
|
AllowFastForwardOnly *bool `json:"allow_fast_forward_only_merge,omitempty"`
|
||||||
// either `true` to allow mark pr as merged manually, or `false` to prevent it.
|
// either `true` to allow mark pr as merged manually, or `false` to prevent it.
|
||||||
AllowManualMerge *bool `json:"allow_manual_merge,omitempty"`
|
AllowManualMerge *bool `json:"allow_manual_merge,omitempty"`
|
||||||
// either `true` to enable AutodetectManualMerge, or `false` to prevent it. Note: In some special cases, misjudgments can occur.
|
// either `true` to enable AutodetectManualMerge, or `false` to prevent it. Note: In some special cases, misjudgments can occur.
|
||||||
|
@ -206,7 +209,7 @@ type EditRepoOption struct {
|
||||||
AllowRebaseUpdate *bool `json:"allow_rebase_update,omitempty"`
|
AllowRebaseUpdate *bool `json:"allow_rebase_update,omitempty"`
|
||||||
// set to `true` to delete pr branch after merge by default
|
// set to `true` to delete pr branch after merge by default
|
||||||
DefaultDeleteBranchAfterMerge *bool `json:"default_delete_branch_after_merge,omitempty"`
|
DefaultDeleteBranchAfterMerge *bool `json:"default_delete_branch_after_merge,omitempty"`
|
||||||
// set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", or "squash".
|
// set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", "squash", or "fast-forward-only".
|
||||||
DefaultMergeStyle *string `json:"default_merge_style,omitempty"`
|
DefaultMergeStyle *string `json:"default_merge_style,omitempty"`
|
||||||
// set to `true` to allow edits from maintainers by default
|
// set to `true` to allow edits from maintainers by default
|
||||||
DefaultAllowMaintainerEdit *bool `json:"default_allow_maintainer_edit,omitempty"`
|
DefaultAllowMaintainerEdit *bool `json:"default_allow_maintainer_edit,omitempty"`
|
||||||
|
|
|
@ -1816,6 +1816,7 @@ pulls.merge_pull_request = Create merge commit
|
||||||
pulls.rebase_merge_pull_request = Rebase then fast-forward
|
pulls.rebase_merge_pull_request = Rebase then fast-forward
|
||||||
pulls.rebase_merge_commit_pull_request = Rebase then create merge commit
|
pulls.rebase_merge_commit_pull_request = Rebase then create merge commit
|
||||||
pulls.squash_merge_pull_request = Create squash commit
|
pulls.squash_merge_pull_request = Create squash commit
|
||||||
|
pulls.fast_forward_only_merge_pull_request = Fast-forward only
|
||||||
pulls.merge_manually = Manually merged
|
pulls.merge_manually = Manually merged
|
||||||
pulls.merge_commit_id = The merge commit ID
|
pulls.merge_commit_id = The merge commit ID
|
||||||
pulls.require_signed_wont_sign = The branch requires signed commits but this merge will not be signed
|
pulls.require_signed_wont_sign = The branch requires signed commits but this merge will not be signed
|
||||||
|
|
|
@ -897,6 +897,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
|
||||||
AllowRebase: true,
|
AllowRebase: true,
|
||||||
AllowRebaseMerge: true,
|
AllowRebaseMerge: true,
|
||||||
AllowSquash: true,
|
AllowSquash: true,
|
||||||
|
AllowFastForwardOnly: true,
|
||||||
AllowManualMerge: true,
|
AllowManualMerge: true,
|
||||||
AutodetectManualMerge: false,
|
AutodetectManualMerge: false,
|
||||||
AllowRebaseUpdate: true,
|
AllowRebaseUpdate: true,
|
||||||
|
@ -923,6 +924,9 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
|
||||||
if opts.AllowSquash != nil {
|
if opts.AllowSquash != nil {
|
||||||
config.AllowSquash = *opts.AllowSquash
|
config.AllowSquash = *opts.AllowSquash
|
||||||
}
|
}
|
||||||
|
if opts.AllowFastForwardOnly != nil {
|
||||||
|
config.AllowFastForwardOnly = *opts.AllowFastForwardOnly
|
||||||
|
}
|
||||||
if opts.AllowManualMerge != nil {
|
if opts.AllowManualMerge != nil {
|
||||||
config.AllowManualMerge = *opts.AllowManualMerge
|
config.AllowManualMerge = *opts.AllowManualMerge
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ func TestRepoEdit(t *testing.T) {
|
||||||
allowRebase := false
|
allowRebase := false
|
||||||
allowRebaseMerge := false
|
allowRebaseMerge := false
|
||||||
allowSquashMerge := false
|
allowSquashMerge := false
|
||||||
|
allowFastForwardOnlyMerge := false
|
||||||
archived := true
|
archived := true
|
||||||
opts := api.EditRepoOption{
|
opts := api.EditRepoOption{
|
||||||
Name: &ctx.Repo.Repository.Name,
|
Name: &ctx.Repo.Repository.Name,
|
||||||
|
@ -50,6 +51,7 @@ func TestRepoEdit(t *testing.T) {
|
||||||
AllowRebase: &allowRebase,
|
AllowRebase: &allowRebase,
|
||||||
AllowRebaseMerge: &allowRebaseMerge,
|
AllowRebaseMerge: &allowRebaseMerge,
|
||||||
AllowSquash: &allowSquashMerge,
|
AllowSquash: &allowSquashMerge,
|
||||||
|
AllowFastForwardOnly: &allowFastForwardOnlyMerge,
|
||||||
Archived: &archived,
|
Archived: &archived,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1871,6 +1871,8 @@ func ViewIssue(ctx *context.Context) {
|
||||||
mergeStyle = repo_model.MergeStyleRebaseMerge
|
mergeStyle = repo_model.MergeStyleRebaseMerge
|
||||||
} else if prConfig.AllowSquash {
|
} else if prConfig.AllowSquash {
|
||||||
mergeStyle = repo_model.MergeStyleSquash
|
mergeStyle = repo_model.MergeStyleSquash
|
||||||
|
} else if prConfig.AllowFastForwardOnly {
|
||||||
|
mergeStyle = repo_model.MergeStyleFastForwardOnly
|
||||||
} else if prConfig.AllowManualMerge {
|
} else if prConfig.AllowManualMerge {
|
||||||
mergeStyle = repo_model.MergeStyleManuallyMerged
|
mergeStyle = repo_model.MergeStyleManuallyMerged
|
||||||
}
|
}
|
||||||
|
|
|
@ -251,6 +251,7 @@ func UnitsPost(ctx *context.Context) {
|
||||||
AllowRebase: form.PullsAllowRebase,
|
AllowRebase: form.PullsAllowRebase,
|
||||||
AllowRebaseMerge: form.PullsAllowRebaseMerge,
|
AllowRebaseMerge: form.PullsAllowRebaseMerge,
|
||||||
AllowSquash: form.PullsAllowSquash,
|
AllowSquash: form.PullsAllowSquash,
|
||||||
|
AllowFastForwardOnly: form.PullsAllowFastForwardOnly,
|
||||||
AllowManualMerge: form.PullsAllowManualMerge,
|
AllowManualMerge: form.PullsAllowManualMerge,
|
||||||
AutodetectManualMerge: form.EnableAutodetectManualMerge,
|
AutodetectManualMerge: form.EnableAutodetectManualMerge,
|
||||||
AllowRebaseUpdate: form.PullsAllowRebaseUpdate,
|
AllowRebaseUpdate: form.PullsAllowRebaseUpdate,
|
||||||
|
|
|
@ -93,6 +93,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
||||||
allowRebase := false
|
allowRebase := false
|
||||||
allowRebaseMerge := false
|
allowRebaseMerge := false
|
||||||
allowSquash := false
|
allowSquash := false
|
||||||
|
allowFastForwardOnly := false
|
||||||
allowRebaseUpdate := false
|
allowRebaseUpdate := false
|
||||||
defaultDeleteBranchAfterMerge := false
|
defaultDeleteBranchAfterMerge := false
|
||||||
defaultMergeStyle := repo_model.MergeStyleMerge
|
defaultMergeStyle := repo_model.MergeStyleMerge
|
||||||
|
@ -105,6 +106,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
||||||
allowRebase = config.AllowRebase
|
allowRebase = config.AllowRebase
|
||||||
allowRebaseMerge = config.AllowRebaseMerge
|
allowRebaseMerge = config.AllowRebaseMerge
|
||||||
allowSquash = config.AllowSquash
|
allowSquash = config.AllowSquash
|
||||||
|
allowFastForwardOnly = config.AllowFastForwardOnly
|
||||||
allowRebaseUpdate = config.AllowRebaseUpdate
|
allowRebaseUpdate = config.AllowRebaseUpdate
|
||||||
defaultDeleteBranchAfterMerge = config.DefaultDeleteBranchAfterMerge
|
defaultDeleteBranchAfterMerge = config.DefaultDeleteBranchAfterMerge
|
||||||
defaultMergeStyle = config.GetDefaultMergeStyle()
|
defaultMergeStyle = config.GetDefaultMergeStyle()
|
||||||
|
@ -220,6 +222,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
||||||
AllowRebase: allowRebase,
|
AllowRebase: allowRebase,
|
||||||
AllowRebaseMerge: allowRebaseMerge,
|
AllowRebaseMerge: allowRebaseMerge,
|
||||||
AllowSquash: allowSquash,
|
AllowSquash: allowSquash,
|
||||||
|
AllowFastForwardOnly: allowFastForwardOnly,
|
||||||
AllowRebaseUpdate: allowRebaseUpdate,
|
AllowRebaseUpdate: allowRebaseUpdate,
|
||||||
DefaultDeleteBranchAfterMerge: defaultDeleteBranchAfterMerge,
|
DefaultDeleteBranchAfterMerge: defaultDeleteBranchAfterMerge,
|
||||||
DefaultMergeStyle: string(defaultMergeStyle),
|
DefaultMergeStyle: string(defaultMergeStyle),
|
||||||
|
|
|
@ -170,6 +170,7 @@ type RepoUnitSettingForm struct {
|
||||||
PullsAllowRebase bool
|
PullsAllowRebase bool
|
||||||
PullsAllowRebaseMerge bool
|
PullsAllowRebaseMerge bool
|
||||||
PullsAllowSquash bool
|
PullsAllowSquash bool
|
||||||
|
PullsAllowFastForwardOnly bool
|
||||||
PullsAllowManualMerge bool
|
PullsAllowManualMerge bool
|
||||||
PullsDefaultMergeStyle string
|
PullsDefaultMergeStyle string
|
||||||
EnableAutodetectManualMerge bool
|
EnableAutodetectManualMerge bool
|
||||||
|
@ -609,8 +610,8 @@ func (f *InitializeLabelsForm) Validate(req *http.Request, errs binding.Errors)
|
||||||
// swagger:model MergePullRequestOption
|
// swagger:model MergePullRequestOption
|
||||||
type MergePullRequestForm struct {
|
type MergePullRequestForm struct {
|
||||||
// required: true
|
// required: true
|
||||||
// enum: merge,rebase,rebase-merge,squash,manually-merged
|
// enum: merge,rebase,rebase-merge,squash,fast-forward-only,manually-merged
|
||||||
Do string `binding:"Required;In(merge,rebase,rebase-merge,squash,manually-merged)"`
|
Do string `binding:"Required;In(merge,rebase,rebase-merge,squash,fast-forward-only,manually-merged)"`
|
||||||
MergeTitleField string
|
MergeTitleField string
|
||||||
MergeMessageField string
|
MergeMessageField string
|
||||||
MergeCommitID string // only used for manually-merged
|
MergeCommitID string // only used for manually-merged
|
||||||
|
|
|
@ -273,6 +273,10 @@ func doMergeAndPush(ctx context.Context, pr *issues_model.PullRequest, doer *use
|
||||||
if err := doMergeStyleSquash(mergeCtx, message); err != nil {
|
if err := doMergeStyleSquash(mergeCtx, message); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
case repo_model.MergeStyleFastForwardOnly:
|
||||||
|
if err := doMergeStyleFastForwardOnly(mergeCtx); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return "", models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle}
|
return "", models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle}
|
||||||
}
|
}
|
||||||
|
@ -383,6 +387,13 @@ func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *g
|
||||||
StdErr: ctx.errbuf.String(),
|
StdErr: ctx.errbuf.String(),
|
||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
|
} else if mergeStyle == repo_model.MergeStyleFastForwardOnly && strings.Contains(ctx.errbuf.String(), "Not possible to fast-forward, aborting") {
|
||||||
|
log.Debug("MergeDivergingFastForwardOnly %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
|
||||||
|
return models.ErrMergeDivergingFastForwardOnly{
|
||||||
|
StdOut: ctx.outbuf.String(),
|
||||||
|
StdErr: ctx.errbuf.String(),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
log.Error("git merge %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
|
log.Error("git merge %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
|
||||||
return fmt.Errorf("git merge %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
|
return fmt.Errorf("git merge %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
|
||||||
|
|
21
services/pull/merge_ff_only.go
Normal file
21
services/pull/merge_ff_only.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package pull
|
||||||
|
|
||||||
|
import (
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// doMergeStyleFastForwardOnly merges the tracking into the current HEAD - which is assumed to be staging branch (equal to the pr.BaseBranch)
|
||||||
|
func doMergeStyleFastForwardOnly(ctx *mergeContext) error {
|
||||||
|
cmd := git.NewCommand(ctx, "merge", "--ff-only").AddDynamicArguments(trackingBranch)
|
||||||
|
if err := runMergeCommand(ctx, repo_model.MergeStyleFastForwardOnly, cmd); err != nil {
|
||||||
|
log.Error("%-v Unable to merge tracking into base: %v", ctx.pr, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// doMergeStyleMerge merges the tracking into the current HEAD - which is assumed to tbe staging branch (equal to the pr.BaseBranch)
|
// doMergeStyleMerge merges the tracking branch into the current HEAD - which is assumed to be the staging branch (equal to the pr.BaseBranch)
|
||||||
func doMergeStyleMerge(ctx *mergeContext, message string) error {
|
func doMergeStyleMerge(ctx *mergeContext, message string) error {
|
||||||
cmd := git.NewCommand(ctx, "merge", "--no-ff", "--no-commit").AddDynamicArguments(trackingBranch)
|
cmd := git.NewCommand(ctx, "merge", "--no-ff", "--no-commit").AddDynamicArguments(trackingBranch)
|
||||||
if err := runMergeCommand(ctx, repo_model.MergeStyleMerge, cmd); err != nil {
|
if err := runMergeCommand(ctx, repo_model.MergeStyleMerge, cmd); err != nil {
|
||||||
|
|
|
@ -197,7 +197,7 @@
|
||||||
{{if .AllowMerge}} {{/* user is allowed to merge */}}
|
{{if .AllowMerge}} {{/* user is allowed to merge */}}
|
||||||
{{$prUnit := .Repository.MustGetUnit $.Context $.UnitTypePullRequests}}
|
{{$prUnit := .Repository.MustGetUnit $.Context $.UnitTypePullRequests}}
|
||||||
{{$approvers := (.Issue.PullRequest.GetApprovers ctx)}}
|
{{$approvers := (.Issue.PullRequest.GetApprovers ctx)}}
|
||||||
{{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash}}
|
{{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash $prUnit.PullRequestsConfig.AllowFastForwardOnly}}
|
||||||
{{$hasPendingPullRequestMergeTip := ""}}
|
{{$hasPendingPullRequestMergeTip := ""}}
|
||||||
{{if .HasPendingPullRequestMerge}}
|
{{if .HasPendingPullRequestMerge}}
|
||||||
{{$createdPRMergeStr := TimeSinceUnix .PendingPullRequestMerge.CreatedUnix ctx.Locale}}
|
{{$createdPRMergeStr := TimeSinceUnix .PendingPullRequestMerge.CreatedUnix ctx.Locale}}
|
||||||
|
@ -268,6 +268,13 @@
|
||||||
'mergeMessageFieldText': {{.GetCommitMessages}} + defaultSquashMergeMessage,
|
'mergeMessageFieldText': {{.GetCommitMessages}} + defaultSquashMergeMessage,
|
||||||
'hideAutoMerge': generalHideAutoMerge,
|
'hideAutoMerge': generalHideAutoMerge,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'name': 'fast-forward-only',
|
||||||
|
'allowed': {{and $prUnit.PullRequestsConfig.AllowFastForwardOnly (eq .Issue.PullRequest.CommitsBehind 0)}},
|
||||||
|
'textDoMerge': {{ctx.Locale.Tr "repo.pulls.fast_forward_only_merge_pull_request"}},
|
||||||
|
'hideMergeMessageTexts': true,
|
||||||
|
'hideAutoMerge': generalHideAutoMerge,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'name': 'manually-merged',
|
'name': 'manually-merged',
|
||||||
'allowed': {{$prUnit.PullRequestsConfig.AllowManualMerge}},
|
'allowed': {{$prUnit.PullRequestsConfig.AllowManualMerge}},
|
||||||
|
|
|
@ -35,6 +35,10 @@
|
||||||
<div>git checkout {{.PullRequest.BaseBranch}}</div>
|
<div>git checkout {{.PullRequest.BaseBranch}}</div>
|
||||||
<div>git merge --squash {{$localBranch}}</div>
|
<div>git merge --squash {{$localBranch}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="gt-hidden" data-pull-merge-style="fast-forward-only">
|
||||||
|
<div>git checkout {{.PullRequest.BaseBranch}}</div>
|
||||||
|
<div>git merge --ff-only {{$localBranch}}</div>
|
||||||
|
</div>
|
||||||
<div class="gt-hidden" data-pull-merge-style="manually-merged">
|
<div class="gt-hidden" data-pull-merge-style="manually-merged">
|
||||||
<div>git checkout {{.PullRequest.BaseBranch}}</div>
|
<div>git checkout {{.PullRequest.BaseBranch}}</div>
|
||||||
<div>git merge {{$localBranch}}</div>
|
<div>git merge {{$localBranch}}</div>
|
||||||
|
|
|
@ -42,6 +42,12 @@
|
||||||
<label>{{ctx.Locale.Tr "repo.pulls.squash_merge_pull_request"}}</label>
|
<label>{{ctx.Locale.Tr "repo.pulls.squash_merge_pull_request"}}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input name="pulls_allow_fast_forward_only" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowFastForwardOnly)}}checked{{end}}>
|
||||||
|
<label>{{ctx.Locale.Tr "repo.pulls.fast_forward_only_merge_pull_request"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input name="pulls_allow_manual_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowManualMerge)}}checked{{end}}>
|
<input name="pulls_allow_manual_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowManualMerge)}}checked{{end}}>
|
||||||
|
@ -59,6 +65,7 @@
|
||||||
<option value="rebase" {{if or (not $pullRequestEnabled) (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "rebase")}}selected{{end}}>{{ctx.Locale.Tr "repo.pulls.rebase_merge_pull_request"}}</option>
|
<option value="rebase" {{if or (not $pullRequestEnabled) (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "rebase")}}selected{{end}}>{{ctx.Locale.Tr "repo.pulls.rebase_merge_pull_request"}}</option>
|
||||||
<option value="rebase-merge" {{if or (not $pullRequestEnabled) (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "rebase-merge")}}selected{{end}}>{{ctx.Locale.Tr "repo.pulls.rebase_merge_commit_pull_request"}}</option>
|
<option value="rebase-merge" {{if or (not $pullRequestEnabled) (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "rebase-merge")}}selected{{end}}>{{ctx.Locale.Tr "repo.pulls.rebase_merge_commit_pull_request"}}</option>
|
||||||
<option value="squash" {{if or (not $pullRequestEnabled) (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "squash")}}selected{{end}}>{{ctx.Locale.Tr "repo.pulls.squash_merge_pull_request"}}</option>
|
<option value="squash" {{if or (not $pullRequestEnabled) (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "squash")}}selected{{end}}>{{ctx.Locale.Tr "repo.pulls.squash_merge_pull_request"}}</option>
|
||||||
|
<option value="fast-forward-only" {{if or (not $pullRequestEnabled) (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "fast-forward-only")}}selected{{end}}>{{ctx.Locale.Tr "repo.pulls.fast_forward_only_merge_pull_request"}}</option>
|
||||||
</select>{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
</select>{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||||
<div class="default text">
|
<div class="default text">
|
||||||
{{if (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "merge")}}
|
{{if (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "merge")}}
|
||||||
|
@ -73,12 +80,16 @@
|
||||||
{{if (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "squash")}}
|
{{if (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "squash")}}
|
||||||
{{ctx.Locale.Tr "repo.pulls.squash_merge_pull_request"}}
|
{{ctx.Locale.Tr "repo.pulls.squash_merge_pull_request"}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{if (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "fast-forward-only")}}
|
||||||
|
{{ctx.Locale.Tr "repo.pulls.fast_forward_only_merge_pull_request"}}
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<div class="item" data-value="merge">{{ctx.Locale.Tr "repo.pulls.merge_pull_request"}}</div>
|
<div class="item" data-value="merge">{{ctx.Locale.Tr "repo.pulls.merge_pull_request"}}</div>
|
||||||
<div class="item" data-value="rebase">{{ctx.Locale.Tr "repo.pulls.rebase_merge_pull_request"}}</div>
|
<div class="item" data-value="rebase">{{ctx.Locale.Tr "repo.pulls.rebase_merge_pull_request"}}</div>
|
||||||
<div class="item" data-value="rebase-merge">{{ctx.Locale.Tr "repo.pulls.rebase_merge_commit_pull_request"}}</div>
|
<div class="item" data-value="rebase-merge">{{ctx.Locale.Tr "repo.pulls.rebase_merge_commit_pull_request"}}</div>
|
||||||
<div class="item" data-value="squash">{{ctx.Locale.Tr "repo.pulls.squash_merge_pull_request"}}</div>
|
<div class="item" data-value="squash">{{ctx.Locale.Tr "repo.pulls.squash_merge_pull_request"}}</div>
|
||||||
|
<div class="item" data-value="fast-forward-only">{{ctx.Locale.Tr "repo.pulls.fast_forward_only_merge_pull_request"}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
12
templates/swagger/v1_json.tmpl
generated
12
templates/swagger/v1_json.tmpl
generated
|
@ -19930,6 +19930,11 @@
|
||||||
"description": "EditRepoOption options when editing a repository's properties",
|
"description": "EditRepoOption options when editing a repository's properties",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"allow_fast_forward_only_merge": {
|
||||||
|
"description": "either `true` to allow fast-forward-only merging pull requests, or `false` to prevent fast-forward-only merging.",
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "AllowFastForwardOnly"
|
||||||
|
},
|
||||||
"allow_manual_merge": {
|
"allow_manual_merge": {
|
||||||
"description": "either `true` to allow mark pr as merged manually, or `false` to prevent it.",
|
"description": "either `true` to allow mark pr as merged manually, or `false` to prevent it.",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
@ -19986,7 +19991,7 @@
|
||||||
"x-go-name": "DefaultDeleteBranchAfterMerge"
|
"x-go-name": "DefaultDeleteBranchAfterMerge"
|
||||||
},
|
},
|
||||||
"default_merge_style": {
|
"default_merge_style": {
|
||||||
"description": "set to a merge style to be used by this repository: \"merge\", \"rebase\", \"rebase-merge\", or \"squash\".",
|
"description": "set to a merge style to be used by this repository: \"merge\", \"rebase\", \"rebase-merge\", \"squash\", or \"fast-forward-only\".",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"x-go-name": "DefaultMergeStyle"
|
"x-go-name": "DefaultMergeStyle"
|
||||||
},
|
},
|
||||||
|
@ -21395,6 +21400,7 @@
|
||||||
"rebase",
|
"rebase",
|
||||||
"rebase-merge",
|
"rebase-merge",
|
||||||
"squash",
|
"squash",
|
||||||
|
"fast-forward-only",
|
||||||
"manually-merged"
|
"manually-merged"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -22795,6 +22801,10 @@
|
||||||
"description": "Repository represents a repository",
|
"description": "Repository represents a repository",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"allow_fast_forward_only_merge": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "AllowFastForwardOnly"
|
||||||
|
},
|
||||||
"allow_merge_commits": {
|
"allow_merge_commits": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"x-go-name": "AllowMerge"
|
"x-go-name": "AllowMerge"
|
||||||
|
|
|
@ -65,6 +65,7 @@ func getRepoEditOptionFromRepo(repo *repo_model.Repository) *api.EditRepoOption
|
||||||
allowRebase := false
|
allowRebase := false
|
||||||
allowRebaseMerge := false
|
allowRebaseMerge := false
|
||||||
allowSquash := false
|
allowSquash := false
|
||||||
|
allowFastForwardOnly := false
|
||||||
if unit, err := repo.GetUnit(db.DefaultContext, unit_model.TypePullRequests); err == nil {
|
if unit, err := repo.GetUnit(db.DefaultContext, unit_model.TypePullRequests); err == nil {
|
||||||
config := unit.PullRequestsConfig()
|
config := unit.PullRequestsConfig()
|
||||||
hasPullRequests = true
|
hasPullRequests = true
|
||||||
|
@ -73,6 +74,7 @@ func getRepoEditOptionFromRepo(repo *repo_model.Repository) *api.EditRepoOption
|
||||||
allowRebase = config.AllowRebase
|
allowRebase = config.AllowRebase
|
||||||
allowRebaseMerge = config.AllowRebaseMerge
|
allowRebaseMerge = config.AllowRebaseMerge
|
||||||
allowSquash = config.AllowSquash
|
allowSquash = config.AllowSquash
|
||||||
|
allowFastForwardOnly = config.AllowFastForwardOnly
|
||||||
}
|
}
|
||||||
archived := repo.IsArchived
|
archived := repo.IsArchived
|
||||||
return &api.EditRepoOption{
|
return &api.EditRepoOption{
|
||||||
|
@ -92,6 +94,7 @@ func getRepoEditOptionFromRepo(repo *repo_model.Repository) *api.EditRepoOption
|
||||||
AllowRebase: &allowRebase,
|
AllowRebase: &allowRebase,
|
||||||
AllowRebaseMerge: &allowRebaseMerge,
|
AllowRebaseMerge: &allowRebaseMerge,
|
||||||
AllowSquash: &allowSquash,
|
AllowSquash: &allowSquash,
|
||||||
|
AllowFastForwardOnly: &allowFastForwardOnly,
|
||||||
Archived: &archived,
|
Archived: &archived,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -364,6 +364,90 @@ func TestCantMergeUnrelated(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFastForwardOnlyMerge(t *testing.T) {
|
||||||
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
|
session := loginUser(t, "user1")
|
||||||
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
|
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "update", "README.md", "Hello, World 2\n")
|
||||||
|
|
||||||
|
// Use API to create a pr from update to master
|
||||||
|
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
||||||
|
req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", "user1", "repo1"), &api.CreatePullRequestOption{
|
||||||
|
Head: "update",
|
||||||
|
Base: "master",
|
||||||
|
Title: "create a pr that can be fast-forward-only merged",
|
||||||
|
}).AddTokenAuth(token)
|
||||||
|
session.MakeRequest(t, req, http.StatusCreated)
|
||||||
|
|
||||||
|
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{
|
||||||
|
Name: "user1",
|
||||||
|
})
|
||||||
|
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
|
||||||
|
OwnerID: user1.ID,
|
||||||
|
Name: "repo1",
|
||||||
|
})
|
||||||
|
|
||||||
|
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{
|
||||||
|
HeadRepoID: repo1.ID,
|
||||||
|
BaseRepoID: repo1.ID,
|
||||||
|
HeadBranch: "update",
|
||||||
|
BaseBranch: "master",
|
||||||
|
})
|
||||||
|
|
||||||
|
gitRepo, err := git.OpenRepository(git.DefaultContext, repo_model.RepoPath(user1.Name, repo1.Name))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleFastForwardOnly, "", "FAST-FORWARD-ONLY", false)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
gitRepo.Close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCantFastForwardOnlyMergeDiverging(t *testing.T) {
|
||||||
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
|
session := loginUser(t, "user1")
|
||||||
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
|
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "diverging", "README.md", "Hello, World diverged\n")
|
||||||
|
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World 2\n")
|
||||||
|
|
||||||
|
// Use API to create a pr from diverging to update
|
||||||
|
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
||||||
|
req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", "user1", "repo1"), &api.CreatePullRequestOption{
|
||||||
|
Head: "diverging",
|
||||||
|
Base: "master",
|
||||||
|
Title: "create a pr from a diverging branch",
|
||||||
|
}).AddTokenAuth(token)
|
||||||
|
session.MakeRequest(t, req, http.StatusCreated)
|
||||||
|
|
||||||
|
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{
|
||||||
|
Name: "user1",
|
||||||
|
})
|
||||||
|
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
|
||||||
|
OwnerID: user1.ID,
|
||||||
|
Name: "repo1",
|
||||||
|
})
|
||||||
|
|
||||||
|
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{
|
||||||
|
HeadRepoID: repo1.ID,
|
||||||
|
BaseRepoID: repo1.ID,
|
||||||
|
HeadBranch: "diverging",
|
||||||
|
BaseBranch: "master",
|
||||||
|
})
|
||||||
|
|
||||||
|
gitRepo, err := git.OpenRepository(git.DefaultContext, repo_model.RepoPath(user1.Name, repo1.Name))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleFastForwardOnly, "", "DIVERGING", false)
|
||||||
|
|
||||||
|
assert.Error(t, err, "Merge should return an error due to being for a diverging branch")
|
||||||
|
assert.True(t, models.IsErrMergeDivergingFastForwardOnly(err), "Merge error is not a diverging fast-forward-only error")
|
||||||
|
|
||||||
|
gitRepo.Close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestConflictChecking(t *testing.T) {
|
func TestConflictChecking(t *testing.T) {
|
||||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||||
|
|
Loading…
Reference in a new issue