GithubHelp home page GithubHelp logo

diff-lcs's Introduction

Diff::LCS

home

github.com/halostatue/diff-lcs

code

github.com/halostatue/diff-lcs

bugs

github.com/halostatue/diff-lcs/issues

rdoc

rubydoc.info/github/halostatue/diff-lcs

continuous integration

<img src=“https://github.com/halostatue/diff-lcs/workflows/CI/badge.svg” />

Description

Diff::LCS computes the difference between two Enumerable sequences using the McIlroy-Hunt longest common subsequence (LCS) algorithm. It includes utilities to create a simple HTML diff output format and a standard diff-like tool.

This is release 1.4.3, providing a simple extension that allows for Diff::LCS::Change objects to be treated implicitly as arrays and fixes a number of formatting issues.

Ruby versions below 2.5 are soft-deprecated, which means that older versions are no longer part of the CI test suite. If any changes have been introduced that break those versions, bug reports and patches will be accepted, but it will be up to the reporter to verify any fixes prior to release. The next major release will completely break compatibility.

Synopsis

Using this module is quite simple. By default, Diff::LCS does not extend objects with the Diff::LCS interface, but will be called as if it were a function:

require 'diff/lcs'

seq1 = %w(a b c e h j l m n p)
seq2 = %w(b c d e f j k l m r s t)

lcs = Diff::LCS.LCS(seq1, seq2)
diffs = Diff::LCS.diff(seq1, seq2)
sdiff = Diff::LCS.sdiff(seq1, seq2)
seq = Diff::LCS.traverse_sequences(seq1, seq2, callback_obj)
bal = Diff::LCS.traverse_balanced(seq1, seq2, callback_obj)
seq2 == Diff::LCS.patch!(seq1, diffs)
seq1 == Diff::LCS.unpatch!(seq2, diffs)
seq2 == Diff::LCS.patch!(seq1, sdiff)
seq1 == Diff::LCS.unpatch!(seq2, sdiff)

Objects can be extended with Diff::LCS:

seq1.extend(Diff::LCS)
lcs = seq1.lcs(seq2)
diffs = seq1.diff(seq2)
sdiff = seq1.sdiff(seq2)
seq = seq1.traverse_sequences(seq2, callback_obj)
bal = seq1.traverse_balanced(seq2, callback_obj)
seq2 == seq1.patch!(diffs)
seq1 == seq2.unpatch!(diffs)
seq2 == seq1.patch!(sdiff)
seq1 == seq2.unpatch!(sdiff)

By requiring ‘diff/lcs/array’ or ‘diff/lcs/string’, Array or String will be extended for use this way.

Note that Diff::LCS requires a sequenced enumerable container, which means that the order of enumeration is both predictable and consistent for the same set of data. While it is theoretically possible to generate a diff for an unordered hash, it will only be meaningful if the enumeration of the hashes is consistent. In general, this will mean that containers that behave like String or Array will perform best.

History

Diff::LCS is a port of Perl’s Algorithm::Diff that uses the McIlroy-Hunt longest common subsequence (LCS) algorithm to compute intelligent differences between two sequenced enumerable containers. The implementation is based on Mario I. Wolczko’s Smalltalk version 1.2 (1993) and Ned Konz’s Perl version Algorithm::Diff 1.15. Diff::LCS#sdiff and Diff::LCS#traverse_balanced were originally written for the Perl version by Mike Schilli.

The algorithm is described in A Fast Algorithm for Computing Longest Common Subsequences, CACM, vol.20, no.5, pp.350-353, May 1977, with a few minor improvements to improve the speed. A simplified description of the algorithm, originally written for the Perl version, was written by Mark-Jason Dominus.

diff-lcs's People

Contributors

apuratepp avatar bjfish avatar ged avatar ginriki avatar halostatue avatar hubrix avatar jonrowe avatar justintsteele avatar kachick avatar kevinmook avatar knu avatar m-nakamura145 avatar mark-young-atg avatar nicolasleger avatar p-linnane avatar pck avatar tiendo1011 avatar voxik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

diff-lcs's Issues

Diff::LCS::Hunk#diff(:unified) output changed between 1.3 and 1.4.1

Hi,

The tests for chef/chef are seeing some failures related to diff output after upgrading from version 1.3 to 1.4.1 (see chef/chef#10057 for a bit more detail). I've distilled one of the failing test cases to a small script that demonstrates the changes between versions:

require 'diff/lcs'
require 'diff/lcs/hunk'

puts "::Diff::LCS::VERSION == #{::Diff::LCS::VERSION}"
puts

old_data = <<~DATA_OLD.strip.split("\n").map(&:chomp)
{
  "name": "x",
  "description": "hi"
}
DATA_OLD

new_data = <<~DATA_NEW.strip.split("\n").map(&:chomp)
{
  "name": "x",
  "description": "lo"
}
DATA_NEW

diff = ::Diff::LCS.diff(old_data, new_data)

abort "Got #{diff.size} chunks." unless diff.size == 1

hunk = ::Diff::LCS::Hunk.new(old_data, new_data, diff.first, 3, 0)

puts hunk.diff(:unified)

Running this with 1.3 I get this output:

Petes-MBP:chef pete$ ruby test_lcs_diff.rb
::Diff::LCS::VERSION == 1.3

@@ -1,5 +1,5 @@
 {
   "name": "x",
-  "description": "hi"
+  "description": "lo"
 }

Running the script with 1.4.1 I get this output:

Petes-MBP:chef pete$ ruby test_lcs_diff.rb
::Diff::LCS::VERSION == 1.4.1

@@ -1,3 +1,3 @@
 {
   "name": "x",
-  "description": "hi"
+  "description": "lo"

Diff::LCS.diff breaks under weird circumstances

[#13817] Diff::LCS.diff breaks under weird circumstances

Date: 2007-09-10 04:45

Doing this causes a nasty bug:

seq1, seq2 = "aX", "bXaX"
Diff::LCS.patch(seq1, Diff::LCS.diff(seq1, seq2))
RuntimeError: The provided patchset does not appear to apply to the provided value as either source or destination
value.
        from /var/lib/gems/1.8/gems/diff-lcs-1.1.2/lib/diff/lcs.rb:1022:in `__diff_direction'
        from /var/lib/gems/1.8/gems/diff-lcs-1.1.2/lib/diff/lcs.rb:705:in `patch'
        from (irb):335

Do other people get the same error?

Return grouped objects based on similar actions

I'm liking the output of sdiff but each "segment" is a separate object. Would it be possible to merge adjacent objects with similar actions into 1 object?

In my case I'm feeding in arrays of sentences. If someone adds a paragraph, the difference is shown as a collection of new sentences. Instead, I would like one <ins> tag around the whole new paragraph.

Update: This is what I've done to accommodate for now

def consolidateDiff(sdiff)
  lastAction = ''
  sdiff.each_with_index do |diff, index|
    if diff.action == lastAction
      sdiff[index-1].old_element << diff.old_element unless sdiff[index-1].old_element.nil?
      sdiff[index-1].new_element << diff.new_element unless sdiff[index-1].new_element.nil?
      sdiff.delete_at(index)
      consolidateDiff(sdiff)
    end
    lastAction = diff.action
  end
end

Extract diff display logic from Diff::LCS::Ldiff

I'm looking for a way to quickly display a diff of two strings. The logic in Diff::LCS::Ldiff seems to do the right thing, but it comes with extra stuff needed for a command line program. It would be nice if this were split so the hunk merging and output logic can be re-used.

License ambiguity

The LICENSE.md file indicates that this software is licensed under three licenses: GPL-2.0, PAL, or MIT with one portion licensed under PAL and GPL-1.0 as per the original license terms being "the same terms as Perl itself".

The license file docs/artistic.txt referenced by the sub-heading labeled Perl Artistic License (version 2) is in fact the PAL, not the PAL 2.0. As such, it appears to me that the LICENSE.md file is mislabeled in this section.

Reintroduce a threshold test optimization

According to the original Perl implementation (this hews to Algorithm::DiffOld, which is how old this code is), the removed threshold test is an optimization:

			# optimization: most of the time this will be true
			if ( $k
				and $thresh->[ $k ] > $j
				and $thresh->[ $k - 1 ] < $j )
			{
				$thresh->[ $k ] = $j;
			}
			else
			{
				$k = _replaceNextLargerWith( $thresh, $j, $k );
			}

The comparison is not necessary, but may have an optimization purpose, so should probably not be dropped.

Would like to embed diff-lcs such that it yields a html diff as a string

I currently use

rawDiff     = Diffy::Diff.new(other.trace_orig, self.trace_orig)
diff_as_html=rawDiff.to_s(:html)

But diffy calls another executable. In our case we use ldiff as provided by liff-lcs.

I wanted to achieve something like

diff_as_html=diffs = Diff::LCS.htmldiff('foo', 'bfoobar')

which should yield

"<div class=\"diff\">\n  <ul>\n    <li class=\"del\"><del>foo</del></li>\n    <li class=\"ins\"><ins>foo<strong>bar</strong></ins></li>\n  </ul>\n</div>\n"

How can I achieve this?

Diff::LCS::Ldiff @binary

Seeing below line.
https://github.com/halostatue/diff-lcs/blob/v1.3/lib/diff/lcs/ldiff.rb#L100

@binary = (not old_txt) or (not new_txt)

Current logic

Current logic is like this.

$ cat binary.rb 
# old_txt is empty.
old_txt = true
# new_txt is not empty
new_txt = false

binary = (not old_txt) or (not new_txt)

puts binary
$ ruby binary.rb 
false

In this case, it seems that the (not new_txt) is always not evaluated.

The logic to add brackets (...).

$ cat binary2.rb 
# old_txt is empty.
old_txt = true
# new_txt is not empty
new_txt = false

binary = ((not old_txt) or (not new_txt))

puts binary
$ ruby binary2.rb 
true

Is this logic correct?

Potential type errors

These were caught by a novel type inference system built on RDL. I wanted to bring these to your attention, and also confirm whether they are true bugs so that we may better evaluate our inference system.

  1. Here and here: no method Diff::LCS.YieldingCallbacks was found.

  2. Here Text::Format is undefined.

not enough 'require' to run correctly spec/hunk_spec.rb

Hi,

When running rspec on a checkout of the repository, I get:

Failed examples:

rspec ./spec/hunk_spec.rb:17 # Diff::LCS::Hunk should be able to produce a unified diff from the two pieces
rspec ./spec/hunk_spec.rb:26 # Diff::LCS::Hunk should be able to produce a context diff from the two pieces
rspec ./spec/hunk_spec.rb:38 # Diff::LCS::Hunk should be able to produce an old diff from the two pieces
rspec ./spec/hunk_spec.rb:49 # Diff::LCS::Hunk should be able to produce a reverse ed diff from the two pieces
rspec ./spec/hunk_spec.rb:62 # Diff::LCS::Hunk with empty first data set should be able to produce a unified diff

they all fail with the following error:

 NameError:
       uninitialized constant Diff::LCS::Hunk

There is no 'require' chain loading lib/diff/lcs/hunk.rb from spec_helper. Adding require 'diff/lcs/hunk' to either spec/spec_helper.rb or spec/hunk_spec.rb does the trick.

This bug is also referenced by the Debian project:
http://bugs.debian.org/794164

Can you clarify the license of diff-lcs?

Hi,

I am a bit confused about the license since the LICENSE.rdoc states:

This software is available under three licenses:
the GNU GPL version 2 (or at your option, a later version),
the Perl Artistic license,
or the MIT license. 

and lib/diff/lcs/ldiff.rb file states:

# This program is free software. It may be redistributed and/or modified under
# the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
# Ruby licence.

Can you clarify what is right? Both?

Thank you.

bin/ldiff doesn't always output the correct output

With files aX (containing aX\n) and bXaX (containing bXaX\n), the output of bin/ldiff does not match that of GNU diff.

With bin/ldiff aX bXaX:


1c1
< aX

---
> bXaX

With diff aX bXaX:

1c1
< aX

---
> bXaX

Note the extra blank lines.

The same applies with bin/ldiff -u aX bXaX:

--- aX  2011-08-27 09:36:12 -0400
+++ bXaX    2011-08-27 09:36:18 -0400

@@ -1,2 +1,2 @@
-aX
+bXaX

versus:

--- aX  2011-08-27 09:36:12.000000000 -0400
+++ bXaX    2011-08-27 09:36:18.000000000 -0400
@@ -1 +1 @@
-aX
+bXaX

Diff::LCS.patch! does not change Array instance

If I understand the documentation correctly, when an Array instance a is extended with Diff::LCS, a.patch! a_diff should alter a when a_diff is non-trivial.

Example of Actual behavior:

irb(main):025:0> a, b = [:a, :b, :c], [:a, :b]
[[:a, :b, :c], [:a, :b]]

irb(main):026:0> a.extend Diff::LCS
[:a, :b, :c]

irb(main):027:0> b.extend Diff::LCS
[:a, :b]

irb(main):028:0> a_diff = Diff::LCS.diff a, b
[[#<Diff::LCS::Change:0xd1f0ad @element=:c, @position=2, @action="-">]]

irb(main):029:0> a.patch a_diff
[:a, :b]

irb(main):030:0> a
[:a, :b, :c]

irb(main):031:0> a.patch! a_diff
[:a, :b]

irb(main):032:0> a
[:a, :b, :c]

irb(main):033:0> a_diff2 = a.diff b
[[#<Diff::LCS::Change:0x2870b7 @element=:c, @position=2, @action="-">]]

irb(main):034:0> a
[:a, :b, :c]

irb(main):035:0> b
[:a, :b]

irb(main):036:0> a.patch a_diff2
[:a, :b]

irb(main):037:0> a
[:a, :b, :c]

irb(main):038:0> a.patch! a_diff2
[:a, :b]

irb(main):039:0> a
[:a, :b, :c]

irb(main):040:0> b
[:a, :b]

Shouldn't a == [:a, :b]?

Thanks,
Scott

Versioning request.

Hello!

Your friendly neighbourhood RSpec team here, we've noticed that the latest version shift for diff-lcs is breaking our builds as we introspect on the diff contents, we are working on this and are confident that it is just a semantic issue for us, but I noticed in the course of reading the new readme, that you are intending to hard drop more Ruby versions in the next version, totally understandable, we intend to do that in our next major version too.

What I've come here to request is that your next version, or any version with hard Ruby version changes, be 2.0, as we and probably others have specified < 2 for some time, being the next expected major version, so we were hoping to avoid breaking changes.

You are of course, completely within your rights to say "no, thats not our versioning system" however it is impossible for us to back date the change to "< 1.5" which means that bundler will try to install both new versions of diff-lcs and old versions of RSpec on potentially incompatible Rubies, and cause people problems, so I'm hoping you will agree that releasing a 2.0 next will be a smoother path for everyone!

Thanks for your work!

missing fixtures in the gem

Hi,

It seems that the fixtures old-chef, old-chef2, new-chef, new-chef2, as well as the corresponding outputs in spec/fixtures/ldiff are missing in the gem of the last version (1.4.4).
As a consequence, there are a few failures in the test suite.

Could you please incorporate them in future gems?

Thanks in advance

Diffing two strings sometimes adds a blank change

Diffing two strings sometimes adds a blank change

Sometimes when I diff two strings, the resulting array has a blank change.
For example:

Diff::LCS.diff 'hi', 'hie'

returns a two-element array where the first is a Diff::LCS::Change with @element="". I don't know why that
is there at all.

Diff::LCS.diff 'hi', 'hi'

should return an empty array but instead returns an array with the blank change (@element = "").

This does not always happen, as in the case with:

Diff::LCS.diff 'hie', 'hi'

correctly returns a one-element array.

Problems with the new Diff::LCS::Change#to_ary implementation

@knu: Unfortunately, there has been a bug in the diff-lcs test suite on Travis for a while (the test script is running bundle exec rake travis which is running the test task; something changed to where that was no longer dependent on the spec task), so the successful tests haven’t actually meant anything. I was prepping your merged PR #47 and could not get any of the tests to pass with bundle exec rake for various reasons.

The tests are correctly running now, but now we are seeing 88/274 failures. I suspect that this means we are going to need to revert #47, but I wanted to see if you had any suggestions before I do this.

Consider supporting 1.8.7 in 1.3.

Hi there, first of all thanks for your work on diff-lcs, we've been depending on this gem for a long time in rspec and we're not the only ones (cucumber is another example). You generally make our lives easier! So thank you!

However, the recent release of 1.3 is causing us headaches, as a testing library we have to be the lowest common denominator in terms of Ruby support, which means we have to support 1.8.7. We've been pinning diff-lcs to < 2 expecting that any change in Ruby support would go hand in hand with a major version bump (as semantic versioning would require). So by changing your Ruby support in a minor version you've caused us a bit of bother, bundler will now give 1.8.7 a hard time installing rspec (as there are now multiple scenarios where it will resolve 1.3.0 for diff-lcs but not a patch release version of rspec etc).

I'd really appreciate it if you could fix this here, so as to give a better experience to those poor souls stuck on 1.8.7, you could either yank 1.3.0, and release 2.0.0, which is the quickest fix, or yank 1.3.0 and release 1.3.1 with a relaxed Ruby requirement.

Thanks for taking the time to read this.

ldiff --version

Probably not a huge issue; but an issue non-the-less.

[lovelettr@5VLLTR1-ubuntu ~/src/daavtest] (reports) 
$ ldiff --version
/home/lovelettr/.rvm/gems/ruby-1.9.3-p392@daavtest/gems/diff-lcs-1.2.4/lib/diff/lcs/ldiff.rb:88:in `block (2 levels) in run': uninitialized constant Module::BANNER (NameError)
    from /home/lovelettr/.rvm/rubies/ruby-1.9.3-p392/lib/ruby/1.9.1/optparse.rb:1360:in `call'
    from /home/lovelettr/.rvm/rubies/ruby-1.9.3-p392/lib/ruby/1.9.1/optparse.rb:1360:in `block in parse_in_order'
    from /home/lovelettr/.rvm/rubies/ruby-1.9.3-p392/lib/ruby/1.9.1/optparse.rb:1347:in `catch'
    from /home/lovelettr/.rvm/rubies/ruby-1.9.3-p392/lib/ruby/1.9.1/optparse.rb:1347:in `parse_in_order'
    from /home/lovelettr/.rvm/rubies/ruby-1.9.3-p392/lib/ruby/1.9.1/optparse.rb:1341:in `order!'
    from /home/lovelettr/.rvm/rubies/ruby-1.9.3-p392/lib/ruby/1.9.1/optparse.rb:1432:in `permute!'
    from /home/lovelettr/.rvm/rubies/ruby-1.9.3-p392/lib/ruby/1.9.1/optparse.rb:1453:in `parse!'
    from /home/lovelettr/.rvm/gems/ruby-1.9.3-p392@daavtest/gems/diff-lcs-1.2.4/lib/diff/lcs/ldiff.rb:93:in `block in run'
    from /home/lovelettr/.rvm/rubies/ruby-1.9.3-p392/lib/ruby/1.9.1/optparse.rb:1865:in `options'
    from /home/lovelettr/.rvm/gems/ruby-1.9.3-p392@daavtest/gems/diff-lcs-1.2.4/lib/diff/lcs/ldiff.rb:57:in `run'
    from /home/lovelettr/.rvm/gems/ruby-1.9.3-p392@daavtest/gems/diff-lcs-1.2.4/bin/ldiff:6:in `<top (required)>'
    from /home/lovelettr/.rvm/gems/ruby-1.9.3-p392@daavtest/bin/ldiff:19:in `load'
    from /home/lovelettr/.rvm/gems/ruby-1.9.3-p392@daavtest/bin/ldiff:19:in `<main>'
    from /home/lovelettr/.rvm/gems/ruby-1.9.3-p392@daavtest/bin/ruby_noexec_wrapper:14:in `eval'
    from /home/lovelettr/.rvm/gems/ruby-1.9.3-p392@daavtest/bin/ruby_noexec_wrapper:14:in `<main>'

Diff::LCS::Hunk#diff(:unified) output changed between 1.3 and 1.4.x, part 2

Hi,

Similar to the case I mentioned in #60, there were failures in another Chef project, chef-cli, upon upgrading from diff-lcs 1.3 to 1.4.x. See here for the actual failures: https://buildkite.com/chef-oss/chef-chef-cli-master-verify/builds/227#aa286955-6862-4b7c-9d0b-c347c895a150

I made another test script that demonstrates the differences:

require 'diff/lcs'
require 'diff/lcs/hunk'

puts "::Diff::LCS::VERSION == #{::Diff::LCS::VERSION}"
puts

def diff_lines(old_lines, new_lines)
  file_length_difference = 0
  previous_hunk = nil

  Diff::LCS.diff(old_lines, new_lines).each do |piece|
    hunk = Diff::LCS::Hunk.new(old_lines, new_lines, piece, 3, file_length_difference)
    file_length_difference = hunk.file_length_difference
    maybe_contiguous_hunks = (previous_hunk.nil? || hunk.merge(previous_hunk))

    puts "#{previous_hunk.diff(:unified)}\n" unless maybe_contiguous_hunks

    previous_hunk = hunk
  end
  puts "#{previous_hunk.diff(:unified)}\n" unless previous_hunk.nil?
end

diff_lines(
  ["cf4b8a020bdc1ba6914093a8a07a5514cce8a3a2979a967b1f32ea704a61785b"],
  ["304566f86a620aae85797a3c491a51fb8c6ecf996407e77b8063aa3ee59672c5"],
)

puts "=" * 20
puts "=" * 20
puts "=" * 20

diff_lines(
  ["recipe[a::default]", "recipe[b::default]", "recipe[c::default]", "recipe[d::default]", "recipe[e::default]", "recipe[f::default]", "recipe[g::default]", "recipe[h::default]", "recipe[i::default]", "recipe[j::default]", "recipe[k::default]", "recipe[l::default]", "recipe[m::default]", "recipe[n::default]"],
  ["recipe[a::default]", "recipe[c::default]", "recipe[d::default]", "recipe[e::default]", "recipe[f::default]", "recipe[g::default]", "recipe[h::default]", "recipe[i::default]", "recipe[j::default]", "recipe[k::default]", "recipe[l::default]", "recipe[m::default]", "recipe[n::default]", "recipe[o::new]", "recipe[p::new]", "recipe[q::new]", "recipe[r::new]"],
)

The output on diff-lcs 1.3:

::Diff::LCS::VERSION == 1.3

@@ -1,2 +1,2 @@
-cf4b8a020bdc1ba6914093a8a07a5514cce8a3a2979a967b1f32ea704a61785b
+304566f86a620aae85797a3c491a51fb8c6ecf996407e77b8063aa3ee59672c5
====================
====================
====================
@@ -1,5 +1,4 @@
 recipe[a::default]
-recipe[b::default]
 recipe[c::default]
 recipe[d::default]
 recipe[e::default]
@@ -12,4 +11,8 @@
 recipe[l::default]
 recipe[m::default]
 recipe[n::default]
+recipe[o::new]
+recipe[p::new]
+recipe[q::new]
+recipe[r::new]

The output on diff-cls 1.4.x:

::Diff::LCS::VERSION == 1.4.3

@@ -1 +1 @@
-cf4b8a020bdc1ba6914093a8a07a5514cce8a3a2979a967b1f32ea704a61785b
+304566f86a620aae85797a3c491a51fb8c6ecf996407e77b8063aa3ee59672c5
====================
====================
====================
@@ -1,5 +1,6 @@
 recipe[a::default]
-recipe[b::default]
 recipe[c::default]
 recipe[d::default]
 recipe[e::default]
@@ -10,6 +11,10 @@
 recipe[j::default]
 recipe[k::default]
 recipe[l::default]
+recipe[o::new]
+recipe[p::new]
+recipe[q::new]
+recipe[r::new]
 recipe[m::default]
 recipe[n::default]

BTW, thanks so much for your quick response on the last issue. ❤️

Make Diff::LCS::Hunk Robust

Seeing below lines.
https://github.com/halostatue/diff-lcs/blob/v1.3/lib/diff/lcs/hunk.rb#L42-L45

@start_old = a1 || (b1 - before)
@start_new = b1 || (a1 + before)
@end_old   = a2 || (b2 - after)
@end_new = b2 || (a2 + after)

When both a1 and b1 are nil or both a2 and b2 are nil, NameError is raised unintentionally.
Maybe we can add a code to check it in advance?

$ cat diff.rb 
a1 = nil
b1 = nil
start_old = a1 || (b1 - before)
$ ruby diff.rb
Traceback (most recent call last):
diff.rb:3:in `<main>': undefined local variable or method `before' for main:Object (NameError)

Ruby version isn't limited in gemspec.

Hello (again)!

The gemspec for the library doesn't limit a Ruby version, as you've made changes to your supported Ruby version, it would be good if future releases that make further changes to supported Ruby versions specify the minimum required Ruby versions, this helps bundler pick the right version of the gem (or hard fail to install).

Currently 1.4 is broken on Ruby < 2 due to the use of (at least) Array#lazy.
(RSpec supports back to 1.8.7 in this current major release).

ldiff unified/context outputs do not always include trailing context lines

$ cat > a
123
x
^D
$ cat > b
456
x
^D
$ ldiff -U 3 a b
--- a   2024-04-12 04:32:36.000000000 -0400
+++ b   2024-04-12 04:32:40.000000000 -0400
@@ -1 +1 @@
-123
+456
$ ldiff -C 3 a b
*** a   2024-04-12 04:32:36.000000000 -0400
--- b   2024-04-12 04:32:40.000000000 -0400
***************
*** 1 ****
! 123
--- 1 ----
! 456

The trailing "x" context line isn't there. But if you use "-U 1"/"-C 1", it'll be there.

The problem can be triggered by using Diff::LCS::Hunk directly too (and the amount of context lines you request with Diff::LCS::Hunk#initialize's 4th argument likewise affects if trailing context lines may be missing or not).

The amount of heading context lines for the hunk seem to affect if trailing lines may be missing or not too.

Unified Diff Hunk Description Wrong

I think there is an issue in the ldiff -u output.

Running ldiff -u data_set_1.csv data_set_2.csv > ldiff.patch generates:

--- spec/data/data_set_1.csv  2013-05-07 15:37:17.592816097 -0500
+++ spec/data/data_set_2.csv    2013-05-07 15:37:17.592816097 -0500
@@ -3,7 +3,7 @@
 3,13
 4,21
 5,31
-6,43
+6,42
 7,57
 8,73
 9,91
@@ -16,7 +16,7 @@
 16,273
 17,307
 18,343
-19,381
+19,200
 20,421
 21,463
 22,507
@@ -28,10 +28,10 @@
 28,813
 29,871
 30,931
-31,993
+31,123
 32,1057
 33,1123
-34,1191
+34,1000
 35,1261
 36,1333
 37,1407
@@ -44,8 +44,9 @@
 44,1981
 45,2071
 46,2163
-47,2257
+47,1524
 48,2353
 49,2451
 50,2500
+51,2520

I believe the last hunk description is wrong. It should be @@ -44,7 +44,8 @@. Running Unix diff confirms my suspicions.

All of the files (including data_set_1.csv and data_set_2.csv) can be found in this Gist.

Warning in hunk.rb (overridden setter method)

Ian MacLeod (http://rubyforge.org/users/nevir)

In hunk.rb, there's the following:

attr_accessor :flag_context
def flag_context=(context) #:nodoc:

Which causes the following warning to be emitted:

.../diff-lcs-1.1.2/lib/diff/lcs/hunk.rb:69: warning: method redefined; discarding old flag_context=

It seems like switching the attr macro from attr_accessor to attr_reader does the intended work

`undefined method` error in `hunk.rb` after upgrading to 1.4

I'm using RSpec which uses diff-lcs for expect equal comparisons. In this case I'm doing the following comparison:

actual = {:category=>"app.rack.request"}
expected = {:category=>"rack.middleware", :title=>"Anonymous Middleware"}
expect(actual).to eq(expected)

The backtrace is this:

     NoMethodError:
       undefined method `[]=' for nil:NilClass

     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/diff-lcs-1.4/lib/diff/lcs/hunk.rb:167:in `block (2 levels) in unified_diff'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/diff-lcs-1.4/lib/diff/lcs/hunk.rb:164:in `each'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/diff-lcs-1.4/lib/diff/lcs/hunk.rb:164:in `block in unified_diff'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/diff-lcs-1.4/lib/diff/lcs/hunk.rb:163:in `each'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/diff-lcs-1.4/lib/diff/lcs/hunk.rb:163:in `unified_diff'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/diff-lcs-1.4/lib/diff/lcs/hunk.rb:106:in `diff'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/rspec-support-3.9.3/lib/rspec/support/differ.rb:49:in `diff_as_string'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/rspec-support-3.9.3/lib/rspec/support/differ.rb:60:in `diff_as_object'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/rspec-support-3.9.3/lib/rspec/support/differ.rb:20:in `diff'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/rspec-expectations-3.9.2/lib/rspec/matchers/expecteds_for_multiple_diffs.rb:70:in `block in diffs'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/rspec-expectations-3.9.2/lib/rspec/matchers/expecteds_for_multiple_diffs.rb:69:in `map'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/rspec-expectations-3.9.2/lib/rspec/matchers/expecteds_for_multiple_diffs.rb:69:in `diffs'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/rspec-expectations-3.9.2/lib/rspec/matchers/expecteds_for_multiple_diffs.rb:48:in `message_with_diff'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/rspec-expectations-3.9.2/lib/rspec/expectations/fail_with.rb:33:in `fail_with'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/rspec-expectations-3.9.2/lib/rspec/expectations/handler.rb:38:in `handle_failure'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/rspec-expectations-3.9.2/lib/rspec/expectations/handler.rb:50:in `block in handle_matcher'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/rspec-expectations-3.9.2/lib/rspec/expectations/handler.rb:27:in `with_matcher'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/rspec-expectations-3.9.2/lib/rspec/expectations/handler.rb:48:in `handle_matcher'
     # /Users/peterwagenet/.rvm/gems/ruby-2.7.1/gems/rspec-expectations-3.9.2/lib/rspec/expectations/expectation_target.rb:65:in `to'

Updating require_gem() to gem() for diff-lcs/bin/htmldiff and diff-lcs/bin/ldiff

Could you please update require_gem() to gem() method?

I got following error when I launch htmldiff and ldiff.

$ htmldiff
/Library/Ruby/Gems/1.8/gems/diff-lcs-1.1.2/bin/htmldiff:18: undefined method `require_gem' for main:Object
(NoMethodError)
    from /usr/bin/htmldiff:19:in `load'
    from /usr/bin/htmldiff:19
$ ldiff 
/Library/Ruby/Gems/1.8/gems/diff-lcs-1.1.2/bin/ldiff:24: undefined method `require_gem' for main:Object (NoMethodError)
    from /usr/bin/ldiff:19:in `load'
    from /usr/bin/ldiff:19
$ 
### Eclipse Workspace Patch 1.0
#P diff-lcs
Index: bin/ldiff
===================================================================
--- bin/ldiff   (revision 215)
+++ bin/ldiff   (working copy)
@@ -21,7 +21,7 @@
   begin
     if 1 == load_state
       require 'rubygems'
-      require_gem 'diff-lcs', '= 1.1.1'
+      gem 'diff-lcs', '= 1.1.1'
     else
       require 'diff/lcs'
     end
Index: bin/htmldiff
===================================================================
--- bin/htmldiff    (revision 215)
+++ bin/htmldiff    (working copy)
@@ -15,7 +15,7 @@

 begin
   require 'rubygems'
-  require_gem 'diff-lcs', "1.1.1"
+  gem 'diff-lcs', "1.1.1"
   require 'diff/lcs/string'
 rescue LoadError
   require 'diff/lcs'

Local failures after off-by-one fix

I’m not entirely sure why:

✖ bundle exec rake
DEPRECATED: I want to drop this entirely. Let me know if you use this! from /Users/austin/.gem/ruby/2.7.4/gems/hoe-3.23.0/lib/hoe/test.rb:139:in `define_test_tasks'
/Users/austin/.rubies/ruby-2.7.4/bin/ruby -I/Users/austin/.gem/ruby/2.7.4/gems/rspec-core-3.10.1/lib:/Users/austin/.gem/ruby/2.7.4/gems/rspec-support-3.10.3/lib /Users/austin/.gem/ruby/2.7.4/gems/rspec-core-3.10.1/exe/rspec --pattern spec/\*\*\{,/\*/\*\*\}/\*_spec.rb -Ispec:lib
Run options: exclude {:broken=>true}
......................................FFF...FFFFF.......................FFFFF..................................................................................................................................................................................................................

Failures:

  1) Diff::LCS.diff correctly diffs seq1 to seq2
     Failure/Error: expect(change_diff(correct_forward_diff)).to eq(diff_s1_s2)

       expected: [[#<Diff::LCS::Change: ["-", 0, "a"]>], [#<Diff::LCS::Change: ["+", 2, "d"]>], [#<Diff::LCS::Change: ...Change: ["-", 9, "p"]>, #<Diff::LCS::Change: ["+", 10, "s"]>, #<Diff::LCS::Change: ["+", 11, "t"]>]]
            got: [[#<Diff::LCS::Change: ["-", 0, "a"]>], [#<Diff::LCS::Change: ["+", 2, "d"]>], [#<Diff::LCS::Change: ...Change: ["+", 9, "r"]>, #<Diff::LCS::Change: ["+", 10, "s"]>, #<Diff::LCS::Change: ["+", 11, "t"]>]]

       (compared using ==)
     # ./spec/diff_spec.rb:10:in `block (2 levels) in <top (required)>'

  2) Diff::LCS.diff correctly diffs seq2 to seq1
     Failure/Error: expect(change_diff(correct_backward_diff)).to eq(diff_s2_s1)

       expected: [[#<Diff::LCS::Change: ["+", 0, "a"]>], [#<Diff::LCS::Change: ["-", 2, "d"]>], [#<Diff::LCS::Change: ...Change: ["-", 10, "s"]>, #<Diff::LCS::Change: ["+", 9, "p"]>, #<Diff::LCS::Change: ["-", 11, "t"]>]]
            got: [[#<Diff::LCS::Change: ["+", 0, "a"]>], [#<Diff::LCS::Change: ["-", 2, "d"]>], [#<Diff::LCS::Change: ...:Change: ["+", 8, "n"]>, #<Diff::LCS::Change: ["-", 11, "t"]>, #<Diff::LCS::Change: ["+", 9, "p"]>]]

       (compared using ==)
     # ./spec/diff_spec.rb:15:in `block (2 levels) in <top (required)>'

  3) Diff::LCS.diff correctly diffs against an empty sequence
     Failure/Error: expect(change_diff(correct_diff)).to eq(diff)

       expected: [[#<Diff::LCS::Change: ["-", 1, "efgh"]>, #<Diff::LCS::Change: ["-", 2, "ijkl"]>, #<Diff::LCS::Change: ["-", 3, "mnopqrstuvwxyz"]>]]
            got: [[#<Diff::LCS::Change: ["-", 0, "abcd"]>, #<Diff::LCS::Change: ["-", 1, "efgh"]>, #<Diff::LCS::Change: ["-", 2, "ijkl"]>, #<Diff::LCS::Change: ["-", 3, "mnopqrstuvwxyz"]>]]

       (compared using ==)
     # ./spec/diff_spec.rb:29:in `block (2 levels) in <top (required)>'

  4) Diff::LCS::Hunk produces a unified diff from the two pieces
     Failure/Error: expect(hunk.diff(:unified)).to eq(expected)

       expected: "@@ -1 +1 @@\n-Tu a un cart\u00E9 avec {count} it\u00E9ms\n+Tu a un carte avec {count} items"
            got: "@@ -1 +1,2 @@\n+Tu a un carte avec {count} items\n Tu a un cart\u00E9 avec {count} it\u00E9ms"

       (compared using ==)

       Diff:Could not produce a diff because of the encoding of the string (UTF-16LE)
     # ./spec/hunk_spec.rb:21:in `block (2 levels) in <top (required)>'

  5) Diff::LCS::Hunk produces a unified diff from the two pieces (last entry)
     Failure/Error: expect(hunk.diff(:unified, true)).to eq(expected)

       expected: "@@ -1 +1 @@\n-Tu a un cart\u00E9 avec {count} it\u00E9ms\n+Tu a un carte avec {count} items\n\\ No newline at end of file"
            got: "@@ -1 +2 @@\n+Tu a un carte avec {count} items\n Tu a un cart\u00E9 avec {count} it\u00E9ms\n\\ No newline at end of file"

       (compared using ==)

       Diff:Could not produce a diff because of the encoding of the string (UTF-16LE)
     # ./spec/hunk_spec.rb:32:in `block (2 levels) in <top (required)>'

  6) Diff::LCS::Hunk produces a context diff from the two pieces
     Failure/Error: expect(hunk.diff(:context)).to eq(expected)

       expected: "***************\n*** 1 ****\n! Tu a un cart\u00E9 avec {count} it\u00E9ms\n--- 1 ----\n! Tu a un carte avec {count} items"
            got: "***************\n*** 1 ****\n--- 1,2 ----\n+ Tu a un carte avec {count} items"

       (compared using ==)

       Diff:Could not produce a diff because of the encoding of the string (UTF-16LE)
     # ./spec/hunk_spec.rb:44:in `block (2 levels) in <top (required)>'

  7) Diff::LCS::Hunk produces an old diff from the two pieces
     Failure/Error: expect(hunk.diff(:old)).to eq(expected)

       expected: "1c1\n< Tu a un cart\u00E9 avec {count} it\u00E9ms\n---\n> Tu a un carte avec {count} items\n"
            got: "1a1,2\n> Tu a un carte avec {count} items\n"

       (compared using ==)

       Diff:Could not produce a diff because of the encoding of the string (UTF-16LE)
     # ./spec/hunk_spec.rb:56:in `block (2 levels) in <top (required)>'

  8) Diff::LCS::Hunk produces a reverse ed diff from the two pieces
     Failure/Error: expect(hunk.diff(:reverse_ed)).to eq(expected)

       expected: "c1\nTu a un carte avec {count} items\n.\n"
            got: "a1\nTu a un carte avec {count} items\n.\n"

       (compared using ==)

       Diff:Could not produce a diff because of the encoding of the string (UTF-16LE)
     # ./spec/hunk_spec.rb:67:in `block (2 levels) in <top (required)>'

  9) bin/ldiff  spec/fixtures/aX spec/fixtures/bXaX # => spec/fixtures/ldiff/output.diff
     Failure/Error: expect(run_ldiff(fixture)).to eq(read_fixture(fixture))

       expected: "1c1\n< aX\n---\n> bXaX\n"
            got: "1a1\n> bXaX\n"

       (compared using ==)

       Diff:
       @@ -1,5 +1,3 @@
       -1c1
       -< aX
       ----
       +1a1
        > bXaX

     # ./spec/ldiff_spec.rb:29:in `block in test_ldiff'

  10) bin/ldiff -e spec/fixtures/aX spec/fixtures/bXaX # => spec/fixtures/ldiff/output.diff-e
      Failure/Error: expect(run_ldiff(fixture)).to eq(read_fixture(fixture))

        expected: "1c\nbXaX\n.\n"
             got: "1a\nbXaX\n.\n"

        (compared using ==)

        Diff:
        @@ -1 +1 @@
        -1c
        +1a

      # ./spec/ldiff_spec.rb:29:in `block in test_ldiff'

  11) bin/ldiff -f spec/fixtures/aX spec/fixtures/bXaX # => spec/fixtures/ldiff/output.diff-f
      Failure/Error: expect(run_ldiff(fixture)).to eq(read_fixture(fixture))

        expected: "c1\nbXaX\n.\n"
             got: "a1\nbXaX\n.\n"

        (compared using ==)

        Diff:
        @@ -1 +1 @@
        -c1
        +a1

      # ./spec/ldiff_spec.rb:29:in `block in test_ldiff'

  12) bin/ldiff -c spec/fixtures/aX spec/fixtures/bXaX # => spec/fixtures/ldiff/output.diff-c
      Failure/Error: expect(run_ldiff(fixture)).to eq(read_fixture(fixture))

        expected: "*** spec/fixtures/aX\t0000-00-00 :00 =>:00 =>00.000000000 -0000\n*** spec/fixtures/bXaX\t0000-00-00 :00 =>:00 =>00.000000000 -0000\n***************\n*** 1 ****\n! aX\n--- 1 ----\n! bXaX\n"
             got: "*** spec/fixtures/aX\t0000-00-00 :00 =>:00 =>00.000000000 -0000\n*** spec/fixtures/bXaX\t0000-00-00 :00 =>:00 =>00.000000000 -0000\n***************\n*** 1 ****\n--- 1 ----\n+ bXaX\n"

        (compared using ==)

        Diff:

        @@ -2,7 +2,6 @@
         *** spec/fixtures/bXaX	0000-00-00 :00 =>:00 =>00.000000000 -0000
         ***************
         *** 1 ****
        -! aX
         --- 1 ----
        -! bXaX
        ++ bXaX

      # ./spec/ldiff_spec.rb:29:in `block in test_ldiff'

  13) bin/ldiff -u spec/fixtures/aX spec/fixtures/bXaX # => spec/fixtures/ldiff/output.diff-u
      Failure/Error: expect(run_ldiff(fixture)).to eq(read_fixture(fixture))

        expected: "*** spec/fixtures/aX\t0000-00-00 :00 =>:00 =>00.000000000 -0000\n*** spec/fixtures/bXaX\t0000-00-00 :00 =>:00 =>00.000000000 -0000\n@@ -1 +1 @@\n-aX\n+bXaX\n"
             got: "*** spec/fixtures/aX\t0000-00-00 :00 =>:00 =>00.000000000 -0000\n*** spec/fixtures/bXaX\t0000-00-00 :00 =>:00 =>00.000000000 -0000\n@@ -1 +2 @@\n+bXaX\n aX\n"

        (compared using ==)

        Diff:

        @@ -1,6 +1,6 @@
         *** spec/fixtures/aX	0000-00-00 :00 =>:00 =>00.000000000 -0000
         *** spec/fixtures/bXaX	0000-00-00 :00 =>:00 =>00.000000000 -0000
        -@@ -1 +1 @@
        --aX
        +@@ -1 +2 @@
         +bXaX
        + aX

      # ./spec/ldiff_spec.rb:29:in `block in test_ldiff'

Finished in 1.84 seconds (files took 0.10541 seconds to load)
287 examples, 13 failures

Failed examples:

rspec ./spec/diff_spec.rb:8 # Diff::LCS.diff correctly diffs seq1 to seq2
rspec ./spec/diff_spec.rb:13 # Diff::LCS.diff correctly diffs seq2 to seq1
rspec ./spec/diff_spec.rb:18 # Diff::LCS.diff correctly diffs against an empty sequence
rspec ./spec/hunk_spec.rb:14 # Diff::LCS::Hunk produces a unified diff from the two pieces
rspec ./spec/hunk_spec.rb:24 # Diff::LCS::Hunk produces a unified diff from the two pieces (last entry)
rspec ./spec/hunk_spec.rb:35 # Diff::LCS::Hunk produces a context diff from the two pieces
rspec ./spec/hunk_spec.rb:47 # Diff::LCS::Hunk produces an old diff from the two pieces
rspec ./spec/hunk_spec.rb:59 # Diff::LCS::Hunk produces a reverse ed diff from the two pieces
rspec ./spec/ldiff_spec.rb[1:1] # bin/ldiff  spec/fixtures/aX spec/fixtures/bXaX # => spec/fixtures/ldiff/output.diff
rspec ./spec/ldiff_spec.rb[1:2] # bin/ldiff -e spec/fixtures/aX spec/fixtures/bXaX # => spec/fixtures/ldiff/output.diff-e
rspec ./spec/ldiff_spec.rb[1:3] # bin/ldiff -f spec/fixtures/aX spec/fixtures/bXaX # => spec/fixtures/ldiff/output.diff-f
rspec ./spec/ldiff_spec.rb[1:4] # bin/ldiff -c spec/fixtures/aX spec/fixtures/bXaX # => spec/fixtures/ldiff/output.diff-c
rspec ./spec/ldiff_spec.rb[1:5] # bin/ldiff -u spec/fixtures/aX spec/fixtures/bXaX # => spec/fixtures/ldiff/output.diff-u

@tiendo1011

I’ve invited you to be a full contributor to the repository; you’re doing good work, but the fixes I see for this remove the transitive property.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.