git-fetch: move more code into C.
[gitweb.git] / gitweb / gitweb.perl
index 65fcdb0f289c21b0824546884d826b48586ca712..653ca3cc60ef161f06bd3a6652eba7ad3453cc2e 100755 (executable)
 use File::Basename qw(basename);
 binmode STDOUT, ':utf8';
 
+BEGIN {
+       CGI->compile() if $ENV{MOD_PERL};
+}
+
 our $cgi = new CGI;
 our $version = "++GIT_VERSION++";
 our $my_url = $cgi->url();
@@ -830,7 +834,7 @@ sub file_type_long {
 
 ## ----------------------------------------------------------------------
 ## functions returning short HTML fragments, or transforming HTML fragments
-## which don't beling to other sections
+## which don't belong to other sections
 
 # format line of commit message.
 sub format_log_line_html {
@@ -982,7 +986,7 @@ sub git_get_project_config {
        $key =~ s/^gitweb\.//;
        return if ($key =~ m/\W/);
 
-       my @x = (git_cmd(), 'repo-config');
+       my @x = (git_cmd(), 'config');
        if (defined $type) { push @x, $type; }
        push @x, "--get";
        push @x, "gitweb.$key";
@@ -1271,7 +1275,7 @@ sub parse_tag {
 }
 
 sub parse_commit_text {
-       my ($commit_text) = @_;
+       my ($commit_text, $withparents) = @_;
        my @commit_lines = split '\n', $commit_text;
        my %co;
 
@@ -1281,13 +1285,12 @@ sub parse_commit_text {
        if (!($header =~ m/^[0-9a-fA-F]{40}/)) {
                return;
        }
-       $co{'id'} = $header;
-       my @parents;
+       ($co{'id'}, my @parents) = split ' ', $header;
        while (my $line = shift @commit_lines) {
                last if $line eq "\n";
                if ($line =~ m/^tree ([0-9a-fA-F]{40})$/) {
                        $co{'tree'} = $1;
-               } elsif ($line =~ m/^parent ([0-9a-fA-F]{40})$/) {
+               } elsif ((!defined $withparents) && ($line =~ m/^parent ([0-9a-fA-F]{40})$/)) {
                        push @parents, $1;
                } elsif ($line =~ m/^author (.*) ([0-9]+) (.*)$/) {
                        $co{'author'} = $1;
@@ -1373,12 +1376,13 @@ sub parse_commit {
        local $/ = "\0";
 
        open my $fd, "-|", git_cmd(), "rev-list",
+               "--parents",
                "--header",
                "--max-count=1",
                $commit_id,
                "--",
                or die_error(undef, "Open git-rev-list failed");
-       %co = parse_commit_text(<$fd>);
+       %co = parse_commit_text(<$fd>, 1);
        close $fd;
 
        return %co;
@@ -1686,7 +1690,7 @@ sub git_header_html {
 
        my $title = "$site_name";
        if (defined $project) {
-               $title .= " - $project";
+               $title .= " - " . to_utf8($project);
                if (defined $action) {
                        $title .= "/$action";
                        if (defined $file_name) {
@@ -1711,6 +1715,7 @@ sub git_header_html {
        }
        print $cgi->header(-type=>$content_type, -charset => 'utf-8',
                           -status=> $status, -expires => $expires);
+       my $mod_perl_version = $ENV{'MOD_PERL'} ? " $ENV{'MOD_PERL'}" : '';
        print <<EOF;
 <?xml version="1.0" encoding="utf-8"?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
@@ -1719,7 +1724,7 @@ sub git_header_html {
 <!-- git core binaries version $git_version -->
 <head>
 <meta http-equiv="content-type" content="$content_type; charset=utf-8"/>
-<meta name="generator" content="gitweb/$version git/$git_version"/>
+<meta name="generator" content="gitweb/$version git/$git_version$mod_perl_version"/>
 <meta name="robots" content="index, nofollow"/>
 <title>$title</title>
 EOF
@@ -1958,7 +1963,7 @@ sub git_print_page_path {
 
        print "<div class=\"page_path\">";
        print $cgi->a({-href => href(action=>"tree", hash_base=>$hb),
-                     -title => 'tree root'}, "[$project]");
+                     -title => 'tree root'}, to_utf8("[$project]"));
        print " / ";
        if (defined $name) {
                my @dirname = split '/', $name;
@@ -2234,7 +2239,7 @@ sub git_difftree_body {
                        }
                        print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
                                                     hash_base=>$hash, file_name=>$diff{'file'})},
-                                     "blob") . " | ";
+                                     "blob");
                        print "</td>\n";
 
                } elsif ($diff{'status'} eq "D") { # deleted
@@ -2269,7 +2274,7 @@ sub git_difftree_body {
                        my $mode_chnge = "";
                        if ($diff{'from_mode'} != $diff{'to_mode'}) {
                                $mode_chnge = "<span class=\"file_status mode_chnge\">[changed";
-                               if ($from_file_type != $to_file_type) {
+                               if ($from_file_type ne $to_file_type) {
                                        $mode_chnge .= " from $from_file_type to $to_file_type";
                                }
                                if (($from_mode_oct & 0777) != ($to_mode_oct & 0777)) {
@@ -2373,7 +2378,6 @@ sub git_patchset_body {
        my $patch_line;
        my $diffinfo;
        my (%from, %to);
-       my ($from_id, $to_id);
 
        print "<div class=\"patchset\">\n";
 
@@ -2387,6 +2391,7 @@ sub git_patchset_body {
  PATCH:
        while ($patch_line) {
                my @diff_header;
+               my ($from_id, $to_id);
 
                # git diff header
                #assert($patch_line =~ m/^diff /) if DEBUG;
@@ -2398,7 +2403,7 @@ sub git_patchset_body {
                while ($patch_line = <$fd>) {
                        chomp $patch_line;
 
-                       last EXTENDED_HEADER if ($patch_line =~ m/^--- /);
+                       last EXTENDED_HEADER if ($patch_line =~ m/^--- |^diff /);
 
                        if ($patch_line =~ m/^index ([0-9a-fA-F]{40})..([0-9a-fA-F]{40})/) {
                                $from_id = $1;
@@ -2407,7 +2412,6 @@ sub git_patchset_body {
 
                        push @diff_header, $patch_line;
                }
-               #last PATCH unless $patch_line;
                my $last_patch_line = $patch_line;
 
                # check if current patch belong to current raw line
@@ -2434,11 +2438,15 @@ sub git_patchset_body {
                                $from{'href'} = href(action=>"blob", hash_base=>$hash_parent,
                                                     hash=>$diffinfo->{'from_id'},
                                                     file_name=>$from{'file'});
+                       } else {
+                               delete $from{'href'};
                        }
                        if ($diffinfo->{'status'} ne "D") { # not deleted file
                                $to{'href'} = href(action=>"blob", hash_base=>$hash,
                                                   hash=>$diffinfo->{'to_id'},
                                                   file_name=>$to{'file'});
+                       } else {
+                               delete $to{'href'};
                        }
                        # this is first patch for raw difftree line with $patch_idx index
                        # we index @$difftree array from 0, but number patches from 1
@@ -2470,11 +2478,11 @@ sub git_patchset_body {
                        # match <path>
                        if ($patch_line =~ s!^((copy|rename) from ).*$!$1! && $from{'href'}) {
                                $patch_line .= $cgi->a({-href=>$from{'href'}, -class=>"path"},
-                                                       esc_path($from{'file'}));
+                                                      esc_path($from{'file'}));
                        }
                        if ($patch_line =~ s!^((copy|rename) to ).*$!$1! && $to{'href'}) {
-                               $patch_line = $cgi->a({-href=>$to{'href'}, -class=>"path"},
-                                                     esc_path($to{'file'}));
+                               $patch_line .= $cgi->a({-href=>$to{'href'}, -class=>"path"},
+                                                      esc_path($to{'file'}));
                        }
                        # match <mode>
                        if ($patch_line =~ m/\s(\d{6})$/) {
@@ -2513,8 +2521,13 @@ sub git_patchset_body {
 
                # from-file/to-file diff header
                $patch_line = $last_patch_line;
+               if (! $patch_line) {
+                       print "</div>\n"; # class="patch"
+                       last PATCH;
+               }
+               next PATCH if ($patch_line =~ m/^diff /);
                #assert($patch_line =~ m/^---/) if DEBUG;
-               if ($from{'href'}) {
+               if ($from{'href'} && $patch_line =~ m!^--- "?a/!) {
                        $patch_line = '--- a/' .
                                      $cgi->a({-href=>$from{'href'}, -class=>"path"},
                                              esc_path($from{'file'}));
@@ -2522,11 +2535,10 @@ sub git_patchset_body {
                print "<div class=\"diff from_file\">$patch_line</div>\n";
 
                $patch_line = <$fd>;
-               #last PATCH unless $patch_line;
                chomp $patch_line;
 
                #assert($patch_line =~ m/^+++/) if DEBUG;
-               if ($to{'href'}) {
+               if ($to{'href'} && $patch_line =~ m!^\+\+\+ "?b/!) {
                        $patch_line = '+++ b/' .
                                      $cgi->a({-href=>$to{'href'}, -class=>"path"},
                                              esc_path($to{'file'}));
@@ -2808,8 +2820,12 @@ sub git_tags_body {
                        print "<tr class=\"light\">\n";
                }
                $alternate ^= 1;
-               print "<td><i>$tag{'age'}</i></td>\n" .
-                     "<td>" .
+               if (defined $tag{'age'}) {
+                       print "<td><i>$tag{'age'}</i></td>\n";
+               } else {
+                       print "<td></td>\n";
+               }
+               print "<td>" .
                      $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'}),
                               -class => "list name"}, esc_html($tag{'name'})) .
                      "</td>\n" .
@@ -2990,7 +3006,7 @@ sub git_project_index {
 
        foreach my $pr (@projects) {
                if (!exists $pr->{'owner'}) {
-                       $pr->{'owner'} = get_file_owner("$projectroot/$project");
+                       $pr->{'owner'} = get_file_owner("$projectroot/$pr->{'path'}");
                }
 
                my ($path, $owner) = ($pr->{'path'}, $pr->{'owner'});
@@ -3203,9 +3219,14 @@ sub git_blame2 {
                                      esc_html($rev));
                        print "</td>\n";
                }
+               open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^")
+                       or die_error("could not open git-rev-parse");
+               my $parent_commit = <$dd>;
+               close $dd;
+               chomp($parent_commit);
                my $blamed = href(action => 'blame',
                                  file_name => $meta->{'filename'},
-                                 hash_base => $full_rev);
+                                 hash_base => $parent_commit);
                print "<td class=\"linenr\">";
                print $cgi->a({ -href => "$blamed#l$orig_lineno",
                                -id => "l$lineno",
@@ -3589,7 +3610,7 @@ sub git_snapshot {
                $hash = git_get_head_hash($project);
        }
 
-       my $filename = basename($project) . "-$hash.tar.$suffix";
+       my $filename = to_utf8(basename($project)) . "-$hash.tar.$suffix";
 
        print $cgi->header(
                -type => "application/$ctype",
@@ -4418,7 +4439,7 @@ sub git_shortlog {
        }
        my $refs = git_get_references();
 
-       my @commitlist = parse_commits($head, 101, (100 * $page));
+       my @commitlist = parse_commits($hash, 101, (100 * $page));
 
        my $paging_nav = format_paging_nav('shortlog', $hash, $head, $page, (100 * ($page+1)));
        my $next_link = '';