Merge branch 'jc/upload-pack'
[gitweb.git] / gitweb / gitweb.perl
index 62849014574861d0da899d31c49f401dcaa168f3..626fcc9201c9f2c40e1e1bdc4c9146ae96a7f67f 100755 (executable)
@@ -21,7 +21,6 @@
 our $version = "++GIT_VERSION++";
 our $my_url = $cgi->url();
 our $my_uri = $cgi->url(-absolute => 1);
-our $rss_link = "";
 
 # core git executable to use
 # this can just be "git" if your webserver has a sensible PATH
 
 our $project = ($cgi->param('p') || $ENV{'PATH_INFO'});
 if (defined $project) {
-       $project =~ s|^/||; $project =~ s|/$||;
+       $project =~ s|^/||;
+       $project =~ s|/$||;
+}
+if (defined $project && $project) {
        if (!validate_input($project)) {
                die_error(undef, "Invalid project parameter");
        }
@@ -97,8 +99,6 @@
        if (!(-e "$projectroot/$project/HEAD")) {
                die_error(undef, "No such project");
        }
-       $rss_link = "<link rel=\"alternate\" title=\"" . esc_param($project) . " log\" href=\"" .
-                   "$my_uri?" . esc_param("p=$project;a=rss") . "\" type=\"application/rss+xml\"/>";
        $ENV{'GIT_DIR'} = "$projectroot/$project";
 } else {
        git_project_list();
@@ -224,6 +224,20 @@ sub unquote {
        return $str;
 }
 
+# escape tabs (convert tabs to spaces)
+sub untabify {
+       my $line = shift;
+
+       while ((my $pos = index($line, "\t")) != -1) {
+               if (my $count = (8 - ($pos % 8))) {
+                       my $spaces = ' ' x $count;
+                       $line =~ s/\t/$spaces/;
+               }
+       }
+
+       return $line;
+}
+
 ## ----------------------------------------------------------------------
 ## HTML aware string manipulation
 
@@ -845,7 +859,7 @@ 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 ($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';
@@ -862,11 +876,17 @@ sub git_header_html {
 <meta name="robots" content="index, nofollow"/>
 <title>$title</title>
 <link rel="stylesheet" type="text/css" href="$stylesheet"/>
-$rss_link
-</head>
-<body>
 EOF
-       print "<div class=\"page_header\">\n" .
+       if (defined $project) {
+               printf('<link rel="alternate" title="%s log" '.
+                      'href="%s" type="application/rss+xml"/>'."\n",
+                      esc_param($project),
+                      esc_param("$my_uri?p=$project;a=rss"));
+       }
+
+       print "</head>\n" .
+             "<body>\n" .
+             "<div class=\"page_header\">\n" .
              "<a href=\"http://www.kernel.org/pub/software/scm/git/docs/\" title=\"git documentation\">" .
              "<img src=\"$logo\" width=\"72\" height=\"27\" alt=\"git\" style=\"float:right; border-width:0px;\"/>" .
              "</a>\n";
@@ -983,7 +1003,7 @@ sub git_get_paging_nav {
        if ($page > 0) {
                $paging_nav .= " &sdot; " .
                        $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action;h=$hash;pg=" . ($page-1)),
-                                                        -accesskey => "p", -title => "Alt-p"}, "prev");
+                                -accesskey => "p", -title => "Alt-p"}, "prev");
        } else {
                $paging_nav .= " &sdot; prev";
        }
@@ -991,7 +1011,7 @@ sub git_get_paging_nav {
        if ($nrevs >= (100 * ($page+1)-1)) {
                $paging_nav .= " &sdot; " .
                        $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$action;h=$hash;pg=" . ($page+1)),
-                                                        -accesskey => "n", -title => "Alt-n"}, "next");
+                                -accesskey => "n", -title => "Alt-n"}, "next");
        } else {
                $paging_nav .= " &sdot; next";
        }
@@ -1045,7 +1065,6 @@ sub git_shortlog_body {
                #my $ref = defined $refs ? git_get_referencing($refs, $commit) : '';
                my $ref = git_get_referencing($refs, $commit);
                my %co = git_read_commit($commit);
-               my %ad = date_str($co{'author_epoch'});
                if ($alternate) {
                        print "<tr class=\"dark\">\n";
                } else {
@@ -1238,12 +1257,7 @@ sub git_diff_print {
                                # skip errors
                                next;
                        }
-                       while ((my $pos = index($line, "\t")) != -1) {
-                               if (my $count = (8 - (($pos-1) % 8))) {
-                                       my $spaces = ' ' x $count;
-                                       $line =~ s/\t/$spaces/;
-                               }
-                       }
+                       $line = untabify($line);
                        print "<div class=\"diff$diff_class\">" . esc_html($line) . "</div>\n";
                }
        }
@@ -1265,7 +1279,7 @@ sub git_diff_print {
 sub git_project_list {
        my $order = $cgi->param('o');
        if (defined $order && $order !~ m/project|descr|owner|age/) {
-               die_error(undef, "Invalid order parameter '$order'");
+               die_error(undef, "Unknown order parameter");
        }
 
        my @list = git_read_projects();
@@ -1484,7 +1498,7 @@ sub git_blame2 {
        git_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
        git_header_div('commit', esc_html($co{'title'}), $hash_base);
        git_print_page_path($file_name, $ftype);
-       my @rev_color = (qw(light dark));
+       my @rev_color = (qw(light2 dark2));
        my $num_colors = scalar(@rev_color);
        my $current_color = 0;
        my $last_rev;
@@ -1583,13 +1597,8 @@ sub git_blame {
                $age_class  = age_class($age);
                $author     = esc_html ($author);
                $author     =~ s/ /&nbsp;/g;
-               # escape tabs
-               while ((my $pos = index($data, "\t")) != -1) {
-                       if (my $count = (8 - ($pos % 8))) {
-                               my $spaces = ' ' x $count;
-                               $data =~ s/\t/$spaces/;
-                       }
-               }
+
+               $data = untabify($data);
                $data = esc_html ($data);
 
                print <<HTML;
@@ -1628,7 +1637,6 @@ sub git_heads {
        git_header_div('summary', $project);
 
        my $taglist = git_read_refs("refs/heads");
-       my $alternate = 0;
        if (defined @$taglist) {
                git_heads_body($taglist, $head);
        }
@@ -1712,12 +1720,7 @@ sub git_blob {
        while (my $line = <$fd>) {
                chomp $line;
                $nr++;
-               while ((my $pos = index($line, "\t")) != -1) {
-                       if (my $count = (8 - ($pos % 8))) {
-                               my $spaces = ' ' x $count;
-                               $line =~ s/\t/$spaces/;
-                       }
-               }
+               $line = untabify($line);
                printf "<div class=\"pre\"><a id=\"l%i\" href=\"#l%i\" class=\"linenr\">%4i</a> %s</div>\n", $nr, $nr, $nr, esc_html($line);
        }
        close $fd or print "Reading blob failed.\n";
@@ -1841,9 +1844,9 @@ sub git_log {
                next if !%co;
                my %ad = date_str($co{'author_epoch'});
                git_header_div('commit',
-                                                                        "<span class=\"age\">$co{'age_string'}</span>" .
-                                                                        esc_html($co{'title'}) . $ref,
-                                                                        $commit);
+                              "<span class=\"age\">$co{'age_string'}</span>" .
+                              esc_html($co{'title'}) . $ref,
+                              $commit);
                print "<div class=\"title_text\">\n" .
                      "<div class=\"log_link\">\n" .
                      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") .
@@ -1908,8 +1911,8 @@ sub git_commit {
        }
        git_header_html(undef, $expires);
        git_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff',
-                                                        $hash, $co{'tree'}, $hash,
-                                                        $formats_nav);
+                    $hash, $co{'tree'}, $hash,
+                    $formats_nav);
 
        if (defined $co{'parent'}) {
                git_header_div('commitdiff', esc_html($co{'title'}) . $ref, $hash);
@@ -1985,7 +1988,7 @@ sub git_commit {
        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(.*)$/)) {
+               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;
@@ -2012,11 +2015,11 @@ sub git_commit {
                              "<td class=\"link\">" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, "blob") . "</td>\n";
                } elsif ($status eq "D") {
                        print "<td>" .
-                             $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)) . "</td>\n" .
+                             $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$parent;f=$file"), -class => "list"}, esc_html($file)) . "</td>\n" .
                              "<td><span class=\"file_status deleted\">[deleted " . file_type($from_mode). "]</span></td>\n" .
                              "<td class=\"link\">" .
-                             $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, "blob") .
-                             " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash;f=$file")}, "history") .
+                             $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$parent;f=$file")}, "blob") .
+                             " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$parent;f=$file")}, "history") .
                              "</td>\n"
                } elsif ($status eq "M" || $status eq "T") {
                        my $mode_chnge = "";
@@ -2058,7 +2061,7 @@ sub git_commit {
                        print "<td>" .
                              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$to_file"), -class => "list"}, esc_html($to_file)) . "</td>\n" .
                              "<td><span class=\"file_status moved\">[moved from " .
-                             $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$from_file"), -class => "list"}, esc_html($from_file)) .
+                             $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$parent;f=$from_file"), -class => "list"}, esc_html($from_file)) .
                              " with " . (int $similarity) . "% similarity$mode_chng]</span></td>\n" .
                              "<td class=\"link\">" .
                              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$to_file")}, "blob");
@@ -2111,7 +2114,7 @@ sub git_commitdiff {
                die_error(undef, "Unknown commit object");
        }
        if (!defined $hash_parent) {
-               $hash_parent = $co{'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");
@@ -2158,7 +2161,9 @@ sub git_commitdiff {
        foreach my $line (@difftree) {
                # ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M      ls-files.c'
                # ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M      rev-tree.c'
-               $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/;
+               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;
@@ -2172,15 +2177,17 @@ sub git_commitdiff {
                        git_diff_print(undef, "/dev/null", $to_id, "b/$file");
                } elsif ($status eq "D") {
                        print "<div class=\"diff_info\">" . file_type($from_mode) . ":" .
-                             $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, $from_id) . "(deleted)" .
+                             $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash_parent;f=$file")}, $from_id) . "(deleted)" .
                              "</div>\n";
                        git_diff_print($from_id, "a/$file", undef, "/dev/null");
                } elsif ($status eq "M") {
                        if ($from_id ne $to_id) {
                                print "<div class=\"diff_info\">" .
-                                     file_type($from_mode) . ":" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, $from_id) .
+                                     file_type($from_mode) . ":" .
+                                     $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash_parent;f=$file")}, $from_id) .
                                      " -> " .
-                                     file_type($to_mode) . ":" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, $to_id);
+                                     file_type($to_mode) . ":" .
+                                     $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, $to_id);
                                print "</div>\n";
                                git_diff_print($from_id, "a/$file",  $to_id, "b/$file");
                        }
@@ -2193,6 +2200,13 @@ sub git_commitdiff {
 
 sub git_commitdiff_plain {
        mkdir($git_temp, 0700);
+       my %co = git_read_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>;
@@ -2214,7 +2228,6 @@ sub git_commitdiff_plain {
        }
 
        print $cgi->header(-type => "text/plain", -charset => 'utf-8', '-content-disposition' => "inline; filename=\"git-$hash.patch\"");
-       my %co = git_read_commit($hash);
        my %ad = date_str($co{'author_epoch'}, $co{'author_tz'});
        my $comment = $co{'comment'};
        print "From: $co{'author'}\n" .
@@ -2232,7 +2245,9 @@ sub git_commitdiff_plain {
        print "---\n\n";
 
        foreach my $line (@difftree) {
-               $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/;
+               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;