git-commit-mailer 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/README.md +47 -0
- data/Rakefile +23 -0
- data/bin/git-commit-mailer +137 -0
- data/git-commit-mailer.gemspec +26 -0
- data/lib/git-commit-mailer.rb +1379 -0
- data/lib/git-commit-mailer/commit-info.rb +291 -0
- data/lib/git-commit-mailer/file-diff.rb +356 -0
- data/lib/git-commit-mailer/html-mail-body-formatter.rb +563 -0
- data/lib/git-commit-mailer/info.rb +51 -0
- data/lib/git-commit-mailer/mail-body-formatter.rb +97 -0
- data/lib/git-commit-mailer/push-info.rb +100 -0
- data/lib/git-commit-mailer/text-mail-body-formatter.rb +87 -0
- data/lib/git-commit-mailer/version.rb +4 -0
- data/license/GPL-3.txt +674 -0
- metadata +122 -0
@@ -0,0 +1,291 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Copyright (C) 2009 Ryo Onodera <onodera@clear-code.com>
|
4
|
+
# Copyright (C) 2012-2014 Kouhei Sutou <kou@clear-code.com>
|
5
|
+
#
|
6
|
+
# This program is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
class GitCommitMailer
|
20
|
+
class CommitInfo < Info
|
21
|
+
class << self
|
22
|
+
def unescape_file_path(file_path)
|
23
|
+
if file_path =~ /\A"(.*)"\z/
|
24
|
+
escaped_file_path = $1
|
25
|
+
if escaped_file_path.respond_to?(:encoding)
|
26
|
+
encoding = escaped_file_path.encoding
|
27
|
+
else
|
28
|
+
encoding = nil
|
29
|
+
end
|
30
|
+
unescaped_file_path = escaped_file_path.gsub(/\\\\/, '\\').
|
31
|
+
gsub(/\\\"/, '"').
|
32
|
+
gsub(/\\([0-9]{1,3})/) do
|
33
|
+
$1.to_i(8).chr
|
34
|
+
end
|
35
|
+
unescaped_file_path.force_encoding(encoding) if encoding
|
36
|
+
unescaped_file_path
|
37
|
+
else
|
38
|
+
file_path
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_reader :mailer, :revision, :reference
|
44
|
+
attr_reader :added_files, :copied_files, :deleted_files, :updated_files
|
45
|
+
attr_reader :renamed_files, :type_changed_files, :diffs
|
46
|
+
attr_reader :subject, :author_name, :author_email, :date, :summary
|
47
|
+
attr_accessor :merge_status
|
48
|
+
attr_writer :reference
|
49
|
+
attr_reader :merge_revisions
|
50
|
+
def initialize(mailer, reference, revision)
|
51
|
+
@mailer = mailer
|
52
|
+
@reference = reference
|
53
|
+
@revision = revision
|
54
|
+
|
55
|
+
@files = []
|
56
|
+
@added_files = []
|
57
|
+
@copied_files = []
|
58
|
+
@deleted_files = []
|
59
|
+
@updated_files = []
|
60
|
+
@renamed_files = []
|
61
|
+
@type_changed_files = []
|
62
|
+
|
63
|
+
set_records
|
64
|
+
parse_file_status
|
65
|
+
parse_diff
|
66
|
+
|
67
|
+
@merge_status = []
|
68
|
+
@merge_revisions = []
|
69
|
+
end
|
70
|
+
|
71
|
+
def first_parent
|
72
|
+
return nil if @parent_revisions.length.zero?
|
73
|
+
|
74
|
+
@parent_revisions[0]
|
75
|
+
end
|
76
|
+
|
77
|
+
def other_parents
|
78
|
+
return [] if @parent_revisions.length.zero?
|
79
|
+
|
80
|
+
@parent_revisions[1..-1]
|
81
|
+
end
|
82
|
+
|
83
|
+
def merge?
|
84
|
+
@parent_revisions.length >= 2
|
85
|
+
end
|
86
|
+
|
87
|
+
def message_id
|
88
|
+
"<#{@revision}@#{self.class.host_name}>"
|
89
|
+
end
|
90
|
+
|
91
|
+
def headers
|
92
|
+
[
|
93
|
+
"X-Git-Author: #{@author_name}",
|
94
|
+
"X-Git-Revision: #{@revision}",
|
95
|
+
# "X-Git-Repository: #{path}",
|
96
|
+
"X-Git-Repository: XXX",
|
97
|
+
"X-Git-Commit-Id: #{@revision}",
|
98
|
+
"Message-ID: #{message_id}",
|
99
|
+
*related_mail_headers
|
100
|
+
]
|
101
|
+
end
|
102
|
+
|
103
|
+
def related_mail_headers
|
104
|
+
headers = []
|
105
|
+
@merge_revisions.each do |merge_revision|
|
106
|
+
merge_message_id = "<#{merge_revision}@#{self.class.host_name}>"
|
107
|
+
headers << "References: #{merge_message_id}"
|
108
|
+
headers << "In-Reply-To: #{merge_message_id}"
|
109
|
+
end
|
110
|
+
headers
|
111
|
+
end
|
112
|
+
|
113
|
+
def format_mail_subject
|
114
|
+
affected_path_info = ""
|
115
|
+
if @mailer.show_path?
|
116
|
+
_affected_paths = affected_paths
|
117
|
+
unless _affected_paths.empty?
|
118
|
+
affected_path_info = " (#{_affected_paths.join(',')})"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
"[#{short_reference}#{affected_path_info}] " + subject
|
123
|
+
end
|
124
|
+
|
125
|
+
def format_mail_body_text
|
126
|
+
TextMailBodyFormatter.new(self).format
|
127
|
+
end
|
128
|
+
|
129
|
+
def format_mail_body_html
|
130
|
+
HTMLMailBodyFormatter.new(self).format
|
131
|
+
end
|
132
|
+
|
133
|
+
def short_revision
|
134
|
+
GitCommitMailer.short_revision(@revision)
|
135
|
+
end
|
136
|
+
|
137
|
+
def file_index(name)
|
138
|
+
@files.index(name)
|
139
|
+
end
|
140
|
+
|
141
|
+
def rss_title
|
142
|
+
format_mail_subject
|
143
|
+
end
|
144
|
+
|
145
|
+
def rss_content
|
146
|
+
"<pre>#{ERB::Util.h(format_mail_body_text)}</pre>"
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
def sub_paths(prefix)
|
151
|
+
prefixes = prefix.split(/\/+/)
|
152
|
+
results = []
|
153
|
+
@diffs.each do |diff|
|
154
|
+
paths = diff.file_path.split(/\/+/)
|
155
|
+
if prefixes.size < paths.size and prefixes == paths[0, prefixes.size]
|
156
|
+
results << paths[prefixes.size]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
results
|
160
|
+
end
|
161
|
+
|
162
|
+
def affected_paths
|
163
|
+
paths = []
|
164
|
+
sub_paths = sub_paths('')
|
165
|
+
paths.concat(sub_paths)
|
166
|
+
paths.uniq
|
167
|
+
end
|
168
|
+
|
169
|
+
def set_records
|
170
|
+
author_name, author_email, date, subject, parent_revisions =
|
171
|
+
get_records(["%an", "%ae", "%at", "%s", "%P"])
|
172
|
+
@author_name = author_name
|
173
|
+
@author_email = author_email
|
174
|
+
@date = Time.at(date.to_i)
|
175
|
+
@subject = subject
|
176
|
+
@parent_revisions = parent_revisions.split
|
177
|
+
@summary = git("log -n 1 --pretty=format:%s%n%n%b #{@revision}")
|
178
|
+
end
|
179
|
+
|
180
|
+
def parse_diff
|
181
|
+
@diffs = []
|
182
|
+
output = []
|
183
|
+
n_bytes = 0
|
184
|
+
git("log -n 1 --pretty=format:'' -C -p #{@revision}") do |io|
|
185
|
+
io.each_line do |line|
|
186
|
+
n_bytes += line.bytesize
|
187
|
+
break if n_bytes > mailer.max_diff_size
|
188
|
+
utf8_line = force_utf8(line) || "(binary line)\n"
|
189
|
+
output << utf8_line
|
190
|
+
end
|
191
|
+
end
|
192
|
+
return if output.empty?
|
193
|
+
|
194
|
+
output.shift if output.first.strip.empty?
|
195
|
+
|
196
|
+
lines = []
|
197
|
+
|
198
|
+
line = output.shift
|
199
|
+
lines << line.chomp if line # take out the very first 'diff --git' header
|
200
|
+
while line = output.shift
|
201
|
+
line.chomp!
|
202
|
+
case line
|
203
|
+
when /\Adiff --git/
|
204
|
+
@diffs << create_file_diff(lines)
|
205
|
+
lines = [line]
|
206
|
+
else
|
207
|
+
lines << line
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# create the last diff terminated by the EOF
|
212
|
+
@diffs << create_file_diff(lines) if lines.length > 0
|
213
|
+
end
|
214
|
+
|
215
|
+
def create_file_diff(lines)
|
216
|
+
diff = FileDiff.new(@mailer, lines, @revision)
|
217
|
+
diff.index = @files.index(diff.file_path)
|
218
|
+
diff
|
219
|
+
end
|
220
|
+
|
221
|
+
def parse_file_status
|
222
|
+
git("log -n 1 --pretty=format:'' -C --name-status #{@revision}").
|
223
|
+
lines.each do |line|
|
224
|
+
line.rstrip!
|
225
|
+
next if line.empty?
|
226
|
+
case line
|
227
|
+
when /\A([^\t]*?)\t([^\t]*?)\z/
|
228
|
+
status = $1
|
229
|
+
file = CommitInfo.unescape_file_path($2)
|
230
|
+
|
231
|
+
case status
|
232
|
+
when /^A/ # Added
|
233
|
+
@added_files << file
|
234
|
+
when /^M/ # Modified
|
235
|
+
@updated_files << file
|
236
|
+
when /^D/ # Deleted
|
237
|
+
@deleted_files << file
|
238
|
+
when /^T/ # File Type Changed
|
239
|
+
@type_changed_files << file
|
240
|
+
else
|
241
|
+
raise "unsupported status type: #{line.inspect}"
|
242
|
+
end
|
243
|
+
|
244
|
+
@files << file
|
245
|
+
when /\A([^\t]*?)\t([^\t]*?)\t([^\t]*?)\z/
|
246
|
+
status = $1
|
247
|
+
from_file = CommitInfo.unescape_file_path($2)
|
248
|
+
to_file = CommitInfo.unescape_file_path($3)
|
249
|
+
|
250
|
+
case status
|
251
|
+
when /^R/ # Renamed
|
252
|
+
@renamed_files << [from_file, to_file]
|
253
|
+
when /^C/ # Copied
|
254
|
+
@copied_files << [from_file, to_file]
|
255
|
+
else
|
256
|
+
raise "unsupported status type: #{line.inspect}"
|
257
|
+
end
|
258
|
+
|
259
|
+
@files << to_file
|
260
|
+
else
|
261
|
+
raise "unsupported status type: #{line.inspect}"
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def force_utf8(string)
|
267
|
+
string.force_encoding("UTF-8")
|
268
|
+
return string if string.valid_encoding?
|
269
|
+
|
270
|
+
guess_encodings = [
|
271
|
+
"Windows-31J",
|
272
|
+
"EUC-JP",
|
273
|
+
]
|
274
|
+
guess_encodings.each do |guess_encoding|
|
275
|
+
string.force_encoding(guess_encoding)
|
276
|
+
next unless string.valid_encoding?
|
277
|
+
begin
|
278
|
+
return string.encode("UTF-8")
|
279
|
+
rescue EncodingError
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
nil
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
require "git-commit-mailer/file-diff"
|
289
|
+
require "git-commit-mailer/mail-body-formatter"
|
290
|
+
require "git-commit-mailer/text-mail-body-formatter"
|
291
|
+
require "git-commit-mailer/html-mail-body-formatter"
|
@@ -0,0 +1,356 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Copyright (C) 2009 Ryo Onodera <onodera@clear-code.com>
|
4
|
+
# Copyright (C) 2012-2014 Kouhei Sutou <kou@clear-code.com>
|
5
|
+
#
|
6
|
+
# This program is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
class GitCommitMailer
|
20
|
+
class FileDiff
|
21
|
+
CHANGED_TYPE = {
|
22
|
+
:added => "Added",
|
23
|
+
:modified => "Modified",
|
24
|
+
:deleted => "Deleted",
|
25
|
+
:copied => "Copied",
|
26
|
+
:renamed => "Renamed",
|
27
|
+
}
|
28
|
+
|
29
|
+
attr_reader :changes
|
30
|
+
attr_accessor :index
|
31
|
+
def initialize(mailer, lines, revision)
|
32
|
+
@mailer = mailer
|
33
|
+
@index = nil
|
34
|
+
@body = ''
|
35
|
+
@changes = []
|
36
|
+
|
37
|
+
@type = :modified
|
38
|
+
@is_binary = false
|
39
|
+
@is_mode_changed = false
|
40
|
+
|
41
|
+
@old_blob = @new_blob = nil
|
42
|
+
|
43
|
+
parse_header(lines)
|
44
|
+
detect_metadata(revision)
|
45
|
+
parse_extended_headers(lines)
|
46
|
+
parse_body(lines)
|
47
|
+
end
|
48
|
+
|
49
|
+
def file_path
|
50
|
+
@to_file
|
51
|
+
end
|
52
|
+
|
53
|
+
def format_header
|
54
|
+
header = " #{CHANGED_TYPE[@type]}: #{@to_file} "
|
55
|
+
header << "(+#{@added_line} -#{@deleted_line})"
|
56
|
+
header << "#{format_file_mode}#{format_similarity_index}\n"
|
57
|
+
header << " Mode: #{@old_mode} -> #{@new_mode}\n" if @is_mode_changed
|
58
|
+
header << diff_separator
|
59
|
+
header
|
60
|
+
end
|
61
|
+
|
62
|
+
def format
|
63
|
+
formatted_diff = format_header
|
64
|
+
|
65
|
+
if @mailer.add_diff?
|
66
|
+
formatted_diff << headers + @body
|
67
|
+
else
|
68
|
+
formatted_diff << git_command
|
69
|
+
end
|
70
|
+
|
71
|
+
formatted_diff
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
def extract_file_path(file_path)
|
76
|
+
case CommitInfo.unescape_file_path(file_path)
|
77
|
+
when /\A[ab]\/(.*)\z/
|
78
|
+
$1
|
79
|
+
else
|
80
|
+
raise "unknown file path format: #{@to_file}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def parse_header(lines)
|
85
|
+
line = lines.shift.strip
|
86
|
+
if line =~ /\Adiff --git ("?a\/.*) ("?b\/.*)/
|
87
|
+
@from_file = extract_file_path($1)
|
88
|
+
@to_file = extract_file_path($2)
|
89
|
+
else
|
90
|
+
raise "Unexpected diff header format: #{line}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def detect_metadata(revision)
|
95
|
+
@new_revision = revision
|
96
|
+
@new_date = Time.at(@mailer.get_record(@new_revision, "%at").to_i)
|
97
|
+
|
98
|
+
begin
|
99
|
+
@old_revision = @mailer.parent_commit(revision)
|
100
|
+
@old_date = Time.at(@mailer.get_record(@old_revision, "%at").to_i)
|
101
|
+
rescue NoParentCommit
|
102
|
+
@old_revision = '0' * 40
|
103
|
+
@old_date = nil
|
104
|
+
end
|
105
|
+
# @old_revision = @mailer.parent_commit(revision)
|
106
|
+
end
|
107
|
+
|
108
|
+
def parse_ordinary_change(line)
|
109
|
+
case line
|
110
|
+
when /\A--- (a\/.*|"a\/.*"|\/dev\/null)\z/
|
111
|
+
@minus_file = CommitInfo.unescape_file_path($1)
|
112
|
+
@type = :added if $1 == '/dev/null'
|
113
|
+
when /\A\+\+\+ (b\/.*|"b\/.*"|\/dev\/null)\z/
|
114
|
+
@plus_file = CommitInfo.unescape_file_path($1)
|
115
|
+
@type = :deleted if $1 == '/dev/null'
|
116
|
+
when /\Aindex ([0-9a-f]{7,})\.\.([0-9a-f]{7,})/
|
117
|
+
@old_blob = $1
|
118
|
+
@new_blob = $2
|
119
|
+
else
|
120
|
+
return false
|
121
|
+
end
|
122
|
+
true
|
123
|
+
end
|
124
|
+
|
125
|
+
def parse_add_and_remove(line)
|
126
|
+
case line
|
127
|
+
when /\Anew file mode (.*)\z/
|
128
|
+
@type = :added
|
129
|
+
@new_file_mode = $1
|
130
|
+
when /\Adeleted file mode (.*)\z/
|
131
|
+
@type = :deleted
|
132
|
+
@deleted_file_mode = $1
|
133
|
+
else
|
134
|
+
return false
|
135
|
+
end
|
136
|
+
true
|
137
|
+
end
|
138
|
+
|
139
|
+
def parse_copy_and_rename(line)
|
140
|
+
case line
|
141
|
+
when /\Arename (from|to) (.*)\z/
|
142
|
+
@type = :renamed
|
143
|
+
when /\Acopy (from|to) (.*)\z/
|
144
|
+
@type = :copied
|
145
|
+
when /\Asimilarity index (.*)%\z/
|
146
|
+
@similarity_index = $1.to_i
|
147
|
+
else
|
148
|
+
return false
|
149
|
+
end
|
150
|
+
true
|
151
|
+
end
|
152
|
+
|
153
|
+
def parse_binary_file_change(line)
|
154
|
+
if line =~ /\ABinary files (.*) and (.*) differ\z/
|
155
|
+
@is_binary = true
|
156
|
+
if $1 == '/dev/null'
|
157
|
+
@type = :added
|
158
|
+
elsif $2 == '/dev/null'
|
159
|
+
@type = :deleted
|
160
|
+
else
|
161
|
+
@type = :modified
|
162
|
+
end
|
163
|
+
true
|
164
|
+
else
|
165
|
+
false
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def parse_mode_change(line)
|
170
|
+
case line
|
171
|
+
when /\Aold mode (.*)\z/
|
172
|
+
@old_mode = $1
|
173
|
+
@is_mode_changed = true
|
174
|
+
when /\Anew mode (.*)\z/
|
175
|
+
@new_mode = $1
|
176
|
+
@is_mode_changed = true
|
177
|
+
else
|
178
|
+
return false
|
179
|
+
end
|
180
|
+
true
|
181
|
+
end
|
182
|
+
|
183
|
+
def parse_extended_headers(lines)
|
184
|
+
line = lines.shift
|
185
|
+
while line != nil and not line =~ /\A@@/
|
186
|
+
is_parsed = false
|
187
|
+
is_parsed ||= parse_ordinary_change(line)
|
188
|
+
is_parsed ||= parse_add_and_remove(line)
|
189
|
+
is_parsed ||= parse_copy_and_rename(line)
|
190
|
+
is_parsed ||= parse_binary_file_change(line)
|
191
|
+
is_parsed ||= parse_mode_change(line)
|
192
|
+
unless is_parsed
|
193
|
+
raise "unexpected extended line header: " + line
|
194
|
+
end
|
195
|
+
|
196
|
+
line = lines.shift
|
197
|
+
end
|
198
|
+
lines.unshift(line) if line
|
199
|
+
end
|
200
|
+
|
201
|
+
def parse_body(lines)
|
202
|
+
@added_line = @deleted_line = 0
|
203
|
+
from_offset = 0
|
204
|
+
to_offset = 0
|
205
|
+
lines.each do |line|
|
206
|
+
case line
|
207
|
+
when /\A@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)?/
|
208
|
+
from_offset = $1.to_i
|
209
|
+
to_offset = $2.to_i
|
210
|
+
@changes << [:hunk_header, [from_offset, to_offset], line]
|
211
|
+
when /\A\+/
|
212
|
+
@added_line += 1
|
213
|
+
@changes << [:added, to_offset, line]
|
214
|
+
to_offset += 1
|
215
|
+
when /\A\-/
|
216
|
+
@deleted_line += 1
|
217
|
+
@changes << [:deleted, from_offset, line]
|
218
|
+
from_offset += 1
|
219
|
+
else
|
220
|
+
@changes << [:not_changed, [from_offset, to_offset], line]
|
221
|
+
from_offset += 1
|
222
|
+
to_offset += 1
|
223
|
+
end
|
224
|
+
|
225
|
+
@body << line + "\n"
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def format_date(date)
|
230
|
+
date.strftime('%Y-%m-%d %X %z')
|
231
|
+
end
|
232
|
+
|
233
|
+
def format_old_date
|
234
|
+
format_date(@old_date)
|
235
|
+
end
|
236
|
+
|
237
|
+
def format_new_date
|
238
|
+
format_date(@new_date)
|
239
|
+
end
|
240
|
+
|
241
|
+
def short_old_revision
|
242
|
+
GitCommitMailer.short_revision(@old_revision)
|
243
|
+
end
|
244
|
+
|
245
|
+
def short_new_revision
|
246
|
+
GitCommitMailer.short_revision(@new_revision)
|
247
|
+
end
|
248
|
+
|
249
|
+
def format_blob(blob)
|
250
|
+
if blob
|
251
|
+
" (#{blob})"
|
252
|
+
else
|
253
|
+
""
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def format_new_blob
|
258
|
+
format_blob(@new_blob)
|
259
|
+
end
|
260
|
+
|
261
|
+
def format_old_blob
|
262
|
+
format_blob(@old_blob)
|
263
|
+
end
|
264
|
+
|
265
|
+
def format_old_date_and_blob
|
266
|
+
format_old_date + format_old_blob
|
267
|
+
end
|
268
|
+
|
269
|
+
def format_new_date_and_blob
|
270
|
+
format_new_date + format_new_blob
|
271
|
+
end
|
272
|
+
|
273
|
+
def from_header
|
274
|
+
"--- #{@from_file} #{format_old_date_and_blob}\n"
|
275
|
+
end
|
276
|
+
|
277
|
+
def to_header
|
278
|
+
"+++ #{@to_file} #{format_new_date_and_blob}\n"
|
279
|
+
end
|
280
|
+
|
281
|
+
def headers
|
282
|
+
if @is_binary
|
283
|
+
"(Binary files differ)\n"
|
284
|
+
else
|
285
|
+
if (@type == :renamed || @type == :copied) && @similarity_index == 100
|
286
|
+
return ""
|
287
|
+
end
|
288
|
+
|
289
|
+
case @type
|
290
|
+
when :added
|
291
|
+
"--- /dev/null\n" + to_header
|
292
|
+
when :deleted
|
293
|
+
from_header + "+++ /dev/null\n"
|
294
|
+
else
|
295
|
+
from_header + to_header
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def git_command
|
301
|
+
case @type
|
302
|
+
when :added
|
303
|
+
command = "show"
|
304
|
+
args = ["#{short_new_revision}:#{@to_file}"]
|
305
|
+
when :deleted
|
306
|
+
command = "show"
|
307
|
+
args = ["#{short_old_revision}:#{@to_file}"]
|
308
|
+
when :modified
|
309
|
+
command = "diff"
|
310
|
+
args = [short_old_revision, short_new_revision, "--", @to_file]
|
311
|
+
when :renamed
|
312
|
+
command = "diff"
|
313
|
+
args = [
|
314
|
+
"-C", "--diff-filter=R",
|
315
|
+
short_old_revision, short_new_revision, "--",
|
316
|
+
@from_file, @to_file,
|
317
|
+
]
|
318
|
+
when :copied
|
319
|
+
command = "diff"
|
320
|
+
args = [
|
321
|
+
"-C", "--diff-filter=C",
|
322
|
+
short_old_revision, short_new_revision, "--",
|
323
|
+
@from_file, @to_file,
|
324
|
+
]
|
325
|
+
else
|
326
|
+
raise "unknown diff type: #{@type}"
|
327
|
+
end
|
328
|
+
|
329
|
+
command += " #{args.join(' ')}" unless args.empty?
|
330
|
+
" % git #{command}\n"
|
331
|
+
end
|
332
|
+
|
333
|
+
def format_file_mode
|
334
|
+
case @type
|
335
|
+
when :added
|
336
|
+
" #{@new_file_mode}"
|
337
|
+
when :deleted
|
338
|
+
" #{@deleted_file_mode}"
|
339
|
+
else
|
340
|
+
""
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
def format_similarity_index
|
345
|
+
if @type == :renamed or @type == :copied
|
346
|
+
" #{@similarity_index}%"
|
347
|
+
else
|
348
|
+
""
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
def diff_separator
|
353
|
+
"#{"=" * 67}\n"
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|