1# frozen_string_literal: true 2module Gitlab 3 module MergeRequests 4 class CommitMessageGenerator 5 def initialize(merge_request:) 6 @merge_request = merge_request 7 end 8 9 def merge_message 10 return unless @merge_request.target_project.merge_commit_template.present? 11 12 replace_placeholders(@merge_request.target_project.merge_commit_template) 13 end 14 15 def squash_message 16 return unless @merge_request.target_project.squash_commit_template.present? 17 18 replace_placeholders(@merge_request.target_project.squash_commit_template) 19 end 20 21 private 22 23 attr_reader :merge_request 24 25 PLACEHOLDERS = { 26 'source_branch' => ->(merge_request) { merge_request.source_branch.to_s }, 27 'target_branch' => ->(merge_request) { merge_request.target_branch.to_s }, 28 'title' => ->(merge_request) { merge_request.title }, 29 'issues' => ->(merge_request) do 30 return "" if merge_request.visible_closing_issues_for.blank? 31 32 closes_issues_references = merge_request.visible_closing_issues_for.map do |issue| 33 issue.to_reference(merge_request.target_project) 34 end 35 "Closes #{closes_issues_references.to_sentence}" 36 end, 37 'description' => ->(merge_request) { merge_request.description.presence || '' }, 38 'reference' => ->(merge_request) { merge_request.to_reference(full: true) }, 39 'first_commit' => -> (merge_request) { merge_request.first_commit&.safe_message&.strip.presence || '' }, 40 'first_multiline_commit' => -> (merge_request) { merge_request.first_multiline_commit&.safe_message&.strip.presence || merge_request.title } 41 }.freeze 42 43 PLACEHOLDERS_REGEX = Regexp.union(PLACEHOLDERS.keys.map do |key| 44 Regexp.new(Regexp.escape(key)) 45 end).freeze 46 47 BLANK_PLACEHOLDERS_REGEXES = (PLACEHOLDERS.map do |key, value| 48 [key, Regexp.new("[\n\r]+%{#{Regexp.escape(key)}}$")] 49 end).to_h.freeze 50 51 def replace_placeholders(message) 52 # convert CRLF to LF 53 message = message.delete("\r") 54 55 # Remove placeholders that correspond to empty values and are the last word in the line 56 # along with all whitespace characters preceding them. 57 # This allows us to recreate previous default merge commit message behaviour - we skipped new line character 58 # before empty description and before closed issues when none were present. 59 PLACEHOLDERS.each do |key, value| 60 unless value.call(merge_request).present? 61 message = message.gsub(BLANK_PLACEHOLDERS_REGEXES[key], '') 62 end 63 end 64 65 Gitlab::StringPlaceholderReplacer 66 .replace_string_placeholders(message, PLACEHOLDERS_REGEX) do |key| 67 PLACEHOLDERS[key].call(merge_request) 68 end 69 end 70 end 71 end 72end 73