git-commit-notifier 0.10.5 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
data/.yardopts CHANGED
@@ -2,4 +2,6 @@
2
2
  --markup-provider redcarpet
3
3
  --charset utf-8
4
4
  --readme README.md
5
-
5
+ -
6
+ README.md
7
+ LICENSE
data/README.md CHANGED
@@ -102,7 +102,7 @@ old commits in processes of forking, branching etc.
102
102
 
103
103
  ## Note on Patches/Pull Requests
104
104
 
105
- * Fork the project.
105
+ * Fork [the project](https://github.com/bitboxer/git-commit-notifier).
106
106
  * Make your feature addition or bug fix.
107
107
  * Add tests for it. This is important so I don't break it in a
108
108
  future version unintentionally.
@@ -126,5 +126,5 @@ Thanks for [putpat.tv](http://www.putpat.tv), [Primalgrasp](http://www.primalgra
126
126
 
127
127
  ## License
128
128
 
129
- MIT License, see the file LICENSE.
129
+ MIT License, see the {file:LICENSE}.
130
130
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.5
1
+ 0.11.0
@@ -4,11 +4,40 @@ ignore_merge: false
4
4
  # Optional parameter for the subject-line of the mail
5
5
  # emailprefix: GIT
6
6
 
7
- # Limit lines per diff
8
- # lines_per_diff: 300
7
+ # Set limit on maximum number of lines per file diff
8
+ lines_per_diff: 300
9
9
 
10
10
  # Show file list only when too many files
11
- # too_many_files: 1000
11
+ too_many_files: 50
12
+
13
+ # Whether to expand css inline. May be compatible with more mail readers
14
+ # but consumes much more space and more cpu. Defaults to true
15
+ # expand_css: true
16
+
17
+ # Determine whether to add plaintext alternative section to generated email.
18
+ # This allows text-only readers an option, but greatly increases the size
19
+ # of generated emails. Defaults to true
20
+ # add_plaintext: true
21
+
22
+ # Subject: the subject may be set by specifying a template.
23
+ #
24
+ # Substitution into the template is available for the following
25
+ # words, via the form ${word}
26
+ #
27
+ # prefix - The emailprefix set, defaulting to repo_name
28
+ # repo_name - Name of the git repo
29
+ # branch_name - Name of the git branch (master is "" unless show_master_branch_name)
30
+ # commit_id - The git commit id (hash)
31
+ # message - The commit message
32
+ # commit_number - The commit number within the push; 1-based
33
+ # commit_count - The number of commits within the push
34
+ # commit_count_phrase - The number of commits, as "1 commit", "2 commits", etc.
35
+ #
36
+ # The default subject template varies a little depending on whether
37
+ # or not group_email_by_push is set.
38
+ #
39
+ #subject: "[${prefix}${branch_name}] ${commit_count_phrase}: ${message}"
40
+ #subject: "[${prefix}${branch_name}][${commit_number}] ${message}"
12
41
 
13
42
  # defines what branches to email for (defaults to all)
14
43
  # include_branches: ['master', 'some_other_branch']
@@ -68,7 +97,8 @@ nntp_settings:
68
97
  address: your.nntp.host.here
69
98
  port: 119
70
99
 
71
- # Decorate files and commit ids with link to a webview. Possible values: none, gitweb, gitorious, cgit or trac
100
+ # Decorate files and commit ids with link to a webview. Possible values: none, gitweb,
101
+ # gitorious, cgit, trac, gitlabhq, or redmine
72
102
  link_files: none
73
103
 
74
104
  # If link_files is set to "gitweb", you need to configure the path to your gitweb
@@ -94,6 +124,21 @@ cgit:
94
124
  trac:
95
125
  path: http://example.com/changeset
96
126
 
127
+ # If link_files is set to "gitlabhq", you need to configure the path to your gitlabhq
128
+ # instance
129
+ gitlabhq:
130
+ path: http://gitlabhq.example.com
131
+ version: 1.2
132
+
133
+ # If link_files is set to "redmine", you need to configure the path to your redmine
134
+ # instance
135
+ redmine:
136
+ path: http://redmine.example.com
137
+ # project: project-name
138
+ # keywords: [refs, fixes]
139
+ # project defaults to git repository name, keywords defaults to refs, fixes (for use with message_integration)
140
+
141
+
97
142
  # commit message URL map
98
143
  message_map:
99
144
  # '\brefs\s*\#(\d+)': 'http://example.com/redmine/issues/show/\1'
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
20
20
 
21
- s.homepage = %q{http://github.com/bitboxer/git-commit-notifier}
21
+ s.homepage = %q{http://bitboxer.github.com/git-commit-notifier/}
22
22
  s.require_paths = ["lib"]
23
23
  s.rubygems_version = %q{1.3.7}
24
24
  s.summary = %q{Sends git commit messages with diffs}
@@ -47,11 +47,9 @@ module GitCommitNotifier
47
47
  project_path = Dir.getwd
48
48
  recipient = config["mailinglist"] || Git.mailing_list_address
49
49
 
50
+ # If no recipients specified, bail out gracefully. This is not an error, and might be intentional
50
51
  if recipient.nil? || recipient.length == 0
51
- CommitHook.show_error(
52
- "Please add a recipient for the emails. Eg : \n" +
53
- " git config hooks.mailinglist developer@example.com"
54
- )
52
+ info("bypassing commit notification; no recipients specified (consider setting git config hooks.mailinglist)")
55
53
  return
56
54
  end
57
55
 
@@ -62,7 +60,9 @@ module GitCommitNotifier
62
60
  logger.debug("rev2: #{rev2}")
63
61
  logger.debug("included branches: #{include_branches.join(', ')}") unless include_branches.nil?
64
62
 
65
- prefix = config["emailprefix"] || Git.repo_name
63
+ repo_name = Git.repo_name
64
+ prefix = config["emailprefix"] || repo_name
65
+
66
66
  branch_name = if ref_name =~ /^refs\/heads\/(.+)$/
67
67
  $1
68
68
  else
@@ -76,10 +76,30 @@ module GitCommitNotifier
76
76
  info("Supressing mail for branch #{branch_name}...")
77
77
  return
78
78
  end
79
-
79
+
80
80
  branch_name = "/#{branch_name}"
81
81
  branch_name = "" if !config["show_master_branch_name"] && branch_name == '/master'
82
-
82
+
83
+ # Replacements for subject template
84
+ # prefix
85
+ # repo_name
86
+ # branch_name
87
+ # commit_id (hash)
88
+ # short_message
89
+ # commit_number
90
+ # commit_count
91
+ # commit_count_phrase (1 commit, 2 commits, etc)
92
+ subject_words = {
93
+ :prefix => prefix,
94
+ :repo_name => repo_name,
95
+ :branch_name => branch_name,
96
+ :commit_id => nil,
97
+ :message => nil,
98
+ :commit_number => nil,
99
+ :commit_count => nil,
100
+ :commit_count_phrase => nil
101
+ }
102
+
83
103
  info("Sending mail...")
84
104
 
85
105
  diff2html = DiffToHtml.new(Dir.pwd, config)
@@ -102,39 +122,63 @@ module GitCommitNotifier
102
122
  text << result[:text_content]
103
123
  html << result[:html_content]
104
124
  end
125
+
126
+ # Form the subject from template
127
+ revised_subject_words = subject_words.merge({
128
+ :commit_id => result[:commit_info][:commit],
129
+ :message => result[:commit_info][:message],
130
+ :commit_number => 1,
131
+ :commit_count => diffresult.size,
132
+ :commit_count_phrase => diffresult.size == 1 ? "#{diffresult.size} commit" : "#{diffresult.size} commits"
133
+ })
134
+ subject_template = config['subject'] || "[${prefix}${branch_name}] ${commit_count_phrase}: ${message}"
135
+ subject = subject_template.gsub(/\$\{(\w+)\}/) { |m| revised_subject_words[$1.intern] }
105
136
 
106
137
  emailer = Emailer.new(config,
107
138
  :project_path => project_path,
108
139
  :recipient => recipient,
109
140
  :from_address => config["from"] || result[:commit_info][:email],
110
141
  :from_alias => result[:commit_info][:author],
111
- :subject => "[#{prefix}#{branch_name}] #{diffresult.size > 1 ? "#{diffresult.size} commits: " : ''}#{result[:commit_info][:message]}",
142
+ :subject => subject,
112
143
  :text_message => text.join("------------------------------------------\n\n"),
113
144
  :html_message => html.join("<hr /><br />"),
114
145
  :old_rev => rev1,
115
146
  :new_rev => rev2,
116
- :ref_name => ref_name
147
+ :ref_name => ref_name,
148
+ :repo_name => repo_name
117
149
  )
118
150
  emailer.send
119
151
  else
120
- i = 0
152
+ commit_number = 1
121
153
  diff2html.diff_between_revisions(rev1, rev2, prefix, ref_name) do |result|
122
- next if config["ignore_merge"] && merge_commit?(result)
123
- nr = number(i)
154
+ next if config["ignore_merge"] && merge_commit?(result)
155
+
156
+ # Form the subject from template
157
+ revised_subject_words = subject_words.merge({
158
+ :commit_id => result[:commit_info][:commit],
159
+ :message => result[:commit_info][:message],
160
+ :commit_number => commit_number,
161
+ :commit_count => 1,
162
+ :commit_count_phrase => "1 commit"
163
+ })
164
+ subject_template = config['subject'] || "[${prefix}${branch_name}][${commit_number}] ${message}"
165
+ subject = subject_template.gsub(/\$\{(\w+)\}/) { |m| revised_subject_words[$1.intern] }
166
+
124
167
  emailer = Emailer.new(config,
125
168
  :project_path => project_path,
126
169
  :recipient => recipient,
127
170
  :from_address => config["from"] || result[:commit_info][:email],
128
171
  :from_alias => result[:commit_info][:author],
129
- :subject => "[#{prefix}#{branch_name}]#{nr} #{result[:commit_info][:message]}",
172
+ :subject => subject,
130
173
  :text_message => result[:text_content],
131
174
  :html_message => result[:html_content],
132
175
  :old_rev => rev1,
133
176
  :new_rev => rev2,
134
- :ref_name => ref_name
177
+ :ref_name => ref_name,
178
+ :repo_name => repo_name
135
179
  )
136
180
  emailer.send
137
- i += 1
181
+ commit_number += 1
138
182
  end
139
183
  end
140
184
  end
@@ -10,20 +10,27 @@ module GitCommitNotifier
10
10
 
11
11
  INTEGRATION_MAP = {
12
12
  :mediawiki => { :search_for => /\[\[([^\[\]]+)\]\]/, :replace_with => '#{url}/\1' },
13
- :redmine => { :search_for => /\b(?:refs|fixes)([\s&,]+\#\d+)+/i, :replace_with => lambda do |m, url|
14
- # we can provide Proc that gets matched string and configuration url.
15
- # result should be in form of:
16
- # { :phrase => 'phrase started with', :links => [ { :title => 'title of url', :url => 'target url' }, ... ] }
17
- match = m.match(/^(refs|fixes)(.*)$/i)
18
- return m unless match
19
- r = { :phrase => match[1] }
20
- captures = match[2].split(/[\s\&\,]+/).map { |m| (m =~ /(\d+)/) ? $1 : m }.reject { |c| c.empty? }
21
- r[:links] = captures.map { |mn| { :title => "##{mn}", :url => "#{url}/issues/show/#{mn}" } }
22
- r
23
- end },
13
+ :redmine => {
14
+ :search_for => lambda do |config|
15
+ keywords = (config['redmine'] && config['redmine']['keywords']) || ["refs", "fixes"]
16
+ /\b(?:#{keywords.join('\b|')})([\s&,]+\#\d+)+/i
17
+ end,
18
+ :replace_with => lambda do |m, url, config|
19
+ # we can provide Proc that gets matched string and configuration url.
20
+ # result should be in form of:
21
+ # { :phrase => 'phrase started with', :links => [ { :title => 'title of url', :url => 'target url' }, ... ] }
22
+ keywords = (config['redmine'] && config['redmine']['keywords']) || ["refs", "fixes"]
23
+ match = m.match(/^(#{keywords.join('\b|')})(.*)$/i)
24
+ return m unless match
25
+ r = { :phrase => match[1] }
26
+ captures = match[2].split(/[\s\&\,]+/).map { |m| (m =~ /(\d+)/) ? $1 : m }.reject { |c| c.empty? }
27
+ r[:links] = captures.map { |mn| { :title => "##{mn}", :url => "#{url}/issues/show/#{mn}" } }
28
+ r
29
+ end },
24
30
  :bugzilla => { :search_for => /\bBUG\s*(\d+)/i, :replace_with => '#{url}/show_bug.cgi?id=\1' },
25
31
  :fogbugz => { :search_for => /\bbugzid:\s*(\d+)/i, :replace_with => '#{url}\1' }
26
32
  }.freeze
33
+ MAX_LINE_LENGTH = 512
27
34
  MAX_COMMITS_PER_ACTION = 10000
28
35
  HANDLED_COMMITS_FILE = 'previously.txt'.freeze
29
36
  NEW_HANDLED_COMMITS_FILE = 'previously_new.txt'.freeze
@@ -64,17 +71,13 @@ module GitCommitNotifier
64
71
  end
65
72
 
66
73
  def lines_per_diff
67
- config['lines_per_diff']
74
+ @config['lines_per_diff']
68
75
  end
69
76
 
70
- def ignore_whitespace?
77
+ def ignore_whitespaces?
71
78
  @config['ignore_whitespace'].nil? || @config['ignore_whitespace']
72
79
  end
73
80
 
74
- def skip_lines?
75
- lines_per_diff && (@lines_added >= lines_per_diff)
76
- end
77
-
78
81
  def add_separator
79
82
  @diff_result << '<tr class="sep"><td class="sep" colspan="3" title="Unchanged content skipped between diff. blocks">&hellip;</td></tr>'
80
83
  end
@@ -84,7 +87,6 @@ module GitCommitNotifier
84
87
  end
85
88
 
86
89
  def add_line_to_result(line, escape)
87
- @lines_added += 1
88
90
  klass = line_class(line)
89
91
  content = (escape == :escape) ? escape_content(line[:content]) : line[:content]
90
92
  padding = '&nbsp;' if klass != ''
@@ -155,7 +157,8 @@ module GitCommitNotifier
155
157
  end
156
158
 
157
159
  file_name = @current_file_name
158
-
160
+
161
+ # TODO: these filenames, etc, should likely be properly html escaped
159
162
  if config['link_files']
160
163
  file_name = if config["link_files"] == "gitweb" && config["gitweb"]
161
164
  "<a href='#{config['gitweb']['path']}?p=#{Git.repo_name}.git;f=#{file_name};h=#{@current_sha};hb=#{@current_commit}'>#{file_name}</a>"
@@ -163,10 +166,18 @@ module GitCommitNotifier
163
166
  "<a href='#{config['gitorious']['path']}/#{config['gitorious']['project']}/#{config['gitorious']['repository']}/blobs/#{branch_name}/#{file_name}'>#{file_name}</a>"
164
167
  elsif config["link_files"] == "cgit" && config["cgit"]
165
168
  "<a href='#{config['cgit']['path']}/#{config['cgit']['project']}/tree/#{file_name}'>#{file_name}</a>"
169
+ elsif config["link_files"] == "gitlabhq" && config["gitlabhq"]
170
+ if config["gitlabhq"]["version"] && config["gitlabhq"]["version"] < 1.2
171
+ "<a href='#{config['gitlabhq']['path']}/#{Git.repo_name.gsub(".", "_")}/tree/#{@current_commit}/#{file_name}'>#{file_name}</a>"
172
+ else
173
+ "<a href='#{config['gitlabhq']['path']}/#{Git.repo_name.gsub(".", "_")}/#{@current_commit}/tree/#{file_name}'>#{file_name}</a>"
174
+ end
175
+ elsif config["link_files"] == "redmine" && config["redmine"]
176
+ "<a href='#{config['redmine']['path']}/projects/#{config['redmine']['project'] || Git.repo_name}/repository/revisions/#{@current_commit}/entry/#{file_name}'>#{file_name}</a>"
166
177
  else
167
178
  file_name
168
179
  end
169
- end
180
+ end
170
181
 
171
182
  header = "#{op} #{binary}file #{file_name}"
172
183
  "<h2>#{header}</h2>\n"
@@ -184,19 +195,26 @@ module GitCommitNotifier
184
195
 
185
196
  def add_changes_to_result
186
197
  return if @current_file_name.nil?
198
+
199
+ @lines_added = 0
187
200
  @diff_result << operation_description
188
201
  if !@diff_lines.empty? && !@too_many_files
189
202
  @diff_result << '<table>'
190
203
  removals = []
191
204
  additions = []
192
- @diff_lines.each_with_index do |line, index|
193
- if skip_lines?
194
- add_skip_notification
195
- break
196
- end
205
+
206
+ lines = if lines_per_diff.nil?
207
+ line_budget = nil
208
+ @diff_lines
209
+ else
210
+ line_budget = lines_per_diff - @lines_added
211
+ @diff_lines.slice(0, line_budget)
212
+ end
213
+
214
+ lines.each_with_index do |line, index|
197
215
  removals << line if line[:op] == :removal
198
216
  additions << line if line[:op] == :addition
199
- if line[:op] == :unchanged || index == @diff_lines.size - 1 # unchanged line or end of block, add prev lines to result
217
+ if line[:op] == :unchanged || index == lines.size - 1 # unchanged line or end of block, add prev lines to result
200
218
  if removals.size > 0 && additions.size > 0 # block of removed and added lines - perform intelligent diff
201
219
  add_block_to_results(lcs_diff(removals, additions), :dont_escape)
202
220
  else # some lines removed or added - no need to perform intelligent diff
@@ -204,14 +222,17 @@ module GitCommitNotifier
204
222
  end
205
223
  removals = []
206
224
  additions = []
207
- if index > 0 && index != @diff_lines.size - 1
208
- prev_line = @diff_lines[index - 1]
225
+ if index > 0 && index != lines.size - 1
226
+ prev_line = lines[index - 1]
209
227
  add_separator unless lines_are_sequential?(prev_line, line)
210
228
  end
211
229
  add_line_to_result(line, :escape) if line[:op] == :unchanged
212
230
  end
213
-
231
+ @lines_added += 1
214
232
  end
233
+
234
+ add_skip_notification if !line_budget.nil? && line_budget < @diff_lines.size
235
+
215
236
  @diff_result << '</table>'
216
237
  @diff_lines = []
217
238
  end
@@ -424,10 +445,50 @@ module GitCommitNotifier
424
445
  def merge_commit?(commit_info)
425
446
  ! commit_info[:merge].nil?
426
447
  end
448
+
449
+ def truncate_long_lines(text)
450
+ StringIO.open("", "w") do |output|
451
+ # Match encoding of output string to that of input string
452
+ output.string.force_encoding(text.encoding) if output.string.respond_to?(:force_encoding)
453
+
454
+ input = StringIO.new(text, "r")
455
+ input.each_line "\n" do |line|
456
+ if line.length > MAX_LINE_LENGTH && MAX_LINE_LENGTH >= 9
457
+ # Truncate the line
458
+ line.slice!(MAX_LINE_LENGTH-3..-1)
459
+
460
+ # Ruby < 1.9 doesn't know how to slice between
461
+ # characters, so deal specially with that case
462
+ # so that we don't truncate in the middle of a UTF8 sequence,
463
+ # which would be invalid.
464
+ if !line.respond_to?(:force_encoding)
465
+ # If the last remaining character is part of a UTF8 multibyte character,
466
+ # keep truncating until we go past the start of a UTF8 character.
467
+ # This assumes that this is a UTF8 string, which may be a false assumption
468
+ # unless somebody has taken care to check the encoding of the source file.
469
+ # We truncate at most 6 additional bytes, which is the length of the longest
470
+ # UTF8 sequence
471
+ 6.times do
472
+ c = line[-1, 1].to_i
473
+ break if (c & 0x80) == 0 # Last character is plain ASCII: don't truncate
474
+ line.slice!(-1, 1) # Truncate character
475
+ break if (c & 0xc0) == 0xc0 # Last character was the start of a UTF8 sequence, so we can stop now
476
+ end
477
+ end
478
+
479
+ # Append three dots to the end of line to indicate it's been truncated
480
+ # (avoiding ellipsis character so as not to introduce more encoding issues)
481
+ line << "...\n"
482
+ end
483
+ output << line
484
+ end
485
+ output.string
486
+ end
487
+ end
427
488
 
428
489
  def diff_for_commit(commit)
429
490
  @current_commit = commit
430
- raw_diff = Git.show(commit, ignore_whitespace?)
491
+ raw_diff = truncate_long_lines(Git.show(commit, :ignore_whitespaces => ignore_whitespaces?))
431
492
  raise "git show output is empty" if raw_diff.empty?
432
493
 
433
494
  commit_info = extract_commit_info_from_git_show_output(raw_diff)
@@ -457,6 +518,10 @@ module GitCommitNotifier
457
518
  "<a href='#{config['trac']['path']}/#{commit_info[:commit]}'>#{commit_info[:commit]}</a>"
458
519
  elsif config["link_files"] == "cgit" && config["cgit"]
459
520
  "<a href='#{config['cgit']['path']}/#{config['cgit']['project']}/commit/?id=#{commit_info[:commit]}'>#{commit_info[:commit]}</a>"
521
+ elsif config["link_files"] == "gitlabhq" && config["gitlabhq"]
522
+ "<a href='#{config['gitlabhq']['path']}/#{Git.repo_name.gsub(".", "_")}/commits/#{commit_info[:commit]}'>#{commit_info[:commit]}</a>"
523
+ elsif config["link_files"] == "redmine" && config["redmine"]
524
+ "<a href='#{config['redmine']['path']}/projects/#{config['redmine']['project'] || Git.repo_name}/repository/revisions/#{commit_info[:commit]}'>#{commit_info[:commit]}</a>"
460
525
  else
461
526
  " #{commit_info[:commit]}"
462
527
  end
@@ -504,13 +569,12 @@ module GitCommitNotifier
504
569
  []
505
570
  else
506
571
  log = Git.log(rev1, rev2)
507
- log.scan(/^commit\s([a-f0-9]+)/).map { |a| a.first }
572
+ log.scan(/^commit\s([a-f0-9]+)/).map { |a| a.first }.reverse
508
573
  end
509
574
 
510
575
  commits = check_handled_commits(commits)
511
576
 
512
577
  commits.each do |commit|
513
- @lines_added = 0 unless config["group_email_by_push"]
514
578
  begin
515
579
  commit_result = diff_for_commit(commit)
516
580
  next if commit_result.nil?
@@ -539,9 +603,11 @@ module GitCommitNotifier
539
603
  return message unless config['message_integration'].respond_to?(:each_pair)
540
604
  config['message_integration'].each_pair do |pm, url|
541
605
  pm_def = DiffToHtml::INTEGRATION_MAP[pm.to_sym] or next
606
+ search_for = pm_def[:search_for]
607
+ search_for = search_for.kind_of?(Proc) ? search_for.call(@config) : search_for
542
608
  replace_with = pm_def[:replace_with]
543
- replace_with = replace_with.kind_of?(Proc) ? lambda { |m| pm_def[:replace_with].call(m, url) } : replace_with.gsub('#{url}', url)
544
- message_replace!(message, pm_def[:search_for], replace_with)
609
+ replace_with = replace_with.kind_of?(Proc) ? lambda { |m| pm_def[:replace_with].call(m, url, @config) } : replace_with.gsub('#{url}', url)
610
+ message_replace!(message, search_for, replace_with)
545
611
  end
546
612
  message
547
613
  end
@@ -3,7 +3,7 @@ require 'premailer'
3
3
  class GitCommitNotifier::Emailer
4
4
  DEFAULT_STYLESHEET_PATH = File.join(File.dirname(__FILE__), '/../../template/styles.css').freeze
5
5
  TEMPLATE = File.join(File.dirname(__FILE__), '/../../template/email.html.erb').freeze
6
- PARAMETERS = %w[project_path recipient from_address from_alias subject text_message html_message ref_name old_rev new_rev].freeze
6
+ PARAMETERS = %w[project_path recipient from_address from_alias subject text_message html_message repo_name ref_name old_rev new_rev].freeze
7
7
 
8
8
  def config
9
9
  @@config
@@ -43,8 +43,11 @@ class GitCommitNotifier::Emailer
43
43
 
44
44
  def mail_html_message
45
45
  html = GitCommitNotifier::Emailer.template.result(binding)
46
- premailer = Premailer.new(html, :with_html_string => true, :adapter => :nokogiri)
47
- premailer.to_inline_css
46
+ if config['expand_css'].nil? || config['expand_css']
47
+ premailer = Premailer.new(html, :with_html_string => true, :adapter => :nokogiri)
48
+ html = premailer.to_inline_css
49
+ end
50
+ html
48
51
  end
49
52
 
50
53
  def boundary
@@ -78,10 +81,13 @@ class GitCommitNotifier::Emailer
78
81
  main_smtp.start( settings['domain'],
79
82
  settings['user_name'], settings['password'], settings['authentication']) do |smtp|
80
83
 
81
- recp = @recipient.split(",")
82
- smtp.open_message_stream(@from_address, recp) do |f|
84
+ recipients = @recipient.dup
85
+ recipients.force_encoding('ASCII-8BIT') if recipients.respond_to?(:force_encoding)
86
+ recipients = recipients.split(",")
87
+ smtp.open_message_stream(@from_address, recipients) do |f|
83
88
  content.each do |line|
84
- f.puts line
89
+ line.force_encoding('ASCII-8BIT') if line.respond_to?(:force_encoding)
90
+ f.print(line, "\r\n")
85
91
  end
86
92
  end
87
93
  end
@@ -94,7 +100,9 @@ class GitCommitNotifier::Emailer
94
100
  }.merge(options || {})
95
101
  command = "#{sendmail_settings['location']} #{sendmail_settings['arguments']}"
96
102
  IO.popen(command, "w+") do |f|
97
- f.write(content.join("\n"))
103
+ content.each do |line|
104
+ f.print(line, "\r\n")
105
+ end
98
106
  f.flush
99
107
  end
100
108
  end
@@ -110,27 +118,37 @@ class GitCommitNotifier::Emailer
110
118
  to_tag = config['delivery_method'] == 'nntp' ? 'Newsgroups' : 'To'
111
119
  quoted_from_alias = quote_if_necessary("#{@from_alias}",'utf-8')
112
120
  from = @from_alias.empty? ? @from_address : "#{quoted_from_alias} <#{@from_address}>"
121
+
122
+ plaintext = if config['add_plaintext'].nil? || config['add_plaintext']
123
+ @text_message
124
+ else
125
+ "Plain text part omitted. Consider setting add_plaintext in configuration."
126
+ end
113
127
 
114
128
  content = [
115
129
  "From: #{from}",
116
130
  "#{to_tag}: #{quote_if_necessary(@recipient, 'utf-8')}",
117
131
  "Subject: #{quote_if_necessary(@subject, 'utf-8')}",
118
132
  "X-Mailer: git-commit-notifier",
133
+ "X-Git-Repository: #{@repo_name}",
119
134
  "X-Git-Refname: #{@ref_name}",
120
135
  "X-Git-Oldrev: #{@old_rev}",
121
136
  "X-Git-Newrev: #{@new_rev}",
122
137
  "Mime-Version: 1.0",
123
- "Content-Type: multipart/alternative; boundary=#{boundary}\n\n\n",
138
+ "Content-Type: multipart/alternative; boundary=#{boundary}",
139
+ "",
124
140
  "--#{boundary}",
125
141
  "Content-Type: text/plain; charset=utf-8",
126
- "Content-Transfer-Encoding: 8bit",
127
- "Content-Disposition: inline\n\n\n",
128
- @text_message,
129
- "\n--#{boundary}",
142
+ "Content-Transfer-Encoding: quoted-printable",
143
+ "Content-Disposition: inline",
144
+ "",
145
+ encode_quoted_printable_message(plaintext),
146
+ "--#{boundary}",
130
147
  "Content-Type: text/html; charset=utf-8",
131
- "Content-Transfer-Encoding: 8bit",
132
- "Content-Disposition: inline\n\n\n",
133
- mail_html_message,
148
+ "Content-Transfer-Encoding: quoted-printable",
149
+ "Content-Disposition: inline",
150
+ "",
151
+ encode_quoted_printable_message(mail_html_message),
134
152
  "--#{boundary}--"]
135
153
 
136
154
  if @recipient.empty?
@@ -147,6 +165,45 @@ class GitCommitNotifier::Emailer
147
165
  end
148
166
  end
149
167
 
168
+ # Convert a message into quoted printable encoding,
169
+ # limiting line length to 76 characters per spec
170
+ # Encoding messages in this way ensures that they
171
+ # won't violate rules for maximum line length, which
172
+ # can result in the MTA breaking lines at inconvenient points,
173
+ # such as in the middle of UTF8 characters.
174
+ def encode_quoted_printable_message(text)
175
+ StringIO.open("", "w") do |output|
176
+ # Character encoding of output string can be plain US-ASCII since quoted-printable is plain ASCII
177
+ output.string.force_encoding("US-ASCII") if output.string.respond_to?(:force_encoding)
178
+
179
+ line_max = 76
180
+ line_len = 0
181
+
182
+ input = StringIO.new(text, "r")
183
+ input.each_byte do |b|
184
+ case (b)
185
+ when 9, 32..60, 62..126
186
+ if line_len >= line_max - 1
187
+ output << "=\r\n"
188
+ line_len = 0
189
+ end
190
+ output << b.chr
191
+ line_len += 1
192
+ else
193
+ if line_len >= line_max - 3
194
+ output << "=\r\n"
195
+ line_len = 0
196
+ end
197
+ output << "=%02X" % b
198
+ line_len += 3
199
+ end
200
+ end
201
+
202
+ output << "=\r\n" if line_len > 0
203
+ output.string
204
+ end
205
+ end
206
+
150
207
  # Convert the given text into quoted printable format, with an instruction
151
208
  # that the text be eventually interpreted in the given charset.
152
209
  def quoted_printable(text, charset)
@@ -2,8 +2,13 @@
2
2
 
3
3
  require 'git_commit_notifier'
4
4
 
5
+ # Git commit notifier namespace
5
6
  module GitCommitNotifier
7
+ # binaries code
6
8
  class Executor
9
+ # runs git commit notifier life
10
+ # @param [Array(String)] args Command line arguments
11
+ # @return [nil] Nothing
7
12
  def self.run!(args)
8
13
  case args.length
9
14
  when 0
@@ -24,6 +29,7 @@ module GitCommitNotifier
24
29
  else
25
30
  GitCommitNotifier::CommitHook.run args.first, args[1], args[2], args[3]
26
31
  end
32
+ nil
27
33
  end
28
34
  end
29
35
  end
@@ -1,5 +1,9 @@
1
+ # Git methods
1
2
  class GitCommitNotifier::Git
2
3
  class << self
4
+ # Runs specified command.
5
+ # @return (String) Shell command STDOUT (forced to UTF-8)
6
+ # @raise [ArgumentError] when command exits with nonzero status.
3
7
  def from_shell(cmd)
4
8
  r = `#{cmd}`
5
9
  raise ArgumentError.new("#{cmd} failed") unless $?.exitstatus.zero?
@@ -7,19 +11,38 @@ class GitCommitNotifier::Git
7
11
  r
8
12
  end
9
13
 
10
- def show(rev, ignore_whitespace)
14
+ # runs `git show`
15
+ # @note uses "--pretty=fuller" option.
16
+ # @return [String] Its output
17
+ # @see from_shell
18
+ # @param [String] rev Revision
19
+ # @param [Hash] opts Options
20
+ # @option opts [Boolean] :ignore_whitespaces Ignore whitespaces or not
21
+ def show(rev, opts = {})
11
22
  gitopt = ""
12
23
  gitopt += " --pretty=fuller"
13
- gitopt += " -w" if ignore_whitespace
24
+ gitopt += " -w" if opts[:ignore_whitespaces]
14
25
  data = from_shell("git show #{rev.strip}#{gitopt}")
15
26
  data
16
27
  end
17
28
 
29
+ # runs `git log`
30
+ # @note uses "--pretty=fuller" option.
31
+ # @return [String] Its output
32
+ # @see from_shell
33
+ # @param [String] rev1 First revision
34
+ # @param [String] rev2 Second revision
18
35
  def log(rev1, rev2)
19
36
  data = from_shell("git log --pretty=fuller #{rev1}..#{rev2}").strip
20
37
  data
21
38
  end
22
39
 
40
+ # runs `git log` and extract filenames only
41
+ # @note uses "--pretty=fuller" and "--name-status" options.
42
+ # @return [Array(String)] File names
43
+ # @see from_shell
44
+ # @param [String] rev1 First revision
45
+ # @param [String] rev2 Second revision
23
46
  def changed_files(rev1, rev2)
24
47
  output = ""
25
48
  lines = from_shell("git log #{rev1}..#{rev2} --name-status --pretty=oneline")
@@ -59,6 +82,8 @@ class GitCommitNotifier::Git
59
82
 
60
83
  def mailing_list_address
61
84
  from_shell("git config hooks.mailinglist").strip
85
+ rescue ArgumentError
86
+ nil
62
87
  end
63
88
  end
64
89
  end
@@ -75,7 +75,7 @@ describe GitCommitNotifier::CommitHook do
75
75
  mock(GitCommitNotifier::Git).repo_name { 'testproject' }
76
76
  mock(GitCommitNotifier::Git).changed_files('7e4f6b4', '4f13525') { [] }
77
77
  REVISIONS.each do |rev|
78
- mock(GitCommitNotifier::Git).show(rev, true) { IO.read(FIXTURES_PATH + "git_show_#{rev}") }
78
+ mock(GitCommitNotifier::Git).show(rev, :ignore_whitespaces => true) { IO.read(FIXTURES_PATH + "git_show_#{rev}") }
79
79
  end
80
80
  end
81
81
 
@@ -104,10 +104,10 @@ describe GitCommitNotifier::CommitHook do
104
104
  end
105
105
 
106
106
  describe :run do
107
- it "should report error when no recipients specified" do
107
+ it "should report informational message when no recipients specified" do
108
108
  mock(File).exists?(:noconfig) { false }
109
109
  mock(GitCommitNotifier::Git).mailing_list_address { nil }
110
- mock(GitCommitNotifier::CommitHook).show_error(/recipient/)
110
+ mock(GitCommitNotifier::CommitHook).info(/recipient/)
111
111
  GitCommitNotifier::CommitHook.run(:noconfig, :rev1, :rev2, 'master')
112
112
  end
113
113
  end
@@ -129,9 +129,9 @@ describe GitCommitNotifier::DiffToHtml do
129
129
  mock(GitCommitNotifier::Git).changed_files('7e4f6b4', '4f13525') { [] }
130
130
  mock(GitCommitNotifier::Git).log(REVISIONS.first, REVISIONS.last) { IO.read(FIXTURES_PATH + 'git_log') }
131
131
  REVISIONS.each do |rev|
132
- mock(GitCommitNotifier::Git).show(rev, true) { IO.read(FIXTURES_PATH + 'git_show_' + rev) }
132
+ mock(GitCommitNotifier::Git).show(rev, :ignore_whitespaces => true) { IO.read(FIXTURES_PATH + 'git_show_' + rev) }
133
133
  end
134
-
134
+
135
135
  diff = GitCommitNotifier::DiffToHtml.new
136
136
  mock(diff).check_handled_commits(anything) { |commits| commits }
137
137
  diff.diff_between_revisions REVISIONS.first, REVISIONS.last, 'testproject', 'master'
@@ -142,22 +142,16 @@ describe GitCommitNotifier::DiffToHtml do
142
142
  html.should_not be_include('@@') # diff correctly processed
143
143
  end
144
144
 
145
- # first commit
146
- hp = Nokogiri::HTML diff.result.first[:html_content]
147
- (hp/"table").should have(2).tables # 2 files updated - one table for each of the files
145
+ # second commit - 51b986619d88f7ba98be7d271188785cbbb541a0
146
+ hp = Nokogiri::HTML diff.result[1][:html_content]
147
+ (hp/"table").should have(3).tables # 3 files updated
148
148
  (hp/"table"/"tr"/"td").each do |td|
149
- if td.inner_html == "require&nbsp;'iconv'"
150
- # first added line in changeset a4629e707d80a5769f7a71ca6ed9471015e14dc9
151
- td.parent.search('td')[0].inner_text.should == '' # left
152
- td.parent.search('td')[1].inner_text.should == '2' # right
153
- td.parent.search('td')[2].inner_html.should == "require&nbsp;'iconv'" # change
149
+ if td.inner_html =~ /create_btn/
150
+ cols = td.parent.search('td')
151
+ ['405', '408', ''].should be_include(cols[0].inner_text) # line 405 changed
154
152
  end
155
153
  end
156
154
 
157
- # second commit
158
- hp = Nokogiri::HTML diff.result[1][:html_content]
159
- (hp/"table").should have(1).table # 1 file updated
160
-
161
155
  # third commit - dce6ade4cdc2833b53bd600ef10f9bce83c7102d
162
156
  hp = Nokogiri::HTML diff.result[2][:html_content]
163
157
  (hp/"h2").should have(6).headers # 6 files in commit
@@ -167,13 +161,19 @@ describe GitCommitNotifier::DiffToHtml do
167
161
  (hp/"h2")[3].inner_text.should == 'Deleted file railties/doc/guides/source/icons/README'
168
162
  (hp/"h2")[4].inner_text.should == 'Added file railties/doc/guides/source/images/icons/README'
169
163
 
170
- # fourth commit - 51b986619d88f7ba98be7d271188785cbbb541a0
164
+ # fourth commit
171
165
  hp = Nokogiri::HTML diff.result[3][:html_content]
172
- (hp/"table").should have(3).tables # 3 files updated
166
+ (hp/"table").should have(1).table # 1 file updated
167
+
168
+ # fifth commit
169
+ hp = Nokogiri::HTML diff.result[4][:html_content]
170
+ (hp/"table").should have(2).tables # 2 files updated - one table for each of the files
173
171
  (hp/"table"/"tr"/"td").each do |td|
174
- if td.inner_html =~ /create_btn/
175
- cols = td.parent.search('td')
176
- ['405', '408', ''].should be_include(cols[0].inner_text) # line 405 changed
172
+ if td.inner_html == "require&nbsp;'iconv'"
173
+ # first added line in changeset a4629e707d80a5769f7a71ca6ed9471015e14dc9
174
+ td.parent.search('td')[0].inner_text.should == '' # left
175
+ td.parent.search('td')[1].inner_text.should == '2' # right
176
+ td.parent.search('td')[2].inner_html.should == "require&nbsp;'iconv'" # change
177
177
  end
178
178
  end
179
179
  end
@@ -182,7 +182,7 @@ describe GitCommitNotifier::DiffToHtml do
182
182
  first_rev, last_rev = %w[ 0000000000000000000000000000000000000000 9b15cebcc5434e27c00a4a2acea43509f9faea21 ]
183
183
  mock(GitCommitNotifier::Git).branch_commits('rvm') { %w[ ff037a73fc1094455e7bbf506171a3f3cf873ae6 ] }
184
184
  %w[ ff037a73fc1094455e7bbf506171a3f3cf873ae6 ].each do |rev|
185
- mock(GitCommitNotifier::Git).show(rev, true) { IO.read(FIXTURES_PATH + 'git_show_' + rev) }
185
+ mock(GitCommitNotifier::Git).show(rev, :ignore_whitespaces => true) { IO.read(FIXTURES_PATH + 'git_show_' + rev) }
186
186
  end
187
187
  diff = GitCommitNotifier::DiffToHtml.new
188
188
  mock(diff).check_handled_commits(anything) { |commits| commits }
@@ -15,18 +15,18 @@ describe GitCommitNotifier::Git do
15
15
  it "should get data from shell: git show without whitespaces" do
16
16
  expected = 'some data from git show'
17
17
  mock(GitCommitNotifier::Git).from_shell("git show #{SAMPLE_REV} --pretty=fuller -w") { expected }
18
- GitCommitNotifier::Git.show(SAMPLE_REV, true).should == expected
18
+ GitCommitNotifier::Git.show(SAMPLE_REV, :ignore_whitespaces => true).should == expected
19
19
  end
20
20
 
21
21
  it "should get data from shell: git show with whitespaces" do
22
22
  expected = 'some data from git show'
23
23
  mock(GitCommitNotifier::Git).from_shell("git show #{SAMPLE_REV} --pretty=fuller") { expected }
24
- GitCommitNotifier::Git.show(SAMPLE_REV, false).should == expected
24
+ GitCommitNotifier::Git.show(SAMPLE_REV, :ignore_whitespaces => false).should == expected
25
25
  end
26
26
 
27
27
  it "should strip given revision" do
28
28
  mock(GitCommitNotifier::Git).from_shell("git show #{SAMPLE_REV} --pretty=fuller -w")
29
- GitCommitNotifier::Git.show("#{SAMPLE_REV}\n", true)
29
+ GitCommitNotifier::Git.show("#{SAMPLE_REV}\n", :ignore_whitespaces => true)
30
30
  end
31
31
  end
32
32
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-commit-notifier
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.5
4
+ version: 0.11.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2011-11-06 00:00:00.000000000 Z
13
+ date: 2011-12-09 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: diff-lcs
17
- requirement: &70277632158420 !ruby/object:Gem::Requirement
17
+ requirement: &70202081729720 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ~>
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 1.1.2
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *70277632158420
25
+ version_requirements: *70202081729720
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: nntp
28
- requirement: &70277632157800 !ruby/object:Gem::Requirement
28
+ requirement: &70202081729240 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ~>
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: '1.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *70277632157800
36
+ version_requirements: *70202081729240
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: premailer
39
- requirement: &70277632157260 !ruby/object:Gem::Requirement
39
+ requirement: &70202081728720 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ~>
@@ -50,10 +50,10 @@ dependencies:
50
50
  version: 1.7.2
51
51
  type: :runtime
52
52
  prerelease: false
53
- version_requirements: *70277632157260
53
+ version_requirements: *70202081728720
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: nokogiri
56
- requirement: &70277632156160 !ruby/object:Gem::Requirement
56
+ requirement: &70202081727760 !ruby/object:Gem::Requirement
57
57
  none: false
58
58
  requirements:
59
59
  - - ~>
@@ -61,10 +61,10 @@ dependencies:
61
61
  version: '1.4'
62
62
  type: :runtime
63
63
  prerelease: false
64
- version_requirements: *70277632156160
64
+ version_requirements: *70202081727760
65
65
  - !ruby/object:Gem::Dependency
66
66
  name: rake
67
- requirement: &70277632155540 !ruby/object:Gem::Requirement
67
+ requirement: &70202081727220 !ruby/object:Gem::Requirement
68
68
  none: false
69
69
  requirements:
70
70
  - - ~>
@@ -75,10 +75,10 @@ dependencies:
75
75
  version: 0.9.0
76
76
  type: :development
77
77
  prerelease: false
78
- version_requirements: *70277632155540
78
+ version_requirements: *70202081727220
79
79
  - !ruby/object:Gem::Dependency
80
80
  name: bundler
81
- requirement: &70277632154480 !ruby/object:Gem::Requirement
81
+ requirement: &70202081726400 !ruby/object:Gem::Requirement
82
82
  none: false
83
83
  requirements:
84
84
  - - ~>
@@ -89,10 +89,10 @@ dependencies:
89
89
  version: 1.0.10
90
90
  type: :development
91
91
  prerelease: false
92
- version_requirements: *70277632154480
92
+ version_requirements: *70202081726400
93
93
  - !ruby/object:Gem::Dependency
94
94
  name: code-cleaner
95
- requirement: &70277632153000 !ruby/object:Gem::Requirement
95
+ requirement: &70202081725280 !ruby/object:Gem::Requirement
96
96
  none: false
97
97
  requirements:
98
98
  - - ! '>='
@@ -100,10 +100,10 @@ dependencies:
100
100
  version: '0'
101
101
  type: :development
102
102
  prerelease: false
103
- version_requirements: *70277632153000
103
+ version_requirements: *70202081725280
104
104
  - !ruby/object:Gem::Dependency
105
105
  name: rspec-core
106
- requirement: &70277632152400 !ruby/object:Gem::Requirement
106
+ requirement: &70202081724720 !ruby/object:Gem::Requirement
107
107
  none: false
108
108
  requirements:
109
109
  - - ! '>='
@@ -111,10 +111,10 @@ dependencies:
111
111
  version: '0'
112
112
  type: :development
113
113
  prerelease: false
114
- version_requirements: *70277632152400
114
+ version_requirements: *70202081724720
115
115
  - !ruby/object:Gem::Dependency
116
116
  name: rspec-expectations
117
- requirement: &70277632151660 !ruby/object:Gem::Requirement
117
+ requirement: &70202081724240 !ruby/object:Gem::Requirement
118
118
  none: false
119
119
  requirements:
120
120
  - - ! '>='
@@ -122,10 +122,10 @@ dependencies:
122
122
  version: '0'
123
123
  type: :development
124
124
  prerelease: false
125
- version_requirements: *70277632151660
125
+ version_requirements: *70202081724240
126
126
  - !ruby/object:Gem::Dependency
127
127
  name: rr
128
- requirement: &70277632150500 !ruby/object:Gem::Requirement
128
+ requirement: &70202081723720 !ruby/object:Gem::Requirement
129
129
  none: false
130
130
  requirements:
131
131
  - - ~>
@@ -133,10 +133,10 @@ dependencies:
133
133
  version: '1.0'
134
134
  type: :development
135
135
  prerelease: false
136
- version_requirements: *70277632150500
136
+ version_requirements: *70202081723720
137
137
  - !ruby/object:Gem::Dependency
138
138
  name: faker
139
- requirement: &70277632243320 !ruby/object:Gem::Requirement
139
+ requirement: &70202081723240 !ruby/object:Gem::Requirement
140
140
  none: false
141
141
  requirements:
142
142
  - - ~>
@@ -144,10 +144,10 @@ dependencies:
144
144
  version: 0.9.5
145
145
  type: :development
146
146
  prerelease: false
147
- version_requirements: *70277632243320
147
+ version_requirements: *70202081723240
148
148
  - !ruby/object:Gem::Dependency
149
149
  name: yard
150
- requirement: &70277632241680 !ruby/object:Gem::Requirement
150
+ requirement: &70202081722760 !ruby/object:Gem::Requirement
151
151
  none: false
152
152
  requirements:
153
153
  - - ~>
@@ -155,10 +155,10 @@ dependencies:
155
155
  version: 0.7.3
156
156
  type: :development
157
157
  prerelease: false
158
- version_requirements: *70277632241680
158
+ version_requirements: *70202081722760
159
159
  - !ruby/object:Gem::Dependency
160
160
  name: redcarpet
161
- requirement: &70277632240700 !ruby/object:Gem::Requirement
161
+ requirement: &70202081858160 !ruby/object:Gem::Requirement
162
162
  none: false
163
163
  requirements:
164
164
  - - ~>
@@ -166,7 +166,7 @@ dependencies:
166
166
  version: 1.17.2
167
167
  type: :development
168
168
  prerelease: false
169
- version_requirements: *70277632240700
169
+ version_requirements: *70202081858160
170
170
  description: This git commit notifier sends html mails with nice diffs for every changed
171
171
  file.
172
172
  email: bodo@bitboxer.de
@@ -223,7 +223,7 @@ files:
223
223
  - spec/spec_helper.rb
224
224
  - template/email.html.erb
225
225
  - template/styles.css
226
- homepage: http://github.com/bitboxer/git-commit-notifier
226
+ homepage: http://bitboxer.github.com/git-commit-notifier/
227
227
  licenses: []
228
228
  post_install_message:
229
229
  rdoc_options: []
@@ -237,7 +237,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
237
237
  version: '0'
238
238
  segments:
239
239
  - 0
240
- hash: -486808045600569914
240
+ hash: -3538399353583787974
241
241
  required_rubygems_version: !ruby/object:Gem::Requirement
242
242
  none: false
243
243
  requirements:
@@ -246,7 +246,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
246
246
  version: '0'
247
247
  requirements: []
248
248
  rubyforge_project:
249
- rubygems_version: 1.8.11
249
+ rubygems_version: 1.8.10
250
250
  signing_key:
251
251
  specification_version: 3
252
252
  summary: Sends git commit messages with diffs