Merge branch 'maint'
[gitweb.git] / gitweb / gitweb.perl
index e788ef90c981e93f59400332da8bb7de1c2b3952..5e88637b5e0e67143692bac336a57ab6cc1c0c38 100755 (executable)
@@ -695,10 +695,9 @@ sub validate_refname {
 # in utf-8 thanks to "binmode STDOUT, ':utf8'" at beginning
 sub to_utf8 {
        my $str = shift;
-       my $res;
-       eval { $res = decode_utf8($str, Encode::FB_CROAK); };
-       if (defined $res) {
-               return $res;
+       if (utf8::valid($str)) {
+               utf8::decode($str);
+               return $str;
        } else {
                return decode($fallback_encoding, $str, Encode::FB_DEFAULT);
        }
@@ -1512,7 +1511,7 @@ sub config_to_int {
 sub config_to_multi {
        my $val = shift;
 
-       return ref($val) ? $val : [ $val ];
+       return ref($val) ? $val : (defined($val) ? [ $val ] : []);
 }
 
 sub git_get_project_config {
@@ -1607,7 +1606,7 @@ sub git_get_project_description {
        my $path = shift;
 
        $git_dir = "$projectroot/$path";
-       open my $fd, "$projectroot/$path/description"
+       open my $fd, "$git_dir/description"
                or return git_get_project_config('description');
        my $descr = <$fd>;
        close $fd;
@@ -1621,7 +1620,7 @@ sub git_get_project_url_list {
        my $path = shift;
 
        $git_dir = "$projectroot/$path";
-       open my $fd, "$projectroot/$path/cloneurl"
+       open my $fd, "$git_dir/cloneurl"
                or return wantarray ?
                @{ config_to_multi(git_get_project_config('url')) } :
                   config_to_multi(git_get_project_config('url'));
@@ -2234,6 +2233,7 @@ sub git_get_heads_list {
                my ($hash, $name, $title) = split(' ', $refinfo, 3);
                my ($committer, $epoch, $tz) =
                        ($committerinfo =~ /^(.*) ([0-9]+) (.*)$/);
+               $ref_item{'fullname'}  = $name;
                $name =~ s!^refs/heads/!!;
 
                $ref_item{'name'}  = $name;
@@ -2271,6 +2271,7 @@ sub git_get_tags_list {
                my ($id, $type, $name, $refid, $reftype, $title) = split(' ', $refinfo, 6);
                my ($creator, $epoch, $tz) =
                        ($creatorinfo =~ /^(.*) ([0-9]+) (.*)$/);
+               $ref_item{'fullname'} = $name;
                $name =~ s!^refs/tags/!!;
 
                $ref_item{'type'} = $type;
@@ -3544,7 +3545,7 @@ sub git_shortlog_body {
        $from = 0 unless defined $from;
        $to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
 
-       print "<table class=\"shortlog\" cellspacing=\"0\">\n";
+       print "<table class=\"shortlog\">\n";
        my $alternate = 1;
        for (my $i = $from; $i <= $to; $i++) {
                my %co = %{$commitlist->[$i]};
@@ -3590,7 +3591,7 @@ sub git_history_body {
        $from = 0 unless defined $from;
        $to = $#{$commitlist} unless (defined $to && $to <= $#{$commitlist});
 
-       print "<table class=\"history\" cellspacing=\"0\">\n";
+       print "<table class=\"history\">\n";
        my $alternate = 1;
        for (my $i = $from; $i <= $to; $i++) {
                my %co = %{$commitlist->[$i]};
@@ -3650,7 +3651,7 @@ sub git_tags_body {
        $from = 0 unless defined $from;
        $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to);
 
-       print "<table class=\"tags\" cellspacing=\"0\">\n";
+       print "<table class=\"tags\">\n";
        my $alternate = 1;
        for (my $i = $from; $i <= $to; $i++) {
                my $entry = $taglist->[$i];
@@ -3691,8 +3692,8 @@ sub git_tags_body {
                      "<td class=\"link\">" . " | " .
                      $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'})}, $tag{'reftype'});
                if ($tag{'reftype'} eq "commit") {
-                       print " | " . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'})}, "shortlog") .
-                             " | " . $cgi->a({-href => href(action=>"log", hash=>$tag{'name'})}, "log");
+                       print " | " . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'fullname'})}, "shortlog") .
+                             " | " . $cgi->a({-href => href(action=>"log", hash=>$tag{'fullname'})}, "log");
                } elsif ($tag{'reftype'} eq "blob") {
                        print " | " . $cgi->a({-href => href(action=>"blob_plain", hash=>$tag{'refid'})}, "raw");
                }
@@ -3713,7 +3714,7 @@ sub git_heads_body {
        $from = 0 unless defined $from;
        $to = $#{$headlist} if (!defined $to || $#{$headlist} < $to);
 
-       print "<table class=\"heads\" cellspacing=\"0\">\n";
+       print "<table class=\"heads\">\n";
        my $alternate = 1;
        for (my $i = $from; $i <= $to; $i++) {
                my $entry = $headlist->[$i];
@@ -3727,13 +3728,13 @@ sub git_heads_body {
                $alternate ^= 1;
                print "<td><i>$ref{'age'}</i></td>\n" .
                      ($curr ? "<td class=\"current_head\">" : "<td>") .
-                     $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'name'}),
+                     $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'fullname'}),
                               -class => "list name"},esc_html($ref{'name'})) .
                      "</td>\n" .
                      "<td class=\"link\">" .
-                     $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'name'})}, "shortlog") . " | " .
-                     $cgi->a({-href => href(action=>"log", hash=>$ref{'name'})}, "log") . " | " .
-                     $cgi->a({-href => href(action=>"tree", hash=>$ref{'name'}, hash_base=>$ref{'name'})}, "tree") .
+                     $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'fullname'})}, "shortlog") . " | " .
+                     $cgi->a({-href => href(action=>"log", hash=>$ref{'fullname'})}, "log") . " | " .
+                     $cgi->a({-href => href(action=>"tree", hash=>$ref{'fullname'}, hash_base=>$ref{'name'})}, "tree") .
                      "</td>\n" .
                      "</tr>";
        }
@@ -3750,7 +3751,7 @@ sub git_search_grep_body {
        $from = 0 unless defined $from;
        $to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
 
-       print "<table class=\"grep\" cellspacing=\"0\">\n";
+       print "<table class=\"commit_search\">\n";
        my $alternate = 1;
        for (my $i = $from; $i <= $to; $i++) {
                my %co = %{$commitlist->[$i]};
@@ -3786,6 +3787,8 @@ sub git_search_grep_body {
                      "<td class=\"link\">" .
                      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
                      " | " .
+                     $cgi->a({-href => href(action=>"commitdiff", hash=>$co{'id'})}, "commitdiff") .
+                     " | " .
                      $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree");
                print "</td>\n" .
                      "</tr>\n";
@@ -3891,7 +3894,7 @@ sub git_summary {
        git_print_page_nav('summary','', $head);
 
        print "<div class=\"title\">&nbsp;</div>\n";
-       print "<table cellspacing=\"0\">\n" .
+       print "<table class=\"projects_list\">\n" .
              "<tr><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
              "<tr><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
        if (defined $cd{'rfc2822'}) {
@@ -3912,8 +3915,10 @@ sub git_summary {
 
        if (-s "$projectroot/$project/README.html") {
                if (open my $fd, "$projectroot/$project/README.html") {
-                       print "<div class=\"title\">readme</div>\n";
+                       print "<div class=\"title\">readme</div>\n" .
+                             "<div class=\"readme\">\n";
                        print $_ while (<$fd>);
+                       print "\n</div>\n"; # class="readme"
                        close $fd;
                }
        }
@@ -3965,7 +3970,7 @@ sub git_tag {
 
        git_print_header_div('commit', esc_html($tag{'name'}), $hash);
        print "<div class=\"title_text\">\n" .
-             "<table cellspacing=\"0\">\n" .
+             "<table class=\"object_header\">\n" .
              "<tr>\n" .
              "<td>object</td>\n" .
              "<td>" . $cgi->a({-class => "list", -href => href(action=>$tag{'type'}, hash=>$tag{'object'})},
@@ -4285,7 +4290,7 @@ sub git_blob {
        open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
                or die_error(undef, "Couldn't cat $file_name, $hash");
        my $mimetype = blob_mimetype($fd, $file_name);
-       if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)!) {
+       if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)! && -B $fd) {
                close $fd;
                return git_blob_plain($mimetype);
        }
@@ -4326,16 +4331,7 @@ sub git_blob {
        }
        git_print_page_path($file_name, "blob", $hash_base);
        print "<div class=\"page_body\">\n";
-       if ($mimetype =~ m!^text/!) {
-               my $nr;
-               while (my $line = <$fd>) {
-                       chomp $line;
-                       $nr++;
-                       $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, -nbsp=>1);
-               }
-       } elsif ($mimetype =~ m!^image/!) {
+       if ($mimetype =~ m!^image/!) {
                print qq!<img type="$mimetype"!;
                if ($file_name) {
                        print qq! alt="$file_name" title="$file_name"!;
@@ -4344,6 +4340,15 @@ sub git_blob {
                      href(action=>"blob_plain", hash=>$hash,
                           hash_base=>$hash_base, file_name=>$file_name) .
                      qq!" />\n!;
+       } else {
+               my $nr;
+               while (my $line = <$fd>) {
+                       chomp $line;
+                       $nr++;
+                       $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, -nbsp=>1);
+               }
        }
        close $fd
                or print "Reading blob failed.\n";
@@ -4405,7 +4410,7 @@ sub git_tree {
        }
        git_print_page_path($file_name, 'tree', $hash_base);
        print "<div class=\"page_body\">\n";
-       print "<table cellspacing=\"0\">\n";
+       print "<table class=\"tree\">\n";
        my $alternate = 1;
        # '..' (top directory) link if possible
        if (defined $hash_base &&
@@ -4627,7 +4632,7 @@ sub git_commit {
                git_print_header_div('tree', esc_html($co{'title'}) . $ref, $co{'tree'}, $hash);
        }
        print "<div class=\"title_text\">\n" .
-             "<table cellspacing=\"0\">\n";
+             "<table class=\"object_header\">\n";
        print "<tr><td>author</td><td>" . esc_html($co{'author'}) . "</td></tr>\n".
              "<tr>" .
              "<td></td><td> $ad{'rfc2822'}";
@@ -5043,16 +5048,15 @@ sub git_commitdiff {
                        -expires => $expires,
                        -content_disposition => 'inline; filename="' . "$filename" . '"');
                my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'});
-               print <<TEXT;
-From: $co{'author'}
-Date: $ad{'rfc2822'} ($ad{'tz_local'})
-Subject: $co{'title'}
-TEXT
+               print "From: " . to_utf8($co{'author'}) . "\n";
+               print "Date: $ad{'rfc2822'} ($ad{'tz_local'})\n";
+               print "Subject: " . to_utf8($co{'title'}) . "\n";
+
                print "X-Git-Tag: $tagname\n" if $tagname;
                print "X-Git-Url: " . $cgi->self_url() . "\n\n";
 
                foreach my $line (@{$co{'comment'}}) {
-                       print "$line\n";
+                       print to_utf8($line) . "\n";
                }
                print "---\n\n";
        }
@@ -5226,7 +5230,7 @@ sub git_search {
                git_print_page_nav('','', $hash,$co{'tree'},$hash);
                git_print_header_div('commit', esc_html($co{'title'}), $hash);
 
-               print "<table cellspacing=\"0\">\n";
+               print "<table class=\"pickaxe search\">\n";
                my $alternate = 1;
                $/ = "\n";
                my $git_command = git_cmd_str();
@@ -5293,7 +5297,7 @@ sub git_search {
                git_print_page_nav('','', $hash,$co{'tree'},$hash);
                git_print_header_div('commit', esc_html($co{'title'}), $hash);
 
-               print "<table cellspacing=\"0\">\n";
+               print "<table class=\"grep_search\">\n";
                my $alternate = 1;
                my $matches = 0;
                $/ = "\n";
@@ -5561,7 +5565,7 @@ sub git_feed {
                        or next;
 
                # print element (entry, item)
-               my $co_url = href(-full=>1, action=>"commit", hash=>$commit);
+               my $co_url = href(-full=>1, action=>"commitdiff", hash=>$commit);
                if ($format eq 'rss') {
                        print "<item>\n" .
                              "<title>" . esc_html($co{'title'}) . "</title>\n" .