diff-lcs 1.5.0 → 1.6.2
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 +4 -4
- data/CHANGELOG.md +518 -0
- data/CODE_OF_CONDUCT.md +128 -0
- data/CONTRIBUTING.md +71 -0
- data/CONTRIBUTORS.md +49 -0
- data/{License.md → LICENCE.md} +21 -20
- data/Manifest.txt +61 -5
- data/README.md +92 -0
- data/Rakefile +76 -82
- data/SECURITY.md +41 -0
- data/bin/htmldiff +4 -4
- data/docs/artistic.txt +1 -1
- data/lib/diff/lcs/array.rb +1 -1
- data/lib/diff/lcs/backports.rb +6 -2
- data/lib/diff/lcs/block.rb +4 -4
- data/lib/diff/lcs/callbacks.rb +9 -7
- data/lib/diff/lcs/change.rb +22 -22
- data/lib/diff/lcs/htmldiff.rb +26 -16
- data/lib/diff/lcs/hunk.rb +66 -45
- data/lib/diff/lcs/internals.rb +17 -17
- data/lib/diff/lcs/ldiff.rb +93 -75
- data/lib/diff/lcs/version.rb +7 -0
- data/lib/diff/lcs.rb +66 -63
- data/lib/diff-lcs.rb +1 -1
- data/mise.toml +5 -0
- data/spec/change_spec.rb +50 -50
- data/spec/diff_spec.rb +14 -14
- data/spec/fixtures/123_x +2 -0
- data/spec/fixtures/456_x +2 -0
- data/spec/fixtures/empty +0 -0
- data/spec/fixtures/file1.bin +0 -0
- data/spec/fixtures/file2.bin +0 -0
- data/spec/fixtures/four_lines +4 -0
- data/spec/fixtures/four_lines_with_missing_new_line +4 -0
- data/spec/fixtures/ldiff/diff.missing_new_line1-e +1 -0
- data/spec/fixtures/ldiff/diff.missing_new_line1-f +1 -0
- data/spec/fixtures/ldiff/diff.missing_new_line2-e +1 -0
- data/spec/fixtures/ldiff/diff.missing_new_line2-f +1 -0
- data/spec/fixtures/ldiff/error.diff.chef-e +2 -0
- data/spec/fixtures/ldiff/error.diff.chef-f +2 -0
- data/spec/fixtures/ldiff/error.diff.missing_new_line1-e +1 -0
- data/spec/fixtures/ldiff/error.diff.missing_new_line1-f +1 -0
- data/spec/fixtures/ldiff/error.diff.missing_new_line2-e +1 -0
- data/spec/fixtures/ldiff/error.diff.missing_new_line2-f +1 -0
- data/spec/fixtures/ldiff/output.diff.bin1 +0 -0
- data/spec/fixtures/ldiff/output.diff.bin1-c +0 -0
- data/spec/fixtures/ldiff/output.diff.bin1-e +0 -0
- data/spec/fixtures/ldiff/output.diff.bin1-f +0 -0
- data/spec/fixtures/ldiff/output.diff.bin1-u +0 -0
- data/spec/fixtures/ldiff/output.diff.bin2 +1 -0
- data/spec/fixtures/ldiff/output.diff.bin2-c +1 -0
- data/spec/fixtures/ldiff/output.diff.bin2-e +1 -0
- data/spec/fixtures/ldiff/output.diff.bin2-f +1 -0
- data/spec/fixtures/ldiff/output.diff.bin2-u +1 -0
- data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines +5 -0
- data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-c +9 -0
- data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-e +6 -0
- data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-f +6 -0
- data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-u +7 -0
- data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty +5 -0
- data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-c +9 -0
- data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-e +1 -0
- data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-f +1 -0
- data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-u +7 -0
- data/spec/fixtures/ldiff/output.diff.issue95_trailing_context +4 -0
- data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-c +9 -0
- data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-e +3 -0
- data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-f +3 -0
- data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-u +6 -0
- data/spec/fixtures/ldiff/output.diff.missing_new_line1 +5 -0
- data/spec/fixtures/ldiff/output.diff.missing_new_line1-c +14 -0
- data/spec/fixtures/ldiff/output.diff.missing_new_line1-e +0 -0
- data/spec/fixtures/ldiff/output.diff.missing_new_line1-f +0 -0
- data/spec/fixtures/ldiff/output.diff.missing_new_line1-u +9 -0
- data/spec/fixtures/ldiff/output.diff.missing_new_line2 +5 -0
- data/spec/fixtures/ldiff/output.diff.missing_new_line2-c +14 -0
- data/spec/fixtures/ldiff/output.diff.missing_new_line2-e +0 -0
- data/spec/fixtures/ldiff/output.diff.missing_new_line2-f +0 -0
- data/spec/fixtures/ldiff/output.diff.missing_new_line2-u +9 -0
- data/spec/hunk_spec.rb +20 -20
- data/spec/issues_spec.rb +76 -70
- data/spec/lcs_spec.rb +11 -11
- data/spec/ldiff_spec.rb +30 -17
- data/spec/patch_spec.rb +84 -84
- data/spec/sdiff_spec.rb +111 -109
- data/spec/spec_helper.rb +162 -160
- data/spec/traverse_balanced_spec.rb +191 -189
- data/spec/traverse_sequences_spec.rb +31 -31
- metadata +90 -61
- data/Code-of-Conduct.md +0 -74
- data/Contributing.md +0 -119
- data/History.md +0 -400
- data/README.rdoc +0 -84
data/lib/diff/lcs/internals.rb
CHANGED
@@ -13,7 +13,7 @@ class << Diff::LCS
|
|
13
13
|
|
14
14
|
if block
|
15
15
|
callbacks.diffs.map do |hunk|
|
16
|
-
if hunk.
|
16
|
+
if hunk.is_a? Array
|
17
17
|
hunk.map { |hunk_block| block[hunk_block] }
|
18
18
|
else
|
19
19
|
block[hunk]
|
@@ -45,14 +45,14 @@ class << Diff::LCS::Internals
|
|
45
45
|
vector = []
|
46
46
|
|
47
47
|
# Collect any common elements at the beginning...
|
48
|
-
while (a_start <= a_finish)
|
48
|
+
while (a_start <= a_finish) && (b_start <= b_finish) && (a[a_start] == b[b_start])
|
49
49
|
vector[a_start] = b_start
|
50
50
|
a_start += 1
|
51
51
|
b_start += 1
|
52
52
|
end
|
53
53
|
|
54
54
|
# Now the end...
|
55
|
-
while (a_start <= a_finish)
|
55
|
+
while (a_start <= a_finish) && (b_start <= b_finish) && (a[a_finish] == b[b_finish])
|
56
56
|
vector[a_finish] = b_finish
|
57
57
|
a_finish -= 1
|
58
58
|
b_finish -= 1
|
@@ -63,8 +63,8 @@ class << Diff::LCS::Internals
|
|
63
63
|
b_matches = position_hash(b, b_start..b_finish)
|
64
64
|
|
65
65
|
thresh = []
|
66
|
-
links
|
67
|
-
string = a.
|
66
|
+
links = []
|
67
|
+
string = a.is_a?(String)
|
68
68
|
|
69
69
|
(a_start..a_finish).each do |i|
|
70
70
|
ai = string ? a[i, 1] : a[i]
|
@@ -75,7 +75,7 @@ class << Diff::LCS::Internals
|
|
75
75
|
# it may have an optimization purpose
|
76
76
|
# An attempt to remove it: https://github.com/halostatue/diff-lcs/pull/72
|
77
77
|
# Why it is reintroduced: https://github.com/halostatue/diff-lcs/issues/78
|
78
|
-
if k
|
78
|
+
if k && (thresh[k] > j) && (thresh[k - 1] < j)
|
79
79
|
thresh[k] = j
|
80
80
|
else
|
81
81
|
k = replace_next_larger(thresh, j, k)
|
@@ -100,7 +100,7 @@ class << Diff::LCS::Internals
|
|
100
100
|
# the object form of same) and detection of whether the patchset represents
|
101
101
|
# changes to be made.
|
102
102
|
def analyze_patchset(patchset, depth = 0)
|
103
|
-
fail
|
103
|
+
fail "Patchset too complex" if depth > 1
|
104
104
|
|
105
105
|
has_changes = false
|
106
106
|
new_patchset = []
|
@@ -145,7 +145,7 @@ class << Diff::LCS::Internals
|
|
145
145
|
# Diff::LCS::Change as its source, as an array will cause the creation
|
146
146
|
# of one of the above.
|
147
147
|
def intuit_diff_direction(src, patchset, limit = nil)
|
148
|
-
string = src.
|
148
|
+
string = src.is_a?(String)
|
149
149
|
count = left_match = left_miss = right_match = right_miss = 0
|
150
150
|
|
151
151
|
patchset.each do |change|
|
@@ -157,22 +157,22 @@ class << Diff::LCS::Internals
|
|
157
157
|
re = string ? src[change.new_position, 1] : src[change.new_position]
|
158
158
|
|
159
159
|
case change.action
|
160
|
-
when
|
160
|
+
when "-" # Remove details from the old string
|
161
161
|
if le == change.old_element
|
162
162
|
left_match += 1
|
163
163
|
else
|
164
164
|
left_miss += 1
|
165
165
|
end
|
166
|
-
when
|
166
|
+
when "+"
|
167
167
|
if re == change.new_element
|
168
168
|
right_match += 1
|
169
169
|
else
|
170
170
|
right_miss += 1
|
171
171
|
end
|
172
|
-
when
|
172
|
+
when "="
|
173
173
|
left_miss += 1 if le != change.old_element
|
174
174
|
right_miss += 1 if re != change.new_element
|
175
|
-
when
|
175
|
+
when "!"
|
176
176
|
if le == change.old_element
|
177
177
|
left_match += 1
|
178
178
|
elsif re == change.new_element
|
@@ -189,19 +189,19 @@ class << Diff::LCS::Internals
|
|
189
189
|
element = string ? src[change.position, 1] : src[change.position]
|
190
190
|
|
191
191
|
case change.action
|
192
|
-
when
|
192
|
+
when "-"
|
193
193
|
if element == change.element
|
194
194
|
left_match += 1
|
195
195
|
else
|
196
196
|
left_miss += 1
|
197
197
|
end
|
198
|
-
when
|
198
|
+
when "+"
|
199
199
|
if element == change.element
|
200
200
|
right_match += 1
|
201
201
|
else
|
202
202
|
right_miss += 1
|
203
203
|
end
|
204
|
-
when
|
204
|
+
when "="
|
205
205
|
if element != change.element
|
206
206
|
left_miss += 1
|
207
207
|
right_miss += 1
|
@@ -251,7 +251,7 @@ enumerable as either source or destination value."
|
|
251
251
|
# This operation preserves the sort order.
|
252
252
|
def replace_next_larger(enum, value, last_index = nil)
|
253
253
|
# Off the end?
|
254
|
-
if enum.empty?
|
254
|
+
if enum.empty? || (value > enum[-1])
|
255
255
|
enum << value
|
256
256
|
return enum.size - 1
|
257
257
|
end
|
@@ -296,7 +296,7 @@ enumerable as either source or destination value."
|
|
296
296
|
# positions it occupies in the Enumerable, optionally restricted to the
|
297
297
|
# elements specified in the range of indexes specified by +interval+.
|
298
298
|
def position_hash(enum, interval)
|
299
|
-
string = enum.
|
299
|
+
string = enum.is_a?(String)
|
300
300
|
hash = Hash.new { |h, k| h[k] = [] }
|
301
301
|
interval.each do |i|
|
302
302
|
k = string ? enum[i, 1] : enum[i]
|
data/lib/diff/lcs/ldiff.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require 'diff/lcs/hunk'
|
3
|
+
require "optparse"
|
4
|
+
require "diff/lcs/hunk"
|
6
5
|
|
7
|
-
|
6
|
+
class Diff::LCS::Ldiff # :nodoc:
|
7
|
+
# standard:disable Layout/HeredocIndentation
|
8
8
|
BANNER = <<-COPYRIGHT
|
9
9
|
ldiff #{Diff::LCS::VERSION}
|
10
|
-
Copyright 2004-
|
10
|
+
Copyright 2004-2025 Austin Ziegler
|
11
11
|
|
12
12
|
Part of Diff::LCS.
|
13
13
|
https://github.com/halostatue/diff-lcs
|
@@ -16,60 +16,73 @@ ldiff #{Diff::LCS::VERSION}
|
|
16
16
|
the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
|
17
17
|
MIT licence.
|
18
18
|
COPYRIGHT
|
19
|
-
|
19
|
+
# standard:enable Layout/HeredocIndentation
|
20
|
+
|
21
|
+
InputInfo = Struct.new(:filename, :data, :stat) do
|
22
|
+
def initialize(filename)
|
23
|
+
super(filename, ::File.read(filename), ::File.stat(filename))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :format, :lines # :nodoc:
|
28
|
+
attr_reader :file_old, :file_new # :nodoc:
|
29
|
+
attr_reader :data_old, :data_new # :nodoc:
|
20
30
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
attr_reader :data_old, :data_new #:nodoc:
|
31
|
+
def self.run(args, input = $stdin, output = $stdout, error = $stderr) # :nodoc:
|
32
|
+
new.run(args, input, output, error)
|
33
|
+
end
|
25
34
|
|
26
|
-
def
|
35
|
+
def initialize
|
27
36
|
@binary = nil
|
37
|
+
@format = :old
|
38
|
+
@lines = 0
|
39
|
+
end
|
28
40
|
|
41
|
+
def run(args, _input = $stdin, output = $stdout, error = $stderr) # :nodoc:
|
29
42
|
args.options do |o|
|
30
43
|
o.banner = "Usage: #{File.basename($0)} [options] oldfile newfile"
|
31
|
-
o.separator
|
44
|
+
o.separator ""
|
32
45
|
o.on(
|
33
|
-
|
34
|
-
|
46
|
+
"-c", "-C", "--context [LINES]", Integer,
|
47
|
+
"Displays a context diff with LINES lines", "of context. Default 3 lines."
|
35
48
|
) do |ctx|
|
36
49
|
@format = :context
|
37
|
-
@lines
|
50
|
+
@lines = ctx || 3
|
38
51
|
end
|
39
52
|
o.on(
|
40
|
-
|
41
|
-
|
53
|
+
"-u", "-U", "--unified [LINES]", Integer,
|
54
|
+
"Displays a unified diff with LINES lines", "of context. Default 3 lines."
|
42
55
|
) do |ctx|
|
43
56
|
@format = :unified
|
44
|
-
@lines
|
57
|
+
@lines = ctx || 3
|
45
58
|
end
|
46
|
-
o.on(
|
59
|
+
o.on("-e", "Creates an 'ed' script to change", "oldfile to newfile.") do |_ctx|
|
47
60
|
@format = :ed
|
48
61
|
end
|
49
|
-
o.on(
|
62
|
+
o.on("-f", "Creates an 'ed' script to change", "oldfile to newfile in reverse order.") do |_ctx|
|
50
63
|
@format = :reverse_ed
|
51
64
|
end
|
52
65
|
o.on(
|
53
|
-
|
54
|
-
|
66
|
+
"-a", "--text",
|
67
|
+
"Treat the files as text and compare them", "line-by-line, even if they do not seem", "to be text."
|
55
68
|
) do |_txt|
|
56
69
|
@binary = false
|
57
70
|
end
|
58
|
-
o.on(
|
71
|
+
o.on("--binary", "Treats the files as binary.") do |_bin|
|
59
72
|
@binary = true
|
60
73
|
end
|
61
|
-
o.on(
|
74
|
+
o.on("-q", "--brief", "Report only whether or not the files", "differ, not the details.") do |_ctx|
|
62
75
|
@format = :report
|
63
76
|
end
|
64
|
-
o.on_tail(
|
77
|
+
o.on_tail("--help", "Shows this text.") do
|
65
78
|
error << o
|
66
79
|
return 0
|
67
80
|
end
|
68
|
-
o.on_tail(
|
81
|
+
o.on_tail("--version", "Shows the version of Diff::LCS.") do
|
69
82
|
error << Diff::LCS::Ldiff::BANNER
|
70
83
|
return 0
|
71
84
|
end
|
72
|
-
o.on_tail
|
85
|
+
o.on_tail ""
|
73
86
|
o.on_tail 'By default, runs produces an "old-style" diff, with output like UNIX diff.'
|
74
87
|
o.parse!
|
75
88
|
end
|
@@ -81,91 +94,96 @@ class << Diff::LCS::Ldiff
|
|
81
94
|
|
82
95
|
# Defaults are for old-style diff
|
83
96
|
@format ||= :old
|
84
|
-
@lines
|
97
|
+
@lines ||= 0
|
85
98
|
|
86
99
|
file_old, file_new = *ARGV
|
100
|
+
diff?(
|
101
|
+
InputInfo.new(file_old),
|
102
|
+
InputInfo.new(file_new),
|
103
|
+
@format,
|
104
|
+
output,
|
105
|
+
binary: @binary,
|
106
|
+
lines: @lines
|
107
|
+
) ? 1 : 0
|
108
|
+
end
|
87
109
|
|
88
|
-
|
110
|
+
def diff?(info_old, info_new, format, output, binary: nil, lines: 0)
|
111
|
+
case format
|
89
112
|
when :context
|
90
|
-
char_old =
|
91
|
-
char_new =
|
113
|
+
char_old = "*" * 3
|
114
|
+
char_new = "-" * 3
|
92
115
|
when :unified
|
93
|
-
char_old =
|
94
|
-
char_new =
|
116
|
+
char_old = "-" * 3
|
117
|
+
char_new = "+" * 3
|
95
118
|
end
|
96
119
|
|
97
120
|
# After we've read up to a certain point in each file, the number of
|
98
121
|
# items we've read from each file will differ by FLD (could be 0).
|
99
122
|
file_length_difference = 0
|
100
123
|
|
101
|
-
data_old = IO.read(file_old)
|
102
|
-
data_new = IO.read(file_new)
|
103
|
-
|
104
124
|
# Test binary status
|
105
|
-
if
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
end
|
110
|
-
|
111
|
-
unless @binary
|
112
|
-
data_old = data_old.lines.to_a
|
113
|
-
data_new = data_new.lines.to_a
|
125
|
+
if binary.nil?
|
126
|
+
old_bin = info_old.data[0, 4096].include?("\0")
|
127
|
+
new_bin = info_new.data[0, 4096].include?("\0")
|
128
|
+
binary = old_bin || new_bin
|
114
129
|
end
|
115
130
|
|
116
131
|
# diff yields lots of pieces, each of which is basically a Block object
|
117
|
-
if
|
118
|
-
|
132
|
+
if binary
|
133
|
+
has_diffs = (info_old.data != info_new.data)
|
134
|
+
if format != :report
|
135
|
+
if has_diffs
|
136
|
+
output << "Binary files #{info_old.filename} and #{info_new.filename} differ\n"
|
137
|
+
return true
|
138
|
+
end
|
139
|
+
return false
|
140
|
+
end
|
119
141
|
else
|
142
|
+
data_old = info_old.data.lines.to_a
|
143
|
+
data_new = info_new.data.lines.to_a
|
120
144
|
diffs = Diff::LCS.diff(data_old, data_new)
|
121
|
-
|
122
|
-
end
|
123
|
-
|
124
|
-
return 0 unless diffs
|
125
|
-
|
126
|
-
if @format == :report
|
127
|
-
output << "Files #{file_old} and #{file_new} differ\n"
|
128
|
-
return 1
|
145
|
+
return false if diffs.empty?
|
129
146
|
end
|
130
147
|
|
131
|
-
|
132
|
-
|
133
|
-
output << "#{
|
134
|
-
|
135
|
-
|
148
|
+
case format
|
149
|
+
when :report
|
150
|
+
output << "Files #{info_old.filename} and #{info_new.filename} differ\n"
|
151
|
+
return true
|
152
|
+
when :unified, :context
|
153
|
+
ft = info_old.stat.mtime.localtime.strftime("%Y-%m-%d %H:%M:%S.000000000 %z")
|
154
|
+
output << "#{char_old} #{info_old.filename}\t#{ft}\n"
|
155
|
+
ft = info_new.stat.mtime.localtime.strftime("%Y-%m-%d %H:%M:%S.000000000 %z")
|
156
|
+
output << "#{char_new} #{info_new.filename}\t#{ft}\n"
|
157
|
+
when :ed
|
158
|
+
real_output = output
|
159
|
+
output = []
|
136
160
|
end
|
137
161
|
|
138
162
|
# Loop over hunks. If a hunk overlaps with the last hunk, join them.
|
139
163
|
# Otherwise, print out the old one.
|
140
164
|
oldhunk = hunk = nil
|
141
|
-
|
142
|
-
if @format == :ed
|
143
|
-
real_output = output
|
144
|
-
output = []
|
145
|
-
end
|
146
|
-
|
147
165
|
diffs.each do |piece|
|
148
|
-
begin
|
149
|
-
hunk = Diff::LCS::Hunk.new(data_old, data_new, piece,
|
166
|
+
begin
|
167
|
+
hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, lines, file_length_difference)
|
150
168
|
file_length_difference = hunk.file_length_difference
|
151
169
|
|
152
170
|
next unless oldhunk
|
153
|
-
next if
|
171
|
+
next if lines.positive? && hunk.merge(oldhunk)
|
154
172
|
|
155
|
-
output << oldhunk.diff(
|
156
|
-
output << "\n" if
|
173
|
+
output << oldhunk.diff(format)
|
174
|
+
output << "\n" if format == :unified
|
157
175
|
ensure
|
158
176
|
oldhunk = hunk
|
159
177
|
end
|
160
178
|
end
|
161
179
|
|
162
|
-
last = oldhunk.diff(
|
163
|
-
last << "\n"
|
180
|
+
last = oldhunk.diff(format, true)
|
181
|
+
last << "\n" unless last.is_a?(Diff::LCS::Hunk) || last.empty? || last.end_with?("\n")
|
164
182
|
|
165
183
|
output << last
|
166
184
|
|
167
|
-
output.reverse_each { |e| real_output << e.diff(:ed_finish) } if
|
185
|
+
output.reverse_each { |e| real_output << e.diff(:ed_finish, e == output[0]) } if format == :ed
|
168
186
|
|
169
|
-
|
187
|
+
true
|
170
188
|
end
|
171
189
|
end
|