git-commit-mailer 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|