+ my $patch_idx = 0;
+ my $in_header = 0;
+ my $patch_found = 0;
+ my $diffinfo;
+
+ print "<div class=\"patchset\">\n";
+
+ LINE:
+ while (my $patch_line = <$fd>) {
+ chomp $patch_line;
+
+ if ($patch_line =~ m/^diff /) { # "git diff" header
+ # beginning of patch (in patchset)
+ if ($patch_found) {
+ # close previous patch
+ print "</div>\n"; # class="patch"
+ } else {
+ # first patch in patchset
+ $patch_found = 1;
+ }
+ print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n";
+
+ if (ref($difftree->[$patch_idx]) eq "HASH") {
+ $diffinfo = $difftree->[$patch_idx];
+ } else {
+ $diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]);
+ }
+ $patch_idx++;
+
+ # for now, no extended header, hence we skip empty patches
+ # companion to next LINE if $in_header;
+ if ($diffinfo->{'from_id'} eq $diffinfo->{'to_id'}) { # no change
+ $in_header = 1;
+ next LINE;
+ }
+
+ if ($diffinfo->{'status'} eq "A") { # added
+ print "<div class=\"diff_info\">" . file_type($diffinfo->{'to_mode'}) . ":" .
+ $cgi->a({-href => href(action=>"blob", hash_base=>$hash,
+ hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'file'})},
+ $diffinfo->{'to_id'}) . "(new)" .
+ "</div>\n"; # class="diff_info"
+
+ } elsif ($diffinfo->{'status'} eq "D") { # deleted
+ print "<div class=\"diff_info\">" . file_type($diffinfo->{'from_mode'}) . ":" .
+ $cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent,
+ hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'file'})},
+ $diffinfo->{'from_id'}) . "(deleted)" .
+ "</div>\n"; # class="diff_info"
+
+ } elsif ($diffinfo->{'status'} eq "R" || # renamed
+ $diffinfo->{'status'} eq "C" || # copied
+ $diffinfo->{'status'} eq "2") { # with two filenames (from git_blobdiff)
+ print "<div class=\"diff_info\">" .
+ file_type($diffinfo->{'from_mode'}) . ":" .
+ $cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent,
+ hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'from_file'})},
+ $diffinfo->{'from_id'}) .
+ " -> " .
+ file_type($diffinfo->{'to_mode'}) . ":" .
+ $cgi->a({-href => href(action=>"blob", hash_base=>$hash,
+ hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'to_file'})},
+ $diffinfo->{'to_id'});
+ print "</div>\n"; # class="diff_info"
+
+ } else { # modified, mode changed, ...
+ print "<div class=\"diff_info\">" .
+ file_type($diffinfo->{'from_mode'}) . ":" .
+ $cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent,
+ hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'file'})},
+ $diffinfo->{'from_id'}) .
+ " -> " .
+ file_type($diffinfo->{'to_mode'}) . ":" .
+ $cgi->a({-href => href(action=>"blob", hash_base=>$hash,
+ hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'file'})},
+ $diffinfo->{'to_id'});
+ print "</div>\n"; # class="diff_info"
+ }
+
+ #print "<div class=\"diff extended_header\">\n";
+ $in_header = 1;
+ next LINE;
+ } # start of patch in patchset
+
+
+ if ($in_header && $patch_line =~ m/^---/) {
+ #print "</div>\n"; # class="diff extended_header"
+ $in_header = 0;
+
+ my $file = $diffinfo->{'from_file'};
+ $file ||= $diffinfo->{'file'};
+ $file = $cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent,
+ hash=>$diffinfo->{'from_id'}, file_name=>$file),
+ -class => "list"}, esc_html($file));
+ $patch_line =~ s|a/.*$|a/$file|g;
+ print "<div class=\"diff from_file\">$patch_line</div>\n";
+
+ $patch_line = <$fd>;
+ chomp $patch_line;
+
+ #$patch_line =~ m/^+++/;
+ $file = $diffinfo->{'to_file'};
+ $file ||= $diffinfo->{'file'};
+ $file = $cgi->a({-href => href(action=>"blob", hash_base=>$hash,
+ hash=>$diffinfo->{'to_id'}, file_name=>$file),
+ -class => "list"}, esc_html($file));
+ $patch_line =~ s|b/.*|b/$file|g;
+ print "<div class=\"diff to_file\">$patch_line</div>\n";
+
+ next LINE;
+ }
+ next LINE if $in_header;
+
+ print format_diff_line($patch_line);
+ }
+ print "</div>\n" if $patch_found; # class="patch"
+
+ print "</div>\n"; # class="patchset"
+}
+
+# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+sub git_shortlog_body {
+ # uses global variable $project
+ my ($revlist, $from, $to, $refs, $extra) = @_;
+
+ my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
+ my $have_snapshot = (defined $ctype && defined $suffix);
+
+ $from = 0 unless defined $from;
+ $to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);
+
+ print "<table class=\"shortlog\" cellspacing=\"0\">\n";
+ my $alternate = 0;
+ for (my $i = $from; $i <= $to; $i++) {
+ my $commit = $revlist->[$i];
+ #my $ref = defined $refs ? format_ref_marker($refs, $commit) : '';
+ my $ref = format_ref_marker($refs, $commit);
+ my %co = parse_commit($commit);
+ if ($alternate) {
+ print "<tr class=\"dark\">\n";
+ } else {
+ print "<tr class=\"light\">\n";
+ }
+ $alternate ^= 1;
+ # git_summary() used print "<td><i>$co{'age_string'}</i></td>\n" .
+ print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+ "<td><i>" . esc_html(chop_str($co{'author_name'}, 10)) . "</i></td>\n" .
+ "<td>";
+ print format_subject_html($co{'title'}, $co{'title_short'},
+ href(action=>"commit", hash=>$commit), $ref);
+ print "</td>\n" .
+ "<td class=\"link\">" .
+ $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " .
+ $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff");
+ if ($have_snapshot) {
+ print " | " . $cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot");
+ }
+ print "</td>\n" .
+ "</tr>\n";
+ }
+ if (defined $extra) {