759
extra_options => "opt",
760
search_use_regexp => "sr",
761
ctag => "by_tag",
762
+ diff_style => "ds",
763
# this must be last entry (for manipulation from JavaScript)
764
javascript => "js"
765
);
2318
return $line;
2319
}
2320
2320
-# format patch (diff) line (not to be used for diff headers)
2321
-sub format_diff_line {
2321
+# process patch (diff) line (not to be used for diff headers),
2322
+# returning class and HTML-formatted (but not wrapped) line
2323
+sub process_diff_line {
2324
my $line = shift;
2325
my ($from, $to) = @_;
2326
2327
my $diff_class = diff_line_class($line, $from, $to);
2326
- my $diff_classes = "diff";
2327
- $diff_classes .= " $diff_class" if ($diff_class);
2328
2329
chomp $line;
2330
$line = untabify($line);
2331
2332
if ($from && $to && $line =~ m/^\@{2} /) {
2333
$line = format_unidiff_chunk_header($line, $from, $to);
2334
- return "<div class=\"$diff_classes\">$line</div>\n";
2334
+ return $diff_class, $line;
2335
2336
} elsif ($from && $to && $line =~ m/^\@{3}/) {
2337
$line = format_cc_diff_chunk_header($line, $from, $to);
2338
- return "<div class=\"$diff_classes\">$line</div>\n";
2338
+ return $diff_class, $line;
2339
2340
}
2341
- return "<div class=\"$diff_classes\">" . esc_html($line, -nbsp=>1) . "</div>\n";
2341
+ return $diff_class, esc_html($line, -nbsp=>1);
2342
}
2343
2344
# Generates undef or something like "_snapshot_" or "snapshot (_tbz2_ _zip_)",
4860
print "</table>\n";
4861
}
4862
4863
+sub print_sidebyside_diff_chunk {
4864
+ my @chunk = @_;
4865
+ my (@ctx, @rem, @add);
4866
+
4867
+ return unless @chunk;
4868
+
4869
+ # incomplete last line might be among removed or added lines,
4870
+ # or both, or among context lines: find which
4871
+ for (my $i = 1; $i < @chunk; $i++) {
4872
+ if ($chunk[$i][0] eq 'incomplete') {
4873
+ $chunk[$i][0] = $chunk[$i-1][0];
4874
+ }
4875
+ }
4876
+
4877
+ # guardian
4878
+ push @chunk, ["", ""];
4879
+
4880
+ foreach my $line_info (@chunk) {
4881
+ my ($class, $line) = @$line_info;
4882
+
4883
+ # print chunk headers
4884
+ if ($class && $class eq 'chunk_header') {
4885
+ print $line;
4886
+ next;
4887
+ }
4888
+
4889
+ ## print from accumulator when type of class of lines change
4890
+ # empty contents block on start rem/add block, or end of chunk
4891
+ if (@ctx && (!$class || $class eq 'rem' || $class eq 'add')) {
4892
+ print join '',
4893
+ '<div class="chunk_block">',
4894
+ '<div class="old">',
4895
+ @ctx,
4896
+ '</div>',
4897
+ '<div class="new">',
4898
+ @ctx,
4899
+ '</div>',
4900
+ '</div>';
4901
+ @ctx = ();
4902
+ }
4903
+ # empty add/rem block on start context block, or end of chunk
4904
+ if ((@rem || @add) && (!$class || $class eq 'ctx')) {
4905
+ print join '',
4906
+ '<div class="chunk_block">',
4907
+ '<div class="old">',
4908
+ @rem,
4909
+ '</div>',
4910
+ '<div class="new">',
4911
+ @add,
4912
+ '</div>',
4913
+ '</div>';
4914
+ @rem = @add = ();
4915
+ }
4916
+
4917
+ ## adding lines to accumulator
4918
+ # guardian value
4919
+ last unless $line;
4920
+ # rem, add or change
4921
+ if ($class eq 'rem') {
4922
+ push @rem, $line;
4923
+ } elsif ($class eq 'add') {
4924
+ push @add, $line;
4925
+ }
4926
+ # context line
4927
+ if ($class eq 'ctx') {
4928
+ push @ctx, $line;
4929
+ }
4930
+ }
4931
+}
4932
+
4933
sub git_patchset_body {
4864
- my ($fd, $difftree, $hash, @hash_parents) = @_;
4934
+ my ($fd, $diff_style, $difftree, $hash, @hash_parents) = @_;
4935
my ($hash_parent) = $hash_parents[0];
4936
4937
my $is_combined = (@hash_parents > 1);
4941
my $diffinfo;
4942
my $to_name;
4943
my (%from, %to);
4944
+ my @chunk; # for side-by-side diff
4945
4946
print "<div class=\"patchset\">\n";
4947
5048
5049
next PATCH if ($patch_line =~ m/^diff /);
5050
4980
- print format_diff_line($patch_line, \%from, \%to);
5051
+ my ($class, $line) = process_diff_line($patch_line, \%from, \%to);
5052
+ my $diff_classes = "diff";
5053
+ $diff_classes .= " $class" if ($class);
5054
+ $line = "<div class=\"$diff_classes\">$line</div>\n";
5055
+
5056
+ if ($diff_style eq 'sidebyside' && !$is_combined) {
5057
+ if ($class eq 'chunk_header') {
5058
+ print_sidebyside_diff_chunk(@chunk);
5059
+ @chunk = ( [ $class, $line ] );
5060
+ } else {
5061
+ push @chunk, [ $class, $line ];
5062
+ }
5063
+ } else {
5064
+ # default 'inline' style and unknown styles
5065
+ print $line;
5066
+ }
5067
}
5068
5069
} continue {
5070
+ if (@chunk) {
5071
+ print_sidebyside_diff_chunk(@chunk);
5072
+ @chunk = ();
5073
+ }
5074
print "</div>\n"; # class="patch"
5075
}
5076
7066
7067
sub git_blobdiff {
7068
my $format = shift || 'html';
7069
+ my $diff_style = $input_params{'diff_style'} || 'inline';
7070
7071
my $fd;
7072
my @difftree;
7176
if ($format eq 'html') {
7177
print "<div class=\"page_body\">\n";
7178
7088
- git_patchset_body($fd, [ \%diffinfo ], $hash_base, $hash_parent_base);
7179
+ git_patchset_body($fd, $diff_style,
7180
+ [ \%diffinfo ], $hash_base, $hash_parent_base);
7181
close $fd;
7182
7183
print "</div>\n"; # class="page_body"
7205
sub git_commitdiff {
7206
my %params = @_;
7207
my $format = $params{-format} || 'html';
7208
+ my $diff_style = $input_params{'diff_style'} || 'inline';
7209
7210
my ($patch_max) = gitweb_get_feature('patches');
7211
if ($format eq 'patch') {
7409
$use_parents ? @{$co{'parents'}} : $hash_parent);
7410
print "<br/>\n";
7411
7319
- git_patchset_body($fd, \@difftree, $hash,
7412
+ git_patchset_body($fd, $diff_style,
7413
+ \@difftree, $hash,
7414
$use_parents ? @{$co{'parents'}} : $hash_parent);
7415
close $fd;
7416
print "</div>\n"; # class="page_body"