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 +9 -0
- data/README.md +125 -0
- data/Rakefile +0 -6
- data/VERSION +1 -1
- data/config/git-notifier-config.yml.sample +8 -2
- data/git-commit-notifier.gemspec +3 -5
- data/lib/git_commit_notifier/commit_hook.rb +23 -19
- data/lib/git_commit_notifier/diff_to_html.rb +175 -97
- data/lib/git_commit_notifier/emailer.rb +7 -0
- data/lib/git_commit_notifier/executor.rb +5 -5
- data/lib/git_commit_notifier/git.rb +15 -3
- data/spec/fixtures/git_log_name_status +4 -0
- data/spec/lib/git_commit_notifier/commit_hook_spec.rb +4 -1
- data/spec/lib/git_commit_notifier/diff_to_html_spec.rb +45 -15
- data/spec/lib/git_commit_notifier/git_spec.rb +18 -3
- data/spec/spec_helper.rb +5 -0
- metadata +129 -125
- data/README.textile +0 -111
data/.travis.yml
ADDED
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.
|
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
|
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
|
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
|
data/git-commit-notifier.gemspec
CHANGED
@@ -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.
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
!result
|
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
|
-
|
112
|
-
|
113
|
-
|
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(
|
133
|
-
|
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
|
-
|
48
|
+
matches[1..2].map { |m| m.split(',')[0].to_i }
|
47
49
|
end
|
48
50
|
|
49
51
|
def line_class(line)
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
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">…</td></tr>'
|
73
80
|
end
|
74
81
|
|
82
|
+
def add_skip_notification
|
83
|
+
@diff_result << '<tr><td colspan="3">Diff too large and stripped…</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…</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 = ' ' 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
|
-
|
149
|
+
op = if @file_removed
|
150
|
+
"Deleted"
|
148
151
|
elsif @file_added
|
149
|
-
|
152
|
+
"Added"
|
150
153
|
else
|
151
|
-
|
154
|
+
"Changed"
|
152
155
|
end
|
153
156
|
|
154
157
|
file_name = @current_file_name
|
155
158
|
|
156
|
-
if
|
157
|
-
file_name =
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
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
|
-
|
182
|
-
|
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),
|
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
|
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
|
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")
|
223
|
-
|
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[
|
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
|
-
|
354
|
+
! ! config['unique_commits_per_branch']
|
319
355
|
end
|
320
356
|
|
321
357
|
def get_previous_commits(previous_file)
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
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
|
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 *
|
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])} <#{commit_info[:email]}>\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
|
-
|
484
|
+
commits = if rev1 == rev2
|
485
|
+
[ rev1 ]
|
383
486
|
elsif rev1 =~ /^0+$/
|
384
487
|
# creating a new remote branch
|
385
|
-
|
488
|
+
Git.branch_commits(branch)
|
386
489
|
elsif rev2 =~ /^0+$/
|
387
490
|
# deleting an existing remote branch
|
388
|
-
|
491
|
+
[]
|
389
492
|
else
|
390
493
|
log = Git.log(rev1, rev2)
|
391
|
-
|
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.
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
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])} <#{commit_info[:email]}>\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
|
449
|
-
|
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
|
460
|
-
|
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
|