git-commit-mailer 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d41e58adc35fd16bcfca2e295036b47397d548d3
4
- data.tar.gz: 4ed5b675e95ceb15e26a06197cdb45951abd0200
3
+ metadata.gz: 136f22114f43ec2e9948d128a9070084b3af3b0b
4
+ data.tar.gz: 11441281f87e1d875665e935939d5af2eb66c701
5
5
  SHA512:
6
- metadata.gz: 487ef516f9c79d6c1648c0ca2f61d055db5b747214de9fbd1b69e52f70b7f0092eb3bb50dbff02c312ee11a4d28b4d543d3793ec07168fb8e8197db42f937a7d
7
- data.tar.gz: 7fddf6a57b2bcf15842033fff29cff2def697f632c323ce54083b6e42524d64ca7d6032ccd229d730186e0b10de49ec9424903db2b84f20bd97b74b77d528b49
6
+ metadata.gz: 2f6c8edf2e5aebffd8132bdebe699570c2cf0027ce3032f6e1d3d22fc3560b68016312d78ececfd12ff79e5157080c1050e406f1727477123b1b35dc63c2b219
7
+ data.tar.gz: 508e1476fb0050113ccbccde483a0209eb173bbebbb216bb69d2c3bf0b2a30a50cf40566eaf7683511b46e130127b242d530645e016a45c70f29ceb597ab3892
@@ -1,5 +1,30 @@
1
1
  # News
2
2
 
3
+ ## 1.0.4 - 2016-10-07 {#version-1-0-4}
4
+
5
+ ### Improvements
6
+
7
+ * [GitHub] Supported formatting merge message.
8
+
9
+ * [GitHub] Supported `GH-NNN` auto link.
10
+
11
+ * Supported diff in a line.
12
+
13
+ * Improved color schema.
14
+
15
+ * Supported special characters such as "," in author name.
16
+
17
+ * Supported showing diff in merge commit.
18
+
19
+ * [GitLab] Supported GitLab Wiki as a repository browser.
20
+
21
+ ### Fixes
22
+
23
+ * Fixed a bug that `@domain` in email address is handled as mention.
24
+
25
+ * Fixed a bug that commit ID like string in email address is handled
26
+ as commit ID.
27
+
3
28
  ## 1.0.3 - 2015-06-09 {#version-1-0-3}
4
29
 
5
30
  ### Improvements
@@ -19,6 +19,8 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
+ spec.add_runtime_dependency "diff-lcs"
23
+
22
24
  spec.add_development_dependency "bundler"
23
25
  spec.add_development_dependency "rake", "~> 10.0"
24
26
  spec.add_development_dependency "test-unit"
@@ -1,7 +1,5 @@
1
- # -*- coding: utf-8 -*-
2
- #
3
1
  # Copyright (C) 2009 Ryo Onodera <onodera@clear-code.com>
4
- # Copyright (C) 2012-2014 Kouhei Sutou <kou@clear-code.com>
2
+ # Copyright (C) 2012-2016 Kouhei Sutou <kou@clear-code.com>
5
3
  #
6
4
  # This program is free software: you can redistribute it and/or modify
7
5
  # it under the terms of the GNU General Public License as published by
@@ -273,7 +271,7 @@ class GitCommitMailer
273
271
 
274
272
  def make_parser(options)
275
273
  OptionParser.new do |parser|
276
- parser.banner += "TO"
274
+ parser.banner += " TO"
277
275
 
278
276
  add_repository_options(parser, options)
279
277
  add_email_options(parser, options)
@@ -304,7 +302,7 @@ class GitCommitMailer
304
302
  options.reference = reference
305
303
  end
306
304
 
307
- available_software = ["github", "github-wiki", "gitlab"]
305
+ available_software = ["github", "github-wiki", "gitlab", "gitlab-wiki"]
308
306
  label = available_software.join(", ")
309
307
  parser.on("--repository-browser=SOFTWARE",
310
308
  available_software,
@@ -559,11 +557,22 @@ class GitCommitMailer
559
557
  if /\A[^\s<]+@[^\s>]\z/ =~ @from
560
558
  @from
561
559
  else
562
- "#{info.author_name} <#{@from}>"
560
+ "#{format_name(info.author_name)} <#{@from}>"
561
+ end
562
+ else
563
+ "#{format_name(info.author_name)} <#{info.author_email}>"
564
+ end
565
+ end
566
+
567
+ def format_name(name)
568
+ case name
569
+ when /[,"\\]/
570
+ escaped_name = name.gsub(/["\\]/) do |special_character|
571
+ "\\#{special_character}"
563
572
  end
573
+ "\"#{escaped_name}\""
564
574
  else
565
- # return "#{info.author_name}@#{@from_domain}".sub(/@\z/, '') if @from_domain
566
- "#{info.author_name} <#{info.author_email}>"
575
+ name
567
576
  end
568
577
  end
569
578
 
@@ -1037,8 +1046,8 @@ EOF
1037
1046
  end
1038
1047
 
1039
1048
  merge_message = "Merged #{merge_commit.short_revision}: #{merge_commit.subject}"
1040
- if not is_traversing_first_parent and not commit_info.merge_status.index(merge_message)
1041
- commit_info.merge_status << merge_message
1049
+ if not is_traversing_first_parent and not commit_info.merge_messages.index(merge_message)
1050
+ commit_info.merge_messages << merge_message
1042
1051
  commit_info.merge_commits << merge_commit
1043
1052
  end
1044
1053
 
@@ -44,7 +44,7 @@ class GitCommitMailer
44
44
  attr_reader :added_files, :copied_files, :deleted_files, :updated_files
45
45
  attr_reader :renamed_files, :type_changed_files, :diffs
46
46
  attr_reader :subject, :author_name, :author_email, :date, :summary
47
- attr_accessor :merge_status
47
+ attr_accessor :merge_messages
48
48
  attr_writer :reference
49
49
  attr_reader :merge_commits
50
50
  def initialize(mailer, reference, revision)
@@ -64,7 +64,7 @@ class GitCommitMailer
64
64
  parse_file_status
65
65
  parse_diff
66
66
 
67
- @merge_status = []
67
+ @merge_messages = []
68
68
  @merge_commits = []
69
69
  end
70
70
 
@@ -185,7 +185,7 @@ class GitCommitMailer
185
185
  @diffs = []
186
186
  output = []
187
187
  n_bytes = 0
188
- git("log -n 1 --pretty=format:'' -C -p #{@revision}") do |io|
188
+ git("log -n 1 --pretty=format:'' -C --cc -p #{@revision}") do |io|
189
189
  io.each_line do |line|
190
190
  n_bytes += line.bytesize
191
191
  break if n_bytes > mailer.max_diff_size
@@ -83,9 +83,14 @@ class GitCommitMailer
83
83
 
84
84
  def parse_header(lines)
85
85
  line = lines.shift.strip
86
- if line =~ /\Adiff --git ("?a\/.*) ("?b\/.*)/
86
+ case line
87
+ when /\Adiff --git ("?a\/.*) ("?b\/.*)/
87
88
  @from_file = extract_file_path($1)
88
89
  @to_file = extract_file_path($2)
90
+ when /\Adiff --(?:combined|cc) (.*?)/
91
+ path = CommitInfo.unescape_file_path($1)
92
+ @from_file = path
93
+ @to_file = path
89
94
  else
90
95
  raise "Unexpected diff header format: #{line}"
91
96
  end
@@ -116,6 +121,9 @@ class GitCommitMailer
116
121
  when /\Aindex ([0-9a-f]{7,})\.\.([0-9a-f]{7,})/
117
122
  @old_blob = $1
118
123
  @new_blob = $2
124
+ when /\Aindex ([0-9a-f]{7,}),([0-9a-f]{7,}\.\.[0-9a-f]{7,})/
125
+ @old_blob = $1
126
+ @new_blob = $2
119
127
  else
120
128
  return false
121
129
  end
@@ -18,6 +18,8 @@
18
18
 
19
19
  require "digest/md5"
20
20
 
21
+ require "diff/lcs"
22
+
21
23
  class GitCommitMailer
22
24
  class HTMLMailBodyFormatter < MailBodyFormatter
23
25
  include ERB::Util
@@ -42,18 +44,18 @@ class GitCommitMailer
42
44
  <%= dd(h(@mailer.format_time(@info.date))) %>
43
45
  <%= dt("New Revision") %>
44
46
  <%= dd(format_revision) %>
45
- <% unless @info.merge_status.empty? %>
47
+ <% unless @info.merge_messages.empty? %>
46
48
  <%= dt("Merge") %>
47
49
  <%= dd_start %>
48
50
  <ul>
49
- <% @info.merge_status.each do |status| %>
50
- <li><%= h(status) %></li>
51
+ <% @info.merge_messages.each do |message| %>
52
+ <li><%= format_message(message) %></li>
51
53
  <% end %>
52
54
  </ul>
53
55
  </dd>
54
56
  <% end %>
55
57
  <%= dt("Message") %>
56
- <%= dd(format_summary(@info.summary.strip)) %>
58
+ <%= dd(pre(format_message(@info.summary))) %>
57
59
  <%= format_files("Added", @info.added_files) %>
58
60
  <%= format_files("Copied", @info.copied_files) %>
59
61
  <%= format_files("Removed", @info.deleted_files) %>
@@ -72,7 +74,7 @@ class GitCommitMailer
72
74
  revision = @info.revision
73
75
  url = commit_url
74
76
  if url
75
- formatted_revision = "<a href=\"#{h(url)}\">#{h(revision)}</a>"
77
+ formatted_revision = tag("a", {"href" => url}, h(revision))
76
78
  else
77
79
  formatted_revision = h(revision)
78
80
  end
@@ -170,6 +172,8 @@ class GitCommitMailer
170
172
  from_line_column = ""
171
173
  to_line_column = ""
172
174
  content_column = ""
175
+ content_lines = []
176
+
173
177
  file_path = diff.file_path
174
178
  diff.changes.each do |type, line_number, line|
175
179
  case type
@@ -179,6 +183,8 @@ class GitCommitMailer
179
183
  from_line_number)
180
184
  to_line_column << span_line_number_hunk_header(file_path, :to,
181
185
  to_line_number)
186
+
187
+ flush_content_lines(content_column, content_lines)
182
188
  case line
183
189
  when /\A(@@[\s0-9\-+,]+@@\s*)(.+)(\s*)\z/
184
190
  hunk_info = $1
@@ -187,50 +193,152 @@ class GitCommitMailer
187
193
  else
188
194
  formatted_line = h(line)
189
195
  end
190
- content_column << span_diff_hunk_header(formatted_line)
196
+ content_column << span_diff_hunk_header(formatted_line) << "\n"
191
197
  when :added
192
198
  from_line_column << span_line_number_nothing
193
199
  to_line_column << span_line_number_added(file_path, line_number)
194
- content_column << span_diff_added(h(line))
200
+ content_lines << [:added, line]
195
201
  when :deleted
196
202
  from_line_column << span_line_number_deleted(file_path, line_number)
197
203
  to_line_column << span_line_number_nothing
198
- content_column << span_diff_deleted(h(line))
204
+ content_lines << [:deleted, line]
199
205
  when :not_changed
200
206
  from_line_number, to_line_number = line_number
201
207
  from_line_column << span_line_number_not_changed(file_path, :from,
202
208
  from_line_number)
203
209
  to_line_column << span_line_number_not_changed(file_path, :to,
204
210
  to_line_number)
205
- content_column << span_diff_not_changed(h(line))
211
+ flush_content_lines(content_column, content_lines)
212
+ content_column << span_diff_not_changed(h(line)) << "\n"
206
213
  end
207
214
  from_line_column << "\n"
208
215
  to_line_column << "\n"
209
- content_column << "\n"
210
216
  end
217
+ flush_content_lines(content_column, content_lines)
211
218
  [from_line_column, to_line_column, content_column]
212
219
  end
213
220
 
214
- def format_summary(summary)
221
+ def flush_content_lines(column, lines)
222
+ return if lines.empty?
223
+
224
+ added_lines = []
225
+ deleted_lines = []
226
+ lines.each do |type, line|
227
+ if type == :added
228
+ added_lines << line
229
+ else
230
+ deleted_lines << line
231
+ end
232
+ end
233
+
234
+ if added_lines.size == deleted_lines.size
235
+ nth_added = 0
236
+ nth_deleted = 0
237
+ lines.each do |type, line|
238
+ if type == :added
239
+ deleted_line = deleted_lines[nth_added]
240
+ formatted_added_line = format_added_words(line, deleted_line)
241
+ column << span_diff_added(formatted_added_line) << "\n"
242
+ nth_added += 1
243
+ else
244
+ added_line = added_lines[nth_deleted]
245
+ formatted_deleted_line = format_deleted_words(line, added_line)
246
+ column << span_diff_deleted(formatted_deleted_line) << "\n"
247
+ nth_deleted += 1
248
+ end
249
+ end
250
+ else
251
+ lines.each do |type, line|
252
+ if type == :added
253
+ column << span_diff_added(h(line)) << "\n"
254
+ else
255
+ column << span_diff_deleted(h(line)) << "\n"
256
+ end
257
+ end
258
+ end
259
+ lines.clear
260
+ end
261
+
262
+ def format_changed_words(from_line, to_line, &formatter)
263
+ line = h(from_line[0, 1])
264
+ changed_chars = ""
265
+
266
+ flush_changed_chars = lambda do
267
+ unless changed_chars.empty?
268
+ line << formatter.call(h(changed_chars))
269
+ changed_chars.clear
270
+ end
271
+ end
272
+
273
+ Diff::LCS.sdiff(from_line[1..-1], to_line[1..-1]).each do |diff|
274
+ action, from, _to = *diff
275
+ _from_nth, from_char = from
276
+ next if from_char.nil?
277
+
278
+ case action
279
+ when "="
280
+ flush_changed_chars.call
281
+ line << h(from_char)
282
+ when "!", "-"
283
+ changed_chars << from_char
284
+ when "+"
285
+ flush_changed_chars.call
286
+ end
287
+ end
288
+ flush_changed_chars.call
289
+
290
+ line
291
+ end
292
+
293
+ def format_added_words(added_line, deleted_line)
294
+ format_changed_words(added_line, deleted_line) do |chars|
295
+ span_diff_added_word(chars)
296
+ end
297
+ end
298
+
299
+ def format_deleted_words(deleted_line, added_line)
300
+ format_changed_words(deleted_line, added_line) do |chars|
301
+ span_diff_deleted_word(chars)
302
+ end
303
+ end
304
+
305
+ def format_message(message)
306
+ message = message.strip
215
307
  case @mailer.repository_browser
216
308
  when "github"
217
- format_summary_github(summary)
309
+ format_message_github(message)
218
310
  else
219
- pre(h(summary))
311
+ h(message)
220
312
  end
221
313
  end
222
314
 
315
+ PATTERN_HTML_SPECIAL_CHARACTER = /['&\"<>]/
316
+ PATTERN_EMAIL = /[\da-zA-Z\-_]+@[\da-zA-Z\-_.]+/
317
+ GITHUB_MARKUP_PATTERN_GH_ISSUE = /GH-\d+/
223
318
  GITHUB_MARKUP_PATTERN_ISSUE = /\#\d+/
224
319
  GITHUB_MARKUP_PATTERN_COMMIT = /[\da-fA-F]{7,}/
225
320
  GITHUB_MARKUP_PATTERN_MENTION = /@[\da-zA-Z\-]+/
226
- def format_summary_github(summary)
227
- formatted_summary = summary.gsub(/
228
- ['&\"<>]|
229
- #{GITHUB_MARKUP_PATTERN_ISSUE}|
230
- #{GITHUB_MARKUP_PATTERN_COMMIT}|
231
- #{GITHUB_MARKUP_PATTERN_MENTION}
232
- /x) do |matched|
321
+ def format_message_github(message)
322
+ message.gsub(/
323
+ #{PATTERN_HTML_SPECIAL_CHARACTER}|
324
+ #{PATTERN_EMAIL}|
325
+ #{GITHUB_MARKUP_PATTERN_GH_ISSUE}|
326
+ #{GITHUB_MARKUP_PATTERN_ISSUE}|
327
+ #{GITHUB_MARKUP_PATTERN_COMMIT}|
328
+ #{GITHUB_MARKUP_PATTERN_MENTION}
329
+ /x) do |matched|
233
330
  case matched
331
+ when /\A#{PATTERN_HTML_SPECIAL_CHARACTER}\z/
332
+ h(matched)
333
+ when /\A#{PATTERN_EMAIL}\z/
334
+ h(matched)
335
+ when /\A#{GITHUB_MARKUP_PATTERN_GH_ISSUE}\z/
336
+ issue_number = matched.gsub(/\AGH-/, "")
337
+ tag("a",
338
+ {
339
+ "href" => github_issue_url(issue_number),
340
+ },
341
+ h(matched))
234
342
  when /\A#{GITHUB_MARKUP_PATTERN_ISSUE}\z/
235
343
  issue_number = matched.gsub(/\A\#/, "")
236
344
  tag("a",
@@ -256,7 +364,6 @@ class GitCommitMailer
256
364
  h(matched)
257
365
  end
258
366
  end
259
- pre(formatted_summary)
260
367
  end
261
368
 
262
369
  def github_issue_url(id)
@@ -462,14 +569,28 @@ class GitCommitMailer
462
569
 
463
570
  def span_deleted_styles
464
571
  {
465
- "background-color" => "#ffaaaa",
572
+ "background-color" => "#ffecec",
573
+ "color" => "#000000",
574
+ }
575
+ end
576
+
577
+ def span_deleted_word_styles
578
+ {
579
+ "background-color" => "#f8cbcb",
466
580
  "color" => "#000000",
467
581
  }
468
582
  end
469
583
 
470
584
  def span_added_styles
471
585
  {
472
- "background-color" => "#aaffaa",
586
+ "background-color" => "#eaffea",
587
+ "color" => "#000000",
588
+ }
589
+ end
590
+
591
+ def span_added_word_styles
592
+ {
593
+ "background-color" => "#a6f3a6",
473
594
  "color" => "#000000",
474
595
  }
475
596
  end
@@ -605,6 +726,15 @@ class GitCommitMailer
605
726
  content)
606
727
  end
607
728
 
729
+ def span_diff_deleted_word(content)
730
+ tag("span",
731
+ {
732
+ "class" => "diff-deleted-word",
733
+ "style" => span_deleted_word_styles,
734
+ },
735
+ content)
736
+ end
737
+
608
738
  def span_diff_added(content)
609
739
  tag("span",
610
740
  {
@@ -614,6 +744,15 @@ class GitCommitMailer
614
744
  content)
615
745
  end
616
746
 
747
+ def span_diff_added_word(content)
748
+ tag("span",
749
+ {
750
+ "class" => "diff-added-word",
751
+ "style" => span_added_word_styles,
752
+ },
753
+ content)
754
+ end
755
+
617
756
  def span_diff_not_changed(content)
618
757
  tag("span",
619
758
  {
@@ -632,6 +771,8 @@ class GitCommitMailer
632
771
  "#{base_url}#diff-#{file_md5}"
633
772
  when "github-wiki"
634
773
  commit_file_url_github_wiki(file)
774
+ when "gitlab-wiki"
775
+ commit_file_url_gitlab_wiki(file)
635
776
  else
636
777
  nil
637
778
  end
@@ -40,6 +40,9 @@ class GitCommitMailer
40
40
  return nil if @mailer.gitlab_project_uri.nil?
41
41
  revision = @info.revision
42
42
  "#{@mailer.gitlab_project_uri}/commit/#{revision}"
43
+ when "gitlab-wiki"
44
+ file = (@info.updated_files + @info.added_files).first
45
+ commit_file_url_gitlab_wiki(file)
43
46
  else
44
47
  nil
45
48
  end
@@ -66,5 +69,17 @@ class GitCommitMailer
66
69
  revision = @info.revision
67
70
  "#{base_url}/#{user}/#{repository}/wiki/#{page_name_in_url}/#{revision}"
68
71
  end
72
+
73
+ def commit_file_url_gitlab_wiki(file)
74
+ return nil if file.nil?
75
+
76
+ gitlab_project_uri = @mailer.gitlab_project_uri
77
+ return nil if gitlab_project_uri.nil?
78
+
79
+ page_name = file.gsub(/\.[^.]+\z/, "")
80
+ page_name_in_url = ERB::Util.u(page_name)
81
+ revision = @info.revision
82
+ "#{gitlab_project_uri}/wikis/#{page_name_in_url}?version_id=#{revision}"
83
+ end
69
84
  end
70
85
  end
@@ -31,9 +31,9 @@ class GitCommitMailer
31
31
  New Revision: <%= @info.revision %>
32
32
  <%= format_commit_url %>
33
33
 
34
- <% unless @info.merge_status.empty? %>
35
- <% @info.merge_status.each do |status| %>
36
- <%= status %>
34
+ <% unless @info.merge_messages.empty? %>
35
+ <% @info.merge_messages.each do |message| %>
36
+ <%= message %>
37
37
  <% end %>
38
38
 
39
39
  <% end %>
@@ -1,4 +1,4 @@
1
1
  class GitCommitMailer
2
- VERSION = "1.0.3"
2
+ VERSION = "1.0.4"
3
3
  URL = "https://github.com/clear-code/git-commit-mailer"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-commit-mailer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryo Onodera
@@ -10,8 +10,22 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-06-09 00:00:00.000000000 Z
13
+ date: 2016-10-07 00:00:00.000000000 Z
14
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: diff-lcs
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
15
29
  - !ruby/object:Gem::Dependency
16
30
  name: bundler
17
31
  requirement: !ruby/object:Gem::Requirement
@@ -117,9 +131,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
131
  version: '0'
118
132
  requirements: []
119
133
  rubyforge_project:
120
- rubygems_version: 2.2.2
134
+ rubygems_version: 2.5.1
121
135
  signing_key:
122
136
  specification_version: 4
123
137
  summary: A utility to send commit mails for commits pushed to git repositories.
124
138
  test_files: []
125
- has_rdoc: