gitweb: Add combined diff support to git_patchset_body
Calling convention for combined diff similar to the one for git_difftree_body subroutine: difftree info (first parameter) must be result of calling git-diff-tree with -c/--cc option, and all parents of a commit must be passed as last parameters. See also description in "gitweb: Add combined diff support to git_difftree_body" This ability is not used yet. Generating "src" file name for renames in combined diff was separated into fill_from_file_info subroutine; git_difftree_body was modified to use it. Currently git_difftree_body and git_patchset_body fills this info separately. The from-file line in two-line from-file/to-file header is not hyperlinked: there can be more than one "from"/"src" file. This differs from HTML output of ordinary (not combined) diff. format_diff_line subroutine needs extra $from/$to parameters to format combined diff patch line correctly. Signed-off-by: Jakub Narebski <jnareb@gmail.com> Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
parent
ed224deac9
commit
e72c0eaf9b
@ -897,19 +897,34 @@ sub format_subject_html {
|
|||||||
sub format_diff_line {
|
sub format_diff_line {
|
||||||
my $line = shift;
|
my $line = shift;
|
||||||
my ($from, $to) = @_;
|
my ($from, $to) = @_;
|
||||||
my $char = substr($line, 0, 1);
|
|
||||||
my $diff_class = "";
|
my $diff_class = "";
|
||||||
|
|
||||||
chomp $line;
|
chomp $line;
|
||||||
|
|
||||||
if ($char eq '+') {
|
if ($from && $to && ref($from->{'href'}) eq "ARRAY") {
|
||||||
$diff_class = " add";
|
# combined diff
|
||||||
} elsif ($char eq "-") {
|
my $prefix = substr($line, 0, scalar @{$from->{'href'}});
|
||||||
$diff_class = " rem";
|
if ($line =~ m/^\@{3}/) {
|
||||||
} elsif ($char eq "@") {
|
$diff_class = " chunk_header";
|
||||||
$diff_class = " chunk_header";
|
} elsif ($line =~ m/^\\/) {
|
||||||
} elsif ($char eq "\\") {
|
$diff_class = " incomplete";
|
||||||
$diff_class = " incomplete";
|
} elsif ($prefix =~ tr/+/+/) {
|
||||||
|
$diff_class = " add";
|
||||||
|
} elsif ($prefix =~ tr/-/-/) {
|
||||||
|
$diff_class = " rem";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# assume ordinary diff
|
||||||
|
my $char = substr($line, 0, 1);
|
||||||
|
if ($char eq '+') {
|
||||||
|
$diff_class = " add";
|
||||||
|
} elsif ($char eq '-') {
|
||||||
|
$diff_class = " rem";
|
||||||
|
} elsif ($char eq '@') {
|
||||||
|
$diff_class = " chunk_header";
|
||||||
|
} elsif ($char eq "\\") {
|
||||||
|
$diff_class = " incomplete";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$line = untabify($line);
|
$line = untabify($line);
|
||||||
if ($from && $to && $line =~ m/^\@{2} /) {
|
if ($from && $to && $line =~ m/^\@{2} /) {
|
||||||
@ -930,6 +945,39 @@ sub format_diff_line {
|
|||||||
$line = "<span class=\"chunk_info\">@@ $from_text $to_text @@</span>" .
|
$line = "<span class=\"chunk_info\">@@ $from_text $to_text @@</span>" .
|
||||||
"<span class=\"section\">" . esc_html($section, -nbsp=>1) . "</span>";
|
"<span class=\"section\">" . esc_html($section, -nbsp=>1) . "</span>";
|
||||||
return "<div class=\"diff$diff_class\">$line</div>\n";
|
return "<div class=\"diff$diff_class\">$line</div>\n";
|
||||||
|
} elsif ($from && $to && $line =~ m/^\@{3}/) {
|
||||||
|
my ($prefix, $ranges, $section) = $line =~ m/^(\@+) (.*?) \@+(.*)$/;
|
||||||
|
my (@from_text, @from_start, @from_nlines, $to_text, $to_start, $to_nlines);
|
||||||
|
|
||||||
|
@from_text = split(' ', $ranges);
|
||||||
|
for (my $i = 0; $i < @from_text; ++$i) {
|
||||||
|
($from_start[$i], $from_nlines[$i]) =
|
||||||
|
(split(',', substr($from_text[$i], 1)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$to_text = pop @from_text;
|
||||||
|
$to_start = pop @from_start;
|
||||||
|
$to_nlines = pop @from_nlines;
|
||||||
|
|
||||||
|
$line = "<span class=\"chunk_info\">$prefix ";
|
||||||
|
for (my $i = 0; $i < @from_text; ++$i) {
|
||||||
|
if ($from->{'href'}[$i]) {
|
||||||
|
$line .= $cgi->a({-href=>"$from->{'href'}[$i]#l$from_start[$i]",
|
||||||
|
-class=>"list"}, $from_text[$i]);
|
||||||
|
} else {
|
||||||
|
$line .= $from_text[$i];
|
||||||
|
}
|
||||||
|
$line .= " ";
|
||||||
|
}
|
||||||
|
if ($to->{'href'}) {
|
||||||
|
$line .= $cgi->a({-href=>"$to->{'href'}#l$to_start",
|
||||||
|
-class=>"list"}, $to_text);
|
||||||
|
} else {
|
||||||
|
$line .= $to_text;
|
||||||
|
}
|
||||||
|
$line .= " $prefix</span>" .
|
||||||
|
"<span class=\"section\">" . esc_html($section, -nbsp=>1) . "</span>";
|
||||||
|
return "<div class=\"diff$diff_class\">$line</div>\n";
|
||||||
}
|
}
|
||||||
return "<div class=\"diff$diff_class\">" . esc_html($line, -nbsp=>1) . "</div>\n";
|
return "<div class=\"diff$diff_class\">" . esc_html($line, -nbsp=>1) . "</div>\n";
|
||||||
}
|
}
|
||||||
@ -2233,6 +2281,39 @@ sub git_print_tree_entry {
|
|||||||
## ......................................................................
|
## ......................................................................
|
||||||
## functions printing large fragments of HTML
|
## functions printing large fragments of HTML
|
||||||
|
|
||||||
|
sub fill_from_file_info {
|
||||||
|
my ($diff, @parents) = @_;
|
||||||
|
|
||||||
|
$diff->{'from_file'} = [ ];
|
||||||
|
$diff->{'from_file'}[$diff->{'nparents'} - 1] = undef;
|
||||||
|
for (my $i = 0; $i < $diff->{'nparents'}; $i++) {
|
||||||
|
if ($diff->{'status'}[$i] eq 'R' ||
|
||||||
|
$diff->{'status'}[$i] eq 'C') {
|
||||||
|
$diff->{'from_file'}[$i] =
|
||||||
|
git_get_path_by_hash($parents[$i], $diff->{'from_id'}[$i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
# parameters can be strings, or references to arrays of strings
|
||||||
|
sub from_ids_eq {
|
||||||
|
my ($a, $b) = @_;
|
||||||
|
|
||||||
|
if (ref($a) eq "ARRAY" && ref($b) eq "ARRAY" && @$a == @$b) {
|
||||||
|
for (my $i = 0; $i < @$a; ++$i) {
|
||||||
|
return 0 unless ($a->[$i] eq $b->[$i]);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
} elsif (!ref($a) && !ref($b)) {
|
||||||
|
return $a eq $b;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sub git_difftree_body {
|
sub git_difftree_body {
|
||||||
my ($difftree, $hash, @parents) = @_;
|
my ($difftree, $hash, @parents) = @_;
|
||||||
my ($parent) = $parents[0];
|
my ($parent) = $parents[0];
|
||||||
@ -2260,6 +2341,8 @@ sub git_difftree_body {
|
|||||||
|
|
||||||
if (exists $diff{'nparents'}) { # combined diff
|
if (exists $diff{'nparents'}) { # combined diff
|
||||||
|
|
||||||
|
fill_from_file_info(\%diff, @parents);
|
||||||
|
|
||||||
if ($diff{'to_id'} ne ('0' x 40)) {
|
if ($diff{'to_id'} ne ('0' x 40)) {
|
||||||
# file exists in the result (child) commit
|
# file exists in the result (child) commit
|
||||||
print "<td>" .
|
print "<td>" .
|
||||||
@ -2288,16 +2371,12 @@ sub git_difftree_body {
|
|||||||
for (my $i = 0; $i < $diff{'nparents'}; $i++) {
|
for (my $i = 0; $i < $diff{'nparents'}; $i++) {
|
||||||
my $hash_parent = $parents[$i];
|
my $hash_parent = $parents[$i];
|
||||||
my $from_hash = $diff{'from_id'}[$i];
|
my $from_hash = $diff{'from_id'}[$i];
|
||||||
my $from_path = undef;
|
my $from_path = $diff{'from_file'}[$i];
|
||||||
my $status = $diff{'status'}[$i];
|
my $status = $diff{'status'}[$i];
|
||||||
|
|
||||||
$has_history ||= ($status ne 'A');
|
$has_history ||= ($status ne 'A');
|
||||||
$not_deleted ||= ($status ne 'D');
|
$not_deleted ||= ($status ne 'D');
|
||||||
|
|
||||||
if ($status eq 'R' || $status eq 'C') {
|
|
||||||
$from_path = git_get_path_by_hash($hash_parent, $from_hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($status eq 'A') {
|
if ($status eq 'A') {
|
||||||
print "<td class=\"link\" align=\"right\"> | </td>\n";
|
print "<td class=\"link\" align=\"right\"> | </td>\n";
|
||||||
} elsif ($status eq 'D') {
|
} elsif ($status eq 'D') {
|
||||||
@ -2517,7 +2596,8 @@ sub git_difftree_body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub git_patchset_body {
|
sub git_patchset_body {
|
||||||
my ($fd, $difftree, $hash, $hash_parent) = @_;
|
my ($fd, $difftree, $hash, @hash_parents) = @_;
|
||||||
|
my ($hash_parent) = $hash_parents[0];
|
||||||
|
|
||||||
my $patch_idx = 0;
|
my $patch_idx = 0;
|
||||||
my $patch_number = 0;
|
my $patch_number = 0;
|
||||||
@ -2555,6 +2635,9 @@ sub git_patchset_body {
|
|||||||
if ($patch_line =~ m/^index ([0-9a-fA-F]{40})..([0-9a-fA-F]{40})/) {
|
if ($patch_line =~ m/^index ([0-9a-fA-F]{40})..([0-9a-fA-F]{40})/) {
|
||||||
$from_id = $1;
|
$from_id = $1;
|
||||||
$to_id = $2;
|
$to_id = $2;
|
||||||
|
} elsif ($patch_line =~ m/^index ((?:[0-9a-fA-F]{40},)+[0-9a-fA-F]{40})..([0-9a-fA-F]{40})/) {
|
||||||
|
$from_id = [ split(',', $1) ];
|
||||||
|
$to_id = $2;
|
||||||
}
|
}
|
||||||
|
|
||||||
push @diff_header, $patch_line;
|
push @diff_header, $patch_line;
|
||||||
@ -2564,8 +2647,8 @@ sub git_patchset_body {
|
|||||||
# check if current patch belong to current raw line
|
# check if current patch belong to current raw line
|
||||||
# and parse raw git-diff line if needed
|
# and parse raw git-diff line if needed
|
||||||
if (defined $diffinfo &&
|
if (defined $diffinfo &&
|
||||||
$diffinfo->{'from_id'} eq $from_id &&
|
from_ids_eq($diffinfo->{'from_id'}, $from_id) &&
|
||||||
$diffinfo->{'to_id'} eq $to_id) {
|
$diffinfo->{'to_id'} eq $to_id) {
|
||||||
# this is split patch
|
# this is split patch
|
||||||
print "<div class=\"patch cont\">\n";
|
print "<div class=\"patch cont\">\n";
|
||||||
} else {
|
} else {
|
||||||
@ -2579,15 +2662,34 @@ sub git_patchset_body {
|
|||||||
} else {
|
} else {
|
||||||
$diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]);
|
$diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]);
|
||||||
}
|
}
|
||||||
$from{'file'} = $diffinfo->{'from_file'} || $diffinfo->{'file'};
|
if ($diffinfo->{'nparents'}) {
|
||||||
$to{'file'} = $diffinfo->{'to_file'} || $diffinfo->{'file'};
|
# combined diff
|
||||||
if ($diffinfo->{'status'} ne "A") { # not new (added) file
|
$from{'file'} = [];
|
||||||
$from{'href'} = href(action=>"blob", hash_base=>$hash_parent,
|
$from{'href'} = [];
|
||||||
hash=>$diffinfo->{'from_id'},
|
fill_from_file_info($diffinfo, @hash_parents)
|
||||||
file_name=>$from{'file'});
|
unless exists $diffinfo->{'from_file'};
|
||||||
|
for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) {
|
||||||
|
$from{'file'}[$i] = $diffinfo->{'from_file'}[$i] || $diffinfo->{'to_file'};
|
||||||
|
if ($diffinfo->{'status'}[$i] ne "A") { # not new (added) file
|
||||||
|
$from{'href'}[$i] = href(action=>"blob",
|
||||||
|
hash_base=>$hash_parents[$i],
|
||||||
|
hash=>$diffinfo->{'from_id'}[$i],
|
||||||
|
file_name=>$from{'file'}[$i]);
|
||||||
|
} else {
|
||||||
|
$from{'href'}[$i] = undef;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
delete $from{'href'};
|
$from{'file'} = $diffinfo->{'from_file'} || $diffinfo->{'file'};
|
||||||
|
if ($diffinfo->{'status'} ne "A") { # not new (added) file
|
||||||
|
$from{'href'} = href(action=>"blob", hash_base=>$hash_parent,
|
||||||
|
hash=>$diffinfo->{'from_id'},
|
||||||
|
file_name=>$from{'file'});
|
||||||
|
} else {
|
||||||
|
delete $from{'href'};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
$to{'file'} = $diffinfo->{'to_file'} || $diffinfo->{'file'};
|
||||||
if ($diffinfo->{'status'} ne "D") { # not deleted file
|
if ($diffinfo->{'status'} ne "D") { # not deleted file
|
||||||
$to{'href'} = href(action=>"blob", hash_base=>$hash,
|
$to{'href'} = href(action=>"blob", hash_base=>$hash,
|
||||||
hash=>$diffinfo->{'to_id'},
|
hash=>$diffinfo->{'to_id'},
|
||||||
@ -2602,19 +2704,34 @@ sub git_patchset_body {
|
|||||||
|
|
||||||
# print "git diff" header
|
# print "git diff" header
|
||||||
$patch_line = shift @diff_header;
|
$patch_line = shift @diff_header;
|
||||||
$patch_line =~ s!^(diff (.*?) )"?a/.*$!$1!;
|
if ($diffinfo->{'nparents'}) {
|
||||||
if ($from{'href'}) {
|
|
||||||
$patch_line .= $cgi->a({-href => $from{'href'}, -class => "path"},
|
# combined diff
|
||||||
'a/' . esc_path($from{'file'}));
|
$patch_line =~ s!^(diff (.*?) )"?.*$!$1!;
|
||||||
} else { # file was added
|
if ($to{'href'}) {
|
||||||
$patch_line .= 'a/' . esc_path($from{'file'});
|
$patch_line .= $cgi->a({-href => $to{'href'}, -class => "path"},
|
||||||
}
|
esc_path($to{'file'}));
|
||||||
$patch_line .= ' ';
|
} else { # file was deleted
|
||||||
if ($to{'href'}) {
|
$patch_line .= esc_path($to{'file'});
|
||||||
$patch_line .= $cgi->a({-href => $to{'href'}, -class => "path"},
|
}
|
||||||
'b/' . esc_path($to{'file'}));
|
|
||||||
} else { # file was deleted
|
} else {
|
||||||
$patch_line .= 'b/' . esc_path($to{'file'});
|
|
||||||
|
$patch_line =~ s!^(diff (.*?) )"?a/.*$!$1!;
|
||||||
|
if ($from{'href'}) {
|
||||||
|
$patch_line .= $cgi->a({-href => $from{'href'}, -class => "path"},
|
||||||
|
'a/' . esc_path($from{'file'}));
|
||||||
|
} else { # file was added
|
||||||
|
$patch_line .= 'a/' . esc_path($from{'file'});
|
||||||
|
}
|
||||||
|
$patch_line .= ' ';
|
||||||
|
if ($to{'href'}) {
|
||||||
|
$patch_line .= $cgi->a({-href => $to{'href'}, -class => "path"},
|
||||||
|
'b/' . esc_path($to{'file'}));
|
||||||
|
} else { # file was deleted
|
||||||
|
$patch_line .= 'b/' . esc_path($to{'file'});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
print "<div class=\"diff header\">$patch_line</div>\n";
|
print "<div class=\"diff header\">$patch_line</div>\n";
|
||||||
|
|
||||||
@ -2631,14 +2748,37 @@ sub git_patchset_body {
|
|||||||
$patch_line .= $cgi->a({-href=>$to{'href'}, -class=>"path"},
|
$patch_line .= $cgi->a({-href=>$to{'href'}, -class=>"path"},
|
||||||
esc_path($to{'file'}));
|
esc_path($to{'file'}));
|
||||||
}
|
}
|
||||||
# match <mode>
|
# match single <mode>
|
||||||
if ($patch_line =~ m/\s(\d{6})$/) {
|
if ($patch_line =~ m/\s(\d{6})$/) {
|
||||||
$patch_line .= '<span class="info"> (' .
|
$patch_line .= '<span class="info"> (' .
|
||||||
file_type_long($1) .
|
file_type_long($1) .
|
||||||
')</span>';
|
')</span>';
|
||||||
}
|
}
|
||||||
# match <hash>
|
# match <hash>
|
||||||
if ($patch_line =~ m/^index/) {
|
if ($patch_line =~ m/^index [0-9a-fA-F]{40},[0-9a-fA-F]{40}/) {
|
||||||
|
# can match only for combined diff
|
||||||
|
$patch_line = 'index ';
|
||||||
|
for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) {
|
||||||
|
if ($from{'href'}[$i]) {
|
||||||
|
$patch_line .= $cgi->a({-href=>$from{'href'}[$i],
|
||||||
|
-class=>"hash"},
|
||||||
|
substr($diffinfo->{'from_id'}[$i],0,7));
|
||||||
|
} else {
|
||||||
|
$patch_line .= '0' x 7;
|
||||||
|
}
|
||||||
|
# separator
|
||||||
|
$patch_line .= ',' if ($i < $diffinfo->{'nparents'} - 1);
|
||||||
|
}
|
||||||
|
$patch_line .= '..';
|
||||||
|
if ($to{'href'}) {
|
||||||
|
$patch_line .= $cgi->a({-href=>$to{'href'}, -class=>"hash"},
|
||||||
|
substr($diffinfo->{'to_id'},0,7));
|
||||||
|
} else {
|
||||||
|
$patch_line .= '0' x 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
} elsif ($patch_line =~ m/^index [0-9a-fA-F]{40}..[0-9a-fA-F]{40}/) {
|
||||||
|
# can match only for ordinary diff
|
||||||
my ($from_link, $to_link);
|
my ($from_link, $to_link);
|
||||||
if ($from{'href'}) {
|
if ($from{'href'}) {
|
||||||
$from_link = $cgi->a({-href=>$from{'href'}, -class=>"hash"},
|
$from_link = $cgi->a({-href=>$from{'href'}, -class=>"hash"},
|
||||||
@ -2674,7 +2814,8 @@ sub git_patchset_body {
|
|||||||
}
|
}
|
||||||
next PATCH if ($patch_line =~ m/^diff /);
|
next PATCH if ($patch_line =~ m/^diff /);
|
||||||
#assert($patch_line =~ m/^---/) if DEBUG;
|
#assert($patch_line =~ m/^---/) if DEBUG;
|
||||||
if ($from{'href'} && $patch_line =~ m!^--- "?a/!) {
|
if (!$diffinfo->{'nparents'} && # not from-file line for combined diff
|
||||||
|
$from{'href'} && $patch_line =~ m!^--- "?a/!) {
|
||||||
$patch_line = '--- a/' .
|
$patch_line = '--- a/' .
|
||||||
$cgi->a({-href=>$from{'href'}, -class=>"path"},
|
$cgi->a({-href=>$from{'href'}, -class=>"path"},
|
||||||
esc_path($from{'file'}));
|
esc_path($from{'file'}));
|
||||||
|
Loading…
Reference in New Issue
Block a user