diff-lcs 1.1.3 → 1.5.0

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