diff-lcs 1.1.3 → 1.2.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.
- data/.autotest +3 -0
- data/.rspec +2 -0
- data/.travis.yml +21 -0
- data/Contributing.rdoc +63 -0
- data/Gemfile +20 -0
- data/History.rdoc +37 -1
- data/Manifest.txt +10 -1
- data/README.rdoc +48 -23
- data/Rakefile +17 -3
- data/autotest/discover.rb +1 -0
- data/diff-lcs.gemspec +41 -29
- data/docs/COPYING.txt +21 -22
- data/docs/artistic.txt +127 -0
- data/lib/diff-lcs.rb +0 -2
- data/lib/diff/lcs.rb +654 -954
- data/lib/diff/lcs/array.rb +1 -15
- data/lib/diff/lcs/block.rb +4 -18
- data/lib/diff/lcs/callbacks.rb +222 -222
- data/lib/diff/lcs/change.rb +110 -102
- data/lib/diff/lcs/htmldiff.rb +0 -2
- data/lib/diff/lcs/hunk.rb +65 -56
- data/lib/diff/lcs/internals.rb +300 -0
- data/lib/diff/lcs/ldiff.rb +154 -169
- data/lib/diff/lcs/string.rb +1 -15
- data/spec/change_spec.rb +65 -0
- data/spec/diff_spec.rb +8 -2
- data/spec/issues_spec.rb +24 -0
- data/spec/lcs_spec.rb +23 -5
- data/spec/patch_spec.rb +51 -27
- data/spec/sdiff_spec.rb +0 -2
- data/spec/spec_helper.rb +9 -3
- data/spec/traverse_balanced_spec.rb +26 -2
- data/spec/traverse_sequences_spec.rb +84 -28
- metadata +219 -83
- data/docs/artistic.html +0 -289
@@ -0,0 +1,300 @@
|
|
1
|
+
# -*- ruby encoding: utf-8 -*-
|
2
|
+
|
3
|
+
class << Diff::LCS
|
4
|
+
def diff_traversal(method, seq1, seq2, callbacks, &block)
|
5
|
+
callbacks = callbacks_for(callbacks)
|
6
|
+
case method
|
7
|
+
when :diff
|
8
|
+
traverse_sequences(seq1, seq2, callbacks)
|
9
|
+
when :sdiff
|
10
|
+
traverse_balanced(seq1, seq2, callbacks)
|
11
|
+
end
|
12
|
+
callbacks.finish if callbacks.respond_to? :finish
|
13
|
+
|
14
|
+
if block
|
15
|
+
callbacks.diffs.map do |hunk|
|
16
|
+
if hunk.kind_of? Array
|
17
|
+
hunk.map { |hunk_block| block[hunk_block] }
|
18
|
+
else
|
19
|
+
block[hunk]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
else
|
23
|
+
callbacks.diffs
|
24
|
+
end
|
25
|
+
end
|
26
|
+
private :diff_traversal
|
27
|
+
end
|
28
|
+
|
29
|
+
module Diff::LCS::Internals # :nodoc:
|
30
|
+
end
|
31
|
+
|
32
|
+
class << Diff::LCS::Internals
|
33
|
+
# Compute the longest common subsequence between the sequenced
|
34
|
+
# Enumerables +a+ and +b+. The result is an array whose contents is such
|
35
|
+
# that
|
36
|
+
#
|
37
|
+
# result = Diff::LCS::Internals.lcs(a, b)
|
38
|
+
# result.each_with_index do |e, i|
|
39
|
+
# assert_equal(a[i], b[e]) unless e.nil?
|
40
|
+
# end
|
41
|
+
def lcs(a, b)
|
42
|
+
a_start = b_start = 0
|
43
|
+
a_finish = a.size - 1
|
44
|
+
b_finish = b.size - 1
|
45
|
+
vector = []
|
46
|
+
|
47
|
+
# Prune off any common elements at the beginning...
|
48
|
+
while ((a_start <= a_finish) and (b_start <= b_finish) and
|
49
|
+
(a[a_start] == b[b_start]))
|
50
|
+
vector[a_start] = b_start
|
51
|
+
a_start += 1
|
52
|
+
end
|
53
|
+
b_start = a_start
|
54
|
+
|
55
|
+
# Now the end...
|
56
|
+
while ((a_start <= a_finish) and (b_start <= b_finish) and
|
57
|
+
(a[a_finish] == b[b_finish]))
|
58
|
+
vector[a_finish] = b_finish
|
59
|
+
a_finish -= 1
|
60
|
+
b_finish -= 1
|
61
|
+
end
|
62
|
+
|
63
|
+
# Now, compute the equivalence classes of positions of elements.
|
64
|
+
b_matches = position_hash(b, b_start..b_finish)
|
65
|
+
|
66
|
+
thresh = []
|
67
|
+
links = []
|
68
|
+
string = a.kind_of?(String)
|
69
|
+
|
70
|
+
(a_start .. a_finish).each do |i|
|
71
|
+
ai = string ? a[i, 1] : a[i]
|
72
|
+
bm = b_matches[ai]
|
73
|
+
k = nil
|
74
|
+
bm.reverse_each do |j|
|
75
|
+
if k and (thresh[k] > j) and (thresh[k - 1] < j)
|
76
|
+
thresh[k] = j
|
77
|
+
else
|
78
|
+
k = replace_next_larger(thresh, j, k)
|
79
|
+
end
|
80
|
+
links[k] = [ (k > 0) ? links[k - 1] : nil, i, j ] unless k.nil?
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
unless thresh.empty?
|
85
|
+
link = links[thresh.size - 1]
|
86
|
+
while not link.nil?
|
87
|
+
vector[link[1]] = link[2]
|
88
|
+
link = link[0]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
vector
|
93
|
+
end
|
94
|
+
|
95
|
+
# This method will analyze the provided patchset to provide a
|
96
|
+
# single-pass normalization (conversion of the array form of
|
97
|
+
# Diff::LCS::Change objects to the object form of same) and detection of
|
98
|
+
# whether the patchset represents changes to be made.
|
99
|
+
def analyze_patchset(patchset, depth = 0)
|
100
|
+
raise "Patchset too complex" if depth > 1
|
101
|
+
|
102
|
+
has_changes = false
|
103
|
+
|
104
|
+
# Format:
|
105
|
+
# [ # patchset
|
106
|
+
# # hunk (change)
|
107
|
+
# [ # hunk
|
108
|
+
# # change
|
109
|
+
# ]
|
110
|
+
# ]
|
111
|
+
|
112
|
+
patchset = patchset.map do |hunk|
|
113
|
+
case hunk
|
114
|
+
when Diff::LCS::Change
|
115
|
+
has_changes ||= !hunk.unchanged?
|
116
|
+
hunk
|
117
|
+
when Array
|
118
|
+
# Detect if the 'hunk' is actually an array-format
|
119
|
+
# Change object.
|
120
|
+
if Diff::LCS::Change.valid_action? hunk[0]
|
121
|
+
hunk = Diff::LCS::Change.from_a(hunk)
|
122
|
+
has_changes ||= !hunk.unchanged?
|
123
|
+
hunk
|
124
|
+
else
|
125
|
+
with_changes, hunk = analyze_patchset(hunk, depth + 1)
|
126
|
+
has_changes ||= with_changes
|
127
|
+
hunk.flatten
|
128
|
+
end
|
129
|
+
else
|
130
|
+
raise ArgumentError, "Cannot normalise a hunk of class #{hunk.class}."
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
[ has_changes, patchset.flatten(1) ]
|
135
|
+
end
|
136
|
+
|
137
|
+
# Examine the patchset and the source to see in which direction the
|
138
|
+
# patch should be applied.
|
139
|
+
#
|
140
|
+
# WARNING: By default, this examines the whole patch, so this could take
|
141
|
+
# some time. This also works better with Diff::LCS::ContextChange or
|
142
|
+
# Diff::LCS::Change as its source, as an array will cause the creation
|
143
|
+
# of one of the above.
|
144
|
+
#
|
145
|
+
# Note: This will be deprecated as a public function in a future release.
|
146
|
+
def intuit_diff_direction(src, patchset, limit = nil)
|
147
|
+
string = src.kind_of?(String)
|
148
|
+
count = left_match = left_miss = right_match = right_miss = 0
|
149
|
+
|
150
|
+
patchset.each do |change|
|
151
|
+
count += 1
|
152
|
+
|
153
|
+
case change
|
154
|
+
when Diff::LCS::ContextChange
|
155
|
+
le = string ? src[change.old_position, 1] : src[change.old_position]
|
156
|
+
re = string ? src[change.new_position, 1] : src[change.new_position]
|
157
|
+
|
158
|
+
case change.action
|
159
|
+
when '-' # Remove details from the old string
|
160
|
+
if le == change.old_element
|
161
|
+
left_match += 1
|
162
|
+
else
|
163
|
+
left_miss += 1
|
164
|
+
end
|
165
|
+
when '+'
|
166
|
+
if re == change.new_element
|
167
|
+
right_match += 1
|
168
|
+
else
|
169
|
+
right_miss += 1
|
170
|
+
end
|
171
|
+
when '='
|
172
|
+
left_miss += 1 if le != change.old_element
|
173
|
+
right_miss += 1 if re != change.new_element
|
174
|
+
when '!'
|
175
|
+
if le == change.old_element
|
176
|
+
left_match += 1
|
177
|
+
else
|
178
|
+
if re == change.new_element
|
179
|
+
right_match += 1
|
180
|
+
else
|
181
|
+
left_miss += 1
|
182
|
+
right_miss += 1
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
when Diff::LCS::Change
|
187
|
+
# With a simplistic change, we can't tell the difference between
|
188
|
+
# the left and right on '!' actions, so we ignore those. On '='
|
189
|
+
# actions, if there's a miss, we miss both left and right.
|
190
|
+
element = string ? src[change.position, 1] : src[change.position]
|
191
|
+
|
192
|
+
case change.action
|
193
|
+
when '-'
|
194
|
+
if element == change.element
|
195
|
+
left_match += 1
|
196
|
+
else
|
197
|
+
left_miss += 1
|
198
|
+
end
|
199
|
+
when '+'
|
200
|
+
if element == change.element
|
201
|
+
right_match += 1
|
202
|
+
else
|
203
|
+
right_miss += 1
|
204
|
+
end
|
205
|
+
when '='
|
206
|
+
if element != change.element
|
207
|
+
left_miss += 1
|
208
|
+
right_miss += 1
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
break if (not limit.nil?) && (count > limit)
|
214
|
+
end
|
215
|
+
|
216
|
+
no_left = (left_match == 0) && (left_miss > 0)
|
217
|
+
no_right = (right_match == 0) && (right_miss > 0)
|
218
|
+
|
219
|
+
case [no_left, no_right]
|
220
|
+
when [false, true]
|
221
|
+
:patch
|
222
|
+
when [true, false]
|
223
|
+
:unpatch
|
224
|
+
else
|
225
|
+
case left_match <=> right_match
|
226
|
+
when 1
|
227
|
+
:patch
|
228
|
+
when -1
|
229
|
+
:unpatch
|
230
|
+
else
|
231
|
+
raise "The provided patchset does not appear to apply to the provided value as either source or destination value."
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Find the place at which +value+ would normally be inserted into the
|
237
|
+
# Enumerable. If that place is already occupied by +value+, do nothing
|
238
|
+
# and return +nil+. If the place does not exist (i.e., it is off the end
|
239
|
+
# of the Enumerable), add it to the end. Otherwise, replace the element
|
240
|
+
# at that point with +value+. It is assumed that the Enumerable's values
|
241
|
+
# are numeric.
|
242
|
+
#
|
243
|
+
# This operation preserves the sort order.
|
244
|
+
def replace_next_larger(enum, value, last_index = nil)
|
245
|
+
# Off the end?
|
246
|
+
if enum.empty? or (value > enum[-1])
|
247
|
+
enum << value
|
248
|
+
return enum.size - 1
|
249
|
+
end
|
250
|
+
|
251
|
+
# Binary search for the insertion point
|
252
|
+
last_index ||= enum.size
|
253
|
+
first_index = 0
|
254
|
+
while (first_index <= last_index)
|
255
|
+
i = (first_index + last_index) >> 1
|
256
|
+
|
257
|
+
found = enum[i]
|
258
|
+
|
259
|
+
if value == found
|
260
|
+
return nil
|
261
|
+
elsif value > found
|
262
|
+
first_index = i + 1
|
263
|
+
else
|
264
|
+
last_index = i - 1
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# The insertion point is in first_index; overwrite the next larger
|
269
|
+
# value.
|
270
|
+
enum[first_index] = value
|
271
|
+
return first_index
|
272
|
+
end
|
273
|
+
private :replace_next_larger
|
274
|
+
|
275
|
+
# If +vector+ maps the matching elements of another collection onto this
|
276
|
+
# Enumerable, compute the inverse of +vector+ that maps this Enumerable
|
277
|
+
# onto the collection. (Currently unused.)
|
278
|
+
def inverse_vector(a, vector)
|
279
|
+
inverse = a.dup
|
280
|
+
(0...vector.size).each do |i|
|
281
|
+
inverse[vector[i]] = i unless vector[i].nil?
|
282
|
+
end
|
283
|
+
inverse
|
284
|
+
end
|
285
|
+
private :inverse_vector
|
286
|
+
|
287
|
+
# Returns a hash mapping each element of an Enumerable to the set of
|
288
|
+
# positions it occupies in the Enumerable, optionally restricted to the
|
289
|
+
# elements specified in the range of indexes specified by +interval+.
|
290
|
+
def position_hash(enum, interval)
|
291
|
+
string = enum.kind_of?(String)
|
292
|
+
hash = Hash.new { |h, k| h[k] = [] }
|
293
|
+
interval.each do |i|
|
294
|
+
k = string ? enum[i, 1] : enum[i]
|
295
|
+
hash[k] << i
|
296
|
+
end
|
297
|
+
hash
|
298
|
+
end
|
299
|
+
private :position_hash
|
300
|
+
end
|
data/lib/diff/lcs/ldiff.rb
CHANGED
@@ -1,39 +1,39 @@
|
|
1
|
-
|
1
|
+
# -*- ruby encoding: utf-8 -*-
|
2
2
|
|
3
3
|
require 'optparse'
|
4
4
|
require 'ostruct'
|
5
5
|
require 'diff/lcs/hunk'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
7
|
+
# == ldiff Usage
|
8
|
+
# ldiff [options] oldfile newfile
|
9
|
+
#
|
10
|
+
# -c:: Displays a context diff with 3 lines of context.
|
11
|
+
# -C [LINES], --context [LINES]:: Displays a context diff with LINES lines of context. Default 3 lines.
|
12
|
+
# -u:: Displays a unified diff with 3 lines of context.
|
13
|
+
# -U [LINES], --unified [LINES]:: Displays a unified diff with LINES lines of context. Default 3 lines.
|
14
|
+
# -e:: Creates an 'ed' script to change oldfile to newfile.
|
15
|
+
# -f:: Creates an 'ed' script to change oldfile to newfile in reverse order.
|
16
|
+
# -a, --text:: Treats the files as text and compares them line-by-line, even if they do not seem to be text.
|
17
|
+
# --binary:: Treats the files as binary.
|
18
|
+
# -q, --brief:: Reports only whether or not the files differ, not the details.
|
19
|
+
# --help:: Shows the command-line help.
|
20
|
+
# --version:: Shows the version of Diff::LCS.
|
21
|
+
#
|
22
|
+
# By default, runs produces an "old-style" diff, with output like UNIX diff.
|
23
|
+
#
|
24
|
+
# == Copyright
|
25
|
+
# Copyright © 2004 Austin Ziegler
|
26
|
+
#
|
27
|
+
# Part of Diff::LCS <http://rubyforge.org/projects/ruwiki/>
|
28
|
+
# Austin Ziegler <diff-lcs@halostatue.ca>
|
29
|
+
#
|
30
|
+
# This program is free software. It may be redistributed and/or modified under
|
31
|
+
# the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
|
32
|
+
# Ruby licence.
|
33
33
|
module Diff::LCS::Ldiff
|
34
34
|
BANNER = <<-COPYRIGHT
|
35
35
|
ldiff #{Diff::LCS::VERSION}
|
36
|
-
Copyright 2004-
|
36
|
+
Copyright 2004-2013 Austin Ziegler
|
37
37
|
|
38
38
|
Part of Diff::LCS.
|
39
39
|
http://rubyforge.org/projects/ruwiki/
|
@@ -43,168 +43,153 @@ ldiff #{Diff::LCS::VERSION}
|
|
43
43
|
This program is free software. It may be redistributed and/or modified under
|
44
44
|
the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
|
45
45
|
MIT licence.
|
46
|
-
|
47
|
-
$Id$
|
48
46
|
COPYRIGHT
|
47
|
+
end
|
49
48
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
def run(args, input = $stdin, output = $stdout, error = $stderr) #:nodoc:
|
56
|
-
@binary = nil
|
57
|
-
|
58
|
-
args.options do |o|
|
59
|
-
o.banner = "Usage: #{File.basename($0)} [options] oldfile newfile"
|
60
|
-
o.separator ""
|
61
|
-
o.on('-c', 'Displays a context diff with 3 lines of', 'context.') do |ctx|
|
62
|
-
@format = :context
|
63
|
-
@lines = 3
|
64
|
-
end
|
65
|
-
o.on('-C', '--context [LINES]', Numeric, 'Displays a context diff with LINES lines', 'of context. Default 3 lines.') do |ctx|
|
66
|
-
@format = :context
|
67
|
-
@lines = ctx || 3
|
68
|
-
end
|
69
|
-
o.on('-u', 'Displays a unified diff with 3 lines of', 'context.') do |ctx|
|
70
|
-
@format = :unified
|
71
|
-
@lines = 3
|
72
|
-
end
|
73
|
-
o.on('-U', '--unified [LINES]', Numeric, 'Displays a unified diff with LINES lines', 'of context. Default 3 lines.') do |ctx|
|
74
|
-
@format = :unified
|
75
|
-
@lines = ctx || 3
|
76
|
-
end
|
77
|
-
o.on('-e', 'Creates an \'ed\' script to change', 'oldfile to newfile.') do |ctx|
|
78
|
-
@format = :ed
|
79
|
-
end
|
80
|
-
o.on('-f', 'Creates an \'ed\' script to change', 'oldfile to newfile in reverse order.') do |ctx|
|
81
|
-
@format = :reverse_ed
|
82
|
-
end
|
83
|
-
o.on('-a', '--text', 'Treat the files as text and compare them', 'line-by-line, even if they do not seem', 'to be text.') do |txt|
|
84
|
-
@binary = false
|
85
|
-
end
|
86
|
-
o.on('--binary', 'Treats the files as binary.') do |bin|
|
87
|
-
@binary = true
|
88
|
-
end
|
89
|
-
o.on('-q', '--brief', 'Report only whether or not the files', 'differ, not the details.') do |ctx|
|
90
|
-
@format = :report
|
91
|
-
end
|
92
|
-
o.on_tail('--help', 'Shows this text.') do
|
93
|
-
error << o
|
94
|
-
return 0
|
95
|
-
end
|
96
|
-
o.on_tail('--version', 'Shows the version of Diff::LCS.') do
|
97
|
-
error << BANNER
|
98
|
-
return 0
|
99
|
-
end
|
100
|
-
o.on_tail ""
|
101
|
-
o.on_tail 'By default, runs produces an "old-style" diff, with output like UNIX diff.'
|
102
|
-
o.parse!
|
103
|
-
end
|
49
|
+
class << Diff::LCS::Ldiff
|
50
|
+
attr_reader :format, :lines #:nodoc:
|
51
|
+
attr_reader :file_old, :file_new #:nodoc:
|
52
|
+
attr_reader :data_old, :data_new #:nodoc:
|
104
53
|
|
105
|
-
|
106
|
-
|
107
|
-
|
54
|
+
def run(args, input = $stdin, output = $stdout, error = $stderr) #:nodoc:
|
55
|
+
@binary = nil
|
56
|
+
|
57
|
+
args.options do |o|
|
58
|
+
o.banner = "Usage: #{File.basename($0)} [options] oldfile newfile"
|
59
|
+
o.separator ""
|
60
|
+
o.on('-c', '-C', '--context [LINES]', Numeric, 'Displays a context diff with LINES lines', 'of context. Default 3 lines.') do |ctx|
|
61
|
+
@format = :context
|
62
|
+
@lines = ctx || 3
|
63
|
+
end
|
64
|
+
o.on('-u', '-U', '--unified [LINES]', Numeric, 'Displays a unified diff with LINES lines', 'of context. Default 3 lines.') do |ctx|
|
65
|
+
@format = :unified
|
66
|
+
@lines = ctx || 3
|
67
|
+
end
|
68
|
+
o.on('-e', 'Creates an \'ed\' script to change', 'oldfile to newfile.') do |ctx|
|
69
|
+
@format = :ed
|
70
|
+
end
|
71
|
+
o.on('-f', 'Creates an \'ed\' script to change', 'oldfile to newfile in reverse order.') do |ctx|
|
72
|
+
@format = :reverse_ed
|
73
|
+
end
|
74
|
+
o.on('-a', '--text', 'Treat the files as text and compare them', 'line-by-line, even if they do not seem', 'to be text.') do |txt|
|
75
|
+
@binary = false
|
76
|
+
end
|
77
|
+
o.on('--binary', 'Treats the files as binary.') do |bin|
|
78
|
+
@binary = true
|
108
79
|
end
|
80
|
+
o.on('-q', '--brief', 'Report only whether or not the files', 'differ, not the details.') do |ctx|
|
81
|
+
@format = :report
|
82
|
+
end
|
83
|
+
o.on_tail('--help', 'Shows this text.') do
|
84
|
+
error << o
|
85
|
+
return 0
|
86
|
+
end
|
87
|
+
o.on_tail('--version', 'Shows the version of Diff::LCS.') do
|
88
|
+
error << BANNER
|
89
|
+
return 0
|
90
|
+
end
|
91
|
+
o.on_tail ""
|
92
|
+
o.on_tail 'By default, runs produces an "old-style" diff, with output like UNIX diff.'
|
93
|
+
o.parse!
|
94
|
+
end
|
109
95
|
|
110
|
-
|
111
|
-
|
112
|
-
|
96
|
+
unless args.size == 2
|
97
|
+
error << args.options
|
98
|
+
return 127
|
99
|
+
end
|
113
100
|
|
114
|
-
|
101
|
+
# Defaults are for old-style diff
|
102
|
+
@format ||= :old
|
103
|
+
@lines ||= 0
|
115
104
|
|
116
|
-
|
117
|
-
when :context
|
118
|
-
char_old = '*' * 3
|
119
|
-
char_new = '-' * 3
|
120
|
-
when :unified
|
121
|
-
char_old = '-' * 3
|
122
|
-
char_new = '+' * 3
|
123
|
-
end
|
105
|
+
file_old, file_new = *ARGV
|
124
106
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
# Test binary status
|
134
|
-
if @binary.nil?
|
135
|
-
old_txt = data_old[0...4096].grep(/\0/).empty?
|
136
|
-
new_txt = data_new[0...4096].grep(/\0/).empty?
|
137
|
-
@binary = (not old_txt) or (not new_txt)
|
138
|
-
old_txt = new_txt = nil
|
139
|
-
end
|
140
|
-
|
141
|
-
unless @binary
|
142
|
-
data_old = data_old.split(/\n/).map! { |e| e.chomp }
|
143
|
-
data_new = data_new.split(/\n/).map! { |e| e.chomp }
|
144
|
-
end
|
145
|
-
else
|
146
|
-
data_old = IO::readlines(file_old).map! { |e| e.chomp }
|
147
|
-
data_new = IO::readlines(file_new).map! { |e| e.chomp }
|
148
|
-
end
|
107
|
+
case @format
|
108
|
+
when :context
|
109
|
+
char_old = '*' * 3
|
110
|
+
char_new = '-' * 3
|
111
|
+
when :unified
|
112
|
+
char_old = '-' * 3
|
113
|
+
char_new = '+' * 3
|
114
|
+
end
|
149
115
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
else
|
154
|
-
diffs = Diff::LCS.diff(data_old, data_new)
|
155
|
-
diffs = nil if diffs.empty?
|
156
|
-
end
|
116
|
+
# After we've read up to a certain point in each file, the number of
|
117
|
+
# items we've read from each file will differ by FLD (could be 0).
|
118
|
+
file_length_difference = 0
|
157
119
|
|
158
|
-
|
120
|
+
if @binary.nil? or @binary
|
121
|
+
data_old = IO::read(file_old)
|
122
|
+
data_new = IO::read(file_new)
|
159
123
|
|
160
|
-
|
161
|
-
|
162
|
-
|
124
|
+
# Test binary status
|
125
|
+
if @binary.nil?
|
126
|
+
old_txt = data_old[0...4096].scan(/\0/).empty?
|
127
|
+
new_txt = data_new[0...4096].scan(/\0/).empty?
|
128
|
+
@binary = (not old_txt) or (not new_txt)
|
129
|
+
old_txt = new_txt = nil
|
163
130
|
end
|
164
131
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
ft = File.stat(file_new).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S %z')
|
169
|
-
puts "#{char_new} #{file_new}\t#{ft}"
|
132
|
+
unless @binary
|
133
|
+
data_old = data_old.split($/).map { |e| e.chomp }
|
134
|
+
data_new = data_new.split($/).map { |e| e.chomp }
|
170
135
|
end
|
136
|
+
else
|
137
|
+
data_old = IO::readlines(file_old).map { |e| e.chomp }
|
138
|
+
data_new = IO::readlines(file_new).map { |e| e.chomp }
|
139
|
+
end
|
171
140
|
|
172
|
-
|
173
|
-
|
174
|
-
|
141
|
+
# diff yields lots of pieces, each of which is basically a Block object
|
142
|
+
if @binary
|
143
|
+
diffs = (data_old == data_new)
|
144
|
+
else
|
145
|
+
diffs = Diff::LCS.diff(data_old, data_new)
|
146
|
+
diffs = nil if diffs.empty?
|
147
|
+
end
|
175
148
|
|
176
|
-
|
177
|
-
real_output = output
|
178
|
-
output = []
|
179
|
-
end
|
149
|
+
return 0 unless diffs
|
180
150
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
output << oldhunk.diff(@format)
|
193
|
-
end
|
194
|
-
ensure
|
195
|
-
oldhunk = hunk
|
196
|
-
output << "\n"
|
197
|
-
end
|
198
|
-
end
|
151
|
+
if (@format == :report) and diffs
|
152
|
+
output << "Files #{file_old} and #{file_new} differ\n"
|
153
|
+
return 1
|
154
|
+
end
|
155
|
+
|
156
|
+
if (@format == :unified) or (@format == :context)
|
157
|
+
ft = File.stat(file_old).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.%N %z')
|
158
|
+
puts "#{char_old} #{file_old}\t#{ft}"
|
159
|
+
ft = File.stat(file_new).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.%N %z')
|
160
|
+
puts "#{char_new} #{file_new}\t#{ft}"
|
161
|
+
end
|
199
162
|
|
200
|
-
|
201
|
-
|
163
|
+
# Loop over hunks. If a hunk overlaps with the last hunk, join them.
|
164
|
+
# Otherwise, print out the old one.
|
165
|
+
oldhunk = hunk = nil
|
202
166
|
|
203
|
-
|
204
|
-
|
167
|
+
if @format == :ed
|
168
|
+
real_output = output
|
169
|
+
output = []
|
170
|
+
end
|
171
|
+
|
172
|
+
diffs.each do |piece|
|
173
|
+
begin
|
174
|
+
hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, @lines,
|
175
|
+
file_length_difference)
|
176
|
+
file_length_difference = hunk.file_length_difference
|
177
|
+
|
178
|
+
next unless oldhunk
|
179
|
+
next if (@lines > 0) and hunk.merge(oldhunk)
|
180
|
+
|
181
|
+
output << oldhunk.diff(@format) << "\n"
|
182
|
+
ensure
|
183
|
+
oldhunk = hunk
|
205
184
|
end
|
185
|
+
end
|
206
186
|
|
207
|
-
|
187
|
+
output << oldhunk.diff(@format) << "\n"
|
188
|
+
|
189
|
+
if @format == :ed
|
190
|
+
output.reverse_each { |e| real_output << e.diff(:ed_finish) }
|
208
191
|
end
|
192
|
+
|
193
|
+
return 1
|
209
194
|
end
|
210
195
|
end
|