Skip to content

Commit 83123b4

Browse files
chrisncalgernon
authored andcommitted
Add merge style fast-forward-only (go-gitea#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 go-gitea#24906
1 parent 05eaf1c commit 83123b4

File tree

25 files changed

+204
-11
lines changed

25 files changed

+204
-11
lines changed

custom/conf/app.example.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1061,7 +1061,7 @@ LEVEL = Info
10611061
;; List of keywords used in Pull Request comments to automatically reopen a related issue
10621062
;REOPEN_KEYWORDS = reopen,reopens,reopened
10631063
;;
1064-
;; Set default merge style for repository creating, valid options: merge, rebase, rebase-merge, squash
1064+
;; Set default merge style for repository creating, valid options: merge, rebase, rebase-merge, squash, fast-forward-only
10651065
;DEFAULT_MERGE_STYLE = merge
10661066
;;
10671067
;; In the default merge message for squash commits include at most this many commits

docs/content/administration/config-cheat-sheet.en-us.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ In addition, there is _`StaticRootPath`_ which can be set as a built-in at build
126126
keywords used in Pull Request comments to automatically close a related issue
127127
- `REOPEN_KEYWORDS`: **reopen**, **reopens**, **reopened**: List of keywords used in Pull Request comments to automatically reopen
128128
a related issue
129-
- `DEFAULT_MERGE_STYLE`: **merge**: Set default merge style for repository creating, valid options: `merge`, `rebase`, `rebase-merge`, `squash`
129+
- `DEFAULT_MERGE_STYLE`: **merge**: Set default merge style for repository creating, valid options: `merge`, `rebase`, `rebase-merge`, `squash`, `fast-forward-only`
130130
- `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
131131
- `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`.
132132
- `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

docs/content/administration/config-cheat-sheet.zh-cn.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ menu:
125125
- `CLOSE_KEYWORDS`: **close**, **closes**, **closed**, **fix**, **fixes**, **fixed**, **resolve**, **resolves**, **resolved**: 在拉取请求评论中用于自动关闭相关问题的关键词列表。
126126
- `REOPEN_KEYWORDS`: **reopen**, **reopens**, **reopened**: 在拉取请求评论中用于自动重新打开相关问题的
127127
关键词列表。
128-
- `DEFAULT_MERGE_STYLE`: **merge**: 设置创建仓库的默认合并方式,可选: `merge`, `rebase`, `rebase-merge`, `squash`
128+
- `DEFAULT_MERGE_STYLE`: **merge**: 设置创建仓库的默认合并方式,可选: `merge`, `rebase`, `rebase-merge`, `squash`, `fast-forward-only`
129129
- `DEFAULT_MERGE_MESSAGE_COMMITS_LIMIT`: **50**: 在默认合并消息中,对于`squash`提交,最多包括此数量的提交。设置为 -1 以包括所有提交。
130130
- `DEFAULT_MERGE_MESSAGE_SIZE`: **5120**: 在默认的合并消息中,对于`squash`提交,限制提交消息的大小。设置为 `-1`以取消限制。仅在`POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES``true`时使用。
131131
- `DEFAULT_MERGE_MESSAGE_ALL_AUTHORS`: **false**: 在默认合并消息中,对于`squash`提交,遍历所有提交以包括所有作者的`Co-authored-by`,否则仅使用限定列表中的作者。

models/error.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,23 @@ func (err ErrMergeUnrelatedHistories) Error() string {
493493
return fmt.Sprintf("Merge UnrelatedHistories Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
494494
}
495495

496+
// ErrMergeDivergingFastForwardOnly represents an error if a fast-forward-only merge fails because the branches diverge
497+
type ErrMergeDivergingFastForwardOnly struct {
498+
StdOut string
499+
StdErr string
500+
Err error
501+
}
502+
503+
// IsErrMergeDivergingFastForwardOnly checks if an error is a ErrMergeDivergingFastForwardOnly.
504+
func IsErrMergeDivergingFastForwardOnly(err error) bool {
505+
_, ok := err.(ErrMergeDivergingFastForwardOnly)
506+
return ok
507+
}
508+
509+
func (err ErrMergeDivergingFastForwardOnly) Error() string {
510+
return fmt.Sprintf("Merge DivergingFastForwardOnly Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
511+
}
512+
496513
// ErrRebaseConflicts represents an error if rebase fails with a conflict
497514
type ErrRebaseConflicts struct {
498515
Style repo_model.MergeStyle

models/repo/git.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ const (
2121
MergeStyleRebaseMerge MergeStyle = "rebase-merge"
2222
// MergeStyleSquash squash commits into single commit before merging
2323
MergeStyleSquash MergeStyle = "squash"
24+
// MergeStyleFastForwardOnly fast-forward merge if possible, otherwise fail
25+
MergeStyleFastForwardOnly MergeStyle = "fast-forward-only"
2426
// MergeStyleManuallyMerged pr has been merged manually, just mark it as merged directly
2527
MergeStyleManuallyMerged MergeStyle = "manually-merged"
2628
// MergeStyleRebaseUpdate not a merge style, used to update pull head by rebase

models/repo/repo_unit.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ type PullRequestsConfig struct {
153153
AllowRebase bool
154154
AllowRebaseMerge bool
155155
AllowSquash bool
156+
AllowFastForwardOnly bool
156157
AllowManualMerge bool
157158
AutodetectManualMerge bool
158159
AllowRebaseUpdate bool
@@ -179,6 +180,7 @@ func (cfg *PullRequestsConfig) IsMergeStyleAllowed(mergeStyle MergeStyle) bool {
179180
mergeStyle == MergeStyleRebase && cfg.AllowRebase ||
180181
mergeStyle == MergeStyleRebaseMerge && cfg.AllowRebaseMerge ||
181182
mergeStyle == MergeStyleSquash && cfg.AllowSquash ||
183+
mergeStyle == MergeStyleFastForwardOnly && cfg.AllowFastForwardOnly ||
182184
mergeStyle == MergeStyleManuallyMerged && cfg.AllowManualMerge
183185
}
184186

modules/git/error.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func (err ErrBranchNotExist) Unwrap() error {
9696
return util.ErrNotExist
9797
}
9898

99-
// ErrPushOutOfDate represents an error if merging fails due to unrelated histories
99+
// ErrPushOutOfDate represents an error if merging fails due to the base branch being updated
100100
type ErrPushOutOfDate struct {
101101
StdOut string
102102
StdErr string

modules/repository/create.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,11 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
8787
units = append(units, repo_model.RepoUnit{
8888
RepoID: repo.ID,
8989
Type: tp,
90-
Config: &repo_model.PullRequestsConfig{AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle), AllowRebaseUpdate: true},
90+
Config: &repo_model.PullRequestsConfig{
91+
AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, AllowFastForwardOnly: true,
92+
DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle),
93+
AllowRebaseUpdate: true,
94+
},
9195
})
9296
} else {
9397
units = append(units, repo_model.RepoUnit{

modules/structs/repo.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ type Repository struct {
9999
AllowRebase bool `json:"allow_rebase"`
100100
AllowRebaseMerge bool `json:"allow_rebase_explicit"`
101101
AllowSquash bool `json:"allow_squash_merge"`
102+
AllowFastForwardOnly bool `json:"allow_fast_forward_only_merge"`
102103
AllowRebaseUpdate bool `json:"allow_rebase_update"`
103104
DefaultDeleteBranchAfterMerge bool `json:"default_delete_branch_after_merge"`
104105
DefaultMergeStyle string `json:"default_merge_style"`
@@ -198,6 +199,8 @@ type EditRepoOption struct {
198199
AllowRebaseMerge *bool `json:"allow_rebase_explicit,omitempty"`
199200
// either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging.
200201
AllowSquash *bool `json:"allow_squash_merge,omitempty"`
202+
// either `true` to allow fast-forward-only merging pull requests, or `false` to prevent fast-forward-only merging.
203+
AllowFastForwardOnly *bool `json:"allow_fast_forward_only_merge,omitempty"`
201204
// either `true` to allow mark pr as merged manually, or `false` to prevent it.
202205
AllowManualMerge *bool `json:"allow_manual_merge,omitempty"`
203206
// 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 {
206209
AllowRebaseUpdate *bool `json:"allow_rebase_update,omitempty"`
207210
// set to `true` to delete pr branch after merge by default
208211
DefaultDeleteBranchAfterMerge *bool `json:"default_delete_branch_after_merge,omitempty"`
209-
// set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", or "squash".
212+
// set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", "squash", or "fast-forward-only".
210213
DefaultMergeStyle *string `json:"default_merge_style,omitempty"`
211214
// set to `true` to allow edits from maintainers by default
212215
DefaultAllowMaintainerEdit *bool `json:"default_allow_maintainer_edit,omitempty"`

options/locale/locale_en-US.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1816,6 +1816,7 @@ pulls.merge_pull_request = Create merge commit
18161816
pulls.rebase_merge_pull_request = Rebase then fast-forward
18171817
pulls.rebase_merge_commit_pull_request = Rebase then create merge commit
18181818
pulls.squash_merge_pull_request = Create squash commit
1819+
pulls.fast_forward_only_merge_pull_request = Fast-forward only
18191820
pulls.merge_manually = Manually merged
18201821
pulls.merge_commit_id = The merge commit ID
18211822
pulls.require_signed_wont_sign = The branch requires signed commits but this merge will not be signed

0 commit comments

Comments
 (0)