git-commit-notifier 0.9.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ notifications:
2
+ recipients:
3
+ - bodo@wannawork.de
4
+ - akzhan.abdulin@gmail.com
5
+ rvm:
6
+ - 1.8.7
7
+ - ree
8
+ - 1.9.2
9
+
data/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # Git Commit Notifier
2
+
3
+ [![Build Status](https://travis-ci.org/bitboxer/git-commit-notifier.png)](http://travis-ci.org/bitboxer/git-commit-notifier)
4
+
5
+ __by Bodo Tasche (bodo 'at' wannawork 'dot' de), Akzhan Abdulin (akzhan 'dot' abdulin 'at' gmail 'dot' com), Csoma Zoltan (info 'at' railsprogrammer 'dot' net)__
6
+
7
+ Sends email commit messages splitting commits that were pushed in one step.
8
+ Email is delivered as text or HTML with changes refined per word. Emails
9
+ have a scannable subject containing the first sentence of the commit as well
10
+ as the author, project and branch name.
11
+
12
+ It's also possible to send a mail to a newsgroup using NNTP.
13
+
14
+ For example:
15
+
16
+ [rails][branch] Fix Brasilia timezone. [#1180 state:resolved]
17
+
18
+ A reply-to header is added containing the author of the commit. This makes
19
+ follow up really simple. If multiple commits are pushed at once, emails are
20
+ numbered in chronological order:
21
+
22
+ [rails][branch][0] Added deprecated warning messages to Float#months and Float#years deprications.
23
+ [rails][branch][1] Enhance testing for fractional days and weeks. Update changelog.
24
+
25
+ Example email:
26
+
27
+ ![Example](http://img171.imageshack.us/img171/954/gitcommitnotifieremailpq3.png "Example")
28
+
29
+ ## Requirements
30
+
31
+ * Ruby 1.8.7 or higher
32
+ * RubyGems
33
+
34
+ We do not support ruby 1.8.6 because of nokogiri gem.
35
+
36
+ ## Installing and Configuring
37
+
38
+ Install the gem:
39
+
40
+ ```bash
41
+ sudo gem install git-commit-notifier
42
+ ```
43
+
44
+ After you installed the gem, you need to configure your git repository. Add a file called
45
+ "post-receive" to the "hooks" directory of your git repository with this content:
46
+
47
+ ```bash
48
+ #!/bin/sh
49
+ git-commit-notifier path_to_config.yml
50
+ ```
51
+
52
+ (Don't forget to make that file executable.)
53
+
54
+ An example for the config file can be found in [config/git-notifier-config.yml.sample](http://github.com/bitboxer/git-commit-notifier/blob/master/config/git-notifier-config.yml.sample).
55
+
56
+ If you want to send mails on each commit instead on each push, you should add a file called "post-commit" with this content:
57
+
58
+ ```bash
59
+ #!/bin/sh
60
+ echo "HEAD^1 HEAD refs/heads/master" | git-commit-notifier path_to_config.yml
61
+ ```
62
+
63
+ ## Integration with Redmine, Bugzilla, MediaWiki
64
+
65
+ Git-commit-notifier supports easy integration with Redmine, Bugzilla and MediaWiki. All you need is to uncomment the according line in the configuration and change the links to your software installations instead of example ones (no trailing slash please).
66
+
67
+ * "BUG 123" sentence in commit message will be replaced with link to bug in Bugzilla.
68
+ * "refs #123" and "fixes #123" sentences in commit message will be replaced with link to issue in Redmine.
69
+ * "[[SomePage]]" sentence in commit message will be replaced with link to page in MediaWiki.
70
+
71
+ ## Integration of links to other websites
72
+
73
+ If you need integration with other websites not supported by git-commit-notifier you can use the message\_map property. For that you need to know the basics of regular expressions syntax.
74
+
75
+ Each key of message\_map is a case sensitive Regexp pattern, and its value is the replacement string.
76
+ Every matched group (that defined by round brackets in regular expression) will be automatically substituted instead of \1-\9 backreferences in replacement string where the number after the backslash informs which group should be substituted instead of the backreference. The first matched group is known as \1.
77
+
78
+ For example, when we need to expand "follow 23" to http://example.com/answer/23, simply type this:
79
+
80
+ ```yaml
81
+ '\bfollow\s+(\d+)': 'http://example.com/answer/\1'
82
+ ```
83
+
84
+ Key and value are each enclosed in single quotes. \b means that "follow" must not be preceded by other word chars, so "befollow" will not match but "be follow" will match. After "follow" we expect one or more spaces followed by group of one or more digits. The \1 in the result url will be replaced with the matched group.
85
+
86
+ More examples can be found in the config file.
87
+
88
+ ## Logic of commits handling
89
+
90
+ The commit notifier uses special files named *previously.txt* to track already
91
+ handled commits to not send repeated messages about commits.
92
+
93
+ By default all commits are tracked through the whole repository so after a merge
94
+ you should not receive messages about those commits already posted in other branches.
95
+
96
+ This behaviour can be changed using unique\_commits\_per\_branch option. When it's true,
97
+ you should receive new message about commit when it's merged in other branch.
98
+
99
+ Yet another option, skip\_commits\_older\_than (in days), should be used to not inform about
100
+ old commits in processes of forking, branching etc.
101
+
102
+ ## Note on Patches/Pull Requests
103
+
104
+ * Fork the project.
105
+ * Make your feature addition or bug fix.
106
+ * Add tests for it. This is important so I don't break it in a
107
+ future version unintentionally.
108
+ * Commit, do not mess with rakefile, version, or history.
109
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
110
+ * Send me a pull request. Bonus points for topic branches.
111
+
112
+ ## Troubleshooting
113
+
114
+ ### Permission denied - /var/git/repo/previously.txt (Errno::EACCES)
115
+
116
+ The file _previously.txt_ is created at the first commit. If you moved the file to the server, you have to check the rights to that file and fix them if needed. The git repo owner needs the right to write into that file.
117
+
118
+ ## Credits
119
+
120
+ Thanks for [Primalgrasp](http://www.primalgrasp.com) and [Undev](http://undev.ru/) for sponsoring this work.
121
+
122
+ ## License
123
+
124
+ MIT License, see the file LICENSE.
125
+
data/Rakefile CHANGED
@@ -26,12 +26,6 @@ end
26
26
 
27
27
  task :default => :spec
28
28
 
29
- begin
30
- require 'metric_fu'
31
- rescue LoadError
32
- $stderr.puts "metric_fu not available. Install it with: gem install metric_fu"
33
- end
34
-
35
29
  require 'rake/rdoctask'
36
30
  Rake::RDocTask.new do |rdoc|
37
31
  version = File.exists?('VERSION') ? IO.read('VERSION') : ""
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.2
1
+ 0.10.0
@@ -7,6 +7,9 @@ ignore_merge: false
7
7
  # Limit lines per diff
8
8
  # lines_per_diff: 300
9
9
 
10
+ # Show file list only when too many files
11
+ # too_many_files: 1000
12
+
10
13
  # defines what branches to email for (defaults to all)
11
14
  # include_branches: ['master', 'some_other_branch']
12
15
  # include_branches: 'master, some_other_branch'
@@ -18,14 +21,14 @@ ignore_merge: false
18
21
  # It can send to multiple destinations, just separate email addresses by ",".
19
22
  mailinglist: developers@example.com,dev2@example.com,dev3@example.com,cto@example.com
20
23
 
21
- # The from addres. If not defined it will use
24
+ # The from address. If not defined it will use
22
25
  # the address that is configured in git
23
26
  # from: sender@example.com
24
27
 
25
28
  # stylesheet file (embedded template/styles.css by default)
26
29
  # stylesheet: /absolute/path/to/readable/stylesheet.css
27
30
 
28
- # select the delivery method: smtp, nntp, or sendmail
31
+ # select the delivery method: smtp, nntp, sendmail, or debug
29
32
  delivery_method: sendmail
30
33
 
31
34
  # Optionally group email by push: don't send an email for each commit when true.
@@ -107,6 +110,9 @@ unique_commits_per_branch: false
107
110
 
108
111
  show_master_branch_name: false
109
112
 
113
+ # ignore whitespace?
114
+ ignore_whitespace: true
115
+
110
116
  # This is developer debugging options. Do not uncomment it if You aren't Jedi
111
117
  # debug:
112
118
  # enabled: true
@@ -5,15 +5,13 @@ Gem::Specification.new do |s|
5
5
  s.version = IO.read('VERSION').chomp
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
- s.authors = ["Bodo Tasche"]
8
+ s.authors = ["Bodo Tasche", "Akzhan Abdulin"]
9
9
  s.date = Time.now.strftime('%Y-%m-%d')
10
- s.default_executable = %q{git-commit-notifier}
11
10
  s.description = %q{This git commit notifier sends html mails with nice diffs for every changed file.}
12
11
  s.email = %q{bodo@bitboxer.de}
13
- s.executables = ["git-commit-notifier"]
14
12
  s.extra_rdoc_files = [
15
13
  "LICENSE",
16
- "README.textile"
14
+ "README.md"
17
15
  ]
18
16
 
19
17
  s.files = `git ls-files`.split("\n")
@@ -31,6 +29,7 @@ Gem::Specification.new do |s|
31
29
  s.add_runtime_dependency(%q<nntp>, [">= 0"])
32
30
  s.add_runtime_dependency(%q<premailer>, [">= 1.7.1"])
33
31
  s.add_runtime_dependency(%q<nokogiri>, [">= 1.4.4"])
32
+ s.add_development_dependency(%q<rake>, ["!= 0.9.0"])
34
33
  s.add_development_dependency(%q<bundler>, [">= 1.0.10"])
35
34
  s.add_development_dependency(%q<code-cleaner>, [">= 0"])
36
35
  s.add_development_dependency(%q<rspec-core>, [">= 0"])
@@ -38,6 +37,5 @@ Gem::Specification.new do |s|
38
37
  s.add_development_dependency(%q<rr>, [">= 0"])
39
38
  s.add_development_dependency(%q<faker>, [">= 0"])
40
39
  s.add_development_dependency(%q<rcov>, [">= 0"])
41
- s.add_development_dependency(%q<metric_fu>, [">= 0"])
42
40
  end
43
41
 
@@ -37,6 +37,10 @@ module GitCommitNotifier
37
37
  include_branches
38
38
  end
39
39
 
40
+ def merge_commit?(result)
41
+ ! result[:commit_info][:merge].nil?
42
+ end
43
+
40
44
  def run(config_name, rev1, rev2, ref_name)
41
45
  @config = File.exists?(config_name) ? YAML::load_file(config_name) : {}
42
46
 
@@ -75,24 +79,25 @@ module GitCommitNotifier
75
79
  info("Sending mail...")
76
80
 
77
81
  diff2html = DiffToHtml.new(Dir.pwd, config)
78
- diff2html.diff_between_revisions(rev1, rev2, prefix, ref_name)
79
-
80
- diffresult = diff2html.result
81
-
82
- if config["ignore_merge"]
83
- diffresult = diffresult.reject do |result|
84
- !result[:commit_info][:merge].nil?
82
+ if config["group_email_by_push"]
83
+ diff2html.diff_between_revisions(rev1, rev2, prefix, ref_name)
84
+ diffresult = diff2html.result
85
+ diff2html.clear_result
86
+
87
+ if config["ignore_merge"]
88
+ diffresult.reject! do |result|
89
+ merge_commit?(result)
90
+ end
85
91
  end
86
- end
87
92
 
88
- if config["group_email_by_push"]
89
93
  text, html = [], []
94
+ result = diffresult.first
95
+ return if result.nil? || !result[:commit_info]
96
+
90
97
  diffresult.each_with_index do |result, i|
91
98
  text << result[:text_content]
92
99
  html << result[:html_content]
93
100
  end
94
- result = diffresult.first
95
- return if result.nil? || !result[:commit_info]
96
101
 
97
102
  emailer = Emailer.new(config,
98
103
  :project_path => project_path,
@@ -108,10 +113,10 @@ module GitCommitNotifier
108
113
  )
109
114
  emailer.send
110
115
  else
111
- diffresult.reverse.each_with_index do |result, i|
112
- next unless result[:commit_info]
113
- nr = number(diffresult.size, i)
114
-
116
+ i = 0
117
+ diff2html.diff_between_revisions(rev1, rev2, prefix, ref_name) do |result|
118
+ next if config["ignore_merge"] && merge_commit?(result)
119
+ nr = number(i)
115
120
  emailer = Emailer.new(config,
116
121
  :project_path => project_path,
117
122
  :recipient => recipient,
@@ -125,14 +130,13 @@ module GitCommitNotifier
125
130
  :ref_name => ref_name
126
131
  )
127
132
  emailer.send
133
+ i += 1
128
134
  end
129
135
  end
130
136
  end
131
137
 
132
- def number(total_entries, i)
133
- return '' if total_entries <= 1
134
- digits = total_entries < 10 ? 1 : 3
135
- '[' + sprintf("%0#{digits}d", i + 1) + ']'
138
+ def number(i)
139
+ "[#{i + 1}]"
136
140
  end
137
141
  end
138
142
  end
@@ -27,10 +27,12 @@ module GitCommitNotifier
27
27
  MAX_COMMITS_PER_ACTION = 10000
28
28
  HANDLED_COMMITS_FILE = 'previously.txt'.freeze
29
29
  NEW_HANDLED_COMMITS_FILE = 'previously_new.txt'.freeze
30
+ GIT_CONFIG_FILE = File.join('.git', 'config').freeze
31
+ DEFAULT_NEW_FILE_RIGHTS = 0664
30
32
  SECS_PER_DAY = 24 * 60 * 60
31
33
 
32
34
  attr_accessor :file_prefix, :current_file_name
33
- attr_reader :result, :branch
35
+ attr_reader :result, :branch, :config
34
36
 
35
37
  def initialize(previous_dir = nil, config = nil)
36
38
  @previous_dir = previous_dir
@@ -43,16 +45,14 @@ module GitCommitNotifier
43
45
 
44
46
  def range_info(range)
45
47
  matches = range.match(/^@@ \-(\S+) \+(\S+)/)
46
- return matches[1..2].map { |m| m.split(',')[0].to_i }
48
+ matches[1..2].map { |m| m.split(',')[0].to_i }
47
49
  end
48
50
 
49
51
  def line_class(line)
50
- if line[:op] == :removal
51
- return " class=\"r\""
52
- elsif line[:op] == :addition
53
- return " class=\"a\""
54
- else
55
- return ''
52
+ case line[:op]
53
+ when :removal then ' class="r"'
54
+ when :addition then ' class="a"'
55
+ else ''
56
56
  end
57
57
  end
58
58
 
@@ -64,26 +64,29 @@ module GitCommitNotifier
64
64
  end
65
65
 
66
66
  def lines_per_diff
67
- @config['lines_per_diff']
67
+ config['lines_per_diff']
68
+ end
69
+
70
+ def ignore_whitespace?
71
+ @config['ignore_whitespace'].nil? || @config['ignore_whitespace']
72
+ end
73
+
74
+ def skip_lines?
75
+ lines_per_diff && (@lines_added >= lines_per_diff)
68
76
  end
69
77
 
70
78
  def add_separator
71
- return if lines_per_diff && @lines_added >= lines_per_diff
72
79
  @diff_result << '<tr class="sep"><td class="sep" colspan="3" title="Unchanged content skipped between diff. blocks">&hellip;</td></tr>'
73
80
  end
74
81
 
82
+ def add_skip_notification
83
+ @diff_result << '<tr><td colspan="3">Diff too large and stripped&hellip;</td></tr>'
84
+ end
85
+
75
86
  def add_line_to_result(line, escape)
76
87
  @lines_added += 1
77
- if lines_per_diff
78
- if @lines_added == lines_per_diff
79
- @diff_result << '<tr><td colspan="3">Diff too large and stripped&hellip;</td></tr>'
80
- end
81
- if @lines_added >= lines_per_diff
82
- return
83
- end
84
- end
85
88
  klass = line_class(line)
86
- content = escape ? escape_content(line[:content]) : line[:content]
89
+ content = (escape == :escape) ? escape_content(line[:content]) : line[:content]
87
90
  padding = '&nbsp;' if klass != ''
88
91
  @diff_result << "<tr#{klass}>\n<td class=\"ln\">#{line[:removed]}</td>\n<td class=\"ln\">#{line[:added]}</td>\n<td>#{padding}#{content}</td></tr>"
89
92
  end
@@ -143,23 +146,27 @@ module GitCommitNotifier
143
146
 
144
147
  def operation_description
145
148
  binary = @binary ? 'binary ' : ''
146
- if @file_removed
147
- op = "Deleted"
149
+ op = if @file_removed
150
+ "Deleted"
148
151
  elsif @file_added
149
- op = "Added"
152
+ "Added"
150
153
  else
151
- op = "Changed"
154
+ "Changed"
152
155
  end
153
156
 
154
157
  file_name = @current_file_name
155
158
 
156
- if (@config["link_files"] && @config["link_files"] == "gitweb" && @config["gitweb"])
157
- file_name = "<a href='#{@config['gitweb']['path']}?p=#{@config['gitweb']['project']};f=#{file_name};hb=HEAD'>#{file_name}</a>"
158
- elsif (@config["link_files"] && @config["link_files"] == "gitorious" && @config["gitorious"])
159
- file_name = "<a href='#{@config['gitorious']['path']}/#{@config['gitorious']['project']}/#{@config['gitorious']['repository']}/blobs/#{branch_name}/#{file_name}'>#{file_name}</a>"
160
- elsif (@config["link_files"] && @config["link_files"] == "cgit" && @config["cgit"])
161
- file_name = "<a href='#{@config['cgit']['path']}/#{@config['cgit']['project']}/tree/#{file_name}'>#{file_name}</a>"
162
- end
159
+ if config['link_files']
160
+ file_name = if config["link_files"] == "gitweb" && config["gitweb"]
161
+ "<a href='#{config['gitweb']['path']}?p=#{Git.repo_name}.git;f=#{file_name};h=#{@current_sha};hb=#{@current_commit}'>#{file_name}</a>"
162
+ elsif config["link_files"] == "gitorious" && config["gitorious"]
163
+ "<a href='#{config['gitorious']['path']}/#{config['gitorious']['project']}/#{config['gitorious']['repository']}/blobs/#{branch_name}/#{file_name}'>#{file_name}</a>"
164
+ elsif config["link_files"] == "cgit" && config["cgit"]
165
+ "<a href='#{config['cgit']['path']}/#{config['cgit']['project']}/tree/#{file_name}'>#{file_name}</a>"
166
+ else
167
+ file_name
168
+ end
169
+ end
163
170
 
164
171
  header = "#{op} #{binary}file #{file_name}"
165
172
  "<h2>#{header}</h2>\n"
@@ -178,18 +185,22 @@ module GitCommitNotifier
178
185
  def add_changes_to_result
179
186
  return if @current_file_name.nil?
180
187
  @diff_result << operation_description
181
- @diff_result << '<table>'
182
- unless @diff_lines.empty?
188
+ if !@diff_lines.empty? && !@too_many_files
189
+ @diff_result << '<table>'
183
190
  removals = []
184
191
  additions = []
185
192
  @diff_lines.each_with_index do |line, index|
193
+ if skip_lines?
194
+ add_skip_notification
195
+ break
196
+ end
186
197
  removals << line if line[:op] == :removal
187
198
  additions << line if line[:op] == :addition
188
199
  if line[:op] == :unchanged || index == @diff_lines.size - 1 # unchanged line or end of block, add prev lines to result
189
200
  if removals.size > 0 && additions.size > 0 # block of removed and added lines - perform intelligent diff
190
- add_block_to_results(lcs_diff(removals, additions), escape = false)
201
+ add_block_to_results(lcs_diff(removals, additions), :dont_escape)
191
202
  else # some lines removed or added - no need to perform intelligent diff
192
- add_block_to_results(removals + additions, escape = true)
203
+ add_block_to_results(removals + additions, :escape)
193
204
  end
194
205
  removals = []
195
206
  additions = []
@@ -197,11 +208,12 @@ module GitCommitNotifier
197
208
  prev_line = @diff_lines[index - 1]
198
209
  add_separator unless lines_are_sequential?(prev_line, line)
199
210
  end
200
- add_line_to_result(line, escape = true) if line[:op] == :unchanged
211
+ add_line_to_result(line, :escape) if line[:op] == :unchanged
201
212
  end
213
+
202
214
  end
203
- @diff_lines = []
204
215
  @diff_result << '</table>'
216
+ @diff_lines = []
205
217
  end
206
218
  # reset values
207
219
  @right_ln = nil
@@ -211,6 +223,9 @@ module GitCommitNotifier
211
223
  @binary = false
212
224
  end
213
225
 
226
+ RE_DIFF_FILE_NAME = /^diff\s\-\-git\sa\/(.*)\sb\//
227
+ RE_DIFF_SHA = /^index [0-9a-fA-F]+\.\.([0-9a-fA-F]+)/
228
+
214
229
  def diff_for_revision(content)
215
230
  @left_ln = @right_ln = nil
216
231
 
@@ -218,16 +233,37 @@ module GitCommitNotifier
218
233
  @diff_lines = []
219
234
  @removed_files = []
220
235
  @current_file_name = nil
236
+ @current_sha = nil
237
+ @too_many_files = false
221
238
 
222
- content.split("\n").each do |line|
223
- if line =~ /^diff\s\-\-git\sa\/(.*)\sb\//
239
+ lines = content.split("\n")
240
+
241
+ if config['too_many_files'] && config['too_many_files'].to_i > 0
242
+ file_count = lines.inject(0) do |count, line|
243
+ (line =~ RE_DIFF_FILE_NAME) ? (count + 1) : count
244
+ end
245
+
246
+ if file_count >= config['too_many_files'].to_i
247
+ @too_many_files = true
248
+ end
249
+ end
250
+
251
+ lines.each do |line|
252
+ case line
253
+ when RE_DIFF_FILE_NAME then
224
254
  file_name = $1
225
255
  add_changes_to_result
226
256
  @current_file_name = file_name
257
+ when RE_DIFF_SHA then
258
+ @current_sha = $1
259
+ else
260
+ op = line[0, 1]
261
+ if @left_ln.nil? || op == '@'
262
+ process_info_line(line, op)
263
+ else
264
+ process_code_line(line, op)
265
+ end
227
266
  end
228
-
229
- op = line[0,1]
230
- @left_ln.nil? || op == '@' ? process_info_line(line, op) : process_code_line(line, op)
231
267
  end
232
268
  add_changes_to_result
233
269
  @diff_result.join("\n")
@@ -288,7 +324,7 @@ module GitCommitNotifier
288
324
  elsif line =~ /^Date/
289
325
  result[:date] = line[8..-1]
290
326
  elsif line =~ /^Merge/
291
- result[:merge] = line[8..-1]
327
+ result[:merge] = line[7..-1]
292
328
  else
293
329
  clean_line = line.strip
294
330
  result[:message] << clean_line unless clean_line.empty?
@@ -315,18 +351,14 @@ module GitCommitNotifier
315
351
  end
316
352
 
317
353
  def unique_commits_per_branch?
318
- !!@config['unique_commits_per_branch']
354
+ ! ! config['unique_commits_per_branch']
319
355
  end
320
356
 
321
357
  def get_previous_commits(previous_file)
322
- previous_list = []
323
- if File.exists?(previous_file)
324
- lines = IO.read(previous_file)
325
- lines = lines.lines if lines.respond_to?(:lines) # Ruby 1.9 tweak
326
- previous_list = lines.to_a.map { |s| s.chomp }.compact.uniq
327
- lines = nil
328
- end
329
- previous_list
358
+ return [] unless File.exists?(previous_file)
359
+ lines = IO.read(previous_file)
360
+ lines = lines.lines if lines.respond_to?(:lines) # Ruby 1.9 tweak
361
+ lines.to_a.map { |s| s.chomp }.compact.uniq
330
362
  end
331
363
 
332
364
  def previous_dir
@@ -347,12 +379,21 @@ module GitCommitNotifier
347
379
  File.join(previous_dir, new_name)
348
380
  end
349
381
 
382
+ def new_file_rights
383
+ git_config_path = File.expand_path(GIT_CONFIG_FILE, '.')
384
+ # we should copy rights from git config if possible
385
+ File.stat(git_config_path).mode
386
+ rescue
387
+ DEFAULT_NEW_FILE_RIGHTS
388
+ end
389
+
350
390
  def save_handled_commits(previous_list, flatten_commits)
351
391
  return if flatten_commits.empty?
352
392
  current_list = (previous_list + flatten_commits).last(MAX_COMMITS_PER_ACTION)
353
393
 
354
394
  # use new file, unlink and rename to make it more atomic
355
395
  File.open(new_file_path, 'w') { |f| f << current_list.join("\n") }
396
+ File.chmod(new_file_rights, new_file_path) rescue nil
356
397
  File.unlink(previous_file_path) if File.exists?(previous_file_path)
357
398
  File.rename(new_file_path, previous_file_path)
358
399
  end
@@ -370,65 +411,102 @@ module GitCommitNotifier
370
411
  end
371
412
 
372
413
  def old_commit?(commit_info)
373
- return false if !@config.include?('skip_commits_older_than') || @config['skip_commits_older_than'].to_i <= 0
414
+ return false if ! config.include?('skip_commits_older_than') || (config['skip_commits_older_than'].to_i <= 0)
374
415
  commit_when = Time.parse(commit_info[:date])
375
- (Time.now - commit_when) > (SECS_PER_DAY * @config['skip_commits_older_than'].to_i)
416
+ (Time.now - commit_when) > (SECS_PER_DAY * config['skip_commits_older_than'].to_i)
417
+ end
418
+
419
+ def merge_commit?(commit_info)
420
+ ! commit_info[:merge].nil?
421
+ end
422
+
423
+ def diff_for_commit(commit)
424
+ @current_commit = commit
425
+ raw_diff = Git.show(commit, ignore_whitespace?)
426
+ raise "git show output is empty" if raw_diff.empty?
427
+
428
+ commit_info = extract_commit_info_from_git_show_output(raw_diff)
429
+ return nil if old_commit?(commit_info)
430
+ changed_files = ""
431
+ if merge_commit?(commit_info)
432
+ merge_revisions = commit_info[:merge].split
433
+ changed_files += "Changed files:\n\n#{Git.changed_files(*merge_revisions)}\n"
434
+ end
435
+
436
+ title = "<div class=\"title\">"
437
+ title += "<strong>Message:</strong> #{message_array_as_html(commit_info[:message])}<br />\n"
438
+ title += "<strong>Commit:</strong> "
439
+
440
+ title += if config["link_files"]
441
+ if config["link_files"] == "gitweb" && config["gitweb"]
442
+ "<a href='#{config['gitweb']['path']}?p=#{Git.repo_name}.git;a=commitdiff;h=#{commit_info[:commit]}'>#{commit_info[:commit]}</a>"
443
+ elsif config["link_files"] == "gitorious" && config["gitorious"]
444
+ "<a href='#{config['gitorious']['path']}/#{config['gitorious']['project']}/#{config['gitorious']['repository']}/commit/#{commit_info[:commit]}'>#{commit_info[:commit]}</a>"
445
+ elsif config["link_files"] == "trac" && config["trac"]
446
+ "<a href='#{config['trac']['path']}/#{commit_info[:commit]}'>#{commit_info[:commit]}</a>"
447
+ elsif config["link_files"] == "cgit" && config["cgit"]
448
+ "<a href='#{config['cgit']['path']}/#{config['cgit']['project']}/commit/?id=#{commit_info[:commit]}'>#{commit_info[:commit]}</a>"
449
+ else
450
+ " #{commit_info[:commit]}"
451
+ end
452
+ else
453
+ " #{commit_info[:commit]}"
454
+ end
455
+
456
+ title += "<br />\n"
457
+
458
+ title += "<strong>Branch:</strong> #{CGI.escapeHTML(branch_name)}\n<br />"
459
+ title += "<strong>Date:</strong> #{CGI.escapeHTML commit_info[:date]}\n<br />"
460
+ title += "<strong>Author:</strong> #{CGI.escapeHTML(commit_info[:author])} &lt;#{commit_info[:email]}&gt;\n</div>"
461
+ text = "#{raw_diff}"
462
+ text += "#{changed_files}\n\n\n"
463
+
464
+ html = title
465
+ html += diff_for_revision(extract_diff_from_git_show_output(raw_diff))
466
+ html += message_array_as_html(changed_files.split("\n"))
467
+ html += "<br /><br />"
468
+ commit_info[:message] = first_sentence(commit_info[:message])
469
+
470
+ {
471
+ :commit_info => commit_info,
472
+ :html_content => html,
473
+ :text_content => text
474
+ }
475
+ end
476
+
477
+ def clear_result
478
+ @result = []
376
479
  end
377
480
 
378
481
  def diff_between_revisions(rev1, rev2, repo, branch)
379
482
  @branch = branch
380
483
  @result = []
381
- if rev1 == rev2
382
- commits = [rev1]
484
+ commits = if rev1 == rev2
485
+ [ rev1 ]
383
486
  elsif rev1 =~ /^0+$/
384
487
  # creating a new remote branch
385
- commits = Git.branch_commits(branch)
488
+ Git.branch_commits(branch)
386
489
  elsif rev2 =~ /^0+$/
387
490
  # deleting an existing remote branch
388
- commits = []
491
+ []
389
492
  else
390
493
  log = Git.log(rev1, rev2)
391
- commits = log.scan(/^commit\s([a-f0-9]+)/).map { |a| a.first }
494
+ log.scan(/^commit\s([a-f0-9]+)/).map { |a| a.first }
392
495
  end
393
496
 
394
497
  commits = check_handled_commits(commits)
395
498
 
396
- commits.each_with_index do |commit, i|
397
- raw_diff = Git.show(commit)
398
- raise "git show output is empty" if raw_diff.empty?
399
-
400
- commit_info = extract_commit_info_from_git_show_output(raw_diff)
401
- next if old_commit?(commit_info)
402
-
403
- title = "<div class=\"title\">"
404
- title += "<strong>Message:</strong> #{message_array_as_html commit_info[:message]}<br />\n"
405
- title += "<strong>Commit:</strong> "
406
-
407
- if (@config["link_files"] && @config["link_files"] == "gitweb" && @config["gitweb"])
408
- title += "<a href='#{@config['gitweb']['path']}?p=#{@config['gitweb']['project']};a=commitdiff;h=#{commit_info[:commit]}'>#{commit_info[:commit]}</a>"
409
- elsif (@config["link_files"] && @config["link_files"] == "gitorious" && @config["gitorious"])
410
- title += "<a href='#{@config['gitorious']['path']}/#{@config['gitorious']['project']}/#{@config['gitorious']['repository']}/commit/#{commit_info[:commit]}'>#{commit_info[:commit]}</a>"
411
- elsif (@config["link_files"] && @config["link_files"] == "trac" && @config["trac"])
412
- title += "<a href='#{@config['trac']['path']}/#{commit_info[:commit]}'>#{commit_info[:commit]}</a>"
413
- elsif (@config["link_files"] && @config["link_files"] == "cgit" && @config["cgit"])
414
- title += "<a href='#{@config['cgit']['path']}/#{@config['cgit']['project']}/commit/?id=#{commit_info[:commit]}'>#{commit_info[:commit]}</a>"
415
- else
416
- title += " #{commit_info[:commit]}"
499
+ commits.each do |commit|
500
+ @lines_added = 0 unless config["group_email_by_push"]
501
+ begin
502
+ commit_result = diff_for_commit(commit)
503
+ next if commit_result.nil?
504
+ if block_given?
505
+ yield commit_result
506
+ else
507
+ @result << commit_result
508
+ end
417
509
  end
418
-
419
- title += "<br />\n"
420
-
421
- title += "<strong>Branch:</strong> #{CGI.escapeHTML(branch_name)}\n<br />"
422
- title += "<strong>Date:</strong> #{CGI.escapeHTML commit_info[:date]}\n<br />"
423
- title += "<strong>Author:</strong> #{CGI.escapeHTML(commit_info[:author])} &lt;#{commit_info[:email]}&gt;\n</div>"
424
-
425
- text = "#{raw_diff}\n\n\n"
426
-
427
- html = title
428
- html += diff_for_revision(extract_diff_from_git_show_output(raw_diff))
429
- html += "<br /><br />"
430
- commit_info[:message] = first_sentence(commit_info[:message])
431
- @result << {:commit_info => commit_info, :html_content => html, :text_content => text }
432
510
  end
433
511
  end
434
512
 
@@ -445,8 +523,8 @@ module GitCommitNotifier
445
523
  end
446
524
 
447
525
  def do_message_integration(message)
448
- return message unless @config['message_integration'].respond_to?(:each_pair)
449
- @config['message_integration'].each_pair do |pm, url|
526
+ return message unless config['message_integration'].respond_to?(:each_pair)
527
+ config['message_integration'].each_pair do |pm, url|
450
528
  pm_def = DiffToHtml::INTEGRATION_MAP[pm.to_sym] or next
451
529
  replace_with = pm_def[:replace_with]
452
530
  replace_with = replace_with.kind_of?(Proc) ? lambda { |m| pm_def[:replace_with].call(m, url) } : replace_with.gsub('#{url}', url)
@@ -456,8 +534,8 @@ module GitCommitNotifier
456
534
  end
457
535
 
458
536
  def do_message_map(message)
459
- return message unless @config['message_map'].respond_to?(:each_pair)
460
- @config['message_map'].each_pair do |search_for, replace_with|
537
+ return message unless config['message_map'].respond_to?(:each_pair)
538
+ config['message_map'].each_pair do |search_for, replace_with|
461
539
  message_replace!(message, Regexp.new(search_for), replace_with)
462
540
  end
463
541
  message