prepend '--' to filelist when calling git-diff-tree
[git.git] / gitweb.cgi
index fb0ff76..9ee24b8 100755 (executable)
@@ -2,8 +2,8 @@
 
 # gitweb - simple web interface to track changes in git repositories
 #
-# (C) 2005, Kay Sievers <kay.sievers@vrfy.org>
-# (C) 2005, Christian Gierke <ch@gierke.de>
+# (C) 2005-2006, Kay Sievers <kay.sievers@vrfy.org>
+# (C) 2005, Christian Gierke
 #
 # This program is licensed under the GPLv2
 
@@ -17,7 +17,7 @@ use Fcntl ':mode';
 binmode STDOUT, ':utf8';
 
 my $cgi = new CGI;
-my $version =          "256";
+my $version =          "266";
 my $my_url =           $cgi->url();
 my $my_uri =           $cgi->url(-absolute => 1);
 my $rss_link =         "";
@@ -146,7 +146,7 @@ sub validate_input {
        if ($input =~ m/(^|\/)(|\.|\.\.)($|\/)/) {
                return undef;
        }
-       if ($input =~ m/[^a-zA-Z0-9_\x80-\xff\ \.\/\-\+\#\~\%]/) {
+       if ($input =~ m/[^a-zA-Z0-9_\x80-\xff\ \t\.\/\-\+\#\~\%]/) {
                return undef;
        }
        return $input;
@@ -253,14 +253,17 @@ sub git_header_html {
 <?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">
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
-<!-- git web interface v$version, (C) 2005, Kay Sievers <kay.sievers\@vrfy.org>, Christian Gierke <ch\@gierke.de> -->
+<!-- git web interface v$version, (C) 2005-2006, Kay Sievers <kay.sievers\@vrfy.org>, Christian Gierke -->
 <head>
 <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
 <meta name="robots" content="index, nofollow"/>
 <title>$title</title>
 $rss_link
 <style type="text/css">
-body { font-family: sans-serif; font-size: 12px; margin:0px; border:solid #d9d8d1; border-width:1px; margin:10px; }
+body {
+       font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px;
+       margin:10px; background-color:#ffffff; color:#000000;
+}
 a { color:#0000cc; }
 a:hover, a:visited, a:active { color:#880000; }
 div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
@@ -311,6 +314,10 @@ a.rss_logo {
        text-align:center; text-decoration:none;
 }
 a.rss_logo:hover { background-color:#ee5500; }
+span.tag {
+       padding:0px 4px; font-size:10px; font-weight:normal;
+       background-color:#ffffaa; border:1px solid; border-color:#ffffcc #ffee00 #ffee00 #ffffcc;
+}
 </style>
 </head>
 <body>
@@ -389,6 +396,24 @@ sub git_get_type {
        return $type;
 }
 
+sub git_read_head {
+       my $project = shift;
+       my $oENV = $ENV{'GIT_DIR'};
+       my $retval = undef;
+       $ENV{'GIT_DIR'} = "$projectroot/$project";
+       if (open my $fd, "-|", "$gitbin/git-rev-parse", "--verify", "HEAD") {
+               my $head = <$fd>;
+               close $fd;
+               if (defined $head && $head =~ /^([0-9a-fA-F]{40})$/) {
+                       $retval = $1;
+               }
+       }
+       if (defined $oENV) {
+               $ENV{'GIT_DIR'} = $oENV;
+       }
+       return $retval;
+}
+
 sub git_read_hash {
        my $path = shift;
 
@@ -527,6 +552,7 @@ sub git_read_commit {
        };
 
        foreach my $title (@commit_lines) {
+               $title =~ s/^    //;
                if ($title ne "") {
                        $co{'title'} = chop_str($title, 80, 5);
                        # remove leading stuff of merges to make the interesting part visible
@@ -761,7 +787,7 @@ sub get_file_owner {
        }
        my $owner = $gcos;
        $owner =~ s/[,;].*$//;
-       return $owner;
+       return decode("utf8", $owner, Encode::FB_DEFAULT);
 }
 
 sub git_read_projects {
@@ -797,7 +823,7 @@ sub git_read_projects {
                        if (-e "$projectroot/$path/HEAD") {
                                my $pr = {
                                        path => $path,
-                                       owner => $owner,
+                                       owner => decode("utf8", $owner, Encode::FB_DEFAULT),
                                };
                                push @list, $pr
                        }
@@ -815,7 +841,7 @@ sub git_project_list {
                die_error(undef, "No project found.");
        }
        foreach my $pr (@list) {
-               my $head = git_read_hash("$pr->{'path'}/HEAD");
+               my $head = git_read_head($pr->{'path'});
                if (!defined $head) {
                        next;
                }
@@ -901,6 +927,26 @@ sub git_project_list {
        git_footer_html();
 }
 
+sub read_info_ref {
+       my $type = shift || "";
+       my %refs;
+       # 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c      refs/tags/v2.6.11
+       # c39ae07f393806ccf406ef966e9a15afc43cc36a      refs/tags/v2.6.11^{}
+       open my $fd, "$projectroot/$project/info/refs" or return;
+       while (my $line = <$fd>) {
+               chomp($line);
+               if ($line =~ m/^([0-9a-fA-F]{40})\t.*$type\/([^\^]+)/) {
+                       if (defined $refs{$1}) {
+                               $refs{$1} .= " / $2";
+                       } else {
+                               $refs{$1} = $2;
+                       }
+               }
+       }
+       close $fd or return;
+       return \%refs;
+}
+
 sub git_read_refs {
        my $ref_dir = shift;
        my @reflist;
@@ -966,7 +1012,7 @@ sub git_read_refs {
 
 sub git_summary {
        my $descr = git_read_description($project) || "none";
-       my $head = git_read_hash("$project/HEAD");
+       my $head = git_read_head($project);
        my %co = git_read_commit($head);
        my %cd = date_str($co{'committer_epoch'}, $co{'committer_tz'});
 
@@ -979,7 +1025,7 @@ sub git_summary {
                        $pr = unescape($pr);
                        $ow = unescape($ow);
                        if ($pr eq $project) {
-                               $owner = $ow;
+                               $owner = decode("utf8", $ow, Encode::FB_DEFAULT);
                                last;
                        }
                }
@@ -989,6 +1035,7 @@ sub git_summary {
                $owner = get_file_owner("$projectroot/$project");
        }
 
+       my $refs = read_info_ref();
        git_header_html();
        print "<div class=\"page_nav\">\n" .
              "summary".
@@ -1005,7 +1052,7 @@ sub git_summary {
              "<tr><td>owner</td><td>$owner</td></tr>\n" .
              "<tr><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n" .
              "</table>\n";
-       open my $fd, "-|", "$gitbin/git-rev-list --max-count=17 " . git_read_hash("$project/HEAD") or die_error(undef, "Open failed.");
+       open my $fd, "-|", "$gitbin/git-rev-list --max-count=17 " . git_read_head($project) or die_error(undef, "Open failed.");
        my (@revlist) = map { chomp; $_ } <$fd>;
        close $fd;
        print "<div>\n" .
@@ -1024,15 +1071,19 @@ sub git_summary {
                }
                $alternate ^= 1;
                if ($i-- > 0) {
+                       my $ref = "";
+                       if (defined $refs->{$commit}) {
+                               $ref = " <span class=\"tag\">" . esc_html($refs->{$commit}) . "</span>";
+                       }
                        print "<td><i>$co{'age_string'}</i></td>\n" .
                              "<td><i>" . esc_html(chop_str($co{'author_name'}, 10)) . "</i></td>\n" .
                              "<td>";
                        if (length($co{'title_short'}) < length($co{'title'})) {
                                print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list", -title => "$co{'title'}"},
-                                     "<b>" . esc_html($co{'title_short'}) . "</b>");
+                                     "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
                        } else {
                                print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list"},
-                                     "<b>" . esc_html($co{'title'}) . "</b>");
+                                     "<b>" . esc_html($co{'title'}) . "$ref</b>");
                        }
                        print "</td>\n" .
                              "<td class=\"link\">" .
@@ -1139,7 +1190,7 @@ sub git_summary {
 }
 
 sub git_tag {
-       my $head = git_read_hash("$project/HEAD");
+       my $head = git_read_head($project);
        git_header_html();
        print "<div class=\"page_nav\">\n" .
              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
@@ -1178,7 +1229,7 @@ sub git_tag {
 }
 
 sub git_tags {
-       my $head = git_read_hash("$project/HEAD");
+       my $head = git_read_head($project);
        git_header_html();
        print "<div class=\"page_nav\">\n" .
              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
@@ -1237,7 +1288,7 @@ sub git_tags {
 }
 
 sub git_heads {
-       my $head = git_read_hash("$project/HEAD");
+       my $head = git_read_head($project);
        git_header_html();
        print "<div class=\"page_nav\">\n" .
              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
@@ -1310,7 +1361,7 @@ sub git_get_hash_by_path {
 
 sub git_blob {
        if (!defined $hash && defined $file_name) {
-               my $base = $hash_base || git_read_hash("$project/HEAD");
+               my $base = $hash_base || git_read_head($project);
                $hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file.");
        }
        open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or die_error(undef, "Open failed.");
@@ -1374,13 +1425,13 @@ sub git_blob_plain {
 
 sub git_tree {
        if (!defined $hash) {
-               $hash = git_read_hash("$project/HEAD");
+               $hash = git_read_head($project);
                if (defined $file_name) {
-                       my $base = $hash_base || git_read_hash("$project/HEAD");
+                       my $base = $hash_base || $hash;
                        $hash = git_get_hash_by_path($base, $file_name, "tree");
                }
                if (!defined $hash_base) {
-                       $hash_base = git_read_hash("$project/HEAD");
+                       $hash_base = $hash;
                }
        }
        $/ = "\0";
@@ -1389,6 +1440,11 @@ sub git_tree {
        close $fd or die_error(undef, "Reading tree failed.");
        $/ = "\n";
 
+       my $refs = read_info_ref();
+       my $ref = "";
+       if (defined $refs->{$hash_base}) {
+               $ref = " <span class=\"tag\">" . esc_html($refs->{$hash_base}) . "</span>";
+       }
        git_header_html();
        my $base_key = "";
        my $base = "";
@@ -1404,7 +1460,7 @@ sub git_tree {
                      "<br/><br/>\n" .
                      "</div>\n";
                print "<div>\n" .
-                     $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . "\n" .
+                     $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'}) . $ref) . "\n" .
                      "</div>\n";
        } else {
                print "<div class=\"page_nav\">\n";
@@ -1459,7 +1515,7 @@ sub git_tree {
 
 sub git_rss {
        # http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
-       open my $fd, "-|", "$gitbin/git-rev-list --max-count=150 " . git_read_hash("$project/HEAD") or die_error(undef, "Open failed.");
+       open my $fd, "-|", "$gitbin/git-rev-list --max-count=150 " . git_read_head($project) or die_error(undef, "Open failed.");
        my (@revlist) = map { chomp; $_ } <$fd>;
        close $fd or die_error(undef, "Reading rev-list failed.");
        print $cgi->header(-type => 'text/xml', -charset => 'utf-8');
@@ -1528,7 +1584,7 @@ sub git_opml {
 
        foreach my $pr (@list) {
                my %proj = %$pr;
-               my $head = git_read_hash("$proj{'path'}/HEAD");
+               my $head = git_read_head($proj{'path'});
                if (!defined $head) {
                        next;
                }
@@ -1549,13 +1605,14 @@ sub git_opml {
 }
 
 sub git_log {
-       my $head = git_read_hash("$project/HEAD");
+       my $head = git_read_head($project);
        if (!defined $hash) {
                $hash = $head;
        }
        if (!defined $page) {
                $page = 0;
        }
+       my $refs = read_info_ref();
        git_header_html();
        print "<div class=\"page_nav\">\n";
        print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
@@ -1598,13 +1655,17 @@ sub git_log {
        }
        for (my $i = ($page * 100); $i <= $#revlist; $i++) {
                my $commit = $revlist[$i];
+               my $ref = "";
+               if (defined $refs->{$commit}) {
+                       $ref = " <span class=\"tag\">" . esc_html($refs->{$commit}) . "</span>";
+               }
                my %co = git_read_commit($commit);
                next if !%co;
                my %ad = date_str($co{'author_epoch'});
                print "<div>\n" .
                      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "title"},
-                     "<span class=\"age\">$co{'age_string'}</span>" . esc_html($co{'title'})) . "\n" .
-                     "</div>\n";
+                     "<span class=\"age\">$co{'age_string'}</span>" . esc_html($co{'title'}) . $ref) . "\n";
+               print "</div>\n";
                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") .
@@ -1662,6 +1723,11 @@ sub git_commit {
        if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
                $expires = "+1d";
        }
+       my $refs = read_info_ref();
+       my $ref = "";
+       if (defined $refs->{$co{'id'}}) {
+               $ref = " <span class=\"tag\">" . esc_html($refs->{$co{'id'}}) . "</span>";
+       }
        git_header_html(undef, $expires);
        print "<div class=\"page_nav\">\n" .
              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
@@ -1675,7 +1741,7 @@ sub git_commit {
              "<br/><br/></div>\n";
        if (defined $co{'parent'}) {
                print "<div>\n" .
-                     $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash"), -class => "title"}, esc_html($co{'title'})) . "\n" .
+                     $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash"), -class => "title"}, esc_html($co{'title'}) . $ref) . "\n" .
                      "</div>\n";
        } else {
                print "<div>\n" .
@@ -1899,6 +1965,11 @@ sub git_commitdiff {
        if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
                $expires = "+1d";
        }
+       my $refs = read_info_ref();
+       my $ref = "";
+       if (defined $refs->{$co{'id'}}) {
+               $ref = " <span class=\"tag\">" . esc_html($refs->{$co{'id'}}) . "</span>";
+       }
        git_header_html(undef, $expires);
        print "<div class=\"page_nav\">\n" .
              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
@@ -1910,7 +1981,7 @@ sub git_commitdiff {
        print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff_plain;h=$hash;hp=$hash_parent")}, "plain") . "\n" .
              "</div>\n";
        print "<div>\n" .
-             $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($co{'title'})) . "\n" .
+             $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($co{'title'}) . $ref) . "\n" .
              "</div>\n";
        print "<div class=\"page_body\">\n";
        my $comment = $co{'comment'};
@@ -1981,23 +2052,18 @@ sub git_commitdiff_plain {
 
        # try to figure out the next tag after this commit
        my $tagname;
-       my %taghash;
-       my $tags = git_read_refs("refs/tags");
-       foreach my $entry (@$tags) {
-               my %tag = %$entry;
-               $taghash{$tag{'refid'}} = $tag{'name'};
-       }
+       my $refs = read_info_ref("tags");
        open $fd, "-|", "$gitbin/git-rev-list HEAD";
-       while (my $commit = <$fd>) {
-               chomp $commit;
-               if ($taghash{$commit}) {
-                       $tagname = $taghash{$commit};
+       chomp (my (@commits) = <$fd>);
+       close $fd;
+       foreach my $commit (@commits) {
+               if (defined $refs->{$commit}) {
+                       $tagname = $refs->{$commit}
                }
                if ($commit eq $hash) {
                        last;
                }
        }
-       close $fd;
 
        print $cgi->header(-type => "text/plain", -charset => 'utf-8', '-content-disposition' => "inline; filename=\"git-$hash.patch\"");
        my %co = git_read_commit($hash);
@@ -2013,7 +2079,7 @@ sub git_commitdiff_plain {
              "\n";
 
        foreach my $line (@$comment) {;
-               print "  $line\n";
+               print "$line\n";
        }
        print "---\n\n";
 
@@ -2035,12 +2101,13 @@ sub git_commitdiff_plain {
 
 sub git_history {
        if (!defined $hash) {
-               $hash = git_read_hash("$project/HEAD");
+               $hash = git_read_head($project);
        }
        my %co = git_read_commit($hash);
        if (!%co) {
                die_error(undef, "Unknown commit object.");
        }
+       my $refs = read_info_ref();
        git_header_html();
        print "<div class=\"page_nav\">\n" .
              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
@@ -2056,7 +2123,7 @@ sub git_history {
              "</div>\n";
        print "<div class=\"page_path\"><b>/" . esc_html($file_name) . "</b><br/></div>\n";
 
-       open my $fd, "-|", "$gitbin/git-rev-list $hash | $gitbin/git-diff-tree -r --stdin \'$file_name\'";
+       open my $fd, "-|", "$gitbin/git-rev-list $hash | $gitbin/git-diff-tree -r --stdin -- \'$file_name\'";
        my $commit;
        print "<table cellspacing=\"0\">\n";
        my $alternate = 0;
@@ -2070,6 +2137,10 @@ sub git_history {
                        if (!%co) {
                                next;
                        }
+                       my $ref = "";
+                       if (defined $refs->{$commit}) {
+                               $ref = " <span class=\"tag\">" . esc_html($refs->{$commit}) . "</span>";
+                       }
                        if ($alternate) {
                                print "<tr class=\"dark\">\n";
                        } else {
@@ -2079,7 +2150,7 @@ sub git_history {
                        print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
                              "<td><i>" . esc_html(chop_str($co{'author_name'}, 15, 3)) . "</i></td>\n" .
                              "<td>" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list"}, "<b>" .
-                             esc_html(chop_str($co{'title'}, 50)) . "</b>") . "</td>\n" .
+                             esc_html(chop_str($co{'title'}, 50)) . "$ref</b>") . "</td>\n" .
                              "<td class=\"link\">" .
                              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") .
                              " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") .
@@ -2106,7 +2177,7 @@ sub git_search {
                die_error("", "Text field empty.");
        }
        if (!defined $hash) {
-               $hash = git_read_hash("$project/HEAD");
+               $hash = git_read_head($project);
        }
        my %co = git_read_commit($hash);
        if (!%co) {
@@ -2247,13 +2318,14 @@ sub git_search {
 }
 
 sub git_shortlog {
-       my $head = git_read_hash("$project/HEAD");
+       my $head = git_read_head($project);
        if (!defined $hash) {
                $hash = $head;
        }
        if (!defined $page) {
                $page = 0;
        }
+       my $refs = read_info_ref();
        git_header_html();
        print "<div class=\"page_nav\">\n" .
              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
@@ -2294,6 +2366,10 @@ sub git_shortlog {
        my $alternate = 0;
        for (my $i = ($page * 100); $i <= $#revlist; $i++) {
                my $commit = $revlist[$i];
+               my $ref = "";
+               if (defined $refs->{$commit}) {
+                       $ref = " <span class=\"tag\">" . esc_html($refs->{$commit}) . "</span>";
+               }
                my %co = git_read_commit($commit);
                my %ad = date_str($co{'author_epoch'});
                if ($alternate) {
@@ -2307,10 +2383,10 @@ sub git_shortlog {
                      "<td>";
                if (length($co{'title_short'}) < length($co{'title'})) {
                        print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list", -title => "$co{'title'}"},
-                             "<b>" . esc_html($co{'title_short'}) . "</b>");
+                             "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
                } else {
                        print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list"},
-                             "<b>" . esc_html($co{'title_short'}) . "</b>");
+                             "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
                }
                print "</td>\n" .
                      "<td class=\"link\">" .