Commit cc39c13a authored by Tiger Watson's avatar Tiger Watson

Merge branch 'squash-commit-message-template' into 'master'

Add squash commit message template

See merge request gitlab-org/gitlab!74340
parents 304b19a8 c8e78181
...@@ -41,7 +41,6 @@ export default { ...@@ -41,7 +41,6 @@ export default {
rows="7" rows="7"
@input="$emit('input', $event.target.value)" @input="$emit('input', $event.target.value)"
></textarea> ></textarea>
<slot name="text-muted"></slot>
</div> </div>
</li> </li>
</template> </template>
...@@ -181,9 +181,16 @@ export default { ...@@ -181,9 +181,16 @@ export default {
return this.mr.canRemoveSourceBranch; return this.mr.canRemoveSourceBranch;
}, },
commitTemplateHelpPage() { commitTemplateHelpPage() {
return helpPagePath('user/project/merge_requests/commit_templates.md', { return helpPagePath('user/project/merge_requests/commit_templates.md');
anchor: 'merge-commit-message-template', },
}); commitTemplateHintText() {
if (this.shouldShowSquashEdit && this.shouldShowMergeEdit) {
return this.$options.i18n.mergeAndSquashCommitTemplatesHintText;
}
if (this.shouldShowSquashEdit) {
return this.$options.i18n.squashCommitTemplateHintText;
}
return this.$options.i18n.mergeCommitTemplateHintText;
}, },
commits() { commits() {
if (this.glFeatures.mergeRequestWidgetGraphql) { if (this.glFeatures.mergeRequestWidgetGraphql) {
...@@ -509,6 +516,12 @@ export default { ...@@ -509,6 +516,12 @@ export default {
mergeCommitTemplateHintText: s__( mergeCommitTemplateHintText: s__(
'mrWidget|To change this default message, edit the template for merge commit messages. %{linkStart}Learn more.%{linkEnd}', 'mrWidget|To change this default message, edit the template for merge commit messages. %{linkStart}Learn more.%{linkEnd}',
), ),
squashCommitTemplateHintText: s__(
'mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}',
),
mergeAndSquashCommitTemplatesHintText: s__(
'mrWidget|To change these default messages, edit the templates for both the merge and squash commit messages. %{linkStart}Learn more.%{linkEnd}',
),
}, },
}; };
</script> </script>
...@@ -674,23 +687,22 @@ export default { ...@@ -674,23 +687,22 @@ export default {
:label="__('Merge commit message')" :label="__('Merge commit message')"
input-id="merge-message-edit" input-id="merge-message-edit"
class="gl-m-0! gl-p-0!" class="gl-m-0! gl-p-0!"
> />
<template #text-muted> <li class="gl-m-0! gl-p-0!">
<p class="form-text text-muted"> <p class="form-text text-muted">
<gl-sprintf :message="$options.i18n.mergeCommitTemplateHintText"> <gl-sprintf :message="commitTemplateHintText">
<template #link="{ content }"> <template #link="{ content }">
<gl-link <gl-link
:href="commitTemplateHelpPage" :href="commitTemplateHelpPage"
class="inline-link" class="inline-link"
target="_blank" target="_blank"
> >
{{ content }} {{ content }}
</gl-link> </gl-link>
</template> </template>
</gl-sprintf> </gl-sprintf>
</p> </p>
</template> </li>
</commit-edit>
</ul> </ul>
</div> </div>
<div <div
...@@ -792,19 +804,18 @@ export default { ...@@ -792,19 +804,18 @@ export default {
v-model="commitMessage" v-model="commitMessage"
:label="__('Merge commit message')" :label="__('Merge commit message')"
input-id="merge-message-edit" input-id="merge-message-edit"
> />
<template #text-muted> <li>
<p class="form-text text-muted"> <p class="form-text text-muted">
<gl-sprintf :message="$options.i18n.mergeCommitTemplateHintText"> <gl-sprintf :message="commitTemplateHintText">
<template #link="{ content }"> <template #link="{ content }">
<gl-link :href="commitTemplateHelpPage" class="inline-link" target="_blank"> <gl-link :href="commitTemplateHelpPage" class="inline-link" target="_blank">
{{ content }} {{ content }}
</gl-link> </gl-link>
</template> </template>
</gl-sprintf> </gl-sprintf>
</p> </p>
</template> </li>
</commit-edit>
</ul> </ul>
</commits-header> </commits-header>
</template> </template>
......
...@@ -452,6 +452,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -452,6 +452,7 @@ class ProjectsController < Projects::ApplicationController
:packages_enabled, :packages_enabled,
:service_desk_enabled, :service_desk_enabled,
:merge_commit_template, :merge_commit_template,
:squash_commit_template,
project_setting_attributes: project_setting_attributes project_setting_attributes: project_setting_attributes
] + [project_feature_attributes: project_feature_attributes] ] + [project_feature_attributes: project_feature_attributes]
end end
......
...@@ -386,6 +386,11 @@ module Types ...@@ -386,6 +386,11 @@ module Types
null: true, null: true,
description: 'Template used to create merge commit message in merge requests.' description: 'Template used to create merge commit message in merge requests.'
field :squash_commit_template,
GraphQL::Types::String,
null: true,
description: 'Template used to create squash commit message in merge requests.'
def label(title:) def label(title:)
BatchLoader::GraphQL.for(title).batch(key: project) do |titles, loader, args| BatchLoader::GraphQL.for(title).batch(key: project) do |titles, loader, args|
LabelsFinder LabelsFinder
......
...@@ -1317,7 +1317,7 @@ class MergeRequest < ApplicationRecord ...@@ -1317,7 +1317,7 @@ class MergeRequest < ApplicationRecord
def default_merge_commit_message(include_description: false) def default_merge_commit_message(include_description: false)
if self.target_project.merge_commit_template.present? && !include_description if self.target_project.merge_commit_template.present? && !include_description
return ::Gitlab::MergeRequests::MergeCommitMessage.new(merge_request: self).message return ::Gitlab::MergeRequests::CommitMessageGenerator.new(merge_request: self).merge_message
end end
closes_issues_references = visible_closing_issues_for.map do |issue| closes_issues_references = visible_closing_issues_for.map do |issue|
...@@ -1340,6 +1340,10 @@ class MergeRequest < ApplicationRecord ...@@ -1340,6 +1340,10 @@ class MergeRequest < ApplicationRecord
end end
def default_squash_commit_message def default_squash_commit_message
if self.target_project.squash_commit_template.present?
return ::Gitlab::MergeRequests::CommitMessageGenerator.new(merge_request: self).squash_message
end
title title
end end
......
...@@ -452,6 +452,7 @@ class Project < ApplicationRecord ...@@ -452,6 +452,7 @@ class Project < ApplicationRecord
to: :project_setting to: :project_setting
delegate :active?, to: :prometheus_integration, allow_nil: true, prefix: true delegate :active?, to: :prometheus_integration, allow_nil: true, prefix: true
delegate :merge_commit_template, :merge_commit_template=, to: :project_setting, allow_nil: true delegate :merge_commit_template, :merge_commit_template=, to: :project_setting, allow_nil: true
delegate :squash_commit_template, :squash_commit_template=, to: :project_setting, allow_nil: true
delegate :log_jira_dvcs_integration_usage, :jira_dvcs_server_last_sync_at, :jira_dvcs_cloud_last_sync_at, to: :feature_usage delegate :log_jira_dvcs_integration_usage, :jira_dvcs_server_last_sync_at, :jira_dvcs_cloud_last_sync_at, to: :feature_usage
......
...@@ -13,6 +13,7 @@ class ProjectSetting < ApplicationRecord ...@@ -13,6 +13,7 @@ class ProjectSetting < ApplicationRecord
self.primary_key = :project_id self.primary_key = :project_id
validates :merge_commit_template, length: { maximum: 500 } validates :merge_commit_template, length: { maximum: 500 }
validates :squash_commit_template, length: { maximum: 500 }
def squash_enabled_by_default? def squash_enabled_by_default?
%w[always default_on].include?(squash_option) %w[always default_on].include?(squash_option)
......
...@@ -12,6 +12,6 @@ ...@@ -12,6 +12,6 @@
%p.form-text.text-muted %p.form-text.text-muted
= s_('ProjectSettings|Maximum 500 characters.') = s_('ProjectSettings|Maximum 500 characters.')
= s_('ProjectSettings|Supported variables:') = s_('ProjectSettings|Supported variables:')
- Gitlab::MergeRequests::MergeCommitMessage::PLACEHOLDERS.keys.each do |placeholder| - Gitlab::MergeRequests::CommitMessageGenerator::PLACEHOLDERS.keys.each do |placeholder|
%code %code
= "%{#{placeholder}}".html_safe = "%{#{placeholder}}".html_safe
...@@ -12,5 +12,7 @@ ...@@ -12,5 +12,7 @@
= render 'projects/merge_request_merge_commit_template', project: @project, form: form = render 'projects/merge_request_merge_commit_template', project: @project, form: form
= render 'projects/merge_request_squash_commit_template', project: @project, form: form
- if @project.forked? - if @project.forked?
= render 'projects/merge_request_target_project_settings', project: @project, form: form = render 'projects/merge_request_target_project_settings', project: @project, form: form
- form = local_assigns.fetch(:form)
.form-group
%b= s_('ProjectSettings|Squash commit message template')
%p.text-secondary
- configure_the_squash_commit_message_help_link_url = help_page_path('user/project/merge_requests/commit_templates.md', anchor: 'squash-commit-message-template')
- configure_the_squash_commit_message_help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: configure_the_squash_commit_message_help_link_url }
= s_('ProjectSettings|The commit message used when squashing commits. %{link_start}Learn more about syntax and variables.%{link_end}').html_safe % { link_start: configure_the_squash_commit_message_help_link_start, link_end: '</a>'.html_safe }
.mb-2
= form.text_area :squash_commit_template, class: 'form-control gl-form-input', rows: 8, maxlength: 500, placeholder: '%{title}'
%p.form-text.text-muted
= s_('ProjectSettings|Maximum 500 characters.')
= s_('ProjectSettings|Supported variables:')
- Gitlab::MergeRequests::CommitMessageGenerator::PLACEHOLDERS.keys.each do |placeholder|
%code
= "%{#{placeholder}}".html_safe
# frozen_string_literal: true
class AddSquashCommitTemplateToProjectSettings < Gitlab::Database::Migration[1.0]
enable_lock_retries!
def change
add_column :project_settings, :squash_commit_template, :text # rubocop:disable Migration/AddLimitToTextColumns
end
end
# frozen_string_literal: true
class AddSquashCommitTemplateLimitToProjectSettings < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
add_text_limit :project_settings, :squash_commit_template, 500
end
def down
remove_text_limit :project_settings, :squash_commit_template
end
end
d78fe687517e14ff67dc76eff63391e33b73d29446d2a0445595175c7cd6806a
\ No newline at end of file
c8ed7f8c0f818156dba9c25be848da97d4eb6dbf0aa9c48f87e940f3ca0967d9
\ No newline at end of file
...@@ -18299,7 +18299,9 @@ CREATE TABLE project_settings ( ...@@ -18299,7 +18299,9 @@ CREATE TABLE project_settings (
warn_about_potentially_unwanted_characters boolean DEFAULT true NOT NULL, warn_about_potentially_unwanted_characters boolean DEFAULT true NOT NULL,
merge_commit_template text, merge_commit_template text,
has_shimo boolean DEFAULT false NOT NULL, has_shimo boolean DEFAULT false NOT NULL,
squash_commit_template text,
CONSTRAINT check_3a03e7557a CHECK ((char_length(previous_default_branch) <= 4096)), CONSTRAINT check_3a03e7557a CHECK ((char_length(previous_default_branch) <= 4096)),
CONSTRAINT check_b09644994b CHECK ((char_length(squash_commit_template) <= 500)),
CONSTRAINT check_bde223416c CHECK ((show_default_award_emojis IS NOT NULL)), CONSTRAINT check_bde223416c CHECK ((show_default_award_emojis IS NOT NULL)),
CONSTRAINT check_eaf7cfb6a7 CHECK ((char_length(merge_commit_template) <= 500)) CONSTRAINT check_eaf7cfb6a7 CHECK ((char_length(merge_commit_template) <= 500))
); );
...@@ -12867,6 +12867,7 @@ Represents vulnerability finding of a security report on the pipeline. ...@@ -12867,6 +12867,7 @@ Represents vulnerability finding of a security report on the pipeline.
| <a id="projectservicedeskenabled"></a>`serviceDeskEnabled` | [`Boolean`](#boolean) | Indicates if the project has service desk enabled. | | <a id="projectservicedeskenabled"></a>`serviceDeskEnabled` | [`Boolean`](#boolean) | Indicates if the project has service desk enabled. |
| <a id="projectsharedrunnersenabled"></a>`sharedRunnersEnabled` | [`Boolean`](#boolean) | Indicates if shared runners are enabled for the project. | | <a id="projectsharedrunnersenabled"></a>`sharedRunnersEnabled` | [`Boolean`](#boolean) | Indicates if shared runners are enabled for the project. |
| <a id="projectsnippetsenabled"></a>`snippetsEnabled` | [`Boolean`](#boolean) | Indicates if Snippets are enabled for the current user. | | <a id="projectsnippetsenabled"></a>`snippetsEnabled` | [`Boolean`](#boolean) | Indicates if Snippets are enabled for the current user. |
| <a id="projectsquashcommittemplate"></a>`squashCommitTemplate` | [`String`](#string) | Template used to create squash commit message in merge requests. |
| <a id="projectsquashreadonly"></a>`squashReadOnly` | [`Boolean!`](#boolean) | Indicates if `squashReadOnly` is enabled. | | <a id="projectsquashreadonly"></a>`squashReadOnly` | [`Boolean!`](#boolean) | Indicates if `squashReadOnly` is enabled. |
| <a id="projectsshurltorepo"></a>`sshUrlToRepo` | [`String`](#string) | URL to connect to the project via SSH. | | <a id="projectsshurltorepo"></a>`sshUrlToRepo` | [`String`](#string) | URL to connect to the project via SSH. |
| <a id="projectstarcount"></a>`starCount` | [`Int!`](#int) | Number of times the project has been starred. | | <a id="projectstarcount"></a>`starCount` | [`Int!`](#int) | Number of times the project has been starred. |
...@@ -183,6 +183,7 @@ When the user is authenticated and `simple` is not set this returns something li ...@@ -183,6 +183,7 @@ When the user is authenticated and `simple` is not set this returns something li
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_commit_template": null, "merge_commit_template": null,
"squash_commit_template": null,
"marked_for_deletion_at": "2020-04-03", // Deprecated and will be removed in API v5 in favor of marked_for_deletion_on "marked_for_deletion_at": "2020-04-03", // Deprecated and will be removed in API v5 in favor of marked_for_deletion_on
"marked_for_deletion_on": "2020-04-03", "marked_for_deletion_on": "2020-04-03",
"statistics": { "statistics": {
...@@ -300,6 +301,7 @@ When the user is authenticated and `simple` is not set this returns something li ...@@ -300,6 +301,7 @@ When the user is authenticated and `simple` is not set this returns something li
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_commit_template": null, "merge_commit_template": null,
"squash_commit_template": null,
"statistics": { "statistics": {
"commit_count": 12, "commit_count": 12,
"storage_size": 2066080, "storage_size": 2066080,
...@@ -470,6 +472,7 @@ GET /users/:user_id/projects ...@@ -470,6 +472,7 @@ GET /users/:user_id/projects
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_commit_template": null, "merge_commit_template": null,
"squash_commit_template": null,
"marked_for_deletion_at": "2020-04-03", // Deprecated and will be removed in API v5 in favor of marked_for_deletion_on "marked_for_deletion_at": "2020-04-03", // Deprecated and will be removed in API v5 in favor of marked_for_deletion_on
"marked_for_deletion_on": "2020-04-03", "marked_for_deletion_on": "2020-04-03",
"statistics": { "statistics": {
...@@ -587,6 +590,7 @@ GET /users/:user_id/projects ...@@ -587,6 +590,7 @@ GET /users/:user_id/projects
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_commit_template": null, "merge_commit_template": null,
"squash_commit_template": null,
"statistics": { "statistics": {
"commit_count": 12, "commit_count": 12,
"storage_size": 2066080, "storage_size": 2066080,
...@@ -714,6 +718,7 @@ Example response: ...@@ -714,6 +718,7 @@ Example response:
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_commit_template": null, "merge_commit_template": null,
"squash_commit_template": null,
"statistics": { "statistics": {
"commit_count": 37, "commit_count": 37,
"storage_size": 1038090, "storage_size": 1038090,
...@@ -826,6 +831,7 @@ Example response: ...@@ -826,6 +831,7 @@ Example response:
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_commit_template": null, "merge_commit_template": null,
"squash_commit_template": null,
"statistics": { "statistics": {
"commit_count": 12, "commit_count": 12,
"storage_size": 2066080, "storage_size": 2066080,
...@@ -994,6 +1000,7 @@ GET /projects/:id ...@@ -994,6 +1000,7 @@ GET /projects/:id
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_commit_template": null, "merge_commit_template": null,
"squash_commit_template": null,
"marked_for_deletion_at": "2020-04-03", // Deprecated and will be removed in API v5 in favor of marked_for_deletion_on "marked_for_deletion_at": "2020-04-03", // Deprecated and will be removed in API v5 in favor of marked_for_deletion_on
"marked_for_deletion_on": "2020-04-03", "marked_for_deletion_on": "2020-04-03",
"compliance_frameworks": [ "sox" ], "compliance_frameworks": [ "sox" ],
...@@ -1307,6 +1314,7 @@ POST /projects/user/:user_id ...@@ -1307,6 +1314,7 @@ POST /projects/user/:user_id
| `jobs_enabled` | boolean | **{dotted-circle}** No | _(Deprecated)_ Enable jobs for this project. Use `builds_access_level` instead. | | `jobs_enabled` | boolean | **{dotted-circle}** No | _(Deprecated)_ Enable jobs for this project. Use `builds_access_level` instead. |
| `lfs_enabled` | boolean | **{dotted-circle}** No | Enable LFS. | | `lfs_enabled` | boolean | **{dotted-circle}** No | Enable LFS. |
| `merge_commit_template` | string | **{dotted-circle}** No | [Template](../user/project/merge_requests/commit_templates.md) used to create merge commit message in merge requests. _([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/20263) in GitLab 14.5.)_ | | `merge_commit_template` | string | **{dotted-circle}** No | [Template](../user/project/merge_requests/commit_templates.md) used to create merge commit message in merge requests. _([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/20263) in GitLab 14.5.)_ |
| `squash_commit_template` | string | **{dotted-circle}** No | [Template](../user/project/merge_requests/commit_templates.md#squash-commit-message-template) used to create squash commit message in merge requests. _([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345275) in GitLab 14.6.)_ |
| `merge_method` | string | **{dotted-circle}** No | Set the [merge method](#project-merge-method) used. | | `merge_method` | string | **{dotted-circle}** No | Set the [merge method](#project-merge-method) used. |
| `merge_requests_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. | | `merge_requests_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `merge_requests_enabled` | boolean | **{dotted-circle}** No | _(Deprecated)_ Enable merge requests for this project. Use `merge_requests_access_level` instead. | | `merge_requests_enabled` | boolean | **{dotted-circle}** No | _(Deprecated)_ Enable merge requests for this project. Use `merge_requests_access_level` instead. |
......
...@@ -31,7 +31,7 @@ This commit message can be customized to follow any guidelines you might have. ...@@ -31,7 +31,7 @@ This commit message can be customized to follow any guidelines you might have.
To do so, expand the **Merge requests** tab within your project's **General** To do so, expand the **Merge requests** tab within your project's **General**
settings and change the **Merge commit message template** text: settings and change the **Merge commit message template** text:
![Custom commit message for applied suggestions](img/merge_commit_message_template_v14_5.png) ![Custom commit message for merge commit](img/merge_commit_message_template_v14_5.png)
You can use static text and following variables: You can use static text and following variables:
...@@ -49,3 +49,18 @@ Empty variables that are the only word in a line will be removed along with all ...@@ -49,3 +49,18 @@ Empty variables that are the only word in a line will be removed along with all
Merge commit template field has a limit of 500 characters. This limit only applies to the template Merge commit template field has a limit of 500 characters. This limit only applies to the template
itself. itself.
## Squash commit message template
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345275) in GitLab 14.6.
As a project maintainer, you're able to configure squash commit message template. It will be used during merge with
squash to create squash commit message. It uses the same syntax and variables as merge commit message template.
![Custom commit message for squash commit](img/squash_commit_message_template_v14_6.png)
Default squash commit message can be recreated using following template:
```plaintext
%{title}
```
...@@ -28,9 +28,9 @@ NOTE: ...@@ -28,9 +28,9 @@ NOTE:
The squashed commit in this example is followed by a merge commit, because the merge method for this repository uses a merge commit. You can disable merge commits in The squashed commit in this example is followed by a merge commit, because the merge method for this repository uses a merge commit. You can disable merge commits in
**Project Settings > General > Merge requests > Merge method > Fast-forward merge**. **Project Settings > General > Merge requests > Merge method > Fast-forward merge**.
The squashed commit's default commit message is taken from the merge request title. The squashed commit's default commit message is taken from the merge request title. It can be changed using [squash commit message template](commit_templates.md#squash-commit-message-template).
It can be customized before merging a merge request. It can also be customized before merging a merge request.
![A squash commit message editor](img/squash_mr_message.png) ![A squash commit message editor](img/squash_mr_message.png)
......
...@@ -315,7 +315,7 @@ Set up your project's merge request settings: ...@@ -315,7 +315,7 @@ Set up your project's merge request settings:
- Enable [require an associated issue from Jira](../../../integration/jira/issues.md#require-associated-jira-issue-for-merge-requests-to-be-merged). - Enable [require an associated issue from Jira](../../../integration/jira/issues.md#require-associated-jira-issue-for-merge-requests-to-be-merged).
- Enable [`delete source branch after merge` option by default](../merge_requests/getting_started.md#deleting-the-source-branch). - Enable [`delete source branch after merge` option by default](../merge_requests/getting_started.md#deleting-the-source-branch).
- Configure [suggested changes commit messages](../merge_requests/reviews/suggestions.md#configure-the-commit-message-for-applied-suggestions). - Configure [suggested changes commit messages](../merge_requests/reviews/suggestions.md#configure-the-commit-message-for-applied-suggestions).
- Configure [merge commit message template](../merge_requests/commit_templates.md). - Configure [merge and squash commit message templates](../merge_requests/commit_templates.md).
- Configure [the default target project](../merge_requests/creating_merge_requests.md#set-the-default-target-project) for merge requests coming from forks. - Configure [the default target project](../merge_requests/creating_merge_requests.md#set-the-default-target-project) for merge requests coming from forks.
### Service Desk ### Service Desk
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
= render_ce 'projects/merge_request_merge_commit_template', project: @project, form: form = render_ce 'projects/merge_request_merge_commit_template', project: @project, form: form
= render_ce 'projects/merge_request_squash_commit_template', project: @project, form: form
- if @project.forked? - if @project.forked?
= render_ce 'projects/merge_request_target_project_settings', project: @project, form: form = render_ce 'projects/merge_request_target_project_settings', project: @project, form: form
......
...@@ -115,6 +115,7 @@ module API ...@@ -115,6 +115,7 @@ module API
expose :squash_option expose :squash_option
expose :suggestion_commit_message expose :suggestion_commit_message
expose :merge_commit_template expose :merge_commit_template
expose :squash_commit_template
expose :statistics, using: 'API::Entities::ProjectStatistics', if: -> (project, options) { expose :statistics, using: 'API::Entities::ProjectStatistics', if: -> (project, options) {
options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project) options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project)
} }
......
...@@ -62,6 +62,7 @@ module API ...@@ -62,6 +62,7 @@ module API
optional :merge_method, type: String, values: %w(ff rebase_merge merge), desc: 'The merge method used when merging merge requests' optional :merge_method, type: String, values: %w(ff rebase_merge merge), desc: 'The merge method used when merging merge requests'
optional :suggestion_commit_message, type: String, desc: 'The commit message used to apply merge request suggestions' optional :suggestion_commit_message, type: String, desc: 'The commit message used to apply merge request suggestions'
optional :merge_commit_template, type: String, desc: 'Template used to create merge commit message' optional :merge_commit_template, type: String, desc: 'Template used to create merge commit message'
optional :squash_commit_template, type: String, desc: 'Template used to create squash commit message'
optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md" optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md"
optional :ci_default_git_depth, type: Integer, desc: 'Default number of revisions for shallow cloning' optional :ci_default_git_depth, type: Integer, desc: 'Default number of revisions for shallow cloning'
optional :auto_devops_enabled, type: Boolean, desc: 'Flag indication if Auto DevOps is enabled' optional :auto_devops_enabled, type: Boolean, desc: 'Flag indication if Auto DevOps is enabled'
...@@ -162,6 +163,7 @@ module API ...@@ -162,6 +163,7 @@ module API
:avatar, :avatar,
:suggestion_commit_message, :suggestion_commit_message,
:merge_commit_template, :merge_commit_template,
:squash_commit_template,
:repository_storage, :repository_storage,
:compliance_framework_setting, :compliance_framework_setting,
:packages_enabled, :packages_enabled,
......
# frozen_string_literal: true # frozen_string_literal: true
module Gitlab module Gitlab
module MergeRequests module MergeRequests
class MergeCommitMessage class CommitMessageGenerator
def initialize(merge_request:) def initialize(merge_request:)
@merge_request = merge_request @merge_request = merge_request
end end
def message def merge_message
return unless @merge_request.target_project.merge_commit_template.present? return unless @merge_request.target_project.merge_commit_template.present?
message = @merge_request.target_project.merge_commit_template replace_placeholders(@merge_request.target_project.merge_commit_template)
message = message.delete("\r") end
# Remove placeholders that correspond to empty values and are the last word in the line def squash_message
# along with all whitespace characters preceding them. return unless @merge_request.target_project.squash_commit_template.present?
# This allows us to recreate previous default merge commit message behaviour - we skipped new line character
# before empty description and before closed issues when none were present.
PLACEHOLDERS.each do |key, value|
unless value.call(merge_request).present?
message = message.gsub(BLANK_PLACEHOLDERS_REGEXES[key], '')
end
end
Gitlab::StringPlaceholderReplacer replace_placeholders(@merge_request.target_project.squash_commit_template)
.replace_string_placeholders(message, PLACEHOLDERS_REGEX) do |key|
PLACEHOLDERS[key].call(merge_request)
end
end end
private private
...@@ -55,6 +45,26 @@ module Gitlab ...@@ -55,6 +45,26 @@ module Gitlab
BLANK_PLACEHOLDERS_REGEXES = (PLACEHOLDERS.map do |key, value| BLANK_PLACEHOLDERS_REGEXES = (PLACEHOLDERS.map do |key, value|
[key, Regexp.new("[\n\r]+%{#{Regexp.escape(key)}}$")] [key, Regexp.new("[\n\r]+%{#{Regexp.escape(key)}}$")]
end).to_h.freeze end).to_h.freeze
def replace_placeholders(message)
# convert CRLF to LF
message = message.delete("\r")
# Remove placeholders that correspond to empty values and are the last word in the line
# along with all whitespace characters preceding them.
# This allows us to recreate previous default merge commit message behaviour - we skipped new line character
# before empty description and before closed issues when none were present.
PLACEHOLDERS.each do |key, value|
unless value.call(merge_request).present?
message = message.gsub(BLANK_PLACEHOLDERS_REGEXES[key], '')
end
end
Gitlab::StringPlaceholderReplacer
.replace_string_placeholders(message, PLACEHOLDERS_REGEX) do |key|
PLACEHOLDERS[key].call(merge_request)
end
end
end end
end end
end end
...@@ -27449,6 +27449,9 @@ msgstr "" ...@@ -27449,6 +27449,9 @@ msgstr ""
msgid "ProjectSettings|Snippets" msgid "ProjectSettings|Snippets"
msgstr "" msgstr ""
msgid "ProjectSettings|Squash commit message template"
msgstr ""
msgid "ProjectSettings|Squash commits when merging" msgid "ProjectSettings|Squash commits when merging"
msgstr "" msgstr ""
...@@ -27473,6 +27476,9 @@ msgstr "" ...@@ -27473,6 +27476,9 @@ msgstr ""
msgid "ProjectSettings|The commit message used when merging, if the merge method creates a merge commit. %{link_start}Learn more about syntax and variables.%{link_end}" msgid "ProjectSettings|The commit message used when merging, if the merge method creates a merge commit. %{link_start}Learn more about syntax and variables.%{link_end}"
msgstr "" msgstr ""
msgid "ProjectSettings|The commit message used when squashing commits. %{link_start}Learn more about syntax and variables.%{link_end}"
msgstr ""
msgid "ProjectSettings|The default target project for merge requests created in this fork project." msgid "ProjectSettings|The default target project for merge requests created in this fork project."
msgstr "" msgstr ""
...@@ -41711,9 +41717,15 @@ msgstr "" ...@@ -41711,9 +41717,15 @@ msgstr ""
msgid "mrWidget|To approve this merge request, please enter your password. This project requires all approvals to be authenticated." msgid "mrWidget|To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
msgstr "" msgstr ""
msgid "mrWidget|To change these default messages, edit the templates for both the merge and squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
msgid "mrWidget|To change this default message, edit the template for merge commit messages. %{linkStart}Learn more.%{linkEnd}" msgid "mrWidget|To change this default message, edit the template for merge commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr "" msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description." msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
msgstr "" msgstr ""
......
...@@ -7,19 +7,26 @@ RSpec.describe 'Merge request < User customizes merge commit message', :js do ...@@ -7,19 +7,26 @@ RSpec.describe 'Merge request < User customizes merge commit message', :js do
let(:user) { project.creator } let(:user) { project.creator }
let(:issue_1) { create(:issue, project: project)} let(:issue_1) { create(:issue, project: project)}
let(:issue_2) { create(:issue, project: project)} let(:issue_2) { create(:issue, project: project)}
let(:source_branch) { 'csv' }
let(:target_branch) { 'master' }
let(:squash) { false }
let(:merge_request) do let(:merge_request) do
create( create(
:merge_request, :merge_request,
:simple,
source_project: project, source_project: project,
description: "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}" target_project: project,
source_branch: source_branch,
target_branch: target_branch,
description: "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}",
squash: squash
) )
end end
let(:textbox) { page.find(:css, '#merge-message-edit', visible: false) } let(:merge_textbox) { page.find(:css, '#merge-message-edit', visible: false) }
let(:default_message) do let(:squash_textbox) { page.find(:css, '#squash-message-edit', visible: false) }
let(:default_merge_commit_message) do
[ [
"Merge branch 'feature' into 'master'", "Merge branch '#{source_branch}' into '#{target_branch}'",
merge_request.title, merge_request.title,
"Closes #{issue_1.to_reference} and #{issue_2.to_reference}", "Closes #{issue_1.to_reference} and #{issue_2.to_reference}",
"See merge request #{merge_request.to_reference(full: true)}" "See merge request #{merge_request.to_reference(full: true)}"
...@@ -35,8 +42,8 @@ RSpec.describe 'Merge request < User customizes merge commit message', :js do ...@@ -35,8 +42,8 @@ RSpec.describe 'Merge request < User customizes merge commit message', :js do
it 'has commit message without description' do it 'has commit message without description' do
expect(page).not_to have_selector('#merge-message-edit') expect(page).not_to have_selector('#merge-message-edit')
first('.js-mr-widget-commits-count').click first('.js-mr-widget-commits-count').click
expect(textbox).to be_visible expect(merge_textbox).to be_visible
expect(textbox.value).to eq(default_message) expect(merge_textbox.value).to eq(default_merge_commit_message)
end end
context 'when target project has merge commit template set' do context 'when target project has merge commit template set' do
...@@ -45,8 +52,34 @@ RSpec.describe 'Merge request < User customizes merge commit message', :js do ...@@ -45,8 +52,34 @@ RSpec.describe 'Merge request < User customizes merge commit message', :js do
it 'uses merge commit template' do it 'uses merge commit template' do
expect(page).not_to have_selector('#merge-message-edit') expect(page).not_to have_selector('#merge-message-edit')
first('.js-mr-widget-commits-count').click first('.js-mr-widget-commits-count').click
expect(textbox).to be_visible expect(merge_textbox).to be_visible
expect(textbox.value).to eq(merge_request.title) expect(merge_textbox.value).to eq(merge_request.title)
end
end
context 'when squash is performed' do
let(:squash) { true }
it 'has default message with merge request title' do
expect(page).not_to have_selector('#squash-message-edit')
first('.js-mr-widget-commits-count').click
expect(squash_textbox).to be_visible
expect(merge_textbox).to be_visible
expect(squash_textbox.value).to eq(merge_request.title)
expect(merge_textbox.value).to eq(default_merge_commit_message)
end
context 'when target project has squash commit template set' do
let(:project) { create(:project, :public, :repository, squash_commit_template: '%{description}') }
it 'uses squash commit template' do
expect(page).not_to have_selector('#squash-message-edit')
first('.js-mr-widget-commits-count').click
expect(squash_textbox).to be_visible
expect(merge_textbox).to be_visible
expect(squash_textbox.value).to eq(merge_request.description)
expect(merge_textbox.value).to eq(default_merge_commit_message)
end
end end
end end
end end
...@@ -3,7 +3,6 @@ import CommitEdit from '~/vue_merge_request_widget/components/states/commit_edit ...@@ -3,7 +3,6 @@ import CommitEdit from '~/vue_merge_request_widget/components/states/commit_edit
const testCommitMessage = 'Test commit message'; const testCommitMessage = 'Test commit message';
const testLabel = 'Test label'; const testLabel = 'Test label';
const testTextMuted = 'Test text muted';
const testInputId = 'test-input-id'; const testInputId = 'test-input-id';
describe('Commits edit component', () => { describe('Commits edit component', () => {
...@@ -64,7 +63,6 @@ describe('Commits edit component', () => { ...@@ -64,7 +63,6 @@ describe('Commits edit component', () => {
beforeEach(() => { beforeEach(() => {
createComponent({ createComponent({
header: `<div class="test-header">${testCommitMessage}</div>`, header: `<div class="test-header">${testCommitMessage}</div>`,
'text-muted': `<p class="test-text-muted">${testTextMuted}</p>`,
}); });
}); });
...@@ -74,12 +72,5 @@ describe('Commits edit component', () => { ...@@ -74,12 +72,5 @@ describe('Commits edit component', () => {
expect(headerSlotElement.exists()).toBe(true); expect(headerSlotElement.exists()).toBe(true);
expect(headerSlotElement.text()).toBe(testCommitMessage); expect(headerSlotElement.text()).toBe(testCommitMessage);
}); });
it('renders text-muted slot correctly', () => {
const textMutedElement = wrapper.find('.test-text-muted');
expect(textMutedElement.exists()).toBe(true);
expect(textMutedElement.text()).toBe(testTextMuted);
});
}); });
}); });
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import Vue from 'vue'; import Vue from 'vue';
import { GlSprintf } from '@gitlab/ui';
import simplePoll from '~/lib/utils/simple_poll'; import simplePoll from '~/lib/utils/simple_poll';
import CommitEdit from '~/vue_merge_request_widget/components/states/commit_edit.vue'; import CommitEdit from '~/vue_merge_request_widget/components/states/commit_edit.vue';
import CommitMessageDropdown from '~/vue_merge_request_widget/components/states/commit_message_dropdown.vue'; import CommitMessageDropdown from '~/vue_merge_request_widget/components/states/commit_message_dropdown.vue';
...@@ -487,6 +488,7 @@ describe('ReadyToMerge', () => { ...@@ -487,6 +488,7 @@ describe('ReadyToMerge', () => {
const findCommitEditElements = () => wrapper.findAll(CommitEdit); const findCommitEditElements = () => wrapper.findAll(CommitEdit);
const findCommitDropdownElement = () => wrapper.find(CommitMessageDropdown); const findCommitDropdownElement = () => wrapper.find(CommitMessageDropdown);
const findFirstCommitEditLabel = () => findCommitEditElements().at(0).props('label'); const findFirstCommitEditLabel = () => findCommitEditElements().at(0).props('label');
const findTipLink = () => wrapper.find(GlSprintf);
describe('squash checkbox', () => { describe('squash checkbox', () => {
it('should be rendered when squash before merge is enabled and there is more than 1 commit', () => { it('should be rendered when squash before merge is enabled and there is more than 1 commit', () => {
...@@ -751,6 +753,12 @@ describe('ReadyToMerge', () => { ...@@ -751,6 +753,12 @@ describe('ReadyToMerge', () => {
expect(findCommitDropdownElement().exists()).toBeTruthy(); expect(findCommitDropdownElement().exists()).toBeTruthy();
}); });
}); });
it('renders a tip including a link to docs on templates', () => {
createComponent();
expect(findTipLink().exists()).toBe(true);
});
}); });
describe('Merge request project settings', () => { describe('Merge request project settings', () => {
......
...@@ -34,7 +34,7 @@ RSpec.describe GitlabSchema.types['Project'] do ...@@ -34,7 +34,7 @@ RSpec.describe GitlabSchema.types['Project'] do
container_repositories container_repositories_count container_repositories container_repositories_count
pipeline_analytics squash_read_only sast_ci_configuration pipeline_analytics squash_read_only sast_ci_configuration
cluster_agent cluster_agents agent_configurations cluster_agent cluster_agents agent_configurations
ci_template timelogs merge_commit_template ci_template timelogs merge_commit_template squash_commit_template
] ]
expect(described_class).to include_graphql_fields(*expected_fields) expect(described_class).to include_graphql_fields(*expected_fields)
......
...@@ -563,6 +563,7 @@ Project: ...@@ -563,6 +563,7 @@ Project:
- autoclose_referenced_issues - autoclose_referenced_issues
- suggestion_commit_message - suggestion_commit_message
- merge_commit_template - merge_commit_template
- squash_commit_template
ProjectTracingSetting: ProjectTracingSetting:
- external_url - external_url
Author: Author:
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::MergeRequests::CommitMessageGenerator do
let(:merge_commit_template) { nil }
let(:squash_commit_template) { nil }
let(:project) do
create(
:project,
:public,
:repository,
merge_commit_template: merge_commit_template,
squash_commit_template: squash_commit_template
)
end
let(:user) { project.creator }
let(:merge_request_description) { "Merge Request Description\nNext line" }
let(:merge_request_title) { 'Bugfix' }
let(:merge_request) do
create(
:merge_request,
:simple,
source_project: project,
target_project: project,
author: user,
description: merge_request_description,
title: merge_request_title
)
end
subject { described_class.new(merge_request: merge_request) }
shared_examples_for 'commit message with template' do |message_template_name|
it 'returns nil when template is not set in target project' do
expect(result_message).to be_nil
end
context 'when project has custom commit template' do
let(message_template_name) { <<~MSG.rstrip }
%{title}
See merge request %{reference}
MSG
it 'uses custom template' do
expect(result_message).to eq <<~MSG.rstrip
Bugfix
See merge request #{merge_request.to_reference(full: true)}
MSG
end
end
context 'when project has commit template with closed issues' do
let(message_template_name) { <<~MSG.rstrip }
Merge branch '%{source_branch}' into '%{target_branch}'
%{title}
%{issues}
See merge request %{reference}
MSG
it 'omits issues and new lines when no issues are mentioned in description' do
expect(result_message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
See merge request #{merge_request.to_reference(full: true)}
MSG
end
context 'when MR closes issues' do
let(:issue_1) { create(:issue, project: project) }
let(:issue_2) { create(:issue, project: project) }
let(:merge_request_description) { "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}" }
it 'includes them and keeps new line characters' do
expect(result_message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
Closes #{issue_1.to_reference} and #{issue_2.to_reference}
See merge request #{merge_request.to_reference(full: true)}
MSG
end
end
end
context 'when project has commit template with description' do
let(message_template_name) { <<~MSG.rstrip }
Merge branch '%{source_branch}' into '%{target_branch}'
%{title}
%{description}
See merge request %{reference}
MSG
it 'uses template' do
expect(result_message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
Merge Request Description
Next line
See merge request #{merge_request.to_reference(full: true)}
MSG
end
context 'when description is empty string' do
let(:merge_request_description) { '' }
it 'skips description placeholder and removes new line characters before it' do
expect(result_message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
See merge request #{merge_request.to_reference(full: true)}
MSG
end
end
context 'when description is nil' do
let(:merge_request_description) { nil }
it 'skips description placeholder and removes new line characters before it' do
expect(result_message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
See merge request #{merge_request.to_reference(full: true)}
MSG
end
end
context 'when description is blank string' do
let(:merge_request_description) { "\n\r \n" }
it 'skips description placeholder and removes new line characters before it' do
expect(result_message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
See merge request #{merge_request.to_reference(full: true)}
MSG
end
end
end
context 'when custom commit template contains placeholder in the middle or beginning of the line' do
let(message_template_name) { <<~MSG.rstrip }
Merge branch '%{source_branch}' into '%{target_branch}'
%{description} %{title}
See merge request %{reference}
MSG
it 'uses custom template' do
expect(result_message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Merge Request Description
Next line Bugfix
See merge request #{merge_request.to_reference(full: true)}
MSG
end
context 'when description is empty string' do
let(:merge_request_description) { '' }
it 'does not remove new line characters before empty placeholder' do
expect(result_message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
See merge request #{merge_request.to_reference(full: true)}
MSG
end
end
end
context 'when project has template with CRLF newlines' do
let(message_template_name) do
"Merge branch '%{source_branch}' into '%{target_branch}'\r\n\r\n%{title}\r\n\r\n%{description}\r\n\r\nSee merge request %{reference}"
end
it 'converts it to LF newlines' do
expect(result_message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
Merge Request Description
Next line
See merge request #{merge_request.to_reference(full: true)}
MSG
end
context 'when description is empty string' do
let(:merge_request_description) { '' }
it 'skips description placeholder and removes new line characters before it' do
expect(result_message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
See merge request #{merge_request.to_reference(full: true)}
MSG
end
end
end
end
describe '#merge_message' do
let(:result_message) { subject.merge_message }
it_behaves_like 'commit message with template', :merge_commit_template
end
describe '#squash_message' do
let(:result_message) { subject.squash_message }
it_behaves_like 'commit message with template', :squash_commit_template
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::MergeRequests::MergeCommitMessage do
let(:merge_commit_template) { nil }
let(:project) { create(:project, :public, :repository, merge_commit_template: merge_commit_template) }
let(:user) { project.creator }
let(:merge_request_description) { "Merge Request Description\nNext line" }
let(:merge_request_title) { 'Bugfix' }
let(:merge_request) do
create(
:merge_request,
:simple,
source_project: project,
target_project: project,
author: user,
description: merge_request_description,
title: merge_request_title
)
end
subject { described_class.new(merge_request: merge_request) }
it 'returns nil when template is not set in target project' do
expect(subject.message).to be_nil
end
context 'when project has custom merge commit template' do
let(:merge_commit_template) { <<~MSG.rstrip }
%{title}
See merge request %{reference}
MSG
it 'uses custom template' do
expect(subject.message).to eq <<~MSG.rstrip
Bugfix
See merge request #{merge_request.to_reference(full: true)}
MSG
end
end
context 'when project has merge commit template with closed issues' do
let(:merge_commit_template) { <<~MSG.rstrip }
Merge branch '%{source_branch}' into '%{target_branch}'
%{title}
%{issues}
See merge request %{reference}
MSG
it 'omits issues and new lines when no issues are mentioned in description' do
expect(subject.message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
See merge request #{merge_request.to_reference(full: true)}
MSG
end
context 'when MR closes issues' do
let(:issue_1) { create(:issue, project: project) }
let(:issue_2) { create(:issue, project: project) }
let(:merge_request_description) { "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}" }
it 'includes them and keeps new line characters' do
expect(subject.message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
Closes #{issue_1.to_reference} and #{issue_2.to_reference}
See merge request #{merge_request.to_reference(full: true)}
MSG
end
end
end
context 'when project has merge commit template with description' do
let(:merge_commit_template) { <<~MSG.rstrip }
Merge branch '%{source_branch}' into '%{target_branch}'
%{title}
%{description}
See merge request %{reference}
MSG
it 'uses template' do
expect(subject.message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
Merge Request Description
Next line
See merge request #{merge_request.to_reference(full: true)}
MSG
end
context 'when description is empty string' do
let(:merge_request_description) { '' }
it 'skips description placeholder and removes new line characters before it' do
expect(subject.message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
See merge request #{merge_request.to_reference(full: true)}
MSG
end
end
context 'when description is nil' do
let(:merge_request_description) { nil }
it 'skips description placeholder and removes new line characters before it' do
expect(subject.message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
See merge request #{merge_request.to_reference(full: true)}
MSG
end
end
context 'when description is blank string' do
let(:merge_request_description) { "\n\r \n" }
it 'skips description placeholder and removes new line characters before it' do
expect(subject.message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
See merge request #{merge_request.to_reference(full: true)}
MSG
end
end
end
context 'when custom merge commit template contains placeholder in the middle or beginning of the line' do
let(:merge_commit_template) { <<~MSG.rstrip }
Merge branch '%{source_branch}' into '%{target_branch}'
%{description} %{title}
See merge request %{reference}
MSG
it 'uses custom template' do
expect(subject.message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Merge Request Description
Next line Bugfix
See merge request #{merge_request.to_reference(full: true)}
MSG
end
context 'when description is empty string' do
let(:merge_request_description) { '' }
it 'does not remove new line characters before empty placeholder' do
expect(subject.message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
See merge request #{merge_request.to_reference(full: true)}
MSG
end
end
end
context 'when project has template with CRLF newlines' do
let(:merge_commit_template) do
"Merge branch '%{source_branch}' into '%{target_branch}'\r\n\r\n%{title}\r\n\r\n%{description}\r\n\r\nSee merge request %{reference}"
end
it 'converts it to LF newlines' do
expect(subject.message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
Merge Request Description
Next line
See merge request #{merge_request.to_reference(full: true)}
MSG
end
context 'when description is empty string' do
let(:merge_request_description) { '' }
it 'skips description placeholder and removes new line characters before it' do
expect(subject.message).to eq <<~MSG.rstrip
Merge branch 'feature' into 'master'
Bugfix
See merge request #{merge_request.to_reference(full: true)}
MSG
end
end
end
end
...@@ -178,6 +178,13 @@ RSpec.describe MergeRequest, factory_default: :keep do ...@@ -178,6 +178,13 @@ RSpec.describe MergeRequest, factory_default: :keep do
it 'returns the merge request title' do it 'returns the merge request title' do
expect(subject.default_squash_commit_message).to eq(subject.title) expect(subject.default_squash_commit_message).to eq(subject.title)
end end
it 'uses template from target project' do
subject.target_project.squash_commit_template = 'Squashed branch %{source_branch} into %{target_branch}'
expect(subject.default_squash_commit_message)
.to eq('Squashed branch master into feature')
end
end end
describe 'modules' do describe 'modules' do
......
...@@ -92,6 +92,22 @@ RSpec.describe 'projects/edit' do ...@@ -92,6 +92,22 @@ RSpec.describe 'projects/edit' do
end end
end end
context 'squash template' do
it 'displays a placeholder if none is set' do
render
expect(rendered).to have_field('project[squash_commit_template]', placeholder: '%{title}')
end
it 'displays the user entered value' do
project.update!(squash_commit_template: '%{first_multiline_commit}')
render
expect(rendered).to have_field('project[squash_commit_template]', with: '%{first_multiline_commit}')
end
end
context 'forking' do context 'forking' do
before do before do
assign(:project, project) assign(:project, project)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment