1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
# frozen_string_literal: true
module SystemNotes
class CommitService < ::SystemNotes::BaseService
NEW_COMMIT_DISPLAY_LIMIT = 10
# Called when commits are added to a merge request
#
# new_commits - Array of Commits added since last push
# existing_commits - Array of Commits added in a previous push
# oldrev - Optional String SHA of a previous Commit
#
# See new_commit_summary and existing_commit_summary.
#
# Returns the created Note object
def add_commits(new_commits, existing_commits = [], oldrev = nil)
total_count = new_commits.length + existing_commits.length
commits_text = "#{total_count} commit".pluralize(total_count)
text_parts = ["added #{commits_text}"]
text_parts << commits_list(noteable, new_commits, existing_commits, oldrev)
text_parts << "[Compare with previous version](#{diff_comparison_path(noteable, project, oldrev)})"
body = text_parts.join("\n\n")
create_note(NoteSummary.new(noteable, project, author, body, action: 'commit', commit_count: total_count))
end
# Called when a commit was tagged
#
# tag_name - The created tag name
#
# Returns the created Note object
def tag_commit(tag_name)
link = url_helpers.project_tag_path(project, id: tag_name)
body = "tagged commit #{noteable.sha} to [`#{tag_name}`](#{link})"
create_note(NoteSummary.new(noteable, project, author, body, action: 'tag'))
end
private
# Build an Array of lines detailing each commit added in a merge request
#
# new_commits - Array of new Commit objects
#
# Returns an Array of Strings
def new_commits_list(new_commits)
new_commits.collect do |commit|
content_tag('li', "#{commit.short_id} - #{commit.title}")
end
end
# Builds an Array of lines describing each commit and truncate them based on the limit
# to avoid creating a note with a large number of commits.
#
# commits - Array of Commit objects
#
# Returns an Array of Strings
#
# rubocop: disable CodeReuse/ActiveRecord
def new_commit_summary(commits, start_rev)
if commits.size > NEW_COMMIT_DISPLAY_LIMIT
no_of_commits_to_truncate = commits.size - NEW_COMMIT_DISPLAY_LIMIT
commits_to_truncate = commits.take(no_of_commits_to_truncate)
remaining_commits = commits.drop(no_of_commits_to_truncate)
[truncated_new_commits(commits_to_truncate, start_rev)] + new_commits_list(remaining_commits)
else
new_commits_list(commits)
end
end
# rubocop: enable CodeReuse/ActiveRecord
# Builds a summary line that describes given truncated commits.
#
# commits - Array of Commit objects
# start_rev - String SHA of a Commit that will be used as the starting SHA of the range
#
# Returns a String wrapped in 'li' tag.
def truncated_new_commits(commits, start_rev)
count = commits.size
commit_ids = if count == 1
commits.first.short_id
elsif start_rev && !Gitlab::Git.blank_ref?(start_rev)
"#{Commit.truncate_sha(start_rev)}...#{commits.last.short_id}"
else
# This two-dots notation seems to be not functioning as expected, but we should
# fallback to it as start_rev can be empty.
#
# For more information, please see https://gitlab.com/gitlab-org/gitlab/-/issues/391809
"#{commits.first.short_id}..#{commits.last.short_id}"
end
commits_text = "#{count} earlier commit".pluralize(count)
content_tag('li', "#{commit_ids} - #{commits_text}")
end
# Builds a list of existing and new commits according to existing_commits and
# new_commits methods.
# Returns a String wrapped in `ul` and `li` tags.
def commits_list(noteable, new_commits, existing_commits, oldrev)
existing_commit_summary = existing_commit_summary(noteable, existing_commits, oldrev)
start_rev = existing_commits.empty? ? oldrev : existing_commits.last.id
new_commit_summary = new_commit_summary(new_commits, start_rev).join
content_tag('ul', "#{existing_commit_summary}#{new_commit_summary}".html_safe)
end
# Build a single line summarizing existing commits being added in a merge
# request
#
# existing_commits - Array of existing Commit objects
# oldrev - Optional String SHA of a previous Commit
#
# Examples:
#
# "* ea0f8418...2f4426b7 - 24 commits from branch `master`"
#
# "* ea0f8418..4188f0ea - 15 commits from branch `fork:master`"
#
# "* ea0f8418 - 1 commit from branch `feature`"
#
# Returns a newline-terminated String
def existing_commit_summary(noteable, existing_commits, oldrev = nil)
return '' if existing_commits.empty?
count = existing_commits.size
commit_ids = if count == 1
existing_commits.first.short_id
elsif oldrev && !Gitlab::Git.blank_ref?(oldrev)
"#{Commit.truncate_sha(oldrev)}...#{existing_commits.last.short_id}"
else
"#{existing_commits.first.short_id}..#{existing_commits.last.short_id}"
end
commits_text = "#{count} commit".pluralize(count)
branch = noteable.target_branch
branch = "#{noteable.target_project_namespace}:#{branch}" if noteable.for_fork?
branch_name = content_tag('code', branch)
content_tag('li', "#{commit_ids} - #{commits_text} from branch #{branch_name}".html_safe)
end
def diff_comparison_path(merge_request, project, oldrev)
diff_id = merge_request.merge_request_diff.id
url_helpers.diffs_project_merge_request_path(
project,
merge_request,
diff_id: diff_id,
start_sha: oldrev
)
end
end
end
|