X-Git-Url: https://git.ladys.computer/Gitweb/blobdiff_plain/b431c2da9de0f1a5f2f57ebd90e34020fa7b095130d73cbe60cbe57d73cfcc4e..9c9d435689da11f7e28170a9499e203e9cce56bb61c0846b9adb4e62091435f4:/gitweb.perl diff --git a/gitweb.perl b/gitweb.perl index 9a0859b..b063714 100755 --- a/gitweb.perl +++ b/gitweb.perl @@ -69,22 +69,30 @@ our $mimetypes_file = undef; # You define site-wide feature defaults here; override them with # $GITWEB_CONFIG as necessary. -our %feature = -( - -# feature => {'sub' => feature-sub, 'override' => allow-override, 'default' => [ default options...] - -'blame' => {'sub' => \&feature_blame, 'override' => 0, 'default' => [0]}, -'snapshot' => {'sub' => \&feature_snapshot, 'override' => 0, 'default' => ['x-gzip', 'gz', 'gzip']}, - +our %feature = ( + # feature => {'sub' => feature-sub, 'override' => allow-override, 'default' => [ default options...] + # if feature is overridable, feature-sub will be called with default options; + # return value indicates if to enable specified feature + + 'blame' => { + 'sub' => \&feature_blame, + 'override' => 0, + 'default' => [0]}, + + 'snapshot' => { + 'sub' => \&feature_snapshot, + 'override' => 0, + # => [content-encoding, suffix, program] + 'default' => ['x-gzip', 'gz', 'gzip']}, ); sub gitweb_check_feature { my ($name) = @_; return undef unless exists $feature{$name}; - my ($sub, $override, @defaults) = ($feature{$name}{'sub'}, - $feature{$name}{'override'}, - @{$feature{$name}{'default'}}); + my ($sub, $override, @defaults) = ( + $feature{$name}{'sub'}, + $feature{$name}{'override'}, + @{$feature{$name}{'default'}}); if (!$override) { return @defaults; } return $sub->(@defaults); } @@ -147,11 +155,6 @@ if (defined $action) { if ($action =~ m/[^0-9a-zA-Z\.\-_]/) { die_error(undef, "Invalid action parameter"); } - # action which does not check rest of parameters - if ($action eq "opml") { - git_opml(); - exit; - } } our $project = ($cgi->param('p') || $ENV{'PATH_INFO'}); @@ -171,9 +174,6 @@ if (defined $project) { die_error(undef, "No such project"); } $ENV{'GIT_DIR'} = "$projectroot/$project"; -} else { - git_project_list(); - exit; } our $file_name = $cgi->param('f'); @@ -247,9 +247,16 @@ my %actions = ( "tags" => \&git_tags, "tree" => \&git_tree, "snapshot" => \&git_snapshot, + # those below don't need $project + "opml" => \&git_opml, + "project_list" => \&git_project_list, ); -$action = 'summary' if (!defined($action)); +if (defined $project) { + $action ||= 'summary'; +} else { + $action ||= 'project_list'; +} if (!defined($actions{$action})) { die_error(undef, "Unknown action"); } @@ -260,7 +267,9 @@ exit; ## action links sub href(%) { - my %mapping = ( + my %params = @_; + + my @mapping = ( action => "a", project => "p", file_name => "f", @@ -271,18 +280,18 @@ sub href(%) { page => "pg", searchtext => "s", ); + my %mapping = @mapping; - my %params = @_; $params{"project"} ||= $project; - my $href = "$my_uri?"; - $href .= esc_param( join(";", - map { - "$mapping{$_}=$params{$_}" if defined $params{$_} - } keys %params - ) ); - - return $href; + my @result = (); + for (my $i = 0; $i < @mapping; $i += 2) { + my ($name, $symbol) = ($mapping[$i], $mapping[$i+1]); + if (defined $params{$name}) { + push @result, $symbol . "=" . esc_param($params{$name}); + } + } + return "$my_uri?" . join(';', @result); } @@ -463,7 +472,9 @@ sub format_log_line_html { if ($line =~ m/([0-9a-fA-F]{40})/) { my $hash_text = $1; if (git_get_type($hash_text) eq "commit") { - my $link = $cgi->a({-class => "text", -href => href(action=>"commit", hash=>$hash_text)}, $hash_text); + my $link = + $cgi->a({-href => href(action=>"commit", hash=>$hash_text), + -class => "text"}, $hash_text); $line =~ s/$hash_text/$link/; } } @@ -504,15 +515,35 @@ sub format_subject_html { $extra = '' unless defined($extra); if (length($short) < length($long)) { - return $cgi->a({-href => $href, -class => "list", + return $cgi->a({-href => $href, -class => "list subject", -title => $long}, esc_html($short) . $extra); } else { - return $cgi->a({-href => $href, -class => "list"}, + return $cgi->a({-href => $href, -class => "list subject"}, esc_html($long) . $extra); } } +sub format_diff_line { + my $line = shift; + my $char = substr($line, 0, 1); + my $diff_class = ""; + + chomp $line; + + 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); + return "
" . esc_html($line) . "
\n"; +} + ## ---------------------------------------------------------------------- ## git utility subroutines, invoking git commands @@ -718,6 +749,73 @@ sub git_get_references { return \%refs; } +sub git_get_following_references { + my $hash = shift || return undef; + my $type = shift; + my $base = shift || $hash_base || "HEAD"; + + my $refs = git_get_references($type); + open my $fd, "-|", $GIT, "rev-list", $base + or return undef; + my @commits = map { chomp; $_ } <$fd>; + close $fd + or return undef; + + my @reflist; + my $lastref; + + foreach my $commit (@commits) { + foreach my $ref (@{$refs->{$commit}}) { + $lastref = $ref; + push @reflist, $lastref; + } + if ($commit eq $hash) { + last; + } + } + + return wantarray ? @reflist : $lastref; +} + +sub git_get_preceding_references { + my $hash = shift || return undef; + my $type = shift; + + my $refs = git_get_references($type); + open my $fd, "-|", $GIT, "rev-list", $hash + or return undef; + my @commits = map { chomp; $_ } <$fd>; + close $fd + or return undef; + + my @reflist; + + foreach my $commit (@commits) { + foreach my $ref (@{$refs->{$commit}}) { + return $ref unless wantarray; + push @reflist, $ref; + } + } + + return @reflist; +} + +sub git_get_rev_name_tags { + my $hash = shift || return undef; + + open my $fd, "-|", $GIT, "name-rev", "--tags", $hash + or return; + my $name_rev = <$fd>; + close $fd; + + if ($name_rev =~ m|^$hash tags/(.*)$|) { + return $1; + } else { + # catches also '$hash undefined' output + return undef; + } +} + ## ---------------------------------------------------------------------- ## parse to hash functions @@ -734,8 +832,10 @@ sub parse_date { $date{'mday'} = $mday; $date{'day'} = $days[$wday]; $date{'month'} = $months[$mon]; - $date{'rfc2822'} = sprintf "%s, %d %s %4d %02d:%02d:%02d +0000", $days[$wday], $mday, $months[$mon], 1900+$year, $hour ,$min, $sec; - $date{'mday-time'} = sprintf "%d %s %02d:%02d", $mday, $months[$mon], $hour ,$min; + $date{'rfc2822'} = sprintf "%s, %d %s %4d %02d:%02d:%02d +0000", + $days[$wday], $mday, $months[$mon], 1900+$year, $hour ,$min, $sec; + $date{'mday-time'} = sprintf "%d %s %02d:%02d", + $mday, $months[$mon], $hour ,$min; $tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/; my $local = $epoch + ((int $1 + ($2/60)) * 3600); @@ -792,7 +892,8 @@ sub parse_commit { @commit_lines = @$commit_text; } else { $/ = "\0"; - open my $fd, "-|", $GIT, "rev-list", "--header", "--parents", "--max-count=1", $commit_id or return; + open my $fd, "-|", $GIT, "rev-list", "--header", "--parents", "--max-count=1", $commit_id + or return; @commit_lines = split '\n', <$fd>; close $fd or return; $/ = "\n"; @@ -918,6 +1019,7 @@ sub parse_ref { return %ref_item; } +# parse line of git-diff-tree "raw" output sub parse_difftree_raw_line { my $line = shift; my %res; @@ -932,7 +1034,7 @@ sub parse_difftree_raw_line { $res{'status'} = $5; $res{'similarity'} = $6; if ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied - ($res{'from_file'}, $res{'to_file'}) = map(unquote, split("\t", $7)); + ($res{'from_file'}, $res{'to_file'}) = map { unquote($_) } split("\t", $7); } else { $res{'file'} = unquote($7); } @@ -1085,12 +1187,15 @@ sub git_header_html { # 'application/xhtml+xml', otherwise send it as plain old 'text/html'. # we have to do this because MSIE sometimes globs '*/*', pretending to # support xhtml+xml but choking when it gets what it asked for. - if (defined $cgi->http('HTTP_ACCEPT') && $cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ && $cgi->Accept('application/xhtml+xml') != 0) { + if (defined $cgi->http('HTTP_ACCEPT') && + $cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ && + $cgi->Accept('application/xhtml+xml') != 0) { $content_type = 'application/xhtml+xml'; } else { $content_type = 'text/html'; } - print $cgi->header(-type=>$content_type, -charset => 'utf-8', -status=> $status, -expires => $expires); + print $cgi->header(-type=>$content_type, -charset => 'utf-8', + -status=> $status, -expires => $expires); print < @@ -1169,11 +1274,13 @@ sub die_error { my $error = shift || "Malformed query, file missing or permission denied"; git_header_html($status); - print "
\n" . - "

\n" . - "$status - $error\n" . - "
\n" . - "
\n"; + print < +

+$status - $error +
+ +EOF git_footer_html(); exit; } @@ -1266,17 +1373,20 @@ sub git_print_page_path { my $hb = shift; if (!defined $name) { - print "
/
\n"; + print "
/
\n"; } elsif (defined $type && $type eq 'blob') { - print "
"; + print "
"; if (defined $hb) { - print $cgi->a({-href => href(action=>"blob_plain", hash_base=>$hb, file_name=>$file_name)}, esc_html($name)); + print $cgi->a({-href => href(action=>"blob_plain", file_name=>$file_name, + hash_base=>$hb)}, + esc_html($name)); } else { - print $cgi->a({-href => href(action=>"blob_plain", file_name=>$file_name)}, esc_html($name)); + print $cgi->a({-href => href(action=>"blob_plain", file_name=>$file_name)}, + esc_html($name)); } - print "
\n"; + print "
\n"; } else { - print "
" . esc_html($name) . "
\n"; + print "
" . esc_html($name) . "
\n"; } } @@ -1344,7 +1454,7 @@ sub git_print_simplified_log { ## functions printing large fragments of HTML sub git_difftree_body { - my ($difftree, $parent) = @_; + my ($difftree, $hash, $parent) = @_; print "
\n"; if ($#{$difftree} > 10) { @@ -1355,18 +1465,7 @@ sub git_difftree_body { print "\n"; my $alternate = 0; foreach my $line (@{$difftree}) { - # ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M ls-files.c' - # ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M rev-tree.c' - if ($line !~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/) { - next; - } - my $from_mode = $1; - my $to_mode = $2; - my $from_id = $3; - my $to_id = $4; - my $status = $5; - my $similarity = $6; # score - my $file = validate_input(unquote($7)); + my %diff = parse_difftree_raw_line($line); if ($alternate) { print "\n"; @@ -1375,111 +1474,234 @@ sub git_difftree_body { } $alternate ^= 1; - if ($status eq "A") { # created - my $mode_chng = ""; - if (S_ISREG(oct $to_mode)) { - $mode_chng = sprintf(" with mode: %04o", (oct $to_mode) & 0777); + my ($to_mode_oct, $to_mode_str, $to_file_type); + my ($from_mode_oct, $from_mode_str, $from_file_type); + if ($diff{'to_mode'} ne ('0' x 6)) { + $to_mode_oct = oct $diff{'to_mode'}; + if (S_ISREG($to_mode_oct)) { # only for regular file + $to_mode_str = sprintf("%04o", $to_mode_oct & 0777); # permission bits } + $to_file_type = file_type($diff{'to_mode'}); + } + if ($diff{'from_mode'} ne ('0' x 6)) { + $from_mode_oct = oct $diff{'from_mode'}; + if (S_ISREG($to_mode_oct)) { # only for regular file + $from_mode_str = sprintf("%04o", $from_mode_oct & 0777); # permission bits + } + $from_file_type = file_type($diff{'from_mode'}); + } + + if ($diff{'status'} eq "A") { # created + my $mode_chng = "[new $to_file_type"; + $mode_chng .= " with mode: $to_mode_str" if $to_mode_str; + $mode_chng .= "]"; print "\n" . - "\n" . + "\n" . "\n"; - } elsif ($status eq "D") { # deleted + } elsif ($diff{'status'} eq "D") { # deleted + my $mode_chng = "[deleted $from_file_type]"; print "\n" . - "\n" . + $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'}, + hash_base=>$parent, file_name=>$diff{'file'}), + -class => "list"}, esc_html($diff{'file'})) . + "\n" . + "\n" . "\n" + $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'}, + hash_base=>$parent, file_name=>$diff{'file'})}, + "blob") . + " | " . + $cgi->a({-href => href(action=>"history", hash_base=>$parent, + file_name=>$diff{'file'})},\ + "history") . + "\n"; - } elsif ($status eq "M" || $status eq "T") { # modified, or type changed + } elsif ($diff{'status'} eq "M" || $diff{'status'} eq "T") { # modified, or type changed my $mode_chnge = ""; - if ($from_mode != $to_mode) { - $mode_chnge = " [changed"; - if (((oct $from_mode) & S_IFMT) != ((oct $to_mode) & S_IFMT)) { - $mode_chnge .= " from " . file_type($from_mode) . " to " . file_type($to_mode); + if ($diff{'from_mode'} != $diff{'to_mode'}) { + $mode_chnge = "[changed"; + if ($from_file_type != $to_file_type) { + $mode_chnge .= " from $from_file_type to $to_file_type"; } - if (((oct $from_mode) & 0777) != ((oct $to_mode) & 0777)) { - if (S_ISREG($from_mode) && S_ISREG($to_mode)) { - $mode_chnge .= sprintf(" mode: %04o->%04o", (oct $from_mode) & 0777, (oct $to_mode) & 0777); - } elsif (S_ISREG($to_mode)) { - $mode_chnge .= sprintf(" mode: %04o", (oct $to_mode) & 0777); + if (($from_mode_oct & 0777) != ($to_mode_oct & 0777)) { + if ($from_mode_str && $to_mode_str) { + $mode_chnge .= " mode: $from_mode_str->$to_mode_str"; + } elsif ($to_mode_str) { + $mode_chnge .= " mode: $to_mode_str"; } } $mode_chnge .= "]\n"; } print "\n" . "\n" . "\n"; - - } elsif ($status eq "R") { # renamed - my ($from_file, $to_file) = split "\t", $file; - my $mode_chng = ""; - if ($from_mode != $to_mode) { - $mode_chng = sprintf(", mode: %04o", (oct $to_mode) & 0777); - } - print "\n" . - "\n" . - "\n"; - } elsif ($status eq "C") { # copied - my ($from_file, $to_file) = split "\t", $file; + } elsif ($diff{'status'} eq "R" || $diff{'status'} eq "C") { # renamed or copied + my %status_name = ('R' => 'moved', 'C' => 'copied'); + my $nstatus = $status_name{$diff{'status'}}; my $mode_chng = ""; - if ($from_mode != $to_mode) { - $mode_chng = sprintf(", mode: %04o", (oct $to_mode) & 0777); + if ($diff{'from_mode'} != $diff{'to_mode'}) { + # mode also for directories, so we cannot use $to_mode_str + $mode_chng = sprintf(", mode: %04o", $to_mode_oct & 0777); } print "\n" . - "\n" . + $cgi->a({-href => href(action=>"blob", hash_base=>$hash, + hash=>$diff{'to_id'}, file_name=>$diff{'to_file'}), + -class => "list"}, esc_html($diff{'to_file'})) . "\n" . + "\n" . "\n"; + } # we should not encounter Unmerged (U) or Unknown (X) status print "\n"; } print "
" . - $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$file), - -class => "list"}, esc_html($file)) . + $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'}, + hash_base=>$hash, file_name=>$diff{'file'}), + -class => "list"}, esc_html($diff{'file'})) . "[new " . file_type($to_mode) . "$mode_chng]$mode_chng" . - $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$file)}, "blob") . + $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'}, + hash_base=>$hash, file_name=>$diff{'file'})}, + "blob") . "" . - $cgi->a({-href => href(action=>"blob", hash=>$from_id, hash_base=>$parent, file_name=>$file), - -class => "list"}, esc_html($file)) . "[deleted " . file_type($from_mode). "]$mode_chng" . - $cgi->a({-href => href(action=>"blob", hash=>$from_id, hash_base=>$parent, file_name=>$file)}, "blob") . " | " . - $cgi->a({-href => href(action=>"history", hash_base=>$parent, file_name=>$file)}, "history") . - ""; - if ($to_id ne $from_id) { # modified - print $cgi->a({-href => href(action=>"blobdiff", hash=>$to_id, hash_parent=>$from_id, hash_base=>$hash, file_name=>$file), - -class => "list"}, esc_html($file)); - } else { # mode changed - print $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$file), - -class => "list"}, esc_html($file)); + if ($diff{'to_id'} ne $diff{'from_id'}) { # modified + print $cgi->a({-href => href(action=>"blobdiff", hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, + hash_base=>$hash, file_name=>$diff{'file'}), + -class => "list"}, esc_html($diff{'file'})); + } else { # only mode changed + print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'}, + hash_base=>$hash, file_name=>$diff{'file'}), + -class => "list"}, esc_html($diff{'file'})); } print "$mode_chnge" . - $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$file)}, "blob"); - if ($to_id ne $from_id) { # modified - print " | " . $cgi->a({-href => href(action=>"blobdiff", hash=>$to_id, hash_parent=>$from_id, hash_base=>$hash, file_name=>$file)}, "diff"); - } - print " | " . $cgi->a({-href => href(action=>"history", hash_base=>$hash, file_name=>$file)}, "history") . "\n"; - print "" . - $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$to_file), - -class => "list"}, esc_html($to_file)) . "[moved from " . - $cgi->a({-href => href(action=>"blob", hash=>$from_id, hash_base=>$parent, file_name=>$from_file), - -class => "list"}, esc_html($from_file)) . - " with " . (int $similarity) . "% similarity$mode_chng]" . - $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$to_file)}, "blob"); - if ($to_id ne $from_id) { + $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'}, + hash_base=>$hash, file_name=>$diff{'file'})}, + "blob"); + if ($diff{'to_id'} ne $diff{'from_id'}) { # modified print " | " . - $cgi->a({-href => href(action=>"blobdiff", hash=>$to_id, hash_parent=>$from_id, hash_base=>$hash, file_name=>$to_file, file_parent=>$from_file)}, "diff"); + $cgi->a({-href => href(action=>"blobdiff", hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, + hash_base=>$hash, file_name=>$diff{'file'})}, + "diff"); } + print " | " . + $cgi->a({-href => href(action=>"history", + hash_base=>$hash, file_name=>$diff{'file'})}, + "history"); print "" . - $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$to_file), - -class => "list"}, esc_html($to_file)) . "[copied from " . - $cgi->a({-href => href(action=>"blob", hash=>$from_id, hash_base=>$parent, file_name=>$from_file), - -class => "list"}, esc_html($from_file)) . - " with " . (int $similarity) . "% similarity$mode_chng][$nstatus from " . + $cgi->a({-href => href(action=>"blob", hash_base=>$parent, + hash=>$diff{'from_id'}, file_name=>$diff{'from_file'}), + -class => "list"}, esc_html($diff{'from_file'})) . + " with " . (int $diff{'similarity'}) . "% similarity$mode_chng]" . - $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$to_file)}, "blob"); - if ($to_id ne $from_id) { + $cgi->a({-href => href(action=>"blob", hash_base=>$hash, + hash=>$diff{'to_id'}, file_name=>$diff{'to_file'})}, + "blob"); + if ($diff{'to_id'} ne $diff{'from_id'}) { print " | " . - $cgi->a({-href => href(action=>"blobdiff", hash=>$to_id, hash_parent=>$from_id, hash_base=>$hash, file_name=>$to_file, file_parent=>$from_file)}, "diff"); + $cgi->a({-href => href(action=>"blobdiff", hash_base=>$hash, + hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, + file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})}, + "diff"); } print "
\n"; } +sub git_patchset_body { + my ($fd, $difftree, $hash, $hash_parent) = @_; + + my $patch_idx = 0; + my $in_header = 0; + my $patch_found = 0; + my %diffinfo; + + print "
\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 "
\n"; # class="patch" + } else { + # first patch in patchset + $patch_found = 1; + } + print "
\n"; + + %diffinfo = parse_difftree_raw_line($difftree->[$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 "
" . 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)" . + "
\n"; # class="diff_info" + + } elsif ($diffinfo{'status'} eq "D") { # deleted + print "
" . 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)" . + "
\n"; # class="diff_info" + + } elsif ($diffinfo{'status'} eq "R" || # renamed + $diffinfo{'status'} eq "C") { # copied + print "
" . + 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 "
\n"; # class="diff_info" + + } else { # modified, mode changed, ... + print "
" . + 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 "
\n"; # class="diff_info" + } + + #print "
\n"; + $in_header = 1; + next LINE; + } # start of patch in patchset + + + if ($in_header && $patch_line =~ m/^---/) { + #print "
\n" + $in_header = 0; + } + next LINE if $in_header; + + print format_diff_line($patch_line); + } + print "
\n" if $patch_found; # class="patch" + + print "
\n"; # class="patchset" +} + +# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + sub git_shortlog_body { # uses global variable $project my ($revlist, $from, $to, $refs, $extra) = @_; @@ -1507,7 +1729,8 @@ sub git_shortlog_body { print "$co{'age_string_date'}\n" . "" . esc_html(chop_str($co{'author_name'}, 10)) . "\n" . ""; - print format_subject_html($co{'title'}, $co{'title_short'}, href(action=>"commit", hash=>$commit), $ref); + print format_subject_html($co{'title'}, $co{'title_short'}, + href(action=>"commit", hash=>$commit), $ref); print "\n" . "" . $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " . @@ -1556,7 +1779,8 @@ sub git_history_body { "" . esc_html(chop_str($co{'author_name'}, 15, 3)) . "\n" . ""; # originally git_history used chop_str($co{'title'}, 50) - print format_subject_html($co{'title'}, $co{'title_short'}, href(action=>"commit", hash=>$commit), $ref); + print format_subject_html($co{'title'}, $co{'title_short'}, + href(action=>"commit", hash=>$commit), $ref); print "\n" . "" . $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " . @@ -1569,7 +1793,8 @@ sub git_history_body { if (defined $blob_current && defined $blob_parent && $blob_current ne $blob_parent) { print " | " . - $cgi->a({-href => href(action=>"blobdiff", hash=>$blob_current, hash_parent=>$blob_parent, hash_base=>$commit, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"blobdiff", hash=>$blob_current, hash_parent=>$blob_parent, + hash_base=>$commit, file_name=>$file_name)}, "diff to current"); } } @@ -1610,11 +1835,12 @@ sub git_tags_body { print "$tag{'age'}\n" . "" . $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'}), - -class => "list"}, "" . esc_html($tag{'name'}) . "") . + -class => "list name"}, esc_html($tag{'name'})) . "\n" . ""; if (defined $comment) { - print format_subject_html($comment, $comment_short, href(action=>"tag", hash=>$tag{'id'})); + print format_subject_html($comment, $comment_short, + href(action=>"tag", hash=>$tag{'id'})); } print "\n" . ""; @@ -1664,7 +1890,7 @@ sub git_heads_body { print "$tag{'age'}\n" . ($tag{'id'} eq $head ? "" : "") . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'}), - -class => "list"}, "" . esc_html($tag{'name'}) . "") . + -class => "list name"},esc_html($tag{'name'})) . "\n" . "" . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'})}, "shortlog") . " | " . @@ -1925,13 +2151,17 @@ sub git_tag { "\n" . "\n" . "\n" . - "\n" . - "\n" . + "\n" . + "\n" . "\n"; if (defined($tag{'author'})) { my %ad = parse_date($tag{'epoch'}, $tag{'tz'}); print "\n"; - print "\n"; + print "\n"; } print "
object" . $cgi->a({-class => "list", -href => href(action=>$tag{'type'}, hash=>$tag{'object'})}, $tag{'object'}) . "" . $cgi->a({-href => href(action=>$tag{'type'}, hash=>$tag{'object'})}, $tag{'type'}) . "" . $cgi->a({-class => "list", -href => href(action=>$tag{'type'}, hash=>$tag{'object'})}, + $tag{'object'}) . "" . $cgi->a({-href => href(action=>$tag{'type'}, hash=>$tag{'object'})}, + $tag{'type'}) . "
author" . esc_html($tag{'author'}) . "
" . $ad{'rfc2822'} . sprintf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'}) . "
" . $ad{'rfc2822'} . + sprintf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'}) . + "
\n\n" . "\n"; @@ -1968,8 +2198,11 @@ sub git_blame2 { or die_error(undef, "Open git-blame failed"); git_header_html(); my $formats_nav = - $cgi->a({-href => href(action=>"blobl", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, "blob") . - " | " . $cgi->a({-href => href(action=>"blame", file_name=>$file_name)}, "head"); + $cgi->a({-href => href(action=>"blob", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, + "blob") . + " | " . + $cgi->a({-href => href(action=>"blame", file_name=>$file_name)}, + "head"); git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_header_div('commit', esc_html($co{'title'}), $hash_base); git_print_page_path($file_name, $ftype, $hash_base); @@ -1977,9 +2210,11 @@ sub git_blame2 { my $num_colors = scalar(@rev_color); my $current_color = 0; my $last_rev; - print "
\n"; - print "\n"; - print "\n"; + print < +
CommitLineData
+ +HTML while (<$fd>) { /^([0-9a-fA-F]{40}).*?(\d+)\)\s{1}(\s*.*)/; my $full_rev = $1; @@ -1995,14 +2230,17 @@ sub git_blame2 { } print "\n"; print "\n"; - print "\n"; + $cgi->a({-href => href(action=>"commit", hash=>$full_rev, file_name=>$file_name)}, + esc_html($rev)) . "\n"; + print "\n"; print "\n"; print "\n"; } print "
CommitLineData
" . - $cgi->a({-href => href(action=>"commit", hash=>$full_rev, file_name=>$file_name)}, esc_html($rev)) . "" . esc_html($lineno) . "" . + esc_html($lineno) . "" . esc_html($data) . "
\n"; print "
"; - close $fd or print "Reading blob failed\n"; + close $fd + or print "Reading blob failed\n"; git_footer_html(); } @@ -2025,8 +2263,11 @@ sub git_blame { or die_error(undef, "Open git-annotate failed"); git_header_html(); my $formats_nav = - $cgi->a({-href => href(action=>"blobl", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, "blob") . - " | " . $cgi->a({-href => href(action=>"blame", file_name=>$file_name)}, "head"); + $cgi->a({-href => href(action=>"blob", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, + "blob") . + " | " . + $cgi->a({-href => href(action=>"blame", file_name=>$file_name)}, + "head"); git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_header_div('commit', esc_html($co{'title'}), $hash_base); git_print_page_path($file_name, 'blob', $hash_base); @@ -2090,7 +2331,8 @@ HTML HTML } # while (my $line = <$fd>) print "\n\n"; - close $fd or print "Reading blob failed.\n"; + close $fd + or print "Reading blob failed.\n"; print ""; git_footer_html(); } @@ -2145,7 +2387,8 @@ sub git_blob_plain { $save_as .= '.txt'; } - print $cgi->header(-type => "$type", '-content-disposition' => "inline; filename=\"$save_as\""); + print $cgi->header(-type => "$type", + -content_disposition => "inline; filename=\"$save_as\""); undef $/; binmode STDOUT, ':raw'; print <$fd>; @@ -2177,13 +2420,23 @@ sub git_blob { if (defined $hash_base && (my %co = parse_commit($hash_base))) { if (defined $file_name) { if ($have_blame) { - $formats_nav .= $cgi->a({-href => href(action=>"blame", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, "blame") . " | "; + $formats_nav .= + $cgi->a({-href => href(action=>"blame", hash_base=>$hash_base, + hash=>$hash, file_name=>$file_name)}, + "blame") . + " | "; } $formats_nav .= - $cgi->a({-href => href(action=>"blob_plain", hash=>$hash, file_name=>$file_name)}, "plain") . - " | " . $cgi->a({-href => href(action=>"blob", hash_base=>"HEAD", file_name=>$file_name)}, "head"); + $cgi->a({-href => href(action=>"blob_plain", + hash=>$hash, file_name=>$file_name)}, + "plain") . + " | " . + $cgi->a({-href => href(action=>"blob", + hash_base=>"HEAD", file_name=>$file_name)}, + "head"); } else { - $formats_nav .= $cgi->a({-href => href(action=>"blob_plain", hash=>$hash)}, "plain"); + $formats_nav .= + $cgi->a({-href => href(action=>"blob_plain", hash=>$hash)}, "plain"); } git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_header_div('commit', esc_html($co{'title'}), $hash_base); @@ -2199,9 +2452,11 @@ sub git_blob { chomp $line; $nr++; $line = untabify($line); - printf "
%4i %s
\n", $nr, $nr, $nr, esc_html($line); + printf "
%4i %s
\n", + $nr, $nr, $nr, esc_html($line); } - close $fd or print "Reading blob failed.\n"; + close $fd + or print "Reading blob failed.\n"; print ""; git_footer_html(); } @@ -2262,23 +2517,37 @@ sub git_tree { print "" . mode_str($t_mode) . "\n"; if ($t_type eq "blob") { print "" . - $cgi->a({-href => href(action=>"blob", hash=>$t_hash, file_name=>"$base$t_name", %base_key), -class => "list"}, esc_html($t_name)) . + $cgi->a({-href => href(action=>"blob", hash=>$t_hash, file_name=>"$base$t_name", %base_key), + -class => "list"}, esc_html($t_name)) . "\n" . "" . - $cgi->a({-href => href(action=>"blob", hash=>$t_hash, file_name=>"$base$t_name", %base_key)}, "blob"); + $cgi->a({-href => href(action=>"blob", hash=>$t_hash, file_name=>"$base$t_name", %base_key)}, + "blob"); if ($have_blame) { - print " | " . $cgi->a({-href => href(action=>"blame", hash=>$t_hash, file_name=>"$base$t_name", %base_key)}, "blame"); + print " | " . + $cgi->a({-href => href(action=>"blame", hash=>$t_hash, file_name=>"$base$t_name", %base_key)}, + "blame"); } - print " | " . $cgi->a({-href => href(action=>"history", hash=>$t_hash, hash_base=>$hash_base, file_name=>"$base$t_name")}, "history") . - " | " . $cgi->a({-href => href(action=>"blob_plain", hash=>$t_hash, file_name=>"$base$t_name")}, "raw") . + print " | " . + $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, + hash=>$t_hash, file_name=>"$base$t_name")}, + "history") . + " | " . + $cgi->a({-href => href(action=>"blob_plain", + hash=>$t_hash, file_name=>"$base$t_name")}, + "raw") . "\n"; } elsif ($t_type eq "tree") { print "" . - $cgi->a({-href => href(action=>"tree", hash=>$t_hash, file_name=>"$base$t_name", %base_key)}, esc_html($t_name)) . + $cgi->a({-href => href(action=>"tree", hash=>$t_hash, file_name=>"$base$t_name", %base_key)}, + esc_html($t_name)) . "\n" . "" . - $cgi->a({-href => href(action=>"tree", hash=>$t_hash, file_name=>"$base$t_name", %base_key)}, "tree") . - " | " . $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, file_name=>"$base$t_name")}, "history") . + $cgi->a({-href => href(action=>"tree", hash=>$t_hash, file_name=>"$base$t_name", %base_key)}, + "tree") . + " | " . + $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, file_name=>"$base$t_name")}, + "history") . "\n"; } print "\n"; @@ -2303,10 +2572,9 @@ sub git_snapshot { my $filename = basename($project) . "-$hash.tar.$suffix"; print $cgi->header(-type => 'application/x-tar', - -content-encoding => $ctype, - '-content-disposition' => - "inline; filename=\"$filename\"", - -status => '200 OK'); + -content_encoding => $ctype, + -content_disposition => "inline; filename=\"$filename\"", + -status => '200 OK'); open my $fd, "-|", "$GIT tar-tree $hash \'$project\' | $command" or die_error(undef, "Execute git-tar-tree failed."); @@ -2357,7 +2625,8 @@ sub git_log { print "
\n" . "
\n" . $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . - " | " . $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . + " | " . + $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . "
\n" . "
\n" . "" . esc_html($co{'author_name'}) . " [$ad{'rfc2822'}]
\n" . @@ -2401,12 +2670,14 @@ sub git_commit { my $formats_nav = ''; if (defined $file_name && defined $co{'parent'}) { my $parent = $co{'parent'}; - $formats_nav .= $cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)}, "blame"); + $formats_nav .= + $cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)}, + "blame"); } git_header_html(undef, $expires); git_print_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff', - $hash, $co{'tree'}, $hash, - $formats_nav); + $hash, $co{'tree'}, $hash, + $formats_nav); if (defined $co{'parent'}) { git_print_header_div('commitdiff', esc_html($co{'title'}) . $ref, $hash); @@ -2419,23 +2690,31 @@ sub git_commit { "" . " $ad{'rfc2822'}"; if ($ad{'hour_local'} < 6) { - printf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'}); + printf(" (%02d:%02d %s)", + $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'}); } else { - printf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'}); + printf(" (%02d:%02d %s)", + $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'}); } print "" . "\n"; print "committer" . esc_html($co{'committer'}) . "\n"; - print " $cd{'rfc2822'}" . sprintf(" (%02d:%02d %s)", $cd{'hour_local'}, $cd{'minute_local'}, $cd{'tz_local'}) . "\n"; + print " $cd{'rfc2822'}" . + sprintf(" (%02d:%02d %s)", $cd{'hour_local'}, $cd{'minute_local'}, $cd{'tz_local'}) . + "\n"; print "commit$co{'id'}\n"; print "" . "tree" . "" . - $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash), class => "list"}, $co{'tree'}) . + $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash), + class => "list"}, $co{'tree'}) . "" . - "" . $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash)}, "tree"); + "" . + $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash)}, + "tree"); if ($have_snapshot) { - print " | " . $cgi->a({-href => href(action=>"snapshot", hash=>$hash)}, "snapshot"); + print " | " . + $cgi->a({-href => href(action=>"snapshot", hash=>$hash)}, "snapshot"); } print "" . "\n"; @@ -2443,10 +2722,14 @@ sub git_commit { foreach my $par (@$parents) { print "" . "parent" . - "" . $cgi->a({-href => href(action=>"commit", hash=>$par), class => "list"}, $par) . "" . + "" . + $cgi->a({-href => href(action=>"commit", hash=>$par), + class => "list"}, $par) . + "" . "" . $cgi->a({-href => href(action=>"commit", hash=>$par)}, "commit") . - " | " . $cgi->a({-href => href(action=>"commitdiff", hash=>$hash, hash_parent=>$par)}, "commitdiff") . + " | " . + $cgi->a({-href => href(action=>"commitdiff", hash=>$hash, hash_parent=>$par)}, "commitdiff") . "" . "\n"; } @@ -2457,7 +2740,7 @@ sub git_commit { git_print_log($co{'comment'}); print "
\n"; - git_difftree_body(\@difftree, $parent); + git_difftree_body(\@difftree, $hash, $parent); git_footer_html(); } @@ -2467,23 +2750,30 @@ sub git_blobdiff { git_header_html(); if (defined $hash_base && (my %co = parse_commit($hash_base))) { my $formats_nav = - $cgi->a({-href => href(action=>"blobdiff_plain", hash=>$hash, hash_parent=>$hash_parent)}, "plain"); + $cgi->a({-href => href(action=>"blobdiff_plain", + hash=>$hash, hash_parent=>$hash_parent)}, + "plain"); git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_header_div('commit', esc_html($co{'title'}), $hash_base); } else { - print "
\n" . - "

\n" . - "
$hash vs $hash_parent
\n"; + print <

+
$hash vs $hash_parent
+HTML } git_print_page_path($file_name, "blob", $hash_base); print "
\n" . "
blob:" . - $cgi->a({-href => href(action=>"blob", hash=>$hash_parent, hash_base=>$hash_base, file_name=>($file_parent || $file_name))}, $hash_parent) . + $cgi->a({-href => href(action=>"blob", hash=>$hash_parent, + hash_base=>$hash_base, file_name=>($file_parent || $file_name))}, + $hash_parent) . " -> blob:" . - $cgi->a({-href => href(action=>"blob", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, $hash) . + $cgi->a({-href => href(action=>"blob", hash=>$hash, + hash_base=>$hash_base, file_name=>$file_name)}, + $hash) . "
\n"; git_diff_print($hash_parent, $file_name || $hash_parent, $hash, $file_name || $hash); - print "
"; + print ""; # page_body git_footer_html(); } @@ -2494,7 +2784,7 @@ sub git_blobdiff_plain { } sub git_commitdiff { - mkdir($git_temp, 0700); + my $format = shift || 'html'; my %co = parse_commit($hash); if (!%co) { die_error(undef, "Unknown commit object"); @@ -2502,130 +2792,99 @@ sub git_commitdiff { if (!defined $hash_parent) { $hash_parent = $co{'parent'} || '--root'; } - open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash - or die_error(undef, "Open git-diff-tree failed"); - my @difftree = map { chomp; $_ } <$fd>; - close $fd or die_error(undef, "Reading git-diff-tree failed"); - # non-textual hash id's can be cached - my $expires; - if ($hash =~ m/^[0-9a-fA-F]{40}$/) { - $expires = "+1d"; - } - my $refs = git_get_references(); - my $ref = format_ref_marker($refs, $co{'id'}); - my $formats_nav = - $cgi->a({-href => href(action=>"commitdiff_plain", hash=>$hash, hash_parent=>$hash_parent)}, "plain"); - git_header_html(undef, $expires); - git_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav); - git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash); - print "
\n"; - git_print_simplified_log($co{'comment'}, 1); # skip title - print "
\n"; - foreach my $line (@difftree) { - # ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M ls-files.c' - # ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M rev-tree.c' - if ($line !~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/) { - next; - } - my $from_mode = $1; - my $to_mode = $2; - my $from_id = $3; - my $to_id = $4; - my $status = $5; - my $file = validate_input(unquote($6)); - if ($status eq "A") { - print "
" . file_type($to_mode) . ":" . - $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$file)}, $to_id) . "(new)" . - "
\n"; - git_diff_print(undef, "/dev/null", $to_id, "b/$file"); - } elsif ($status eq "D") { - print "
" . file_type($from_mode) . ":" . - $cgi->a({-href => href(action=>"blob", hash=>$from_id, hash_base=>$hash_parent, file_name=>$file)}, $from_id) . "(deleted)" . - "
\n"; - git_diff_print($from_id, "a/$file", undef, "/dev/null"); - } elsif ($status eq "M") { - if ($from_id ne $to_id) { - print "
" . - file_type($from_mode) . ":" . - $cgi->a({-href => href(action=>"blob", hash=>$from_id, hash_base=>$hash_parent, file_name=>$file)}, $from_id) . - " -> " . - file_type($to_mode) . ":" . - $cgi->a({-href => href(action=>"blob", hash=>$to_id, hash_base=>$hash, file_name=>$file)}, $to_id); - print "
\n"; - git_diff_print($from_id, "a/$file", $to_id, "b/$file"); - } + # read commitdiff + my $fd; + my @difftree; + if ($format eq 'html') { + open $fd, "-|", $GIT, "diff-tree", '-r', '-M', '-C', + "--patch-with-raw", "--full-index", $hash_parent, $hash + or die_error(undef, "Open git-diff-tree failed"); + + while (chomp(my $line = <$fd>)) { + # empty line ends raw part of diff-tree output + last unless $line; + push @difftree, $line; } - } - print "
\n" . - "
"; - git_footer_html(); -} -sub git_commitdiff_plain { - mkdir($git_temp, 0700); - my %co = parse_commit($hash); - if (!%co) { - die_error(undef, "Unknown commit object"); - } - if (!defined $hash_parent) { - $hash_parent = $co{'parent'} || '--root'; - } - open my $fd, "-|", $GIT, "diff-tree", '-r', $hash_parent, $hash - or die_error(undef, "Open git-diff-tree failed"); - my @difftree = map { chomp; $_ } <$fd>; - close $fd or die_error(undef, "Reading diff-tree failed"); + } elsif ($format eq 'plain') { + open $fd, "-|", $GIT, "diff-tree", '-r', '-p', '-B', $hash_parent, $hash + or die_error(undef, "Open git-diff-tree failed"); - # try to figure out the next tag after this commit - my $tagname; - my $refs = git_get_references("tags"); - open $fd, "-|", $GIT, "rev-list", "HEAD"; - my @commits = map { chomp; $_ } <$fd>; - close $fd; - foreach my $commit (@commits) { - if (defined $refs->{$commit}) { - $tagname = $refs->{$commit} - } - if ($commit eq $hash) { - last; - } + } else { + die_error(undef, "Unknown commitdiff format"); } - print $cgi->header(-type => "text/plain", -charset => 'utf-8', '-content-disposition' => "inline; filename=\"git-$hash.patch\""); - my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'}); - my $comment = $co{'comment'}; - print "From: $co{'author'}\n" . - "Date: $ad{'rfc2822'} ($ad{'tz_local'})\n". - "Subject: $co{'title'}\n"; - if (defined $tagname) { - print "X-Git-Tag: $tagname\n"; + # non-textual hash id's can be cached + my $expires; + if ($hash =~ m/^[0-9a-fA-F]{40}$/) { + $expires = "+1d"; } - print "X-Git-Url: $my_url?p=$project;a=commitdiff;h=$hash\n" . - "\n"; - foreach my $line (@$comment) {; - print "$line\n"; - } - print "---\n\n"; + # write commit message + if ($format eq 'html') { + my $refs = git_get_references(); + my $ref = format_ref_marker($refs, $co{'id'}); + my $formats_nav = + $cgi->a({-href => href(action=>"commitdiff_plain", + hash=>$hash, hash_parent=>$hash_parent)}, + "plain"); + + git_header_html(undef, $expires); + git_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav); + git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash); + print "
\n"; + print "
\n"; + git_print_simplified_log($co{'comment'}, 1); # skip title + print "
\n"; # class="log" + + } elsif ($format eq 'plain') { + my $refs = git_get_references("tags"); + my $tagname = git_get_rev_name_tags($hash); + my $filename = basename($project) . "-$hash.patch"; + + print $cgi->header( + -type => 'text/plain', + -charset => 'utf-8', + -expires => $expires, + -content_disposition => qq(inline; filename="$filename")); + my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'}); + print <self_url() . "\n\n"; + + foreach my $line (@{$co{'comment'}}) { + print "$line\n"; + } + print "---\n\n"; + } + + # write patch + if ($format eq 'html') { + #git_difftree_body(\@difftree, $hash, $hash_parent); + #print "
\n"; + + git_patchset_body($fd, \@difftree, $hash, $hash_parent); + close $fd; + print "
\n"; # class="page_body" + git_footer_html(); - foreach my $line (@difftree) { - if ($line !~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/) { - next; - } - my $from_id = $3; - my $to_id = $4; - my $status = $5; - my $file = $6; - if ($status eq "A") { - git_diff_print(undef, "/dev/null", $to_id, "b/$file", "plain"); - } elsif ($status eq "D") { - git_diff_print($from_id, "a/$file", undef, "/dev/null", "plain"); - } elsif ($status eq "M") { - git_diff_print($from_id, "a/$file", $to_id, "b/$file", "plain"); - } + } elsif ($format eq 'plain') { + local $/ = undef; + print <$fd>; + close $fd + or print "Reading git-diff-tree failed\n"; } } +sub git_commitdiff_plain { + git_commitdiff('plain'); +} + sub git_history { if (!defined $hash_base) { $hash_base = git_get_head_hash($project); @@ -2713,7 +2972,8 @@ sub git_search { print "$co{'age_string_date'}\n" . "" . esc_html(chop_str($co{'author_name'}, 15, 5)) . "\n" . "" . - $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}) -class => "list"}, "" . esc_html(chop_str($co{'title'}, 50)) . "
"); + $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), -class => "list subject"}, + esc_html(chop_str($co{'title'}, 50)) . "
"); my $comment = $co{'comment'}; foreach my $line (@$comment) { if ($line =~ m/^(.*)($searchtext)(.*)$/i) { @@ -2729,7 +2989,8 @@ sub git_search { print "\n" . "" . $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") . - " | " . $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree"); + " | " . + $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree"); print "\n" . "\n"; } @@ -2766,18 +3027,22 @@ sub git_search { print "$co{'age_string_date'}\n" . "" . esc_html(chop_str($co{'author_name'}, 15, 5)) . "\n" . "" . - $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), -class => "list"}, "" . - esc_html(chop_str($co{'title'}, 50)) . "
"); + $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), + -class => "list subject"}, + esc_html(chop_str($co{'title'}, 50)) . "
"); while (my $setref = shift @files) { my %set = %$setref; - print $cgi->a({-href => href(action=>"blob", hash=>$set{'id'}, hash_base=>$co{'id'}, file_name=>$set{'file'}), class => "list"}, - "" . esc_html($set{'file'}) . "") . + print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'}, + hash=>$set{'id'}, file_name=>$set{'file'}), + -class => "list"}, + "" . esc_html($set{'file'}) . "") . "
\n"; } print "\n" . "" . $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") . - " | " . $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree"); + " | " . + $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree"); print "\n" . "\n"; } @@ -2834,13 +3099,15 @@ sub git_rss { my @revlist = map { chomp; $_ } <$fd>; close $fd or die_error(undef, "Reading git-rev-list failed"); print $cgi->header(-type => 'text/xml', -charset => 'utf-8'); - print "\n". - "\n"; - print "\n"; - print "$project\n". - "" . esc_html("$my_url?p=$project;a=summary") . "\n". - "$project log\n". - "en\n"; + print < + + +$project $my_uri $my_url +${\esc_html("$my_url?p=$project;a=summary")} +$project log +en +XML for (my $i = 0; $i <= $#revlist; $i++) { my $commit = $revlist[$i]; @@ -2889,13 +3156,15 @@ sub git_opml { my @list = git_get_projects_list(); print $cgi->header(-type => 'text/xml', -charset => 'utf-8'); - print "\n". - "\n". - "". - " $site_name Git OPML Export\n". - "\n". - "\n". - "\n"; + print < + + + $site_name Git OPML Export + + + +XML foreach my $pr (@list) { my %proj = %$pr; @@ -2914,7 +3183,9 @@ sub git_opml { my $html = "$my_url?p=$proj{'path'};a=summary"; print "\n"; } - print "\n". - "\n". - "\n"; + print < + + +XML }