diff -Nru ccdiff-0.21/ccdiff ccdiff-0.26/ccdiff --- ccdiff-0.21/ccdiff 2018-08-19 10:02:47.000000000 +0000 +++ ccdiff-0.26/ccdiff 2018-10-09 06:54:30.000000000 +0000 @@ -1,9 +1,15 @@ #!/usr/bin/env perl -use 5.12.1; +package App::ccdiff; + +use 5.14.0; use warnings; +use charnames (); +use Algorithm::Diff; +use Term::ANSIColor qw(:constants color); +use Getopt::Long qw(:config bundling); -our $VERSION = "0.21 - 20180818"; +our $VERSION = "0.26"; our $CMD = $0 =~ s{.*/}{}r; sub usage { @@ -16,130 +22,183 @@ say " Diff options:"; say " -U --utf-8 Input is in UTF-8"; say " -u[3] --unified=3 Show a unified diff"; - say " -I --index Add indices to the change chunks"; - say " -I n --index=4 Only show chunk n"; + say " --no-header Skip header with file names/stamps"; + say " -I --index Add indices to the change chunks"; + say " -I n --index=4 Only show chunk n"; say " -w --ignore-all-space Ignore all whitespace"; say " -b --ignore-space-change Ignore horizontal whitespace changes"; say " -Z --ignore-trailing-space Ignore whitespace at line ending"; say " -B --ignore-blank-lines Ignore changes where lines are all blank"; say " -i --ignore-case Ignore case changes"; say " Other options:"; + say " -t n --threshold=2 Horizontal line diff threshold"; + say " -h n --heuristics=n Horizontal char diff threshold"; + say " -e n --ellipsis=n Compress horizontal equal sections"; say " -m --markers Use markers to indicate change positions"; say " -a --ascii Use ASCII instead of Unicode indicators"; + say " --no-color Reset all colors to none."; say " --list-colors List available colors and exit"; say " --old=red Color to indicate removed content"; say " --new=green Color to indicate added content"; say " --bg=white Background color for colored indicators"; say " -p --pink Shortcut for --old=magenta"; - say " -r --reverse Reverse the colors of the indicators"; + say " -r --reverse Reverse/invert the colors of the indicators"; + say " --settings Show default settings (after reading rc)"; exit $err; } # usage -my %rc = read_rc (); +my %rc = ( + ascii => 0, + bg => "white", + chr_cml => "\x{21b1}", + chr_cmr => "\x{21b0}", + chr_eli => "\x{2508}", + chr_eli_v => "\x{21a4}\x{21a6}", + chr_eql => " ", + chr_new => "\x{25b2}", + chr_old => "\x{25bc}", + ellipsis => 0, + emacs => 0, + header => 1, + heuristics => 0, + index => 0, + markers => 0, + new => "green", + old => "red", + reverse => 0, + threshold => 2, + utf8 => 0, + verbose => "cyan", + ); +read_rc (); + +my $opt_a = $rc{ascii}; +my $opt_b; +my $opt_B; +#y $opt_c; +my $opt_E; +my $opt_h = $rc{heuristics}; +my $opt_H = $rc{header}; +my $opt_i; +my $opt_I = $rc{index}; +my $opt_m = $rc{markers}; +my $opt_r = $rc{reverse}; +my $opt_t = $rc{threshold}; +my $opt_e = $rc{ellipsis}; +my $opt_u = $rc{unified}; +my $opt_U = $rc{utf8}; +my $opt_v = 0; +my $opt_w; +my $opt_Z; +my $emacs = $rc{emacs}; +my $old_color = $rc{old}; +my $new_color = $rc{new}; +my $rev_color = $rc{bg}; +my $no_colors; +my $list_colors; + +unless (caller) { + $ENV{CCDIFF_OPTIONS} and unshift @ARGV, split m/\s+/ => $ENV{CCDIFF_OPTIONS}; + GetOptions ( + "help|?" => sub { usage (0); }, + "V|version" => sub { say "$CMD [$VERSION]"; exit 0; }, + "man" => sub { exec "pod2man $0 | nroff -man"; }, + "info" => sub { require Pod::Usage; + Pod::Usage::pod2usage (VERBOSE => 2); + exit; + }, + + "U|utf-8!" => \$opt_U, + + # "c|context:3" => \$opt_c, # implement context-diff? + "u|unified:3" => \$opt_u, + "I|idx|index:-1" => \$opt_I, + "t|threshold=i" => \$opt_t, + "H|header!" => \$opt_H, + "h|heuristics=i" => \$opt_h, + "e|ellipsis=i" => \$opt_e, + "emacs!" => \$emacs, + + "a|ascii" => sub { $opt_a ^= 1 }, + "m|markers" => sub { $opt_m ^= 1 }, + "r|reverse|invert" => sub { $opt_r ^= 1 }, + + "i|ignore-case!" => \$opt_i, + "w|ignore-all-space!" => \$opt_w, + "b|ignore-ws|ignore-space-change!" => \$opt_b, + "Z|ignore-trailing-space!" => \$opt_Z, + "E|ignore-tab-expansion!" => \$opt_E, # NYI + "B|ignore-blank-lines!" => \$opt_B, # Partly implemented + + "p|pink!" => sub { $old_color = "magenta" }, + "old=s" => \$old_color, + "new=s" => \$new_color, + "bg=s" => \$rev_color, + "no-colors" => \$no_colors, + "list-colors!" => \$list_colors, + "settings|defaults" => sub { + binmode STDOUT, ":encoding(utf-8)"; + printf "%-10s : %s\n", $_, $rc{$_} // "" for sort keys %rc; + exit 0; + }, -use charnames (); -use Algorithm::Diff; -use Term::ANSIColor qw(:constants color); -use Getopt::Long qw(:config bundling); -my $opt_a = $rc{ascii} // 0; -my $opt_r = $rc{reverse} // 0; -my $opt_m = $rc{markers} // 0; -GetOptions ( - "help|?" => sub { usage (0); }, - "V|version" => sub { say "$CMD [$VERSION]"; exit 0; }, - "man" => sub { exec "pod2man $0 | nroff -man"; }, - "info" => sub { exec "pod2text $0"; }, - - "U|utf-8!" => \(my $opt_U = $rc{utf8} // 0), - -# "c|context:3" => \ my $opt_c, # implement context-diff? - "u|unified:3" => \ my $opt_u, - "I|idx|index:-1" => \(my $opt_I = $rc{index} // 0), - - "a|ascii" => sub { $opt_a ^= 1 }, - "m|markers" => sub { $opt_m ^= 1 }, - "r|reverse" => sub { $opt_r ^= 1 }, - - "i|ignore-case!" => \ my $opt_i, - "w|ignore-all-space!" => \ my $opt_w, - "b|ignore-ws|ignore-space-change!" => \ my $opt_b, - "Z|ignore-trailing-space!" => \ my $opt_Z, - "E|ignore-tab-expansion!" => \ my $opt_E, # NYI - "B|ignore-blank-lines!" => \ my $opt_B, # NYI - - "p|pink!" => \ my $opt_p, - "old=s" => \(my $old_color = $rc{old} // "red" ), - "new=s" => \(my $new_color = $rc{new} // "green"), - "bg=s" => \(my $rev_color = $rc{bg} // "white"), - "list-colors!" => \ my $list_colors, - - "v|verbose:1" => \(my $opt_v = 0), - ) or usage (1); -$opt_w and $opt_b = $opt_Z = $opt_E = $opt_B = 1; + "v|verbose:1" => \$opt_v, + ) or usage (1); + } + +$opt_w and $opt_b = $opt_Z = $opt_E = $opt_B = 1; +$opt_h >= 1 and $opt_h /= 100; # Color initialization -$opt_p and $old_color = "magenta"; for ($old_color, $new_color, $rev_color) { s/^(.*)[ _]bold$/bold $1/i; s/^bold_/bold /i; } my %clr = map { $_ => color (s{^(.*)[ _]bold$}{bold $1}ir =~ s{^bold[ _]}{bold }ir) } - map { $_, "on_$_", "bold $_" } + map {( $_, "on_$_", "bold $_" )} qw( red green blue black white cyan magenta yellow ); +$clr{$_} //= color ($_) for tac_colors (); +$no_colors and $clr{$_} = "" for keys %clr; +$clr{none} = $clr{on_none} = ""; +for ([ \$old_color, $rc{old} ], [ \$new_color, $rc{new} ], [ \$rev_color, $rc{bg} ]) { + my ($c, $def) = @$_; + $$c && exists $clr{$$c} and next; + warn "color ", $$c // "(undefined)", " is unknown, using $def instead\n"; + $$c = $def; + } my $clr_red = $clr{$old_color}; my $clr_grn = $clr{$new_color}; my $clr_rev = $clr{$rev_color}; -my $clr_dbg = $clr{$rc{verbose} || "cyan"}; -my $reset = RESET; +my $clr_dbg = $opt_r && exists $clr{"on_$rc{verbose}"} ? $clr{"on_$rc{verbose}"} : $clr{$rc{verbose}}; +my $reset = $no_colors ? "" : RESET; if ($list_colors) { - say $clr{$_}, $_, $reset for keys %clr; + my @clr = map { sprintf "%s%-18s%s", $clr{$_}, $_, $reset } sort keys %clr; + while (@clr) { + say join " " => map { $_ // "" } splice @clr, 0, 4; + } exit; } -my $clr_old = $opt_r ? $clr_rev . $clr{"on_$old_color" =~ s/bold //ir} - : $clr_red . $clr{"on_$rev_color" =~ s/bold //ir}; -my $clr_new = $opt_r ? $clr_rev . $clr{"on_$new_color" =~ s/bold //ir} - : $clr_grn . $clr{"on_$rev_color" =~ s/bold //ir}; +my $bg_old = $clr{$rc{bg_old} || ($opt_r ? "on_$old_color" =~ s/bold //ir : + "on_$rev_color" =~ s/bold //ir)}; +my $bg_new = $clr{$rc{bg_new} || ($opt_r ? "on_$new_color" =~ s/bold //ir : + "on_$rev_color" =~ s/bold //ir)}; +my $clr_old = $opt_r ? $clr_rev . $bg_old : $clr_red . $bg_old; +my $clr_new = $opt_r ? $clr_rev . $bg_new : $clr_grn . $bg_new; # Indicators -my $chr_old = $clr_old . ($opt_a ? "^" : "\x{25bc}") . $reset; -my $chr_new = $clr_new . ($opt_a ? "^" : "\x{25b2}") . $reset; -my $chr_cml = $clr_dbg . ($opt_a ? ">" : "\x{21b1}") . $reset; -my $chr_cmr = $clr_dbg . ($opt_a ? "<" : "\x{21b0}") . $reset; -my $chr_eql = " "; -my $chr_lft = defined $opt_u ? "-" : "<"; -my $chr_rgt = defined $opt_u ? "+" : ">"; - -binmode STDOUT, ":encoding(utf-8)"; - -my $f1 = shift or usage (1); -my $f2 = shift // "-"; - -$f1 eq "-" && $f2 eq "-" and usage (1); - -$opt_U and binmode STDIN, ":encoding(utf-8)"; -$opt_U and binmode STDOUT, ":encoding(utf-8)"; - -my @d1 = $f1 eq "-" ? <> : do { - open my $fh, "<", $f1 or die "$f1: $!\n"; - $opt_U and binmode $fh, ":encoding(utf-8)"; - <$fh>; - }; -my @d2 = $f2 eq "-" ? <> : do { - open my $fh, "<", $f2 or die "$f2: $!\n"; - $opt_U and binmode $fh, ":encoding(utf-8)"; - <$fh>; - }; -if ($opt_u) { - for ([ "---", $f1 ], [ "+++", $f2 ]) { - if (-f $_->[1]) { - say $_->[0], " $_->[1]\t", scalar localtime ((stat $_->[1])[9]); - } - else { - say $_->[0], " *STDIN\t", scalar localtime; - } - } - } +$opt_a and + @rc{qw( chr_old chr_new chr_cml chr_cmr chr_eli chr_eli_v )} = qw( ^ ^ > < - <> ); +my $chr_old = $clr_old . $rc{chr_old} . $reset; +my $chr_new = $clr_new . $rc{chr_new} . $reset; +my $chr_cml = $clr_dbg . $rc{chr_cml} . $reset; +my $chr_cmr = $clr_dbg . $rc{chr_cmr} . $reset; +my $chr_eql = $rc{chr_eql}; +my $chr_lft = $clr_old . (defined $opt_u ? "-" : "< ") . $reset; +my $chr_rgt = $clr_new . (defined $opt_u ? "+" : "> ") . $reset; +my $chr_ctx = defined $opt_u ? " " : " "; +my $chr_eli = $opt_v >= 2 ? $rc{chr_eli_v} : $rc{chr_eli}; +$opt_m && $opt_v > 1 && length ($chr_eli) > 1 and $opt_m = 0; my $cmp_sub = $opt_i || $opt_b || $opt_Z ? { keyGen => sub { my $line = shift; @@ -149,92 +208,235 @@ return $line; }} : undef; -my $diff = Algorithm::Diff->new (\@d1, \@d2, $cmp_sub); -$diff->Base (1); +caller or ccdiff (@ARGV); -my ($N, $idx, @s) = (0, 0); -while ($diff->Next) { - $N++; - if ($diff->Same) { - if (defined $opt_u) { - @s = $diff->Items (1); - $N > 1 and print " $_" for grep { defined } @s[0..($opt_u - 1)]; - unshift @s, undef while @s < $opt_u; +sub ccdiff { + my $f1 = shift or usage (1); + my $f2 = $_[0] // "-"; + + my $fh; + + if (@_ > 1 && ref $_[1]) { # optional hash with overruling arguments + my %opt = %{$_[1]}; + foreach my $o (keys %opt) { + my $v = $opt{$o}; + $o eq "ascii" and $opt_a = $v; + $o eq "bg" and $rev_color = $v; +# $o eq "context" and $opt_c = $v; + $o eq "ellipsis" and $opt_e = $v; + $o eq "emacs" and $emacs = $v; + $o eq "header" and $opt_H = $v; + $o eq "heuristics" and $opt_h = $v; + $o eq "ignore-all-space" and $opt_w = $v; + $o eq "ignore-blank-lines" and $opt_B = $v; + $o eq "ignore-case" and $opt_i = $v; + $o eq "ignore-space-change" and $opt_b = $v; + $o eq "ignore-tab-expansion" and $opt_E = $v; + $o eq "ignore-trailing-space" and $opt_Z = $v; + $o eq "index" and $opt_I = $v; + $o eq "list-colors" and $list_colors = $v; + $o eq "markers" and $opt_m = $v; + $o eq "new" and $new_color = $v; + $o eq "old" and $old_color = $v; + $o eq "reverse" and $opt_r = $v; + $o eq "threshold" and $opt_t = $v; + $o eq "unified" and $opt_u = $v; + $o eq "unified" and $opt_u = $v; + $o eq "utf-8" and $opt_U = $v; + $o eq "verbose" and $opt_v = $v; + + if ($o eq "out") { + open $fh, ">", $v or die "Cannot select out: $!\n"; + select $fh; + } } - next; } - my $sep = ""; - my @d = map {[ $diff->Items ($_) ]} 1, 2; - if ($opt_B and "@{$d[0]}" !~ m/\S/ && "@{$d[1]}" !~ m/\S/) { - # Modify @s for -u? - next; - } - if ($opt_I) { - $idx++; - $opt_I > 0 && $idx != $opt_I and next; - printf "%s[%03d]%s ", ${clr_dbg}, $idx, $reset; - } + $emacs and @_ == 0 && -f $f1 && -f "$f1~" and ($f1, $f2) = ("$f1~", $f1); - if (!@{$d[1]}) { - printf "%d,%dd%d\n", $diff->Get (qw( Min1 Max1 Max2 )); - $_ = $clr_old . (s/$/$reset/r) for @{$d[0]} - } - elsif (!@{$d[0]}) { - printf "%da%d,%d\n", $diff->Get (qw( Max1 Min2 Max2 )); - $_ = $clr_new . (s/$/$reset/r) for @{$d[1]} + $f1 eq "-" && $f2 eq "-" and usage (1); + + binmode STDERR, ":encoding(utf-8)"; + + $opt_U and binmode STDIN, ":encoding(utf-8)"; + $opt_U and binmode STDOUT, ":encoding(utf-8)"; + + my @d1 = $f1 eq "-" ? <> : do { + open my $fh, "<", $f1 or die "$f1: $!\n"; + $opt_U and binmode $fh, ":encoding(utf-8)"; + <$fh>; + }; + my @d2 = $f2 eq "-" ? <> : do { + open my $fh, "<", $f2 or die "$f2: $!\n"; + $opt_U and binmode $fh, ":encoding(utf-8)"; + <$fh>; + }; + if ($opt_H and defined $opt_u) { + # diff -c also provides (ugly) headers, but opt_c is NYI + for ([ "---", $f1 ], [ "+++", $f2 ]) { + if (-f $_->[1]) { + say $_->[0], " $_->[1]\t", scalar localtime ((stat $_->[1])[9]); + } + else { + say $_->[0], " *STDIN\t", scalar localtime; + } + } } - else { - $sep = "---\n" unless defined $opt_u; - printf "%d,%dc%d,%d\n", $diff->Get (qw( Min1 Max1 Min2 Max2 )); - @d = subdiff (@d); + + my $diff = Algorithm::Diff->new (\@d1, \@d2, $cmp_sub); + $diff->Base (1); + + my ($N, $idx, @s) = (0, 0); + while ($diff->Next) { + $N++; + if ($diff->Same) { + if (defined $opt_u) { + @s = $diff->Items (1); + $N > 1 and print "$chr_ctx$_" for grep { defined } @s[0..($opt_u - 1)]; + unshift @s, undef while @s < $opt_u; + } + next; + } + my $sep = ""; + my @d = map {[ $diff->Items ($_) ]} 1, 2; + my @do = @{$d[0]}; + my @dn = @{$d[1]}; + + if ($opt_B and "@do" !~ m/\S/ && "@dn" !~ m/\S/) { + # Modify @s for -u? + next; + } + if ($opt_I) { + $idx++; + $opt_I > 0 && $idx != $opt_I and next; + printf "%s[%03d]%s ", ${clr_dbg}, $idx, $reset; + } + + if (!@dn) { + printf "%d,%dd%d\n", $diff->Get (qw( Min1 Max1 Max2 )); + $_ = $clr_old . (s/$/$reset/r) for @do; + } + elsif (!@do) { + printf "%da%d,%d\n", $diff->Get (qw( Max1 Min2 Max2 )); + $_ = $clr_new . (s/$/$reset/r) for @dn; + } + else { + $sep = "---\n" unless defined $opt_u; + printf "%d,%dc%d,%d\n", $diff->Get (qw( Min1 Max1 Min2 Max2 )); + if ($opt_t > 0 and abs (@do - @dn) > $opt_t) { + $_ = $clr_old . (s/$/$reset/r) for @do; + $_ = $clr_new . (s/$/$reset/r) for @dn; + } + else { + my @D = subdiff (@d, my $heu = {}); + if ($opt_h and $heu->{pct} > $opt_h) { + $_ = $clr_old . (s/$/$reset/r) for @do; + $_ = $clr_new . (s/$/$reset/r) for @dn; + } + else { + @do = @{$D[0]}; + @dn = @{$D[1]}; + } + } + } + if ($opt_u and @s) { + print "$chr_ctx$_" for grep { defined } map { $s[$#s - $opt_u + $_] } 1..$opt_u; + } + print "$chr_lft$_" for @do; + print $sep; + print "$chr_rgt$_" for @dn; } - if ($opt_u and @s) { - print " $_" for grep { defined } map { $s[$#s - $opt_u + $_] } 1..$opt_u; + + if ($fh) { + select STDOUT; + close $fh; } - print "$chr_lft $_" for @{$d[0]}; - print $sep; - print "$chr_rgt $_" for @{$d[1]}; - } + } # ccdiff sub subdiff { - my $d = Algorithm::Diff->new (map { [ map { split m// } @$_ ] } @_); + my ($old, $new, $heu) = @_; + my $d = Algorithm::Diff->new (map { [ map { split m// } @$_ ] } $old, $new); my ($d1, $d2, $x1, $x2, @h1, @h2) = ("", "", "", ""); my ($cml, $cmr) = $opt_v < 2 ? ("", "") : ($chr_cml, $chr_cmr); my ($cmd, $cma) = ($chr_old, $chr_new); + @{$heu}{qw( old new same )} = (1, 1, 1); # prevent div/0 while ($d->Next) { - my @c = map {[ $d->Items ($_) ]} 1, 2; + my @c = map {[ $d->Items ($_) ]} 1, 2; + my @co = @{$c[0]}; + my @cn = @{$c[1]}; if ($d->Same) { - $d1 .= $_ for @{$c[0]}; - $d2 .= $_ for @{$c[1]}; - $x1 .= $_ for map { s/\S/$chr_eql/gr } @{$c[0]}; - $x2 .= $_ for map { s/\S/$chr_eql/gr } @{$c[0]}; + $heu->{same} += scalar @co; + my $e = $chr_eli; + my $c = join "" => @co; + if ($opt_e) { + my $join = ""; + foreach my $sc (split m/\n/ => $c) { + $_ .= $join for $d1, $d2, $x1, $x2; + $join = "\n"; + my $l = length $sc; # The length of this "same" chunck + my $le = $l - 2 * $opt_e; # The length of the text replaces with ellipsis + my $ee = $opt_v <= 1 ? $e : $e =~ s/^.\K(?=.$)/$le/r; + if ($le > length $ee) { + my $lsc = substr $sc, 0, $opt_e; + $d1 .= $lsc; + $d2 .= $lsc; + $lsc =~ s/\S/$chr_eql/g; + $x1 .= $lsc; + $x2 .= $lsc; + my $rsc = substr $sc, $l - $opt_e, $opt_e; + $d1 .= $clr_dbg . $ee . $reset . $rsc; + $d2 .= $clr_dbg . $ee . $reset . $rsc; + $rsc =~ s/\S/$chr_eql/g; + $x1 .= $chr_eql x length ($ee) . $rsc; + $x2 .= $chr_eql x length ($ee) . $rsc; + next; + } + else { + $d1 .= $sc; + $d2 .= $sc; + $sc =~ s/\S/$chr_eql/g; + $x1 .= $sc; + $x2 .= $sc; + } + } + next; + } + $d1 .= $c; + $d2 .= $c; + $c =~ s/\S/$chr_eql/g; + $x1 .= $c; + $x2 .= $c; next; } - if (@{$c[0]}) { + if (@co) { + $heu->{old} += scalar @co; $d1 .= $cml.$clr_old; - $d1 .= $_ for @{$c[0]}; + $d1 .= s/\n/$reset\n$clr_old/gr for @co; $d1 .= $reset.$cmr; - $x1 .= $_ for map { s/[^\t\r\n]/$cmd/gr } @{$c[0]}; - $opt_v and push @h1, map { $opt_U ? charnames::viacode (ord) : unpack "H*"; } @{$c[0]}; + $x1 .= $_ for map { s/[^\t\r\n]/$cmd/gr } @co; + $opt_v and push @h1, map { $opt_U ? charnames::viacode (ord) : unpack "H*"; } @co; } - if (@{$c[1]}) { + if (@cn) { + $heu->{new} += scalar @cn; $d2 .= $cml.$clr_new; - $d2 .= $_ for @{$c[1]}; + $d2 .= s/\n/$reset\n$clr_new/gr for @cn; $d2 .= $reset.$cmr; - $x2 .= $_ for map { s/[^\t\r\n]/$cma/gr } @{$c[1]}; - $opt_v and push @h2, map { $opt_U ? charnames::viacode (ord) : unpack "H*"; } @{$c[1]}; + $x2 .= $_ for map { s/[^\t\r\n]/$cma/gr } @cn; + $opt_v and push @h2, map { $opt_U ? charnames::viacode (ord) : unpack "H*"; } @cn; } } - my @d = map { [ split m/(?<=\n)/ => $_ ] } $d1, $d2; + $heu->{pct} = ($heu->{old} + $heu->{new}) / (2 * $heu->{same}); + my @d = map { [ split m/(?<=\n)/ => s/\n*\z/\n/r ] } $d1, $d2; if ($opt_m) { $opt_v > 1 and s/(\S+)/ $1 /g for $x1, $x2; - my @x = map { /\S/ ? [ split m/(?<=\n)/ => $_ ] : [] } $x1, $x2; + s/[ \t]*\n*\z/\n/ for $x1, $x2; + my @x = map { /\S/ ? [ split m/(?<=\n)/ ] : [] } $x1, $x2; foreach my $n (0, 1) { - @{$x[$n]} and $d[$n] = [ map { $d[$n][$_], $x[$n][$_] // "" } 0 .. (scalar @{$d[$n]} - 1) ]; + @{$x[$n]} and $d[$n] = [ map {( $d[$n][$_], $x[$n][$_] // "" )} 0 .. (scalar @{$d[$n]} - 1) ]; } } if ($opt_v) { + $opt_U && $opt_v > 2 and $_ .= sprintf " (U+%06X)", charnames::vianame ($_) for @h1, @h2; @h1 and push @{$d[0]}, sprintf " -- ${clr_dbg}verbose$reset : %s\n", join ", " => map { $clr_old.$_.$reset } @h1; @h2 and push @{$d[1]}, sprintf " -- ${clr_dbg}verbose$reset : %s\n", join ", " => map { $clr_new.$_.$reset } @h2; } @@ -242,11 +444,11 @@ } # subdiff sub read_rc { - my %rc; + my $home = $ENV{HOME} || $ENV{USERPROFILE} || $ENV{HOMEPATH}; foreach my $rcf ( - "$ENV{HOME}/ccdiff.rc", - "$ENV{HOME}/.ccdiffrc", - "$ENV{HOME}/.config/ccdiff", + "$home/ccdiff.rc", + "$home/.ccdiffrc", + "$home/.config/ccdiff", ) { -s $rcf or next; (stat $rcf)[2] & 022 and next; @@ -258,18 +460,59 @@ =~ s{background}{bg}ir =~ s{^(?:unicode|utf-?8?)$}{utf8}ir } = $v + =~ s{U\+?([0-9A-Fa-f]{2,7})}{chr hex $1}ger =~ s{^(?:no|false)$}{0}ir =~ s{^(?:yes|true)$}{-1}ir; # -1 is still true } } - %rc; } # read_rc +# Return the known colors from Term::ANSIColor +# Stolen striaght from the pm +sub tac_colors { + my %c256; + foreach my $r (0 .. 5) { + foreach my $g (0 .. 5) { + $c256{lc $_}++ for map {("RGB$r$g$_", "ON_RGB$r$g$_")} 0 .. 5; + } + } + $c256{lc $_}++ for + # Basic colors + qw( + CLEAR RESET BOLD DARK + FAINT ITALIC UNDERLINE UNDERSCORE + BLINK REVERSE CONCEALED + + BLACK RED GREEN YELLOW + BLUE MAGENTA CYAN WHITE + ON_BLACK ON_RED ON_GREEN ON_YELLOW + ON_BLUE ON_MAGENTA ON_CYAN ON_WHITE + + BRIGHT_BLACK BRIGHT_RED BRIGHT_GREEN BRIGHT_YELLOW + BRIGHT_BLUE BRIGHT_MAGENTA BRIGHT_CYAN BRIGHT_WHITE + ON_BRIGHT_BLACK ON_BRIGHT_RED ON_BRIGHT_GREEN ON_BRIGHT_YELLOW + ON_BRIGHT_BLUE ON_BRIGHT_MAGENTA ON_BRIGHT_CYAN ON_BRIGHT_WHITE + ), + # 256 colors + (map { ("ANSI$_", "ON_ANSI$_") } 0 .. 255), + (map { ("GREY$_", "ON_GREY$_") } 0 .. 23); + + my $ACV = $Term::ANSIColor::VERSION; + $ACV < 3.02 and delete @c256{grep m/italic/ => keys %c256}; + $ACV < 4.00 and delete @c256{grep m/rgb|grey/ => keys %c256}; + $ACV < 4.06 and delete @c256{grep m/ansi/ => keys %c256}; + sort keys %c256; + } # tac_colors + +1; + __END__ +=encoding utf-8 + =head1 NAME -ccdiff - Colored character diff +ccdiff - Colored Character diff =head1 SYNOPSIS @@ -324,10 +567,10 @@ a unified diff (-u1) would be 5,5c5,5 - Tue Sep 6 05:43:59 2005,B.O.Q.S.,,1125978239,1943341 - - Sat Dec 18 07:00:33 1993,I.O.D.U.,,756194433,1442539 - + Sat Dec 18 07:08:33 1998,I.O.D.U.,,756194433,1442539 - Mon Feb 23 10:37:02 2004,R.X.K.S.,van,1077529022,1654127 + Tue Sep 6 05:43:59 2005,B.O.Q.S.,,1125978239,1943341 + -Sat Dec 18 07:00:33 1993,I.O.D.U.,,756194433,1442539 + +Sat Dec 18 07:08:33 1998,I.O.D.U.,,756194433,1442539 + Mon Feb 23 10:37:02 2004,R.X.K.S.,van,1077529022,1654127 =item --verbose[=1] -v[1] @@ -339,19 +582,91 @@ C<--verbose> accepts an optional verbosity-level. On level 2 and up, all horizontal changes get left-and-right markers inserted to enable seeing the -location of the ZERO WIDTH or invisible characters. +location of the ZERO WIDTH or invisible characters. With level 3 and up and +Unicode enabled, the changed characters will also show the codepoint in hex. + +An example of this: + +With -Uu0v0: + + 1,1c1,1 + - A BCDE Fg + + A BcdE​Fg + +With -Uu0v1: + + 1,1c1,1 + - A BCDE Fg + - -- verbose : SPACE, LATIN CAPITAL LETTER C, LATIN CAPITAL LETTER D, SPACE + + A BcdE​Fg + + -- verbose : LATIN SMALL LETTER C, LATIN SMALL LETTER D, ZERO WIDTH SPACE + +With -Uu0v2: + + 1,1c1,1 + - A ↱ ↰B↱CD↰E↱ ↰Fg + - -- verbose : SPACE, LATIN CAPITAL LETTER C, LATIN CAPITAL LETTER D, SPACE + + A B↱cd↰E↱​↰Fg + + -- verbose : LATIN SMALL LETTER C, LATIN SMALL LETTER D, ZERO WIDTH SPACE + +With -Uu0v3: + + 1,1c1,1 + - A ↱ ↰B↱CD↰E↱ ↰Fg + - -- verbose : SPACE (U+000020), LATIN CAPITAL LETTER C (U+000043), LATIN CAPITAL LETTER D (U+000044), SPACE (U+000020) + + A B↱cd↰E↱​↰Fg + + -- verbose : LATIN SMALL LETTER C (U+000063), LATIN SMALL LETTER D (U+000064), ZERO WIDTH SPACE (U+00200B) + +With -Uu0v2 --ascii: + + 1,1c1,1 + - A > CD cd color. The characters used for the markers can be defined in your +configuration file as C (the character used as marker on the left) +and C (the character used as marker on the right). =item --markers -m Use markers under each changed character in change-chunks. C<--markers> is especially useful if the terminal does not support colors, or -if you want to copy/paste the output to (ASCII) mail. See also C<--ascii> +if you want to copy/paste the output to (ASCII) mail. See also C<--ascii>. The +markers will have the same color as added or deleted text. + +This will look like (with unified diff): + + 5,5c5,5 + -Sat Dec 18 07:08:33 1998,I.O.D.U.,,756194433,1442539 + - ▼ ▼ + +Sat Dec 18 07:00:33 1993,I.O.D.U.,,756194433,1442539 + + ▲ ▲ + +The characters used for the markers can be defined in your configuration file +as C (the character used as marker under removed characters) and +C (the character used as marker under added characters). + +If C<--ellipsis> is also in effect and either the C is longer than +one character or C<--verbose> level is over 2, this options is automatically +disabled. =item --ascii -a Use (colored) ASCII indicators instead of Unicode. The default indicators are -Unicode characters that stand out better. +Unicode characters that stand out better. The markers will have the same color +as added or deleted text. + +For the vertical markers (C<-m>) that would look like: + + 5,5c5,5 + -Sat Dec 18 07:08:33 1998,I.O.D.U.,,756194433,1442539 + - ^ ^ + +Sat Dec 18 07:00:33 1993,I.O.D.U.,,756194433,1442539 + + ^ ^ For the positional indicators, I did consider using U+034e (COMBINING UPWARDS ARROW BELOW), but as most terminals are probably unable to show it due to line @@ -364,7 +679,7 @@ =item --reverse -r -Reverse the foreground and background for the colored indicators. +Reverse/invert the foreground and background for the colored indicators. If the foreground color has C, it will be stripped from the new background color. @@ -373,6 +688,11 @@ List available colors and exit. +=item --no-colors + +Disable all colors. Useful for redirecting the diff output to a file that is to +be included in documentation. + =item --old=color Define the foreground color for deleted text. @@ -389,33 +709,85 @@ Prefix position indicators with an index. + [001] 5,5c5,5 + -Sat Dec 18 07:08:33 1998,I.O.D.U.,,756194433,1442539 + +Sat Dec 18 07:00:33 1993,I.O.D.U.,,756194433,1442539 + If a positive number is passed (C<--index=4> or C<-I 4>), display just the -chunk with that index. This is useful in combination with C<--verbose>. +chunk with that index, using the C color: + +This is useful in combination with C<--verbose>. + +=item --threshold=2 -t 2 + +Defines the number of lines a change block may differ before the fall-back of +horizontal diff to vertical diff. + +If a chunk describes a change, and the number of lines in the original block +has less or more lines than the new block and that difference exceeds this +threshold, C will fall-back to vertical diff. + +=item --heuristics=n -h n + +Defines the percentage of character-changes a change block may differ before +the fall-back of horizontal diff to vertical diff. + +This percentage is calculated as C<(characters removed + characters added) / +(2 * characters unchanged))>. + +=item --ellipsis=n -e n + +Defines the number of characters to keep on each side of a horizontal-equal +segment. The default is C<0>, meaning do not compress. + +If set to a positive number, and the length of a segment of equal characters +inside a horizontal diff is longer than twice this value, the middle part is +replaced with C<┈ U02508 \N{BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL}> +(instead of … U02026, as HORIZONTAL ELLIPSIS does not stand out enough). + +With C<-u0me3> that would be like + + 5,5c5,5 + -Sat┈07:08:33┈ 1998,I.┈539 + - ▼ ▼ + +Sat┈07:00:33┈ 1993,I.┈539 + + ▲ ▲ + +With C<-u0e3 -v2> like + + 5,5c5,5 + -Sat↤9↦07:0↱0↰:33 199↱3↰,I.↤23↦539 + - -- verbose : DIGIT ZERO, DIGIT THREE + +Sat↤9↦07:0↱8↰:33 199↱8↰,I.↤23↦539 + + -- verbose : DIGIT EIGHT, DIGIT EIGHT + +The text used for the replaced text can be defined in your configuration file +as C and/or C. =item --ignore-case -i Ignore case on comparison. -=item --ignore-all-space -w +=item --ignore-all-space -w Ignore all white-space changes. This will set all options C<-b>, C<-Z>, C<-E>, and C<-B>. -=item --ignore-trailing-space -Z +=item --ignore-trailing-space -Z Ignore changes in trailing white-space (TAB's and spaces). -=item --ignore-ws|ignore-space-change -b +=item --ignore-ws|ignore-space-change -b Ignore changes in horizontal white-space (TAB's and spaces). This does not include white-space changes that splits non-white-space or removes white-space between two non-white-space elements. -=item --ignore-tab-expansion -E +=item --ignore-tab-expansion -E NYI -=item --ignore-blank-lines -B +=item --ignore-blank-lines -B B (WIP) @@ -449,11 +821,20 @@ =over 2 +=item unified (-u) + +If you prefer unified-diff over old-style diff by default, set this to the +desired number of context lines: + + unified : 3 + +The default is undefined + =item markers (-m) markers : false -defines if markers should be used under changed characters. The default is to +Defines if markers should be used under changed characters. The default is to use colors only. The C<-m> command line option will toggle the option when set from a configuration file. @@ -461,14 +842,14 @@ ascii : false -defines to use ASCII markers instead of Unicode markers. The default is to use +Defines to use ASCII markers instead of Unicode markers. The default is to use Unicode markers. =item reverse (-r) reverse : false -defines if changes are displayed as foreground-color over background-color +Defines if changes are displayed as foreground-color over background-color or background-color over foreground-color. The default is C, so it will color the changes with the appropriate color (C or C) over the default background color. @@ -477,9 +858,11 @@ new : green -defines the color to be used for added text. The default is C. +Defines the color to be used for added text. The default is C. -any color accepted by L is allowed. Any other color will +The color C is also accepted and disables this color. + +Any color accepted by L is allowed. Any other color will result in a warning. This option can include C either as prefix or as suffix. @@ -494,9 +877,11 @@ old : red -defines the color to be used for delete text. The default is C. +Defines the color to be used for delete text. The default is C. + +The color C is also accepted and disables this color. -any color accepted by L is allowed. Any other color will +Any color accepted by L is allowed. Any other color will result in a warning. This option can include C either as prefix or as suffix. @@ -511,10 +896,12 @@ bg : white -defines the color to be used as background for changed text. The default is +Defines the color to be used as background for changed text. The default is C. -any color accepted by L is allowed. Any other color will +The color C is also accepted and disables this color. + +Any color accepted by L is allowed. Any other color will result in a warning. The C attribute is not allowed. This option may also be specified as @@ -533,10 +920,12 @@ verbose : cyan -defines the color to be used as color for the verbose tag. The default is +Defines the color to be used as color for the verbose tag. The default is C. This color will only be used under C<--verbose>. -any color accepted by L is allowed. Any other color will +The color C is also accepted and disables this color. + +Any color accepted by L is allowed. Any other color will result in a warning. This option may also be specified as @@ -550,7 +939,7 @@ utf8 : yes -defines whether all I/O is to be interpreted as UTF-8. The default is C. +Defines whether all I/O is to be interpreted as UTF-8. The default is C. This option may also be specified as @@ -562,7 +951,7 @@ index : no -defines if the position indication for a change chunk is prefixed with an +Defines if the position indication for a change chunk is prefixed with an index number. The default is C. The index is 1-based. Without this option, the position indication would be like @@ -580,8 +969,139 @@ When this option contains a positive integer, C will only show diff the diff chunk with that index. +=item emacs + + emacs : no + +If this option is yes/true, calling C with just one single argument, +and that argument being an existing file, the arguments will act as + + $ ccdiff file~ file + +if file~ exists. + +=item threshold (-t) + + threshold : 2 + +Defines the number of lines a change block may differ before the fall-back of +horizontal diff to vertical diff. + +=item heuristics (-h) + + heuristics : 40 + +Defines the percentage of character-changes a change block may differ before +the fall-back of horizontal diff to vertical diff. The default is undefined, +meaning no fallback based on heuristics. + +=item ellipsis (-e) + + ellipsis : 0 + +Defines the number of characters to keep on each side of a horizontal-equal +segment. The default is C<0>, meaning to not compress. See also C. + +=item chr_old + + chr_old : U+25BC + +Defines the character used to indicate the position of removed text on the +line below the text when option C<-m> is in effect. + +=item chr_new + + chr_new : U+25B2 + +Defines the character used to indicate the position of added text on the +line below the text when option C<-m> is in effect. + +=item chr_cml + + chr_cml : U+21B1 + +Defines the character used to indicate the starting position of changed text +in a line when verbose level is 3 and up. + +=item chr_cmr + + chr_cmr : U+21B0 + +Defines the character used to indicate the ending position of changed text +in a line when verbose level is 3 and up. + +=item chr_eli + + chr_eli : U+21B0 + +Defines the character used to indicate omitted text in large unchanged text +when C<--ellipsis>/C<-e> is in effect. + +This character is not equally well visible on all terminals or in all fonts, +so you might want to chane it to something that stands out better in you +environment. Possible suggestions: + + … U+2026 HORIZONTAL ELLIPSIS + ‴ U+2034 TRIPLE PRIME + ‷ U+2037 REVERSED TRIPLE PRIME + ↔ U+2194 LEFT RIGHT ARROW + ↭ U+21ad LEFT RIGHT WAVE ARROW + ↮ U+21ae LEFT RIGHT ARROW WITH STROKE + ↹ U+21b9 LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR + ⇄ U+21c4 RIGHTWARDS ARROW OVER LEFTWARDS ARROW + ⇆ U+21c6 LEFTWARDS ARROW OVER RIGHTWARDS ARROW + ⇎ U+21ce LEFT RIGHT DOUBLE ARROW WITH STROKE + ⇔ U+21d4 LEFT RIGHT DOUBLE ARROW + ⇹ U+21f9 LEFT RIGHT ARROW WITH VERTICAL STROKE + ⇼ U+21fc LEFT RIGHT ARROW WITH DOUBLE VERTICAL STROKE + ⇿ U+21ff LEFT RIGHT OPEN-HEADED ARROW + ≋ U+224b TRIPLE TILDE + ┄ U+2504 BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL + ┅ U+2505 BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL + ┈ U+2508 BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL + ┉ U+2509 BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL + ⧻ U+29fb TRIPLE PLUS + ⬌ U+2b0c LEFT RIGHT BLACK ARROW + +=item chr_eli_v + + chr_eli_v : U+21A4U+21A6 + +When ussing C<--ellipsis> with C<--verbose> level 2 or up, the single character +indicator will be replaced with this character. If it is 2 characters wide, the +length of the compressed part is put between the characters. + +A suggested alternative might be U+21E4U+21E5 + =back +=head1 Git integration + +You can use ccdiff to show diffs in git. It may work like this: + + $ git config --global diff.tool ccdiff + $ git config --global difftool.prompt false + $ git config --global difftool.ccdiff.cmd 'ccdiff --utf-8 -u -r $LOCAL $REMOTE' + $ git difftool SHA~..SHA + $ wget https://github.com/Tux/App-ccdiff/blob/master/Files/git-ccdiff \ + -O ~/bin/git-ccdiff + $ perl -pi -e 's{/pro/bin/perl}{/usr/bin/env perl}' ~/bin/git-ccdiff + $ chmod 755 ~/bin/git-ccdiff + $ git ccdiff SHA + +Of course you can use C instead of C and you can choose your own +(fixed) path to C instead of using C. + +From then on you can do + + $ git ccdiff + $ git ccdiff 5c5a39f2 + +=head1 CAVEATS + +Due to the implementation, where both sides of the comparison are completely +kept in memory, this tool might not be able to deal with (very) large datasets. + =head1 SEE ALSO L, L diff -Nru ccdiff-0.21/ChangeLog ccdiff-0.26/ChangeLog --- ccdiff-0.21/ChangeLog 2018-08-19 10:16:42.000000000 +0000 +++ ccdiff-0.26/ChangeLog 2018-10-14 17:54:31.000000000 +0000 @@ -1,8 +1,49 @@ +0.26 - 2018-10-14 + * Update git-ccdiff + * Docs for git + * Include git-ccdiff as file + * $HOME for Windows users + * Add option to skip header + * Add tests + * Add --settings + * Add repo and provides to META + +0.25 - 2018-09-07 + * Use CORE module for --info + * Show codepoints under -v3 (issue#14) + * Add --no-colors + * Reverse/invert debug color too under -r if possible + * Add --ellipsis / -e + * Allow used character indicators in RC + +0.24 - 2018-08-26 + * Filter colors on Term::ANSIColor version + * Add heuristics + +0.23 - 2018-08-23 + * Add color "none" to disable a color + * Try to prevent unsupported colors + * Also color line-change-indicators (-, +, <, and >) (issue#8) + * Add threshold for horizontal diff (issue#9) + * Remove one leading space for unified diffs + * Add git integration to integrated docs (issue#10) + * Add all Term::ANSIColor colors as known + * Fix color leakage in multiline changes + +0.22 - 2018-08-21 + * Fix issue tracker (issue#6, jwilk) + * Require perl-5.14.0 because of syntax used + * More docs about markers + * Add doc warning about large datasets + * Support emacs users with new config setting + * Fix license issues (gregoa, issue#2) + 0.21 - 2018-08-19 * Add characterchange-marker on verbose level 2 and up * Fancy is default, ascii is optional * Colors are not optional, markers are * Fix markers for spaces + * Fix doc typo (issue#4) 0.20 - 2018-08-16 * Start inline documentation diff -Nru ccdiff-0.21/CONTRIBUTING.md ccdiff-0.26/CONTRIBUTING.md --- ccdiff-0.21/CONTRIBUTING.md 2015-08-07 11:13:52.000000000 +0000 +++ ccdiff-0.26/CONTRIBUTING.md 2018-08-20 08:41:19.000000000 +0000 @@ -1,7 +1,7 @@ # General I am always open to improvements and suggestions. -Use [issues](https://github.com/Tux/App-tkiv/issues) +Use [issues](https://github.com/Tux/App-ccdiff/issues) # Style diff -Nru ccdiff-0.21/debian/ccdiff.examples ccdiff-0.26/debian/ccdiff.examples --- ccdiff-0.21/debian/ccdiff.examples 2018-08-19 12:22:48.000000000 +0000 +++ ccdiff-0.26/debian/ccdiff.examples 2018-10-15 17:16:13.000000000 +0000 @@ -1 +1 @@ -Files/* +Files/git-ccdiff diff -Nru ccdiff-0.21/debian/changelog ccdiff-0.26/debian/changelog --- ccdiff-0.21/debian/changelog 2018-08-19 12:22:48.000000000 +0000 +++ ccdiff-0.26/debian/changelog 2018-10-15 17:16:13.000000000 +0000 @@ -1,3 +1,33 @@ +ccdiff (0.26-1) unstable; urgency=medium + + * Import upstream version 0.26. + * Update handling of example git script. + * New build dependency: libcapture-tiny-perl. + * Add necessary bits to run new tests under autopkgtest as well. + + -- gregor herrmann Mon, 15 Oct 2018 19:16:13 +0200 + +ccdiff (0.25-1) unstable; urgency=medium + + * Import upstream version 0.25. + * debian/rules: change hashbang in another file as well. + + -- gregor herrmann Sun, 09 Sep 2018 02:20:24 +0200 + +ccdiff (0.24-1) unstable; urgency=medium + + * Import upstream version 0.24 + * Declare compliance with Debian Policy 4.2.1. + + -- gregor herrmann Wed, 29 Aug 2018 17:45:08 +0200 + +ccdiff (0.23-1) unstable; urgency=medium + + * Import upstream version 0.23. + * Update debian/upstream/metadata. + + -- gregor herrmann Fri, 24 Aug 2018 17:27:46 +0200 + ccdiff (0.21-1) unstable; urgency=low * Initial release (closes: #906621). diff -Nru ccdiff-0.21/debian/control ccdiff-0.26/debian/control --- ccdiff-0.21/debian/control 2018-08-19 12:22:48.000000000 +0000 +++ ccdiff-0.26/debian/control 2018-10-15 17:16:13.000000000 +0000 @@ -6,8 +6,9 @@ Priority: optional Build-Depends: debhelper (>= 10) Build-Depends-Indep: libalgorithm-diff-perl, + libcapture-tiny-perl, perl -Standards-Version: 4.2.0 +Standards-Version: 4.2.1 Vcs-Browser: https://salsa.debian.org/perl-team/modules/packages/ccdiff Vcs-Git: https://salsa.debian.org/perl-team/modules/packages/ccdiff.git Homepage: https://metacpan.org/release/App-ccdiff diff -Nru ccdiff-0.21/debian/patches/autopkgtest.patch ccdiff-0.26/debian/patches/autopkgtest.patch --- ccdiff-0.21/debian/patches/autopkgtest.patch 1970-01-01 00:00:00.000000000 +0000 +++ ccdiff-0.26/debian/patches/autopkgtest.patch 2018-10-15 17:16:13.000000000 +0000 @@ -0,0 +1,18 @@ +Description: set path to script according to autopkgtest environment +Origin: vendor +Forwarded: not-needed +Author: gregor herrmann +Last-Update: 2018-10-15 + +--- a/t/01-no-color.t ++++ b/t/01-no-color.t +@@ -21,7 +21,8 @@ + $exp =~ s/STAMP:2/$stamp{$f2}/g; + #diag "Description: $dsc"; + #diag "Options: $opt"; +- my @cmd = ($^X, "ccdiff", "--utf-8", "--no-color", "Files/$f1", "Files/$f2"); ++ my $path = ( $ENV{ADTTMP} ? '/usr/bin' : '.' ); ++ my @cmd = ($^X, "$path/ccdiff", "--utf-8", "--no-color", "Files/$f1", "Files/$f2"); + $opt and push @cmd, split m/ / => $opt; + #diag "@cmd"; + my ($out, $err, $exit) = capture { system @cmd; }; diff -Nru ccdiff-0.21/debian/patches/series ccdiff-0.26/debian/patches/series --- ccdiff-0.21/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ ccdiff-0.26/debian/patches/series 2018-10-15 17:16:13.000000000 +0000 @@ -0,0 +1 @@ +autopkgtest.patch diff -Nru ccdiff-0.21/debian/rules ccdiff-0.26/debian/rules --- ccdiff-0.21/debian/rules 2018-08-19 12:22:48.000000000 +0000 +++ ccdiff-0.26/debian/rules 2018-10-15 17:16:13.000000000 +0000 @@ -8,8 +8,9 @@ override_dh_auto_install: dh_auto_install - sed -i '1s|^#!/usr/bin/env perl|#!/usr/bin/perl|' $(TMP)/usr/bin/ccdiff + sed -i '1s|^#!/usr/bin/env perl|#!/usr/bin/perl|' $(TMP)/usr/bin/ccdiff \ + $(TMP)/usr/share/perl5/App/ccdiff.pm override_dh_installexamples: dh_installexamples - sed -i '1s|^#!/usr/bin/sh|#!/bin/sh|' $(TMP)/usr/share/doc/ccdiff/examples/git-ccdiff + sed -i '1s|^#!/pro/bin/perl|#!/usr/bin/perl|' $(TMP)/usr/share/doc/ccdiff/examples/git-ccdiff diff -Nru ccdiff-0.21/debian/tests/pkg-perl/smoke-files ccdiff-0.26/debian/tests/pkg-perl/smoke-files --- ccdiff-0.21/debian/tests/pkg-perl/smoke-files 1970-01-01 00:00:00.000000000 +0000 +++ ccdiff-0.26/debian/tests/pkg-perl/smoke-files 2018-10-15 17:16:13.000000000 +0000 @@ -0,0 +1,2 @@ +t/ +Files/ diff -Nru ccdiff-0.21/debian/upstream/metadata ccdiff-0.26/debian/upstream/metadata --- ccdiff-0.21/debian/upstream/metadata 2018-08-19 12:22:48.000000000 +0000 +++ ccdiff-0.26/debian/upstream/metadata 2018-10-15 17:16:13.000000000 +0000 @@ -1,5 +1,6 @@ --- Archive: CPAN +Bug-Database: https://github.com/Tux/App-ccdiff/issues Contact: H.Merijn Brand Name: App-ccdiff Repository: https://github.com/Tux/App-ccdiff.git diff -Nru ccdiff-0.21/Files/01_1.txt ccdiff-0.26/Files/01_1.txt --- ccdiff-0.21/Files/01_1.txt 1970-01-01 00:00:00.000000000 +0000 +++ ccdiff-0.26/Files/01_1.txt 2018-10-08 15:19:46.000000000 +0000 @@ -0,0 +1,6 @@ +Lorem ipsum dolor sit amet, graecis pertinax at ius, +id vix cibo omittantur, et impetus offendit convenire +usu. Ad duo posse theophrastus, vim in accumsan +efficiantur, sed in congue decore. Ex nullam iudicabit +vim. Quo ad modo dolores. Duo ne iuvaret contentiones, +vim ut mollis scripta. diff -Nru ccdiff-0.21/Files/01_2.txt ccdiff-0.26/Files/01_2.txt --- ccdiff-0.21/Files/01_2.txt 1970-01-01 00:00:00.000000000 +0000 +++ ccdiff-0.26/Files/01_2.txt 2018-10-08 16:03:59.000000000 +0000 @@ -0,0 +1,6 @@ +Lorem ipsum dolor sit amet, graecis pertinax at ius, +id vix cibo omittantur, et impetus offendit convenire +usu. Ad duo posse theophrastus. Vim in accumsan +efficiantur, sed in congue decore. Ex nullam iudicabit +vim. Quo ad modo dolores. Duo ne iuvaret contentiones, +vim ut mollis scripta. diff -Nru ccdiff-0.21/Files/01_3.txt ccdiff-0.26/Files/01_3.txt --- ccdiff-0.21/Files/01_3.txt 1970-01-01 00:00:00.000000000 +0000 +++ ccdiff-0.26/Files/01_3.txt 2018-10-08 15:21:31.000000000 +0000 @@ -0,0 +1,6 @@ +Lorem ipsum dolor sit amet, graecis pertinax at ius, +id vix cibo omittantur, et impetus offendit convenire +usu. Ad duo posse theophrastus, vim in accumsan +efficiantur, sed in congue decore. Ex nullam iudicabit +vim. Quo ad modo dolores. Duo ne iuvaret contentiones, +vim ut mollis scripta. diff -Nru ccdiff-0.21/Files/01_4.txt ccdiff-0.26/Files/01_4.txt --- ccdiff-0.21/Files/01_4.txt 1970-01-01 00:00:00.000000000 +0000 +++ ccdiff-0.26/Files/01_4.txt 2018-10-08 16:01:43.000000000 +0000 @@ -0,0 +1,7 @@ +Lorem ipsum dolor sit amet, graecis pertinax at ius, +id vix cibo omittantur, et impetus offendit convenire +usu. Ad duo posse theophrastus, vim in accumsan + +efficiantur, sed in congue decore. Ex nullam iudicabit +vim. Quo ad modo dolores. Duo ne iuvaret contentiones, +vim ut mollis scripta. diff -Nru ccdiff-0.21/Files/git-ccdiff ccdiff-0.26/Files/git-ccdiff --- ccdiff-0.21/Files/git-ccdiff 2018-08-15 15:03:04.000000000 +0000 +++ ccdiff-0.26/Files/git-ccdiff 2018-09-25 13:45:32.000000000 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/sh +#!/pro/bin/perl # With this file as git-ccdiff in your $PATH # @@ -9,6 +9,26 @@ # -> # git ccdiff 5c5a -commit=$1 -shift -git difftool $commit~1..$commit $@ +use 5.14.2; +use warnings; + +my @opt; + +my @git = qw( git difftool ); +for (@ARGV) { + if (m/^-/) { # an option + push @opt, $_; + } + elsif (-f) { # a file + push @git, $_; + } + else { # a commit + push @git, "$_~1..$_"; + } + } + +#use Data::Peek; +#DDumper \@ARGV; + +@opt and $ENV{CCDIFF_OPTIONS} = join " " => @opt; +system @git; diff -Nru ccdiff-0.21/lib/App/ccdiff.pm ccdiff-0.26/lib/App/ccdiff.pm --- ccdiff-0.21/lib/App/ccdiff.pm 2018-08-19 10:17:09.000000000 +0000 +++ ccdiff-0.26/lib/App/ccdiff.pm 2018-10-09 07:00:09.000000000 +0000 @@ -1,17 +1,518 @@ -package App::ccdiff; +#!/usr/bin/env perl -our $VERSION = "0.21"; +package App::ccdiff; -use strict; -use warnings +use 5.14.0; +use warnings; +use charnames (); +use Algorithm::Diff; +use Term::ANSIColor qw(:constants color); +use Getopt::Long qw(:config bundling); + +our $VERSION = "0.26"; +our $CMD = $0 =~ s{.*/}{}r; + +sub usage { + my $err = shift and select STDERR; + say "usage: $CMD [options] file1 [file2]"; + say " $CMD --man | --info"; + say " file1 or file2 can be - (but not both)"; + say " -V --version Show version and exit"; + say " -v[1] --verbose[=1] Set verbosity"; + say " Diff options:"; + say " -U --utf-8 Input is in UTF-8"; + say " -u[3] --unified=3 Show a unified diff"; + say " --no-header Skip header with file names/stamps"; + say " -I --index Add indices to the change chunks"; + say " -I n --index=4 Only show chunk n"; + say " -w --ignore-all-space Ignore all whitespace"; + say " -b --ignore-space-change Ignore horizontal whitespace changes"; + say " -Z --ignore-trailing-space Ignore whitespace at line ending"; + say " -B --ignore-blank-lines Ignore changes where lines are all blank"; + say " -i --ignore-case Ignore case changes"; + say " Other options:"; + say " -t n --threshold=2 Horizontal line diff threshold"; + say " -h n --heuristics=n Horizontal char diff threshold"; + say " -e n --ellipsis=n Compress horizontal equal sections"; + say " -m --markers Use markers to indicate change positions"; + say " -a --ascii Use ASCII instead of Unicode indicators"; + say " --no-color Reset all colors to none."; + say " --list-colors List available colors and exit"; + say " --old=red Color to indicate removed content"; + say " --new=green Color to indicate added content"; + say " --bg=white Background color for colored indicators"; + say " -p --pink Shortcut for --old=magenta"; + say " -r --reverse Reverse/invert the colors of the indicators"; + say " --settings Show default settings (after reading rc)"; + exit $err; + } # usage + +my %rc = ( + ascii => 0, + bg => "white", + chr_cml => "\x{21b1}", + chr_cmr => "\x{21b0}", + chr_eli => "\x{2508}", + chr_eli_v => "\x{21a4}\x{21a6}", + chr_eql => " ", + chr_new => "\x{25b2}", + chr_old => "\x{25bc}", + ellipsis => 0, + emacs => 0, + header => 1, + heuristics => 0, + index => 0, + markers => 0, + new => "green", + old => "red", + reverse => 0, + threshold => 2, + utf8 => 0, + verbose => "cyan", + ); +read_rc (); + +my $opt_a = $rc{ascii}; +my $opt_b; +my $opt_B; +#y $opt_c; +my $opt_E; +my $opt_h = $rc{heuristics}; +my $opt_H = $rc{header}; +my $opt_i; +my $opt_I = $rc{index}; +my $opt_m = $rc{markers}; +my $opt_r = $rc{reverse}; +my $opt_t = $rc{threshold}; +my $opt_e = $rc{ellipsis}; +my $opt_u = $rc{unified}; +my $opt_U = $rc{utf8}; +my $opt_v = 0; +my $opt_w; +my $opt_Z; +my $emacs = $rc{emacs}; +my $old_color = $rc{old}; +my $new_color = $rc{new}; +my $rev_color = $rc{bg}; +my $no_colors; +my $list_colors; + +unless (caller) { + $ENV{CCDIFF_OPTIONS} and unshift @ARGV, split m/\s+/ => $ENV{CCDIFF_OPTIONS}; + GetOptions ( + "help|?" => sub { usage (0); }, + "V|version" => sub { say "$CMD [$VERSION]"; exit 0; }, + "man" => sub { exec "pod2man $0 | nroff -man"; }, + "info" => sub { require Pod::Usage; + Pod::Usage::pod2usage (VERBOSE => 2); + exit; + }, + + "U|utf-8!" => \$opt_U, + + # "c|context:3" => \$opt_c, # implement context-diff? + "u|unified:3" => \$opt_u, + "I|idx|index:-1" => \$opt_I, + "t|threshold=i" => \$opt_t, + "H|header!" => \$opt_H, + "h|heuristics=i" => \$opt_h, + "e|ellipsis=i" => \$opt_e, + "emacs!" => \$emacs, + + "a|ascii" => sub { $opt_a ^= 1 }, + "m|markers" => sub { $opt_m ^= 1 }, + "r|reverse|invert" => sub { $opt_r ^= 1 }, + + "i|ignore-case!" => \$opt_i, + "w|ignore-all-space!" => \$opt_w, + "b|ignore-ws|ignore-space-change!" => \$opt_b, + "Z|ignore-trailing-space!" => \$opt_Z, + "E|ignore-tab-expansion!" => \$opt_E, # NYI + "B|ignore-blank-lines!" => \$opt_B, # Partly implemented + + "p|pink!" => sub { $old_color = "magenta" }, + "old=s" => \$old_color, + "new=s" => \$new_color, + "bg=s" => \$rev_color, + "no-colors" => \$no_colors, + "list-colors!" => \$list_colors, + "settings|defaults" => sub { + binmode STDOUT, ":encoding(utf-8)"; + printf "%-10s : %s\n", $_, $rc{$_} // "" for sort keys %rc; + exit 0; + }, + + "v|verbose:1" => \$opt_v, + ) or usage (1); + } + +$opt_w and $opt_b = $opt_Z = $opt_E = $opt_B = 1; +$opt_h >= 1 and $opt_h /= 100; + +# Color initialization +for ($old_color, $new_color, $rev_color) { + s/^(.*)[ _]bold$/bold $1/i; + s/^bold_/bold /i; + } +my %clr = map { $_ => color (s{^(.*)[ _]bold$}{bold $1}ir =~ + s{^bold[ _]}{bold }ir) } + map {( $_, "on_$_", "bold $_" )} + qw( red green blue black white cyan magenta yellow ); +$clr{$_} //= color ($_) for tac_colors (); +$no_colors and $clr{$_} = "" for keys %clr; +$clr{none} = $clr{on_none} = ""; +for ([ \$old_color, $rc{old} ], [ \$new_color, $rc{new} ], [ \$rev_color, $rc{bg} ]) { + my ($c, $def) = @$_; + $$c && exists $clr{$$c} and next; + warn "color ", $$c // "(undefined)", " is unknown, using $def instead\n"; + $$c = $def; + } +my $clr_red = $clr{$old_color}; +my $clr_grn = $clr{$new_color}; +my $clr_rev = $clr{$rev_color}; +my $clr_dbg = $opt_r && exists $clr{"on_$rc{verbose}"} ? $clr{"on_$rc{verbose}"} : $clr{$rc{verbose}}; +my $reset = $no_colors ? "" : RESET; +if ($list_colors) { + my @clr = map { sprintf "%s%-18s%s", $clr{$_}, $_, $reset } sort keys %clr; + while (@clr) { + say join " " => map { $_ // "" } splice @clr, 0, 4; + } + exit; + } + +my $bg_old = $clr{$rc{bg_old} || ($opt_r ? "on_$old_color" =~ s/bold //ir : + "on_$rev_color" =~ s/bold //ir)}; +my $bg_new = $clr{$rc{bg_new} || ($opt_r ? "on_$new_color" =~ s/bold //ir : + "on_$rev_color" =~ s/bold //ir)}; +my $clr_old = $opt_r ? $clr_rev . $bg_old : $clr_red . $bg_old; +my $clr_new = $opt_r ? $clr_rev . $bg_new : $clr_grn . $bg_new; +# Indicators +$opt_a and + @rc{qw( chr_old chr_new chr_cml chr_cmr chr_eli chr_eli_v )} = qw( ^ ^ > < - <> ); +my $chr_old = $clr_old . $rc{chr_old} . $reset; +my $chr_new = $clr_new . $rc{chr_new} . $reset; +my $chr_cml = $clr_dbg . $rc{chr_cml} . $reset; +my $chr_cmr = $clr_dbg . $rc{chr_cmr} . $reset; +my $chr_eql = $rc{chr_eql}; +my $chr_lft = $clr_old . (defined $opt_u ? "-" : "< ") . $reset; +my $chr_rgt = $clr_new . (defined $opt_u ? "+" : "> ") . $reset; +my $chr_ctx = defined $opt_u ? " " : " "; +my $chr_eli = $opt_v >= 2 ? $rc{chr_eli_v} : $rc{chr_eli}; +$opt_m && $opt_v > 1 && length ($chr_eli) > 1 and $opt_m = 0; + +my $cmp_sub = $opt_i || $opt_b || $opt_Z ? { keyGen => sub { + my $line = shift; + $opt_i and $line = lc $line; + $opt_Z and $line =~ s/[ \t]+$//g; + $opt_b and $line =~ s/[ \t]+/ /g; + return $line; + }} : undef; + +caller or ccdiff (@ARGV); + +sub ccdiff { + my $f1 = shift or usage (1); + my $f2 = $_[0] // "-"; + + my $fh; + + if (@_ > 1 && ref $_[1]) { # optional hash with overruling arguments + my %opt = %{$_[1]}; + foreach my $o (keys %opt) { + my $v = $opt{$o}; + $o eq "ascii" and $opt_a = $v; + $o eq "bg" and $rev_color = $v; +# $o eq "context" and $opt_c = $v; + $o eq "ellipsis" and $opt_e = $v; + $o eq "emacs" and $emacs = $v; + $o eq "header" and $opt_H = $v; + $o eq "heuristics" and $opt_h = $v; + $o eq "ignore-all-space" and $opt_w = $v; + $o eq "ignore-blank-lines" and $opt_B = $v; + $o eq "ignore-case" and $opt_i = $v; + $o eq "ignore-space-change" and $opt_b = $v; + $o eq "ignore-tab-expansion" and $opt_E = $v; + $o eq "ignore-trailing-space" and $opt_Z = $v; + $o eq "index" and $opt_I = $v; + $o eq "list-colors" and $list_colors = $v; + $o eq "markers" and $opt_m = $v; + $o eq "new" and $new_color = $v; + $o eq "old" and $old_color = $v; + $o eq "reverse" and $opt_r = $v; + $o eq "threshold" and $opt_t = $v; + $o eq "unified" and $opt_u = $v; + $o eq "unified" and $opt_u = $v; + $o eq "utf-8" and $opt_U = $v; + $o eq "verbose" and $opt_v = $v; + + if ($o eq "out") { + open $fh, ">", $v or die "Cannot select out: $!\n"; + select $fh; + } + } + } + + $emacs and @_ == 0 && -f $f1 && -f "$f1~" and ($f1, $f2) = ("$f1~", $f1); + + $f1 eq "-" && $f2 eq "-" and usage (1); + + binmode STDERR, ":encoding(utf-8)"; + + $opt_U and binmode STDIN, ":encoding(utf-8)"; + $opt_U and binmode STDOUT, ":encoding(utf-8)"; + + my @d1 = $f1 eq "-" ? <> : do { + open my $fh, "<", $f1 or die "$f1: $!\n"; + $opt_U and binmode $fh, ":encoding(utf-8)"; + <$fh>; + }; + my @d2 = $f2 eq "-" ? <> : do { + open my $fh, "<", $f2 or die "$f2: $!\n"; + $opt_U and binmode $fh, ":encoding(utf-8)"; + <$fh>; + }; + if ($opt_H and defined $opt_u) { + # diff -c also provides (ugly) headers, but opt_c is NYI + for ([ "---", $f1 ], [ "+++", $f2 ]) { + if (-f $_->[1]) { + say $_->[0], " $_->[1]\t", scalar localtime ((stat $_->[1])[9]); + } + else { + say $_->[0], " *STDIN\t", scalar localtime; + } + } + } + + my $diff = Algorithm::Diff->new (\@d1, \@d2, $cmp_sub); + $diff->Base (1); + + my ($N, $idx, @s) = (0, 0); + while ($diff->Next) { + $N++; + if ($diff->Same) { + if (defined $opt_u) { + @s = $diff->Items (1); + $N > 1 and print "$chr_ctx$_" for grep { defined } @s[0..($opt_u - 1)]; + unshift @s, undef while @s < $opt_u; + } + next; + } + my $sep = ""; + my @d = map {[ $diff->Items ($_) ]} 1, 2; + my @do = @{$d[0]}; + my @dn = @{$d[1]}; + + if ($opt_B and "@do" !~ m/\S/ && "@dn" !~ m/\S/) { + # Modify @s for -u? + next; + } + if ($opt_I) { + $idx++; + $opt_I > 0 && $idx != $opt_I and next; + printf "%s[%03d]%s ", ${clr_dbg}, $idx, $reset; + } + + if (!@dn) { + printf "%d,%dd%d\n", $diff->Get (qw( Min1 Max1 Max2 )); + $_ = $clr_old . (s/$/$reset/r) for @do; + } + elsif (!@do) { + printf "%da%d,%d\n", $diff->Get (qw( Max1 Min2 Max2 )); + $_ = $clr_new . (s/$/$reset/r) for @dn; + } + else { + $sep = "---\n" unless defined $opt_u; + printf "%d,%dc%d,%d\n", $diff->Get (qw( Min1 Max1 Min2 Max2 )); + if ($opt_t > 0 and abs (@do - @dn) > $opt_t) { + $_ = $clr_old . (s/$/$reset/r) for @do; + $_ = $clr_new . (s/$/$reset/r) for @dn; + } + else { + my @D = subdiff (@d, my $heu = {}); + if ($opt_h and $heu->{pct} > $opt_h) { + $_ = $clr_old . (s/$/$reset/r) for @do; + $_ = $clr_new . (s/$/$reset/r) for @dn; + } + else { + @do = @{$D[0]}; + @dn = @{$D[1]}; + } + } + } + if ($opt_u and @s) { + print "$chr_ctx$_" for grep { defined } map { $s[$#s - $opt_u + $_] } 1..$opt_u; + } + print "$chr_lft$_" for @do; + print $sep; + print "$chr_rgt$_" for @dn; + } + + if ($fh) { + select STDOUT; + close $fh; + } + } # ccdiff + +sub subdiff { + my ($old, $new, $heu) = @_; + my $d = Algorithm::Diff->new (map { [ map { split m// } @$_ ] } $old, $new); + my ($d1, $d2, $x1, $x2, @h1, @h2) = ("", "", "", ""); + my ($cml, $cmr) = $opt_v < 2 ? ("", "") : ($chr_cml, $chr_cmr); + my ($cmd, $cma) = ($chr_old, $chr_new); + @{$heu}{qw( old new same )} = (1, 1, 1); # prevent div/0 + while ($d->Next) { + my @c = map {[ $d->Items ($_) ]} 1, 2; + my @co = @{$c[0]}; + my @cn = @{$c[1]}; + if ($d->Same) { + $heu->{same} += scalar @co; + my $e = $chr_eli; + my $c = join "" => @co; + if ($opt_e) { + my $join = ""; + foreach my $sc (split m/\n/ => $c) { + $_ .= $join for $d1, $d2, $x1, $x2; + $join = "\n"; + my $l = length $sc; # The length of this "same" chunck + my $le = $l - 2 * $opt_e; # The length of the text replaces with ellipsis + my $ee = $opt_v <= 1 ? $e : $e =~ s/^.\K(?=.$)/$le/r; + if ($le > length $ee) { + my $lsc = substr $sc, 0, $opt_e; + $d1 .= $lsc; + $d2 .= $lsc; + $lsc =~ s/\S/$chr_eql/g; + $x1 .= $lsc; + $x2 .= $lsc; + my $rsc = substr $sc, $l - $opt_e, $opt_e; + $d1 .= $clr_dbg . $ee . $reset . $rsc; + $d2 .= $clr_dbg . $ee . $reset . $rsc; + $rsc =~ s/\S/$chr_eql/g; + $x1 .= $chr_eql x length ($ee) . $rsc; + $x2 .= $chr_eql x length ($ee) . $rsc; + next; + } + else { + $d1 .= $sc; + $d2 .= $sc; + $sc =~ s/\S/$chr_eql/g; + $x1 .= $sc; + $x2 .= $sc; + } + } + next; + } + $d1 .= $c; + $d2 .= $c; + $c =~ s/\S/$chr_eql/g; + $x1 .= $c; + $x2 .= $c; + next; + } + if (@co) { + $heu->{old} += scalar @co; + $d1 .= $cml.$clr_old; + $d1 .= s/\n/$reset\n$clr_old/gr for @co; + $d1 .= $reset.$cmr; + $x1 .= $_ for map { s/[^\t\r\n]/$cmd/gr } @co; + $opt_v and push @h1, map { $opt_U ? charnames::viacode (ord) : unpack "H*"; } @co; + } + if (@cn) { + $heu->{new} += scalar @cn; + $d2 .= $cml.$clr_new; + $d2 .= s/\n/$reset\n$clr_new/gr for @cn; + $d2 .= $reset.$cmr; + $x2 .= $_ for map { s/[^\t\r\n]/$cma/gr } @cn; + $opt_v and push @h2, map { $opt_U ? charnames::viacode (ord) : unpack "H*"; } @cn; + } + } + $heu->{pct} = ($heu->{old} + $heu->{new}) / (2 * $heu->{same}); + my @d = map { [ split m/(?<=\n)/ => s/\n*\z/\n/r ] } $d1, $d2; + if ($opt_m) { + $opt_v > 1 and s/(\S+)/ $1 /g for $x1, $x2; + s/[ \t]*\n*\z/\n/ for $x1, $x2; + my @x = map { /\S/ ? [ split m/(?<=\n)/ ] : [] } $x1, $x2; + foreach my $n (0, 1) { + @{$x[$n]} and $d[$n] = [ map {( $d[$n][$_], $x[$n][$_] // "" )} 0 .. (scalar @{$d[$n]} - 1) ]; + } + } + if ($opt_v) { + $opt_U && $opt_v > 2 and $_ .= sprintf " (U+%06X)", charnames::vianame ($_) for @h1, @h2; + @h1 and push @{$d[0]}, sprintf " -- ${clr_dbg}verbose$reset : %s\n", join ", " => map { $clr_old.$_.$reset } @h1; + @h2 and push @{$d[1]}, sprintf " -- ${clr_dbg}verbose$reset : %s\n", join ", " => map { $clr_new.$_.$reset } @h2; + } + @d; + } # subdiff + +sub read_rc { + my $home = $ENV{HOME} || $ENV{USERPROFILE} || $ENV{HOMEPATH}; + foreach my $rcf ( + "$home/ccdiff.rc", + "$home/.ccdiffrc", + "$home/.config/ccdiff", + ) { + -s $rcf or next; + (stat $rcf)[2] & 022 and next; + open my $fh, "<", $rcf or next; + while (<$fh>) { + my ($k, $v) = (m/^\s*([-\w]+)\s*[:=]\s*(.*\S)/) or next; + $rc{ lc $k + =~ s{[-_]colou?r$}{}ir + =~ s{background}{bg}ir + =~ s{^(?:unicode|utf-?8?)$}{utf8}ir + } = $v + =~ s{U\+?([0-9A-Fa-f]{2,7})}{chr hex $1}ger + =~ s{^(?:no|false)$}{0}ir + =~ s{^(?:yes|true)$}{-1}ir; # -1 is still true + } + } + } # read_rc + +# Return the known colors from Term::ANSIColor +# Stolen striaght from the pm +sub tac_colors { + my %c256; + foreach my $r (0 .. 5) { + foreach my $g (0 .. 5) { + $c256{lc $_}++ for map {("RGB$r$g$_", "ON_RGB$r$g$_")} 0 .. 5; + } + } + $c256{lc $_}++ for + # Basic colors + qw( + CLEAR RESET BOLD DARK + FAINT ITALIC UNDERLINE UNDERSCORE + BLINK REVERSE CONCEALED + + BLACK RED GREEN YELLOW + BLUE MAGENTA CYAN WHITE + ON_BLACK ON_RED ON_GREEN ON_YELLOW + ON_BLUE ON_MAGENTA ON_CYAN ON_WHITE + + BRIGHT_BLACK BRIGHT_RED BRIGHT_GREEN BRIGHT_YELLOW + BRIGHT_BLUE BRIGHT_MAGENTA BRIGHT_CYAN BRIGHT_WHITE + ON_BRIGHT_BLACK ON_BRIGHT_RED ON_BRIGHT_GREEN ON_BRIGHT_YELLOW + ON_BRIGHT_BLUE ON_BRIGHT_MAGENTA ON_BRIGHT_CYAN ON_BRIGHT_WHITE + ), + # 256 colors + (map { ("ANSI$_", "ON_ANSI$_") } 0 .. 255), + (map { ("GREY$_", "ON_GREY$_") } 0 .. 23); + + my $ACV = $Term::ANSIColor::VERSION; + $ACV < 3.02 and delete @c256{grep m/italic/ => keys %c256}; + $ACV < 4.00 and delete @c256{grep m/rgb|grey/ => keys %c256}; + $ACV < 4.06 and delete @c256{grep m/ansi/ => keys %c256}; + sort keys %c256; + } # tac_colors 1; __END__ +=encoding utf-8 + =head1 NAME -ccdiff - Colored character diff +ccdiff - Colored Character diff =head1 SYNOPSIS @@ -66,10 +567,10 @@ a unified diff (-u1) would be 5,5c5,5 - Tue Sep 6 05:43:59 2005,B.O.Q.S.,,1125978239,1943341 - - Sat Dec 18 07:00:33 1993,I.O.D.U.,,756194433,1442539 - + Sat Dec 18 07:08:33 1998,I.O.D.U.,,756194433,1442539 - Mon Feb 23 10:37:02 2004,R.X.K.S.,van,1077529022,1654127 + Tue Sep 6 05:43:59 2005,B.O.Q.S.,,1125978239,1943341 + -Sat Dec 18 07:00:33 1993,I.O.D.U.,,756194433,1442539 + +Sat Dec 18 07:08:33 1998,I.O.D.U.,,756194433,1442539 + Mon Feb 23 10:37:02 2004,R.X.K.S.,van,1077529022,1654127 =item --verbose[=1] -v[1] @@ -81,19 +582,91 @@ C<--verbose> accepts an optional verbosity-level. On level 2 and up, all horizontal changes get left-and-right markers inserted to enable seeing the -location of the ZERO WIDTH or invisible characters. +location of the ZERO WIDTH or invisible characters. With level 3 and up and +Unicode enabled, the changed characters will also show the codepoint in hex. + +An example of this: + +With -Uu0v0: + + 1,1c1,1 + - A BCDE Fg + + A BcdE​Fg + +With -Uu0v1: + + 1,1c1,1 + - A BCDE Fg + - -- verbose : SPACE, LATIN CAPITAL LETTER C, LATIN CAPITAL LETTER D, SPACE + + A BcdE​Fg + + -- verbose : LATIN SMALL LETTER C, LATIN SMALL LETTER D, ZERO WIDTH SPACE + +With -Uu0v2: + + 1,1c1,1 + - A ↱ ↰B↱CD↰E↱ ↰Fg + - -- verbose : SPACE, LATIN CAPITAL LETTER C, LATIN CAPITAL LETTER D, SPACE + + A B↱cd↰E↱​↰Fg + + -- verbose : LATIN SMALL LETTER C, LATIN SMALL LETTER D, ZERO WIDTH SPACE + +With -Uu0v3: + + 1,1c1,1 + - A ↱ ↰B↱CD↰E↱ ↰Fg + - -- verbose : SPACE (U+000020), LATIN CAPITAL LETTER C (U+000043), LATIN CAPITAL LETTER D (U+000044), SPACE (U+000020) + + A B↱cd↰E↱​↰Fg + + -- verbose : LATIN SMALL LETTER C (U+000063), LATIN SMALL LETTER D (U+000064), ZERO WIDTH SPACE (U+00200B) + +With -Uu0v2 --ascii: + + 1,1c1,1 + - A > CD cd color. The characters used for the markers can be defined in your +configuration file as C (the character used as marker on the left) +and C (the character used as marker on the right). =item --markers -m Use markers under each changed character in change-chunks. C<--markers> is especially useful if the terminal does not support colors, or -if you want to copy/paste the output to (ASCII) mail. See also C<--ascii> +if you want to copy/paste the output to (ASCII) mail. See also C<--ascii>. The +markers will have the same color as added or deleted text. + +This will look like (with unified diff): + + 5,5c5,5 + -Sat Dec 18 07:08:33 1998,I.O.D.U.,,756194433,1442539 + - ▼ ▼ + +Sat Dec 18 07:00:33 1993,I.O.D.U.,,756194433,1442539 + + ▲ ▲ + +The characters used for the markers can be defined in your configuration file +as C (the character used as marker under removed characters) and +C (the character used as marker under added characters). + +If C<--ellipsis> is also in effect and either the C is longer than +one character or C<--verbose> level is over 2, this options is automatically +disabled. =item --ascii -a Use (colored) ASCII indicators instead of Unicode. The default indicators are -Unicode characters that stand out better. +Unicode characters that stand out better. The markers will have the same color +as added or deleted text. + +For the vertical markers (C<-m>) that would look like: + + 5,5c5,5 + -Sat Dec 18 07:08:33 1998,I.O.D.U.,,756194433,1442539 + - ^ ^ + +Sat Dec 18 07:00:33 1993,I.O.D.U.,,756194433,1442539 + + ^ ^ For the positional indicators, I did consider using U+034e (COMBINING UPWARDS ARROW BELOW), but as most terminals are probably unable to show it due to line @@ -106,7 +679,7 @@ =item --reverse -r -Reverse the foreground and background for the colored indicators. +Reverse/invert the foreground and background for the colored indicators. If the foreground color has C, it will be stripped from the new background color. @@ -115,6 +688,11 @@ List available colors and exit. +=item --no-colors + +Disable all colors. Useful for redirecting the diff output to a file that is to +be included in documentation. + =item --old=color Define the foreground color for deleted text. @@ -131,33 +709,85 @@ Prefix position indicators with an index. + [001] 5,5c5,5 + -Sat Dec 18 07:08:33 1998,I.O.D.U.,,756194433,1442539 + +Sat Dec 18 07:00:33 1993,I.O.D.U.,,756194433,1442539 + If a positive number is passed (C<--index=4> or C<-I 4>), display just the -chunk with that index. This is useful in combination with C<--verbose>. +chunk with that index, using the C color: + +This is useful in combination with C<--verbose>. + +=item --threshold=2 -t 2 + +Defines the number of lines a change block may differ before the fall-back of +horizontal diff to vertical diff. + +If a chunk describes a change, and the number of lines in the original block +has less or more lines than the new block and that difference exceeds this +threshold, C will fall-back to vertical diff. + +=item --heuristics=n -h n + +Defines the percentage of character-changes a change block may differ before +the fall-back of horizontal diff to vertical diff. + +This percentage is calculated as C<(characters removed + characters added) / +(2 * characters unchanged))>. + +=item --ellipsis=n -e n + +Defines the number of characters to keep on each side of a horizontal-equal +segment. The default is C<0>, meaning do not compress. + +If set to a positive number, and the length of a segment of equal characters +inside a horizontal diff is longer than twice this value, the middle part is +replaced with C<┈ U02508 \N{BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL}> +(instead of … U02026, as HORIZONTAL ELLIPSIS does not stand out enough). + +With C<-u0me3> that would be like + + 5,5c5,5 + -Sat┈07:08:33┈ 1998,I.┈539 + - ▼ ▼ + +Sat┈07:00:33┈ 1993,I.┈539 + + ▲ ▲ + +With C<-u0e3 -v2> like + + 5,5c5,5 + -Sat↤9↦07:0↱0↰:33 199↱3↰,I.↤23↦539 + - -- verbose : DIGIT ZERO, DIGIT THREE + +Sat↤9↦07:0↱8↰:33 199↱8↰,I.↤23↦539 + + -- verbose : DIGIT EIGHT, DIGIT EIGHT + +The text used for the replaced text can be defined in your configuration file +as C and/or C. =item --ignore-case -i Ignore case on comparison. -=item --ignore-all-space -w +=item --ignore-all-space -w Ignore all white-space changes. This will set all options C<-b>, C<-Z>, C<-E>, and C<-B>. -=item --ignore-trailing-space -Z +=item --ignore-trailing-space -Z Ignore changes in trailing white-space (TAB's and spaces). -=item --ignore-ws|ignore-space-change -b +=item --ignore-ws|ignore-space-change -b Ignore changes in horizontal white-space (TAB's and spaces). This does not include white-space changes that splits non-white-space or removes white-space between two non-white-space elements. -=item --ignore-tab-expansion -E +=item --ignore-tab-expansion -E NYI -=item --ignore-blank-lines -B +=item --ignore-blank-lines -B B (WIP) @@ -191,11 +821,20 @@ =over 2 +=item unified (-u) + +If you prefer unified-diff over old-style diff by default, set this to the +desired number of context lines: + + unified : 3 + +The default is undefined + =item markers (-m) markers : false -defines if markers should be used under changed characters. The default is to +Defines if markers should be used under changed characters. The default is to use colors only. The C<-m> command line option will toggle the option when set from a configuration file. @@ -203,14 +842,14 @@ ascii : false -defines to use ASCII markers instead of Unicode markers. The default is to use +Defines to use ASCII markers instead of Unicode markers. The default is to use Unicode markers. =item reverse (-r) reverse : false -defines if changes are displayed as foreground-color over background-color +Defines if changes are displayed as foreground-color over background-color or background-color over foreground-color. The default is C, so it will color the changes with the appropriate color (C or C) over the default background color. @@ -219,9 +858,11 @@ new : green -defines the color to be used for added text. The default is C. +Defines the color to be used for added text. The default is C. + +The color C is also accepted and disables this color. -any color accepted by L is allowed. Any other color will +Any color accepted by L is allowed. Any other color will result in a warning. This option can include C either as prefix or as suffix. @@ -236,9 +877,11 @@ old : red -defines the color to be used for delete text. The default is C. +Defines the color to be used for delete text. The default is C. -any color accepted by L is allowed. Any other color will +The color C is also accepted and disables this color. + +Any color accepted by L is allowed. Any other color will result in a warning. This option can include C either as prefix or as suffix. @@ -253,10 +896,12 @@ bg : white -defines the color to be used as background for changed text. The default is +Defines the color to be used as background for changed text. The default is C. -any color accepted by L is allowed. Any other color will +The color C is also accepted and disables this color. + +Any color accepted by L is allowed. Any other color will result in a warning. The C attribute is not allowed. This option may also be specified as @@ -275,10 +920,12 @@ verbose : cyan -defines the color to be used as color for the verbose tag. The default is +Defines the color to be used as color for the verbose tag. The default is C. This color will only be used under C<--verbose>. -any color accepted by L is allowed. Any other color will +The color C is also accepted and disables this color. + +Any color accepted by L is allowed. Any other color will result in a warning. This option may also be specified as @@ -292,7 +939,7 @@ utf8 : yes -defines whether all I/O is to be interpreted as UTF-8. The default is C. +Defines whether all I/O is to be interpreted as UTF-8. The default is C. This option may also be specified as @@ -304,7 +951,7 @@ index : no -defines if the position indication for a change chunk is prefixed with an +Defines if the position indication for a change chunk is prefixed with an index number. The default is C. The index is 1-based. Without this option, the position indication would be like @@ -322,8 +969,139 @@ When this option contains a positive integer, C will only show diff the diff chunk with that index. +=item emacs + + emacs : no + +If this option is yes/true, calling C with just one single argument, +and that argument being an existing file, the arguments will act as + + $ ccdiff file~ file + +if file~ exists. + +=item threshold (-t) + + threshold : 2 + +Defines the number of lines a change block may differ before the fall-back of +horizontal diff to vertical diff. + +=item heuristics (-h) + + heuristics : 40 + +Defines the percentage of character-changes a change block may differ before +the fall-back of horizontal diff to vertical diff. The default is undefined, +meaning no fallback based on heuristics. + +=item ellipsis (-e) + + ellipsis : 0 + +Defines the number of characters to keep on each side of a horizontal-equal +segment. The default is C<0>, meaning to not compress. See also C. + +=item chr_old + + chr_old : U+25BC + +Defines the character used to indicate the position of removed text on the +line below the text when option C<-m> is in effect. + +=item chr_new + + chr_new : U+25B2 + +Defines the character used to indicate the position of added text on the +line below the text when option C<-m> is in effect. + +=item chr_cml + + chr_cml : U+21B1 + +Defines the character used to indicate the starting position of changed text +in a line when verbose level is 3 and up. + +=item chr_cmr + + chr_cmr : U+21B0 + +Defines the character used to indicate the ending position of changed text +in a line when verbose level is 3 and up. + +=item chr_eli + + chr_eli : U+21B0 + +Defines the character used to indicate omitted text in large unchanged text +when C<--ellipsis>/C<-e> is in effect. + +This character is not equally well visible on all terminals or in all fonts, +so you might want to chane it to something that stands out better in you +environment. Possible suggestions: + + … U+2026 HORIZONTAL ELLIPSIS + ‴ U+2034 TRIPLE PRIME + ‷ U+2037 REVERSED TRIPLE PRIME + ↔ U+2194 LEFT RIGHT ARROW + ↭ U+21ad LEFT RIGHT WAVE ARROW + ↮ U+21ae LEFT RIGHT ARROW WITH STROKE + ↹ U+21b9 LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR + ⇄ U+21c4 RIGHTWARDS ARROW OVER LEFTWARDS ARROW + ⇆ U+21c6 LEFTWARDS ARROW OVER RIGHTWARDS ARROW + ⇎ U+21ce LEFT RIGHT DOUBLE ARROW WITH STROKE + ⇔ U+21d4 LEFT RIGHT DOUBLE ARROW + ⇹ U+21f9 LEFT RIGHT ARROW WITH VERTICAL STROKE + ⇼ U+21fc LEFT RIGHT ARROW WITH DOUBLE VERTICAL STROKE + ⇿ U+21ff LEFT RIGHT OPEN-HEADED ARROW + ≋ U+224b TRIPLE TILDE + ┄ U+2504 BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL + ┅ U+2505 BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL + ┈ U+2508 BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL + ┉ U+2509 BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL + ⧻ U+29fb TRIPLE PLUS + ⬌ U+2b0c LEFT RIGHT BLACK ARROW + +=item chr_eli_v + + chr_eli_v : U+21A4U+21A6 + +When ussing C<--ellipsis> with C<--verbose> level 2 or up, the single character +indicator will be replaced with this character. If it is 2 characters wide, the +length of the compressed part is put between the characters. + +A suggested alternative might be U+21E4U+21E5 + =back +=head1 Git integration + +You can use ccdiff to show diffs in git. It may work like this: + + $ git config --global diff.tool ccdiff + $ git config --global difftool.prompt false + $ git config --global difftool.ccdiff.cmd 'ccdiff --utf-8 -u -r $LOCAL $REMOTE' + $ git difftool SHA~..SHA + $ wget https://github.com/Tux/App-ccdiff/blob/master/Files/git-ccdiff \ + -O ~/bin/git-ccdiff + $ perl -pi -e 's{/pro/bin/perl}{/usr/bin/env perl}' ~/bin/git-ccdiff + $ chmod 755 ~/bin/git-ccdiff + $ git ccdiff SHA + +Of course you can use C instead of C and you can choose your own +(fixed) path to C instead of using C. + +From then on you can do + + $ git ccdiff + $ git ccdiff 5c5a39f2 + +=head1 CAVEATS + +Due to the implementation, where both sides of the comparison are completely +kept in memory, this tool might not be able to deal with (very) large datasets. + =head1 SEE ALSO L, L @@ -343,4 +1121,3 @@ :ex:se gw=75|color guide #ff0000: =cut - diff -Nru ccdiff-0.21/Makefile.PL ccdiff-0.26/Makefile.PL --- ccdiff-0.21/Makefile.PL 2018-08-19 10:17:54.000000000 +0000 +++ ccdiff-0.26/Makefile.PL 2018-10-14 17:51:53.000000000 +0000 @@ -3,24 +3,53 @@ use ExtUtils::MakeMaker; +my $VERSION; +open my $fh, "<", "ccdiff"; +while (<$fh>) { + m/VERSION\s*=\s*["']?([0-9.]+)/ or next; + $VERSION = $1; + last; + } +close $fh; + my %wmf = ( - NAME => "App::ccdiff", - DISTNAME => "App-ccdiff", - ABSTRACT => "Colored Character Diff", - AUTHOR => "H.Merijn Brand", - EXE_FILES => [ "ccdiff" ], - VERSION_FROM => "lib/App/ccdiff.pm", - MIN_PERL_VERSION => "5.012001", - PREREQ_PM => { + NAME => "App::ccdiff", + DISTNAME => "App-ccdiff", + ABSTRACT => "Colored Character Diff", + AUTHOR => "H.Merijn Brand", + EXE_FILES => [ "ccdiff" ], + VERSION => $VERSION, + MIN_PERL_VERSION => "5.014000", + PREREQ_PM => { charnames => 0, "Algorithm::Diff" => 0, "Term::ANSIColor" => 0, "Getopt::Long" => 0, }, - LICENSE => "perl", - CONFIGURE_REQUIRES => { "ExtUtils::MakeMaker" => 0 }, - TEST_REQUIRES => { "Test::More" => 0 }, - macro => { TARFLAGS => "--format=ustar -c -v -f", }, + CONFIGURE_REQUIRES => { + "ExtUtils::MakeMaker" => 0, + }, + TEST_REQUIRES => { + "Test::More" => 0, + "Capture::Tiny" => 0, + }, + LICENSE => "artistic_2", + macro => { TARFLAGS => "--format=ustar -c -v -f", }, + + META_MERGE => { + "meta-spec" => { version => 2 }, + resources => { + type => "git", + url => "git://github.com/Tux/App-ccdiff.git", + web => "https://github.com/Tux/App-ccdiff", + }, + provides => { + "App::ccdiff" => { + file => "ccdiff", + version => $VERSION, + }, + }, + }, ); eval { ExtUtils::MakeMaker->VERSION (6.63_03) } or @@ -31,19 +60,40 @@ package MY; sub postamble { - my $mpm = (-d ".git" && -x "2lib") - ? "lib/App/ccdiff.pm: ccdiff\n\tperl ./2lib\n" - : ""; + my @mpm = (-d ".git" && -x "2lib") + ? ( '','lib/App/ccdiff.pm: ccdiff', + ' perl ./2lib', + '', + 'all:: lib/App/ccdiff.pm doc spellcheck', + ) + : (); join "\n" => 'spellcheck:', ' pod-spell-check --aspell --ispell ccdiff', '', - 'tgzdist: lib/App/ccdiff.pm spellcheck distmeta $(DISTVNAME).tar.gz distcheck', + 'meta: META.json META.yml', + 'META.json META.yml: ccdiff Makefile.PL', + ' -@make metafile', + ' -@mv $(DISTVNAME)/META.* .', + ' -@rm -rf $(DISTVNAME)', + '', + 'tgzdist: lib/App/ccdiff.pm META.json spellcheck $(DISTVNAME).tar.gz distcheck', ' -@mv -f $(DISTVNAME).tar.gz $(DISTVNAME).tgz', ' -@cpants_lint.pl $(DISTVNAME).tgz', ' -@rm -f Debian_CPANTS.txt', '', - $mpm; + 'doc: doc/ccdiff.md doc/ccdiff.html doc/ccdiff.man doc/README.md', + 'doc/ccdiff.md: ccdiff', + ' pod2markdown < $? > $@', + 'doc/ccdiff.html: ccdiff', + ' pod2html < $? 2>&1 | grep -v "^Cannot find" > $@', + 'doc/ccdiff.3: ccdiff', + ' pod2man < $? > $@', + 'doc/ccdiff.man: doc/ccdiff.3', + ' nroff -mandoc < $? > $@', + 'doc/README.md: README.md', + ' cp -p $? $@', + @mpm; } # postamble 1; diff -Nru ccdiff-0.21/MANIFEST ccdiff-0.26/MANIFEST --- ccdiff-0.21/MANIFEST 2018-08-19 10:01:00.000000000 +0000 +++ ccdiff-0.26/MANIFEST 2018-10-08 16:01:53.000000000 +0000 @@ -1,6 +1,10 @@ ccdiff ChangeLog CONTRIBUTING.md +Files/01_1.txt +Files/01_2.txt +Files/01_3.txt +Files/01_4.txt Files/git-ccdiff lib/App/ccdiff.pm LICENSE @@ -9,3 +13,4 @@ META.json META.yml README.md +t/01-no-color.t diff -Nru ccdiff-0.21/META.json ccdiff-0.26/META.json --- ccdiff-0.21/META.json 2018-08-19 10:18:09.000000000 +0000 +++ ccdiff-0.26/META.json 2018-10-14 17:55:48.000000000 +0000 @@ -6,7 +6,7 @@ "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010", "license" : [ - "perl_5" + "artistic_2" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", @@ -36,16 +36,28 @@ "Getopt::Long" : "0", "Term::ANSIColor" : "0", "charnames" : "0", - "perl" : "5.012001" + "perl" : "5.014000" } }, "test" : { "requires" : { + "Capture::Tiny" : "0", "Test::More" : "0" } } }, + "provides" : { + "App::ccdiff" : { + "file" : "ccdiff", + "version" : "0.26" + } + }, "release_status" : "stable", - "version" : "0.21", + "resources" : { + "X_type" : "git", + "X_url" : "git://github.com/Tux/App-ccdiff.git", + "X_web" : "https://github.com/Tux/App-ccdiff" + }, + "version" : "0.26", "x_serialization_backend" : "JSON::PP version 2.97001" } diff -Nru ccdiff-0.21/META.yml ccdiff-0.26/META.yml --- ccdiff-0.21/META.yml 2018-08-19 10:18:09.000000000 +0000 +++ ccdiff-0.26/META.yml 2018-10-14 17:55:48.000000000 +0000 @@ -3,13 +3,14 @@ author: - 'H.Merijn Brand' build_requires: + Capture::Tiny: '0' ExtUtils::MakeMaker: '0' Test::More: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010' -license: perl +license: artistic_2 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' @@ -18,11 +19,19 @@ directory: - t - inc +provides: + App::ccdiff: + file: ccdiff + version: '0.26' requires: Algorithm::Diff: '0' Getopt::Long: '0' Term::ANSIColor: '0' charnames: '0' - perl: '5.012001' -version: '0.21' + perl: '5.014000' +resources: + X_type: git + X_url: git://github.com/Tux/App-ccdiff.git + X_web: https://github.com/Tux/App-ccdiff +version: '0.26' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' diff -Nru ccdiff-0.21/README.md ccdiff-0.26/README.md --- ccdiff-0.21/README.md 2018-08-15 11:44:09.000000000 +0000 +++ ccdiff-0.26/README.md 2018-09-25 13:50:52.000000000 +0000 @@ -5,24 +5,8 @@ # Synopsis ``` usage: ccdiff [options] file1 [file2] - file1 or file2 can be - (but not both) - -V --version Show version and exit - Diff options: - -U --utf-8 Input is in UTF-8 - -u[3] --unified=3 Show a unified diff - -w --ignore-all-space Ignore all whitespace - -b --ignore-space-change Ignore horizontal whitespace changes - -Z --ignore-trailing-space Ignore whitespace at line ending - -B --ignore-blank-lines Ignore changes where lines are all blank - -i --ignore-case Ignore case changes - Color options: - --no-color Do not use colors - --old=red Color to indicate removed content - --new=green Color to indicate added content - --bg=white Background color for colored indicators - -p --pink Shortcut for --old=magenta - -f --fancy Use Unicode indicators instead of ^ - -r --reverse Reverse the colors of the indicators + ccdiff --help | --man | --info + file1 or file2 can be - (but not both) ``` # Description @@ -45,7 +29,10 @@ # Installation Change the first line of `ccdiff` to start your favorite perl interpreter -and then move the file to a folder in your `$PATH`. +and then move the file to a folder in your `$PATH`, or install from CPAN: +``` +$ cpan App::ccdiff +``` # Alternatives @@ -57,6 +44,8 @@ * `colordiff` + * `klondiff` + * `git` This however requires a long command: @@ -67,6 +56,8 @@ --word-diff=color --word-diff-regex=. \ ``` + An alternative for integration with git is `diff-so-fancy` + ## ASCII * `vimdiff` @@ -96,6 +87,26 @@ * `tkdiff` (Tcl/Tk) +## Other (not checked yet) + +Reasons for not checking include Windows and emacs. + + * araxis + * bc + * bc3 + * codecompare + * deltaworker + * diffmerge + * ecmerge + * emerge + * examdiff + * guiffy + * gvimdiff2 + * gvimdiff3 + * opendiff + * p4merge + * winmerge + # Dependencies This tool will run on recent perl distributions that have @@ -120,26 +131,25 @@ $ git config --global difftool.prompt false $ git config --global difftool.ccdiff.cmd 'ccdiff --utf-8 -u -r $LOCAL $REMOTE' $ git difftool SHA~..SHA -$ cat >~/bin/git-ccdiff < "$_\t".localtime ((stat)[9]) } glob "Files/*"; + +local $/ = "** EOT **\n"; +while () { + chomp; + my ($dsc, $f1, $f2, $opt, $exp) = split m/\n/, $_, 5; + $exp =~ s/STAMP:1/$stamp{$f1}/g; + $exp =~ s/STAMP:2/$stamp{$f2}/g; + #diag "Description: $dsc"; + #diag "Options: $opt"; + my @cmd = ($^X, "ccdiff", "--utf-8", "--no-color", "Files/$f1", "Files/$f2"); + $opt and push @cmd, split m/ / => $opt; + #diag "@cmd"; + my ($out, $err, $exit) = capture { system @cmd; }; + is ($out, $exp, $dsc); + is ($err, "", "No error"); + is ($exit, 0, "Success"); + } + +done_testing; + +__END__ +No options +01_1.txt +01_2.txt + +3,3c3,3 +< usu. Ad duo posse theophrastus, vim in accumsan +--- +> usu. Ad duo posse theophrastus. Vim in accumsan +** EOT ** +Unified with header +01_1.txt +01_2.txt +-u0 +--- STAMP:1 ++++ STAMP:2 +3,3c3,3 +-usu. Ad duo posse theophrastus, vim in accumsan ++usu. Ad duo posse theophrastus. Vim in accumsan +** EOT ** +Unified without header +01_1.txt +01_2.txt +-u0 --no-header +3,3c3,3 +-usu. Ad duo posse theophrastus, vim in accumsan ++usu. Ad duo posse theophrastus. Vim in accumsan +** EOT ** +Unified with ascii markers +01_1.txt +01_2.txt +-mau0 --no-header +3,3c3,3 +-usu. Ad duo posse theophrastus, vim in accumsan +- ^ ^ ++usu. Ad duo posse theophrastus. Vim in accumsan ++ ^ ^ +** EOT ** +Unified with unicode markers +01_1.txt +01_2.txt +-mu0 --no-header +3,3c3,3 +-usu. Ad duo posse theophrastus, vim in accumsan +- ▼ ▼ ++usu. Ad duo posse theophrastus. Vim in accumsan ++ ▲ ▲ +** EOT ** +Unified with ascii markers and context +01_1.txt +01_2.txt +-mau1 +--- STAMP:1 ++++ STAMP:2 +3,3c3,3 + id vix cibo omittantur, et impetus offendit convenire +-usu. Ad duo posse theophrastus, vim in accumsan +- ^ ^ ++usu. Ad duo posse theophrastus. Vim in accumsan ++ ^ ^ + efficiantur, sed in congue decore. Ex nullam iudicabit +** EOT ** +Unified with ascii verbose 1 +01_1.txt +01_2.txt +-au0 -v1 --no-header +3,3c3,3 +-usu. Ad duo posse theophrastus, vim in accumsan +- -- verbose : COMMA, LATIN SMALL LETTER V ++usu. Ad duo posse theophrastus. Vim in accumsan ++ -- verbose : FULL STOP, LATIN CAPITAL LETTER V +** EOT ** +Unified with unicode verbose 1 +01_1.txt +01_2.txt +-u0 -v1 --no-header +3,3c3,3 +-usu. Ad duo posse theophrastus, vim in accumsan +- -- verbose : COMMA, LATIN SMALL LETTER V ++usu. Ad duo posse theophrastus. Vim in accumsan ++ -- verbose : FULL STOP, LATIN CAPITAL LETTER V +** EOT ** +Unified with ascii verbose 2 +01_1.txt +01_2.txt +-au0 -v2 --no-header +3,3c3,3 +-usu. Ad duo posse theophrastus>,< >v.< >V