use base qw(PublicInbox::RepobrowseBase);
use PublicInbox::Hval qw(utf8_html to_attr);
use PublicInbox::RepobrowseGit qw(git_unquote git_commit_title);
+use PublicInbox::RepobrowseGitDiffCommon qw/git_diffstat_emit
+ git_diff_ab_index git_diff_ab_hdr git_diff_ab_hunk/;
use constant GIT_FMT => '--pretty=format:'.join('%n',
'%H', '%h', '%s', '%an <%ae>', '%ai', '%cn <%ce>', '%ci',
$l = <$log>;
chomp $l;
$fh->write(utf8_html($l)."<a\nid=D>---</a>\n");
- my $diff = { anchors => {}, h => $h, p => \@p, rel => $rel };
- git_show_diffstat($diff, $req, $fh, $log);
+ $req->{anchors} = {};
+ $req->{h} = $h;
+ $req->{p} = \@p;
+ $req->{rel} = $rel;
+ {
+ local $/ = "\0\0";
+ my $l = <$log>;
+ chomp $l;
+ git_diffstat_emit($req, $fh, $l);
+ }
my $help;
$help = " This is a merge, showing combined diff:\n\n" if ($np > 1);
$help = undef;
}
if ($l =~ m{^diff --git ("?a/.+) ("?b/.+)$}) { # regular
- $l = git_diff_ab_hdr($diff, $1, $2) . "\n";
+ $l = git_diff_ab_hdr($req, $1, $2) . "\n";
} elsif ($l =~ m{^diff --(cc|combined) (.+)$}) {
- $l = git_diff_cc_hdr($diff, $1, $2) . "\n";
+ $l = git_diff_cc_hdr($req, $1, $2) . "\n";
} elsif ($l =~ /^index ($cmt)\.\.($cmt)(.*)$/o) { # regular
- $l = git_diff_ab_index($diff, $1, $2, $3) . "\n";
+ $l = git_diff_ab_index($1, $2, $3) . "\n";
} elsif ($l =~ /^@@ (\S+) (\S+) @@(.*)$/) { # regular
- $l = git_diff_ab_hunk($diff, $1, $2, $3) . "\n";
-
+ $l = git_diff_ab_hunk($req, $1, $2, $3) . "\n";
} elsif ($l =~ /^\+{1,3}\s*/ || ($cc_ins && $l =~ $cc_ins)) {
$l = git_diff_ins($l) . "\n";
} elsif ($l =~ s/^(\-{1,3}\s*)// ||
($cc_del && $l =~ s/$cc_del//)) {
$l = git_diff_del($1, $l) . "\n";
} elsif ($l =~ /^index ($cmt,[^\.]+)\.\.($cmt)(.*)$/o) { # --cc
- $l = git_diff_cc_index($diff, $1, $2, $3) . "\n";
- $cc_ins ||= $diff->{cc_ins};
- $cc_del ||= $diff->{cc_del};
+ $l = git_diff_cc_index($req, $1, $2, $3) . "\n";
+ $cc_ins ||= $req->{cc_ins};
+ $cc_del ||= $req->{cc_del};
} elsif ($l =~ /^(@@@+) (\S+.*\S+) @@@+(.*)$/) { # --cc
- $l = git_diff_cc_hunk($diff, $1, $2, $3) . "\n";
+ $l = git_diff_cc_hunk($req, $1, $2, $3) . "\n";
} else {
$l = utf8_html($l);
}
$fh->write(" This is a merge, combined diff is empty.\n");
}
- show_unchanged($fh, $diff, $qs);
+ show_unchanged($fh, $req, $qs);
$fh->write('</pre></body></html>');
}
delete($req->{res})->([404, ['Content-Type'=>'text/html'], [ $x ]]);
}
-sub git_show_diffstat {
- my ($diff, $req, $fh, $log) = @_;
- local $/ = "\0\0";
- my $l = <$log>;
- chomp $l;
- my @stat = split("\0", $l);
- my $nr = 0;
- my ($nadd, $ndel) = (0, 0);
- while (defined($l = shift @stat)) {
- $l =~ s/\n?(\S+)\t+(\S+)\t+// or next;
- my ($add, $del) = ($1, $2);
- if ($add =~ /\A\d+\z/) {
- $nadd += $add;
- $ndel += $del;
- $add = "+$add";
- $del = "-$del";
- }
- my $num = sprintf('% 6s/%-6s', $del, $add);
- if (length $l) {
- my $anchor = to_attr(git_unquote($l));
- $diff->{anchors}->{$anchor} = $l;
- $l = utf8_html($l);
- $l = qq(<a\nhref="#$anchor">$l</a>);
- } else {
- my $from = shift @stat;
- my $to = shift @stat;
- $l = git_diffstat_rename($diff, $from, $to);
- }
- ++$nr;
- $fh->write(' '.$num."\t".$l."\n");
- }
- $l = "\n $nr ";
- $l .= $nr == 1 ? 'file changed, ' : 'files changed, ';
- $l .= $nadd;
- $l .= $nadd == 1 ? ' insertion(+), ' : ' insertions(+), ';
- $l .= $ndel;
- $l .= $ndel == 1 ? " deletion(-)\n\n" : " deletions(-)\n\n";
- $fh->write($l);
-}
-
-# index abcdef89..01234567
-sub git_diff_ab_index {
- my ($diff, $xa, $xb, $end) = @_;
- # not wasting bandwidth on links here, yet
- # links in hunk headers are far more useful with line offsets
- $end = utf8_html($end);
- "index $xa..<b>$xb</b>$end";
-}
-
-# diff --git a/foo.c b/bar.c
-sub git_diff_ab_hdr {
- my ($diff, $fa, $fb) = @_;
- my $html_a = utf8_html($fa);
- my $html_b = utf8_html($fb);
- $fa = git_unquote($fa);
- $fb = git_unquote($fb);
- $fa =~ s!\Aa/!!;
- $fb =~ s!\Ab/!!;
- my $anchor = to_attr($fb);
- delete $diff->{anchors}->{$anchor};
- $fa = $diff->{fa} = PublicInbox::Hval->utf8($fa);
- $fb = $diff->{fb} = PublicInbox::Hval->utf8($fb);
- $diff->{path_a} = $fa->as_path;
- $diff->{path_b} = $fb->as_path;
-
- # not wasting bandwidth on links here, yet
- # links in hunk headers are far more useful with line offsets
- qq(<a\nhref=#D\nid="$anchor">diff</a> --git $html_a <b>$html_b</b>);
-}
-
-# @@ -1,2 +3,4 @@ (regular diff)
-sub git_diff_ab_hunk {
- my ($diff, $ca, $cb, $ctx) = @_;
- my ($na) = ($ca =~ /\A-(\d+)/);
- my ($nb) = ($cb =~ /\A\+(\d+)/);
-
- my $rel = $diff->{rel};
- my $rv = '@@ ';
- if ($na == 0) { # new file
- $rv .= $ca;
- } else {
- my $p = $diff->{p}->[0];
- $rv .= qq(<a\nrel=nofollow);
- $rv .= qq(\nhref="${rel}tree/$diff->{path_a}?id=$p#n$na">);
- $rv .= "$ca</a>";
- }
- $rv .= ' ';
- if ($nb == 0) { # deleted file
- $rv .= $cb;
- } else {
- my $h = $diff->{h};
- $rv .= qq(<a\nrel=nofollow);
- $rv .= qq(\nhref="${rel}tree/$diff->{path_b}?id=$h#n$nb">);
- $rv .= "<b>$cb</b></a>";
- }
- $rv . ' @@' . utf8_html($ctx);
-}
-
sub git_diff_cc_hdr {
my ($diff, $combined, $path) = @_;
my $html_path = utf8_html($path);
$rv .= " $at" . utf8_html($ctx);
}
-sub git_diffstat_rename {
- my ($diff, $from, $to) = @_;
- my $anchor = to_attr(git_unquote($to));
- $diff->{anchors}->{$anchor} = $to;
- my @from = split('/', $from);
- my @to = split('/', $to);
- my $orig_to = $to;
- my ($base, @base);
- while (@to && @from && $to[0] eq $from[0]) {
- push @base, shift(@to);
- shift @from;
- }
-
- $base = utf8_html(join('/', @base)) if @base;
- $from = utf8_html(join('/', @from));
- $to = PublicInbox::Hval->utf8(join('/', @to), $orig_to);
- my $tp = $to->as_path;
- my $th = $to->as_html;
- $to = qq(<a\nhref="#$anchor">$th</a>);
- @base ? "$base/{$from => $to}" : "$from => $to";
-}
-
# It would be nice to be able to use colors for showing diff hunks.
# Unfortunately, the default green+red colors in common web viewers
# (gitweb, cgit, etc) are difficult to read for some people, myself
use base qw(PublicInbox::RepobrowseBase);
use PublicInbox::Hval qw(utf8_html to_attr);
use PublicInbox::RepobrowseGit qw(git_unquote git_commit_title);
+use PublicInbox::RepobrowseGitDiffCommon qw/git_diffstat_emit
+ git_diff_ab_index git_diff_ab_hdr git_diff_ab_hunk/;
sub call_git_diff {
my ($self, $req) = @_;
}
}
-sub git_diffstat_to_html ($$$) {
- my ($req, $fh, undef) = @_;
- my @stat = split("\0", $_[2]); # avoiding copy for $_[2]
- my $nr = 0;
- my ($nadd, $ndel) = (0, 0);
- my $s = '';
- while (defined(my $l = shift @stat)) {
- $l =~ s/\n?(\S+)\t+(\S+)\t+// or next;
- my ($add, $del) = ($1, $2);
- if ($add =~ /\A\d+\z/) {
- $nadd += $add;
- $ndel += $del;
- $add = "+$add";
- $del = "-$del";
- }
- my $num = sprintf('% 6s/%-6s', $del, $add);
- if (length $l) {
- my $anchor = to_attr(git_unquote($l));
- $req->{anchors}->{$anchor} = $l;
- $l = utf8_html($l);
- $l = qq(<a\nhref="#$anchor">$l</a>);
- } else {
- my $from = shift @stat;
- my $to = shift @stat;
- $l = git_diffstat_rename($req, $from, $to);
- }
- ++$nr;
- $s .= ' '.$num."\t".$l."\n";
- }
- $s .= "\n $nr ";
- $s .= $nr == 1 ? 'file changed, ' : 'files changed, ';
- $s .= $nadd;
- $s .= $nadd == 1 ? ' insertion(+), ' : ' insertions(+), ';
- $s .= $ndel;
- $s .= $ndel == 1 ? " deletion(-)\n\n" : " deletions(-)\n\n";
- $fh->write($s);
-}
-
sub git_diff_line_i {
my ($req, $l) = @_;
my $cmt = '[a-f0-9]+';
if ($l =~ m{^diff --git ("?a/.+) ("?b/.+)$}) { # regular
$l = git_diff_ab_hdr($req, $1, $2);
} elsif ($l =~ /^index ($cmt)\.\.($cmt)(.*)$/o) { # regular
- $l = git_diff_ab_index($req, $1, $2, $3);
+ $l = git_diff_ab_index($1, $2, $3);
} elsif ($l =~ /^@@ (\S+) (\S+) @@(.*)$/) { # regular
$l = git_diff_ab_hunk($req, $1, $2, $3);
- } elsif ($l =~ /^index ($cmt,[^\.]+)\.\.($cmt)(.*)$/o) { # --cc
- $l = git_diff_cc_index($req, $1, $2, $3);
} else {
$l = utf8_html($l);
}
my ($stat, $buf) = split(/\0\0/, $req->{dbuf}, 2);
return unless defined $buf;
$req->{dbuf} = $buf;
- git_diffstat_to_html($req, $fh, $stat);
+ git_diffstat_emit($req, $fh, $stat);
$req->{diff_state} = 1;
}
my @buf = split(/\n/, $req->{dbuf}, -1);
}
}
-sub git_diffstat_rename {
- my ($req, $from, $to) = @_;
- my $anchor = to_attr(git_unquote($to));
- $req->{anchors}->{$anchor} = $to;
- my @from = split('/', $from);
- my @to = split('/', $to);
- my $orig_to = $to;
- my ($base, @base);
- while (@to && @from && $to[0] eq $from[0]) {
- push @base, shift(@to);
- shift @from;
- }
-
- $base = utf8_html(join('/', @base)) if @base;
- $from = utf8_html(join('/', @from));
- $to = PublicInbox::Hval->utf8(join('/', @to), $orig_to);
- my $tp = $to->as_path;
- my $th = $to->as_html;
- $to = qq(<a\nhref="#$anchor">$th</a>);
- @base ? "$base/{$from => $to}" : "$from => $to";
-}
-
-# index abcdef89..01234567
-sub git_diff_ab_index {
- my ($req, $xa, $xb, $end) = @_;
- # not wasting bandwidth on links here, yet
- # links in hunk headers are far more useful with line offsets
- $end = utf8_html($end);
- "index $xa..$xb$end";
-}
-
-# diff --git a/foo.c b/bar.c
-sub git_diff_ab_hdr {
- my ($req, $fa, $fb) = @_;
- my $html_a = utf8_html($fa);
- my $html_b = utf8_html($fb);
- $fa = git_unquote($fa);
- $fb = git_unquote($fb);
- $fa =~ s!\Aa/!!;
- $fb =~ s!\Ab/!!;
- my $anchor = to_attr($fb);
- delete $req->{anchors}->{$anchor};
- $fa = $req->{fa} = PublicInbox::Hval->utf8($fa);
- $fb = $req->{fb} = PublicInbox::Hval->utf8($fb);
- $req->{path_a} = $fa->as_path;
- $req->{path_b} = $fb->as_path;
-
- # not wasting bandwidth on links here
- # links in hunk headers are far more useful with line offsets
- qq(<a\nhref=#D\nid="$anchor">diff</a> --git $html_a $html_b);
-}
-
-# @@ -1,2 +3,4 @@ (regular diff)
-sub git_diff_ab_hunk {
- my ($req, $ca, $cb, $ctx) = @_;
- my ($na) = ($ca =~ /\A-(\d+)/);
- my ($nb) = ($cb =~ /\A\+(\d+)/);
-
- # we add "rel=nofollow" here to reduce load on search engines, here
- my $rel = $req->{relcmd};
- my $rv = '@@ ';
- if (defined($na) && $na == 0) { # new file
- $rv .= $ca;
- } else {
- $na = defined $na ? "#n$na" : '';
- my $p = $req->{p}->[0];
- $rv .= qq(<a\nrel=nofollow);
- $rv .= qq(\nhref="${rel}tree/$req->{path_a}?id=$p$na">);
- $rv .= "$ca</a>";
- }
- $rv .= ' ';
- if (defined($nb) && $nb == 0) { # deleted file
- $rv .= $cb;
- } else {
- my $h = $req->{h};
- $nb = defined $nb ? "#n$nb" : '';
- $rv .= qq(<a\nrel=nofollow);
- $rv .= qq(\nhref="${rel}tree/$req->{path_b}?id=$h$nb">);
- $rv .= "$cb</a>";
- }
- $rv . ' @@' . utf8_html($ctx);
-}
-
1;
--- /dev/null
+# Copyright (C) 2016 all contributors <meta@public-inbox.org>
+# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+
+# common git diff-related code
+package PublicInbox::RepobrowseGitDiffCommon;
+use strict;
+use warnings;
+use PublicInbox::RepobrowseGit qw/git_unquote git_commit_title/;
+use PublicInbox::Hval qw/utf8_html to_attr/;
+use base qw/Exporter/;
+our @EXPORT_OK = qw/git_diffstat_emit
+ git_diff_ab_index git_diff_ab_hdr git_diff_ab_hunk/;
+
+# index abcdef89..01234567
+sub git_diff_ab_index ($$$) {
+ my ($xa, $xb, $end) = @_;
+ # not wasting bandwidth on links here, yet
+ # links in hunk headers are far more useful with line offsets
+ $end = utf8_html($end);
+ "index $xa..$xb$end";
+}
+
+# diff --git a/foo.c b/bar.c
+sub git_diff_ab_hdr ($$$) {
+ my ($req, $fa, $fb) = @_;
+ my $html_a = utf8_html($fa);
+ my $html_b = utf8_html($fb);
+ $fa = git_unquote($fa);
+ $fb = git_unquote($fb);
+ $fa =~ s!\Aa/!!;
+ $fb =~ s!\Ab/!!;
+ my $anchor = to_attr($fb);
+ delete $req->{anchors}->{$anchor};
+ $fa = $req->{fa} = PublicInbox::Hval->utf8($fa);
+ $fb = $req->{fb} = PublicInbox::Hval->utf8($fb);
+ $req->{path_a} = $fa->as_path;
+ $req->{path_b} = $fb->as_path;
+
+ # not wasting bandwidth on links here
+ # links in hunk headers are far more useful with line offsets
+ qq(<a\nhref=#D\nid="$anchor">diff</a> --git $html_a $html_b);
+}
+
+# @@ -1,2 +3,4 @@ (regular diff)
+sub git_diff_ab_hunk ($$$$) {
+ my ($req, $ca, $cb, $ctx) = @_;
+ my ($na) = ($ca =~ /\A-(\d+)/);
+ my ($nb) = ($cb =~ /\A\+(\d+)/);
+
+ # we add "rel=nofollow" here to reduce load on search engines, here
+ my $rel = $req->{relcmd};
+ my $rv = '@@ ';
+ if (defined($na) && $na == 0) { # new file
+ $rv .= $ca;
+ } else {
+ $na = defined $na ? "#n$na" : '';
+ my $p = $req->{p}->[0];
+ $rv .= qq(<a\nrel=nofollow);
+ $rv .= qq(\nhref="${rel}tree/$req->{path_a}?id=$p$na">);
+ $rv .= "$ca</a>";
+ }
+ $rv .= ' ';
+ if (defined($nb) && $nb == 0) { # deleted file
+ $rv .= $cb;
+ } else {
+ my $h = $req->{h};
+ $nb = defined $nb ? "#n$nb" : '';
+ $rv .= qq(<a\nrel=nofollow);
+ $rv .= qq(\nhref="${rel}tree/$req->{path_b}?id=$h$nb">);
+ $rv .= "$cb</a>";
+ }
+ $rv . ' @@' . utf8_html($ctx);
+}
+
+sub git_diffstat_rename ($$$) {
+ my ($req, $from, $to) = @_;
+ my $anchor = to_attr(git_unquote($to));
+ $req->{anchors}->{$anchor} = $to;
+ my @from = split('/', $from);
+ my @to = split('/', $to);
+ my $orig_to = $to;
+ my ($base, @base);
+ while (@to && @from && $to[0] eq $from[0]) {
+ push @base, shift(@to);
+ shift @from;
+ }
+
+ $base = utf8_html(join('/', @base)) if @base;
+ $from = utf8_html(join('/', @from));
+ $to = PublicInbox::Hval->utf8(join('/', @to), $orig_to);
+ my $tp = $to->as_path;
+ my $th = $to->as_html;
+ $to = qq(<a\nhref="#$anchor">$th</a>);
+ @base ? "$base/{$from => $to}" : "$from => $to";
+}
+
+sub git_diffstat_emit ($$$) {
+ my ($req, $fh, undef) = @_;
+ my @stat = split("\0", $_[2]); # avoiding copy for $_[2]
+ my $nr = 0;
+ my ($nadd, $ndel) = (0, 0);
+ my $s = '';
+ while (defined(my $l = shift @stat)) {
+ $l =~ s/\n?(\S+)\t+(\S+)\t+// or next;
+ my ($add, $del) = ($1, $2);
+ if ($add =~ /\A\d+\z/) {
+ $nadd += $add;
+ $ndel += $del;
+ $add = "+$add";
+ $del = "-$del";
+ }
+ my $num = sprintf('% 6s/%-6s', $del, $add);
+ if (length $l) {
+ my $anchor = to_attr(git_unquote($l));
+ $req->{anchors}->{$anchor} = $l;
+ $l = utf8_html($l);
+ $l = qq(<a\nhref="#$anchor">$l</a>);
+ } else {
+ my $from = shift @stat;
+ my $to = shift @stat;
+ $l = git_diffstat_rename($req, $from, $to);
+ }
+ ++$nr;
+ $s .= ' '.$num."\t".$l."\n";
+ }
+ $s .= "\n $nr ";
+ $s .= $nr == 1 ? 'file changed, ' : 'files changed, ';
+ $s .= $nadd;
+ $s .= $nadd == 1 ? ' insertion(+), ' : ' insertions(+), ';
+ $s .= $ndel;
+ $s .= $ndel == 1 ? " deletion(-)\n\n" : " deletions(-)\n\n";
+ $fh->write($s);
+}
+
+1;