Commit 39dcf147 authored by Patrick Bajao's avatar Patrick Bajao

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

Add merge commit message template

See merge request gitlab-org/gitlab!64437
parents be3e9d52 18819eec
...@@ -41,7 +41,7 @@ export default { ...@@ -41,7 +41,7 @@ export default {
rows="7" rows="7"
@input="$emit('input', $event.target.value)" @input="$emit('input', $event.target.value)"
></textarea> ></textarea>
<slot name="checkbox"></slot> <slot name="text-muted"></slot>
</div> </div>
</li> </li>
</template> </template>
...@@ -18,9 +18,10 @@ import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests ...@@ -18,9 +18,10 @@ import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests
import createFlash from '~/flash'; import createFlash from '~/flash';
import { secondsToMilliseconds } from '~/lib/utils/datetime_utility'; import { secondsToMilliseconds } from '~/lib/utils/datetime_utility';
import simplePoll from '~/lib/utils/simple_poll'; import simplePoll from '~/lib/utils/simple_poll';
import { __ } from '~/locale'; import { __, s__ } from '~/locale';
import SmartInterval from '~/smart_interval'; import SmartInterval from '~/smart_interval';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { helpPagePath } from '~/helpers/help_page_helper';
import MergeRequest from '../../../merge_request'; import MergeRequest from '../../../merge_request';
import { import {
AUTO_MERGE_STRATEGIES, AUTO_MERGE_STRATEGIES,
...@@ -179,6 +180,11 @@ export default { ...@@ -179,6 +180,11 @@ export default {
return this.mr.canRemoveSourceBranch; return this.mr.canRemoveSourceBranch;
}, },
commitTemplateHelpPage() {
return helpPagePath('user/project/merge_requests/commit_templates.md', {
anchor: 'merge-commit-message-template',
});
},
commits() { commits() {
if (this.glFeatures.mergeRequestWidgetGraphql) { if (this.glFeatures.mergeRequestWidgetGraphql) {
return this.state.commitsWithoutMergeCommits.nodes; return this.state.commitsWithoutMergeCommits.nodes;
...@@ -347,15 +353,6 @@ export default { ...@@ -347,15 +353,6 @@ export default {
updateGraphqlState() { updateGraphqlState() {
return this.$apollo.queries.state.refetch(); return this.$apollo.queries.state.refetch();
}, },
updateMergeCommitMessage(includeDescription) {
const commitMessage = this.glFeatures.mergeRequestWidgetGraphql
? this.state.defaultMergeCommitMessage
: this.mr.commitMessage;
const commitMessageWithDescription = this.glFeatures.mergeRequestWidgetGraphql
? this.state.defaultMergeCommitMessageWithDescription
: this.mr.commitMessageWithDescription;
this.commitMessage = includeDescription ? commitMessageWithDescription : commitMessage;
},
handleMergeButtonClick(useAutoMerge, mergeImmediately = false, confirmationClicked = false) { handleMergeButtonClick(useAutoMerge, mergeImmediately = false, confirmationClicked = false) {
if (this.showFailedPipelineModal && !confirmationClicked) { if (this.showFailedPipelineModal && !confirmationClicked) {
this.isPipelineFailedModalVisible = true; this.isPipelineFailedModalVisible = true;
...@@ -508,6 +505,11 @@ export default { ...@@ -508,6 +505,11 @@ export default {
}); });
}, },
}, },
i18n: {
mergeCommitTemplateHintText: s__(
'mrWidget|To change this default message, edit the template for merge commit messages. %{linkStart}Learn more.%{linkEnd}',
),
},
}; };
</script> </script>
...@@ -679,15 +681,20 @@ export default { ...@@ -679,15 +681,20 @@ export default {
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 #checkbox> <template #text-muted>
<label> <p class="form-text text-muted">
<input <gl-sprintf :message="$options.i18n.mergeCommitTemplateHintText">
id="include-description" <template #link="{ content }">
type="checkbox" <gl-link
@change="updateMergeCommitMessage($event.target.checked)" :href="commitTemplateHelpPage"
/> class="inline-link"
{{ __('Include merge request description') }} target="_blank"
</label> >
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</p>
</template> </template>
</commit-edit> </commit-edit>
</ul> </ul>
...@@ -792,15 +799,16 @@ export default { ...@@ -792,15 +799,16 @@ export default {
:label="__('Merge commit message')" :label="__('Merge commit message')"
input-id="merge-message-edit" input-id="merge-message-edit"
> >
<template #checkbox> <template #text-muted>
<label> <p class="form-text text-muted">
<input <gl-sprintf :message="$options.i18n.mergeCommitTemplateHintText">
id="include-description" <template #link="{ content }">
type="checkbox" <gl-link :href="commitTemplateHelpPage" class="inline-link" target="_blank">
@change="updateMergeCommitMessage($event.target.checked)" {{ content }}
/> </gl-link>
{{ __('Include merge request description') }} </template>
</label> </gl-sprintf>
</p>
</template> </template>
</commit-edit> </commit-edit>
</ul> </ul>
......
...@@ -447,6 +447,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -447,6 +447,7 @@ class ProjectsController < Projects::ApplicationController
:suggestion_commit_message, :suggestion_commit_message,
:packages_enabled, :packages_enabled,
:service_desk_enabled, :service_desk_enabled,
:merge_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
......
...@@ -99,7 +99,8 @@ module Types ...@@ -99,7 +99,8 @@ module Types
field :default_merge_commit_message, GraphQL::Types::String, null: true, field :default_merge_commit_message, GraphQL::Types::String, null: true,
description: 'Default merge commit message of the merge request.' description: 'Default merge commit message of the merge request.'
field :default_merge_commit_message_with_description, GraphQL::Types::String, null: true, field :default_merge_commit_message_with_description, GraphQL::Types::String, null: true,
description: 'Default merge commit message of the merge request with description.' description: 'Default merge commit message of the merge request with description. Will have the same value as `defaultMergeCommitMessage` when project has `mergeCommitTemplate` set.',
deprecated: { reason: 'Define merge commit template in project and use `defaultMergeCommitMessage`', milestone: '14.5' }
field :default_squash_commit_message, GraphQL::Types::String, null: true, calls_gitaly: true, field :default_squash_commit_message, GraphQL::Types::String, null: true, calls_gitaly: true,
description: 'Default squash commit message of the merge request.' description: 'Default squash commit message of the merge request.'
field :merge_ongoing, GraphQL::Types::Boolean, method: :merge_ongoing?, null: false, field :merge_ongoing, GraphQL::Types::Boolean, method: :merge_ongoing?, null: false,
......
...@@ -381,6 +381,11 @@ module Types ...@@ -381,6 +381,11 @@ module Types
description: 'Cluster agents associated with the project.', description: 'Cluster agents associated with the project.',
resolver: ::Resolvers::Clusters::AgentsResolver resolver: ::Resolvers::Clusters::AgentsResolver
field :merge_commit_template,
GraphQL::Types::String,
null: true,
description: 'Template used to create merge 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
......
...@@ -1316,6 +1316,10 @@ class MergeRequest < ApplicationRecord ...@@ -1316,6 +1316,10 @@ class MergeRequest < ApplicationRecord
end end
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
return ::Gitlab::MergeRequests::MergeCommitMessage.new(merge_request: self).message
end
closes_issues_references = visible_closing_issues_for.map do |issue| closes_issues_references = visible_closing_issues_for.map do |issue|
issue.to_reference(target_project) issue.to_reference(target_project)
end end
......
...@@ -452,6 +452,7 @@ class Project < ApplicationRecord ...@@ -452,6 +452,7 @@ class Project < ApplicationRecord
:allow_merge_on_skipped_pipeline=, :has_confluence?, :allow_merge_on_skipped_pipeline=, :has_confluence?,
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 :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
......
...@@ -12,6 +12,8 @@ class ProjectSetting < ApplicationRecord ...@@ -12,6 +12,8 @@ class ProjectSetting < ApplicationRecord
self.primary_key = :project_id self.primary_key = :project_id
validates :merge_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)
end end
......
- form = local_assigns.fetch(:form)
.form-group
%b= s_('ProjectSettings|Merge commit message template')
%p.text-secondary
- configure_the_merge_commit_message_help_link_url = help_page_path('user/project/merge_requests/commit_templates.md', anchor: 'merge-commit-message-template')
- configure_the_merge_commit_message_help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: configure_the_merge_commit_message_help_link_url }
= s_('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}').html_safe % { link_start: configure_the_merge_commit_message_help_link_start, link_end: '</a>'.html_safe }
.mb-2
- default_merge_commit_template = "Merge branch '%{source_branch}' into '%{target_branch}'\n\n%{title}\n\n%{issues}\n\nSee merge request %{reference}"
= form.text_area :merge_commit_template, class: 'form-control gl-form-input', rows: 8, maxlength: 500, placeholder: default_merge_commit_template
%p.form-text.text-muted
= s_('ProjectSettings|Maximum 500 characters.')
= s_('ProjectSettings|Supported variables:')
- Gitlab::MergeRequests::MergeCommitMessage::PLACEHOLDERS.keys.each do |placeholder|
%code
= "%{#{placeholder}}".html_safe
...@@ -10,5 +10,7 @@ ...@@ -10,5 +10,7 @@
= render 'projects/merge_request_merge_suggestions_settings', project: @project, form: form = render 'projects/merge_request_merge_suggestions_settings', project: @project, form: form
= render 'projects/merge_request_merge_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
# frozen_string_literal: true
class AddMergeCommitTemplateToProjectSettings < Gitlab::Database::Migration[1.0]
enable_lock_retries!
def change
add_column :project_settings, :merge_commit_template, :text # rubocop:disable Migration/AddLimitToTextColumns
end
end
# frozen_string_literal: true
class AddMergeCommitTemplateLimitToProjectSettings < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
add_text_limit :project_settings, :merge_commit_template, 500
end
def down
remove_text_limit :project_settings, :merge_commit_template
end
end
687fa7d06a8d74b561d2b392e706fb209dbb1c0c8a483ad066820d29f7df059b
\ No newline at end of file
d3cafd6eb712ba3f11aa0e2bddc15bf312230e52d53ba8b7ae6c8d3cfd4aabcc
\ No newline at end of file
...@@ -18300,8 +18300,10 @@ CREATE TABLE project_settings ( ...@@ -18300,8 +18300,10 @@ CREATE TABLE project_settings (
mr_default_target_self boolean DEFAULT false NOT NULL, mr_default_target_self boolean DEFAULT false NOT NULL,
previous_default_branch text, previous_default_branch text,
warn_about_potentially_unwanted_characters boolean DEFAULT true NOT NULL, warn_about_potentially_unwanted_characters boolean DEFAULT true NOT NULL,
merge_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_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))
); );
CREATE TABLE project_statistics ( CREATE TABLE project_statistics (
...@@ -11489,7 +11489,7 @@ Maven metadata. ...@@ -11489,7 +11489,7 @@ Maven metadata.
| <a id="mergerequestconflicts"></a>`conflicts` | [`Boolean!`](#boolean) | Indicates if the merge request has conflicts. | | <a id="mergerequestconflicts"></a>`conflicts` | [`Boolean!`](#boolean) | Indicates if the merge request has conflicts. |
| <a id="mergerequestcreatedat"></a>`createdAt` | [`Time!`](#time) | Timestamp of when the merge request was created. | | <a id="mergerequestcreatedat"></a>`createdAt` | [`Time!`](#time) | Timestamp of when the merge request was created. |
| <a id="mergerequestdefaultmergecommitmessage"></a>`defaultMergeCommitMessage` | [`String`](#string) | Default merge commit message of the merge request. | | <a id="mergerequestdefaultmergecommitmessage"></a>`defaultMergeCommitMessage` | [`String`](#string) | Default merge commit message of the merge request. |
| <a id="mergerequestdefaultmergecommitmessagewithdescription"></a>`defaultMergeCommitMessageWithDescription` | [`String`](#string) | Default merge commit message of the merge request with description. | | <a id="mergerequestdefaultmergecommitmessagewithdescription"></a>`defaultMergeCommitMessageWithDescription` **{warning-solid}** | [`String`](#string) | **Deprecated** in 14.5. Define merge commit template in project and use `defaultMergeCommitMessage`. |
| <a id="mergerequestdefaultsquashcommitmessage"></a>`defaultSquashCommitMessage` | [`String`](#string) | Default squash commit message of the merge request. | | <a id="mergerequestdefaultsquashcommitmessage"></a>`defaultSquashCommitMessage` | [`String`](#string) | Default squash commit message of the merge request. |
| <a id="mergerequestdescription"></a>`description` | [`String`](#string) | Description of the merge request (Markdown rendered as HTML for caching). | | <a id="mergerequestdescription"></a>`description` | [`String`](#string) | Description of the merge request (Markdown rendered as HTML for caching). |
| <a id="mergerequestdescriptionhtml"></a>`descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. | | <a id="mergerequestdescriptionhtml"></a>`descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. |
...@@ -12832,6 +12832,7 @@ Represents vulnerability finding of a security report on the pipeline. ...@@ -12832,6 +12832,7 @@ Represents vulnerability finding of a security report on the pipeline.
| <a id="projectjobsenabled"></a>`jobsEnabled` | [`Boolean`](#boolean) | Indicates if CI/CD pipeline jobs are enabled for the current user. | | <a id="projectjobsenabled"></a>`jobsEnabled` | [`Boolean`](#boolean) | Indicates if CI/CD pipeline jobs are enabled for the current user. |
| <a id="projectlastactivityat"></a>`lastActivityAt` | [`Time`](#time) | Timestamp of the project last activity. | | <a id="projectlastactivityat"></a>`lastActivityAt` | [`Time`](#time) | Timestamp of the project last activity. |
| <a id="projectlfsenabled"></a>`lfsEnabled` | [`Boolean`](#boolean) | Indicates if the project has Large File Storage (LFS) enabled. | | <a id="projectlfsenabled"></a>`lfsEnabled` | [`Boolean`](#boolean) | Indicates if the project has Large File Storage (LFS) enabled. |
| <a id="projectmergecommittemplate"></a>`mergeCommitTemplate` | [`String`](#string) | Template used to create merge commit message in merge requests. |
| <a id="projectmergerequestsenabled"></a>`mergeRequestsEnabled` | [`Boolean`](#boolean) | Indicates if Merge Requests are enabled for the current user. | | <a id="projectmergerequestsenabled"></a>`mergeRequestsEnabled` | [`Boolean`](#boolean) | Indicates if Merge Requests are enabled for the current user. |
| <a id="projectmergerequestsffonlyenabled"></a>`mergeRequestsFfOnlyEnabled` | [`Boolean`](#boolean) | Indicates if no merge commits should be created and all merges should instead be fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded. | | <a id="projectmergerequestsffonlyenabled"></a>`mergeRequestsFfOnlyEnabled` | [`Boolean`](#boolean) | Indicates if no merge commits should be created and all merges should instead be fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded. |
| <a id="projectname"></a>`name` | [`String!`](#string) | Name of the project (without namespace). | | <a id="projectname"></a>`name` | [`String!`](#string) | Name of the project (without namespace). |
...@@ -182,6 +182,7 @@ When the user is authenticated and `simple` is not set this returns something li ...@@ -182,6 +182,7 @@ When the user is authenticated and `simple` is not set this returns something li
"squash_option": "default_on", "squash_option": "default_on",
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_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": {
...@@ -298,6 +299,7 @@ When the user is authenticated and `simple` is not set this returns something li ...@@ -298,6 +299,7 @@ When the user is authenticated and `simple` is not set this returns something li
"service_desk_address": null, "service_desk_address": null,
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_commit_template": null,
"statistics": { "statistics": {
"commit_count": 12, "commit_count": 12,
"storage_size": 2066080, "storage_size": 2066080,
...@@ -464,6 +466,7 @@ GET /users/:user_id/projects ...@@ -464,6 +466,7 @@ GET /users/:user_id/projects
"squash_option": "default_on", "squash_option": "default_on",
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_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": {
...@@ -580,6 +583,7 @@ GET /users/:user_id/projects ...@@ -580,6 +583,7 @@ GET /users/:user_id/projects
"service_desk_address": null, "service_desk_address": null,
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_commit_template": null,
"statistics": { "statistics": {
"commit_count": 12, "commit_count": 12,
"storage_size": 2066080, "storage_size": 2066080,
...@@ -706,6 +710,7 @@ Example response: ...@@ -706,6 +710,7 @@ Example response:
"squash_option": "default_on", "squash_option": "default_on",
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_commit_template": null,
"statistics": { "statistics": {
"commit_count": 37, "commit_count": 37,
"storage_size": 1038090, "storage_size": 1038090,
...@@ -817,6 +822,7 @@ Example response: ...@@ -817,6 +822,7 @@ Example response:
"service_desk_address": null, "service_desk_address": null,
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_commit_template": null,
"statistics": { "statistics": {
"commit_count": 12, "commit_count": 12,
"storage_size": 2066080, "storage_size": 2066080,
...@@ -984,6 +990,7 @@ GET /projects/:id ...@@ -984,6 +990,7 @@ GET /projects/:id
"service_desk_address": null, "service_desk_address": null,
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_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" ],
...@@ -1296,6 +1303,7 @@ POST /projects/user/:user_id ...@@ -1296,6 +1303,7 @@ POST /projects/user/:user_id
| `issues_enabled` | boolean | **{dotted-circle}** No | _(Deprecated)_ Enable issues for this project. Use `issues_access_level` instead. | | `issues_enabled` | boolean | **{dotted-circle}** No | _(Deprecated)_ Enable issues for this project. Use `issues_access_level` instead. |
| `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_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. |
...@@ -1373,6 +1381,7 @@ PUT /projects/:id ...@@ -1373,6 +1381,7 @@ PUT /projects/:id
| `issues_enabled` | boolean | **{dotted-circle}** No | _(Deprecated)_ Enable issues for this project. Use `issues_access_level` instead. | | `issues_enabled` | boolean | **{dotted-circle}** No | _(Deprecated)_ Enable issues for this project. Use `issues_access_level` instead. |
| `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_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. |
...@@ -1531,6 +1540,7 @@ Example responses: ...@@ -1531,6 +1540,7 @@ Example responses:
"squash_option": "default_on", "squash_option": "default_on",
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_commit_template": null,
"container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site", "container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site",
"_links": { "_links": {
"self": "http://example.com/api/v4/projects", "self": "http://example.com/api/v4/projects",
...@@ -1632,6 +1642,7 @@ Example response: ...@@ -1632,6 +1642,7 @@ Example response:
"squash_option": "default_on", "squash_option": "default_on",
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_commit_template": null,
"container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site", "container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site",
"_links": { "_links": {
"self": "http://example.com/api/v4/projects", "self": "http://example.com/api/v4/projects",
...@@ -1731,6 +1742,7 @@ Example response: ...@@ -1731,6 +1742,7 @@ Example response:
"squash_option": "default_on", "squash_option": "default_on",
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_commit_template": null,
"container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site", "container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site",
"_links": { "_links": {
"self": "http://example.com/api/v4/projects", "self": "http://example.com/api/v4/projects",
...@@ -1924,6 +1936,7 @@ Example response: ...@@ -1924,6 +1936,7 @@ Example response:
"squash_option": "default_on", "squash_option": "default_on",
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_commit_template": null,
"container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site", "container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site",
"_links": { "_links": {
"self": "http://example.com/api/v4/projects", "self": "http://example.com/api/v4/projects",
...@@ -2044,6 +2057,7 @@ Example response: ...@@ -2044,6 +2057,7 @@ Example response:
"squash_option": "default_on", "squash_option": "default_on",
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_commit_template": null,
"container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site", "container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site",
"_links": { "_links": {
"self": "http://example.com/api/v4/projects", "self": "http://example.com/api/v4/projects",
...@@ -2670,6 +2684,7 @@ Example response: ...@@ -2670,6 +2684,7 @@ Example response:
"merge_method": "merge", "merge_method": "merge",
"squash_option": "default_on", "squash_option": "default_on",
"suggestion_commit_message": null, "suggestion_commit_message": null,
"merge_commit_template": null,
"auto_devops_enabled": true, "auto_devops_enabled": true,
"auto_devops_deploy_strategy": "continuous", "auto_devops_deploy_strategy": "continuous",
"autoclose_referenced_issues": true, "autoclose_referenced_issues": true,
......
---
stage: Create
group: Code Review
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
type: reference, howto
---
# Commit message templates **(FREE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/20263) in GitLab 14.5.
## Merge commit message template
As a project maintainer, you're able to configure merge commit message template. It will be used during merge to
create commit message. Template uses similar syntax to
[review suggestions](reviews/suggestions.md#configure-the-commit-message-for-applied-suggestions).
Default merge commit message can be recreated using following template:
```plaintext
Merge branch '%{source_branch}' into '%{target_branch}'
%{title}
%{issues}
See merge request %{reference}
```
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**
settings and change the **Merge commit message template** text:
![Custom commit message for applied suggestions](img/merge_commit_message_template_v14_5.png)
You can use static text and following variables:
| Variable | Description | Output example |
|--------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------|
| `%{source_branch}` | The name of the branch that is being merged. | `my-feature-branch` |
| `%{target_branch}` | The name of the branch that the changes are applied to. | `master` |
| `%{title}` | Title of the merge request. | Fix stuff |
| `%{issues}` | String with phrase "Closes <issue numbers>" with all issues mentioned in the MR description matching [issue closing patterns](../issues/managing_issues.md#closing-issues-automatically). It will be empty when no issues were mentioned. | `Closes #465, #190 and #400` |
| `%{description}` | Description of the merge request. | Merge request description.<br>Can be multiline. |
| `%{reference}` | Reference to the merge request. | group-name/project-name!72359 |
NOTE:
Empty variables that are the only word in a line will be removed along with all newline characters preceding it.
Merge commit template field has a limit of 500 characters. This limit only applies to the template
itself.
...@@ -310,6 +310,7 @@ Set up your project's merge request settings: ...@@ -310,6 +310,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 [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
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
= render 'projects/merge_request_merge_suggestions_settings', project: @project, form: form = render 'projects/merge_request_merge_suggestions_settings', project: @project, form: form
= render_ce 'projects/merge_request_merge_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
......
...@@ -114,6 +114,7 @@ module API ...@@ -114,6 +114,7 @@ module API
expose :merge_method expose :merge_method
expose :squash_option expose :squash_option
expose :suggestion_commit_message expose :suggestion_commit_message
expose :merge_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)
} }
......
...@@ -61,6 +61,7 @@ module API ...@@ -61,6 +61,7 @@ module API
optional :printing_merge_request_link_enabled, type: Boolean, desc: 'Show link to create/view merge request when pushing from the command line' optional :printing_merge_request_link_enabled, type: Boolean, desc: 'Show link to create/view merge request when pushing from the command line'
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 :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'
...@@ -160,6 +161,7 @@ module API ...@@ -160,6 +161,7 @@ module API
:wiki_access_level, :wiki_access_level,
:avatar, :avatar,
:suggestion_commit_message, :suggestion_commit_message,
:merge_commit_template,
:repository_storage, :repository_storage,
:compliance_framework_setting, :compliance_framework_setting,
:packages_enabled, :packages_enabled,
......
# frozen_string_literal: true
module Gitlab
module MergeRequests
class MergeCommitMessage
def initialize(merge_request:)
@merge_request = merge_request
end
def message
return unless @merge_request.target_project.merge_commit_template.present?
message = @merge_request.target_project.merge_commit_template
# 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
private
attr_reader :merge_request
PLACEHOLDERS = {
'source_branch' => ->(merge_request) { merge_request.source_branch.to_s },
'target_branch' => ->(merge_request) { merge_request.target_branch.to_s },
'title' => ->(merge_request) { merge_request.title },
'issues' => ->(merge_request) do
return "" if merge_request.visible_closing_issues_for.blank?
closes_issues_references = merge_request.visible_closing_issues_for.map do |issue|
issue.to_reference(merge_request.target_project)
end
"Closes #{closes_issues_references.to_sentence}"
end,
'description' => ->(merge_request) { merge_request.description.presence || '' },
'reference' => ->(merge_request) { merge_request.to_reference(full: true) }
}.freeze
PLACEHOLDERS_REGEX = Regexp.union(PLACEHOLDERS.keys.map do |key|
Regexp.new(Regexp.escape(key))
end).freeze
BLANK_PLACEHOLDERS_REGEXES = (PLACEHOLDERS.map do |key, value|
[key, Regexp.new("[\n\r]+%{#{Regexp.escape(key)}}$")]
end).to_h.freeze
end
end
end
...@@ -18266,9 +18266,6 @@ msgstr "" ...@@ -18266,9 +18266,6 @@ msgstr ""
msgid "Include description in commit message" msgid "Include description in commit message"
msgstr "" msgstr ""
msgid "Include merge request description"
msgstr ""
msgid "Include new features from all tiers." msgid "Include new features from all tiers."
msgstr "" msgstr ""
...@@ -27156,12 +27153,18 @@ msgstr "" ...@@ -27156,12 +27153,18 @@ msgstr ""
msgid "ProjectSettings|Manages large files such as audio, video, and graphics files." msgid "ProjectSettings|Manages large files such as audio, video, and graphics files."
msgstr "" msgstr ""
msgid "ProjectSettings|Maximum 500 characters."
msgstr ""
msgid "ProjectSettings|Merge checks" msgid "ProjectSettings|Merge checks"
msgstr "" msgstr ""
msgid "ProjectSettings|Merge commit" msgid "ProjectSettings|Merge commit"
msgstr "" msgstr ""
msgid "ProjectSettings|Merge commit message template"
msgstr ""
msgid "ProjectSettings|Merge commit with semi-linear history" msgid "ProjectSettings|Merge commit with semi-linear history"
msgstr "" msgstr ""
...@@ -27279,6 +27282,9 @@ msgstr "" ...@@ -27279,6 +27282,9 @@ msgstr ""
msgid "ProjectSettings|The commit message used when applying merge request suggestions. %{link_start}Learn more about suggestions.%{link_end}" msgid "ProjectSettings|The commit message used when applying merge request suggestions. %{link_start}Learn more about suggestions.%{link_end}"
msgstr "" 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}"
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 ""
...@@ -41409,6 +41415,9 @@ msgstr "" ...@@ -41409,6 +41415,9 @@ 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 this default message, edit the template for merge 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 ""
......
...@@ -26,33 +26,27 @@ RSpec.describe 'Merge request < User customizes merge commit message', :js do ...@@ -26,33 +26,27 @@ RSpec.describe 'Merge request < User customizes merge commit message', :js do
].join("\n\n") ].join("\n\n")
end end
let(:message_with_description) do
[
"Merge branch 'feature' into 'master'",
merge_request.title,
merge_request.description,
"See merge request #{merge_request.to_reference(full: true)}"
].join("\n\n")
end
before do before do
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
visit project_merge_request_path(project, merge_request) visit project_merge_request_path(project, merge_request)
end end
it 'toggles commit message between message with description and 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(textbox).to be_visible
expect(textbox.value).to eq(default_message) expect(textbox.value).to eq(default_message)
end
check('Include merge request description') context 'when target project has merge commit template set' do
let(:project) { create(:project, :public, :repository, merge_commit_template: '%{title}') }
expect(textbox.value).to eq(message_with_description)
uncheck('Include merge request description')
expect(textbox.value).to eq(default_message) it 'uses merge commit template' do
expect(page).not_to have_selector('#merge-message-edit')
first('.js-mr-widget-commits-count').click
expect(textbox).to be_visible
expect(textbox.value).to eq(merge_request.title)
end
end end
end end
...@@ -3,6 +3,7 @@ import CommitEdit from '~/vue_merge_request_widget/components/states/commit_edit ...@@ -3,6 +3,7 @@ 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', () => {
...@@ -63,7 +64,7 @@ describe('Commits edit component', () => { ...@@ -63,7 +64,7 @@ describe('Commits edit component', () => {
beforeEach(() => { beforeEach(() => {
createComponent({ createComponent({
header: `<div class="test-header">${testCommitMessage}</div>`, header: `<div class="test-header">${testCommitMessage}</div>`,
checkbox: `<label class="test-checkbox">${testLabel}</label >`, 'text-muted': `<p class="test-text-muted">${testTextMuted}</p>`,
}); });
}); });
...@@ -74,11 +75,11 @@ describe('Commits edit component', () => { ...@@ -74,11 +75,11 @@ describe('Commits edit component', () => {
expect(headerSlotElement.text()).toBe(testCommitMessage); expect(headerSlotElement.text()).toBe(testCommitMessage);
}); });
it('renders checkbox slot correctly', () => { it('renders text-muted slot correctly', () => {
const checkboxSlotElement = wrapper.find('.test-checkbox'); const textMutedElement = wrapper.find('.test-text-muted');
expect(checkboxSlotElement.exists()).toBe(true); expect(textMutedElement.exists()).toBe(true);
expect(checkboxSlotElement.text()).toBe(testLabel); expect(textMutedElement.text()).toBe(testTextMuted);
}); });
}); });
}); });
...@@ -269,19 +269,6 @@ describe('ReadyToMerge', () => { ...@@ -269,19 +269,6 @@ describe('ReadyToMerge', () => {
}); });
describe('methods', () => { describe('methods', () => {
describe('updateMergeCommitMessage', () => {
it('should revert flag and change commitMessage', () => {
createComponent();
wrapper.vm.updateMergeCommitMessage(true);
expect(wrapper.vm.commitMessage).toEqual(commitMessageWithDescription);
wrapper.vm.updateMergeCommitMessage(false);
expect(wrapper.vm.commitMessage).toEqual(commitMessage);
});
});
describe('handleMergeButtonClick', () => { describe('handleMergeButtonClick', () => {
const returnPromise = (status) => const returnPromise = (status) =>
new Promise((resolve) => { new Promise((resolve) => {
......
...@@ -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 ci_template timelogs merge_commit_template
] ]
expect(described_class).to include_graphql_fields(*expected_fields) expect(described_class).to include_graphql_fields(*expected_fields)
......
...@@ -561,6 +561,7 @@ Project: ...@@ -561,6 +561,7 @@ Project:
- require_password_to_approve - require_password_to_approve
- autoclose_referenced_issues - autoclose_referenced_issues
- suggestion_commit_message - suggestion_commit_message
- merge_commit_template
ProjectTracingSetting: ProjectTracingSetting:
- external_url - external_url
Author: Author:
......
# 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
end
...@@ -1638,6 +1638,22 @@ RSpec.describe MergeRequest, factory_default: :keep do ...@@ -1638,6 +1638,22 @@ RSpec.describe MergeRequest, factory_default: :keep do
expect(request.default_merge_commit_message) expect(request.default_merge_commit_message)
.not_to match("By removing all code\n\n") .not_to match("By removing all code\n\n")
end end
it 'uses template from target project' do
request = build(:merge_request, title: 'Fix everything')
subject.target_project.merge_commit_template = '%{title}'
expect(request.default_merge_commit_message)
.to eq('Fix everything')
end
it 'ignores template when include_description is true' do
request = build(:merge_request, title: 'Fix everything')
subject.target_project.merge_commit_template = '%{title}'
expect(request.default_merge_commit_message(include_description: true))
.to match("See merge request #{request.to_reference(full: true)}")
end
end end
describe "#auto_merge_strategy" do describe "#auto_merge_strategy" do
......
...@@ -57,6 +57,41 @@ RSpec.describe 'projects/edit' do ...@@ -57,6 +57,41 @@ RSpec.describe 'projects/edit' do
end end
end end
context 'merge commit template' do
it 'displays all possible variables' do
render
expect(rendered).to have_content('%{source_branch}')
expect(rendered).to have_content('%{target_branch}')
expect(rendered).to have_content('%{title}')
expect(rendered).to have_content('%{issues}')
expect(rendered).to have_content('%{description}')
expect(rendered).to have_content('%{reference}')
end
it 'displays a placeholder if none is set' do
render
expect(rendered).to have_field('project[merge_commit_template]', placeholder: <<~MSG.rstrip)
Merge branch '%{source_branch}' into '%{target_branch}'
%{title}
%{issues}
See merge request %{reference}
MSG
end
it 'displays the user entered value' do
project.update!(merge_commit_template: '%{title}')
render
expect(rendered).to have_field('project[merge_commit_template]', with: '%{title}')
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