update to version 2.1.4
authorStricted <Stricted@nexus-irc.de>
Thu, 19 Nov 2015 17:39:47 +0000 (18:39 +0100)
committerStricted <Stricted@nexus-irc.de>
Thu, 19 Nov 2015 17:39:47 +0000 (18:39 +0100)
gitweb.css
index.cgi

index 7144fb205efa719176fba5907963c18ea86de2c6..23e8602c5561526caab72d64dd67634aa18602c9 100644 (file)
@@ -32,6 +32,11 @@ img.avatar {
        vertical-align: middle;
 }
 
+img.blob {
+       max-height: 100%;
+       max-width: 100%;
+}
+
 a.list img.avatar {
        border-style: none;
 }
@@ -268,7 +273,7 @@ tr.color2 { background-color: #f6f6ff; }
 tr.color3 { background-color: #fff6f6; }
 
 td {
-       padding: 0px 5px;
+       padding: 2px 5px;
        font-size: 100%;
        vertical-align: top;
 }
@@ -321,32 +326,43 @@ table.diff_tree span.file_status.copied {
 }
 
 /* noage: "No commits" */
-.noage {
+table.project_list td.noage {
        color: #808080;
        font-style: italic;
 }
 
 /* age2: 60*60*24*2 <= age */
-.age2 {
+table.project_list td.age2, table.blame td.age2 {
        font-style: italic;
 }
 
 /* age1: 60*60*2 <= age < 60*60*24*2 */
-.age1 {
+table.project_list td.age1 {
        color: #009900;
        font-style: italic;
 }
 
+table.blame td.age1 {
+       color: #009900;
+       background: transparent;
+}
+
 /* age0: age < 60*60*2 */
-.age0 {
+table.project_list td.age0 {
        color: #009900;
        font-style: italic;
        font-weight: bold;
 }
 
+table.blame td.age0 {
+       color: #009900;
+       background: transparent;
+       font-weight: bold;
+}
+
 img.graph {
     padding: 0px;
-    margin: 0px;
+    margin: -2px;
     display: block;
 }
 
index f365d897655bd20bca959f6f8a4e33b4b48b3eff..705c6d44a61a32fc4bf9cf99073c4fd7bac5eb22 100644 (file)
--- a/index.cgi
+++ b/index.cgi
@@ -20,6 +20,10 @@ use File::Basename qw(basename);
 use Time::HiRes qw(gettimeofday tv_interval);
 binmode STDOUT, ':utf8';
 
+if (!defined($CGI::VERSION) || $CGI::VERSION < 4.08) {
+       eval 'sub CGI::multi_param { CGI::param(@_) }'
+}
+
 our $t0 = [ gettimeofday() ];
 our $number_of_git_cmds = 0;
 
@@ -27,7 +31,7 @@ BEGIN {
        CGI->compile() if $ENV{'MOD_PERL'};
 }
 
-our $version = "1.8.4.1-Stricted";
+our $version = "2.1.4-Stricted";
 
 our ($my_url, $my_uri, $base_url, $path_info, $home_link);
 sub evaluate_uri {
@@ -552,6 +556,20 @@ our %feature = (
                'sub' => sub { feature_bool('remote_heads', @_) },
                'override' => 0,
                'default' => [0]},
+
+       # Enable showing branches under other refs in addition to heads
+
+       # To set system wide extra branch refs have in $GITWEB_CONFIG
+       # $feature{'extra-branch-refs'}{'default'} = ['dirs', 'of', 'choice'];
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'extra-branch-refs'}{'override'} = 1;
+       # and in project config gitweb.extrabranchrefs = dirs of choice
+       # Every directory is separated with whitespace.
+
+       'extra-branch-refs' => {
+               'sub' => \&feature_extra_branch_refs,
+               'override' => 0,
+               'default' => []},
 );
 
 sub gitweb_get_feature {
@@ -630,6 +648,21 @@ sub feature_avatar {
        return @val ? @val : @_;
 }
 
+sub feature_extra_branch_refs {
+       my (@branch_refs) = @_;
+       my $values = git_get_project_config('extrabranchrefs');
+
+       if ($values) {
+               $values = config_to_multi ($values);
+               @branch_refs = ();
+               foreach my $value (@{$values}) {
+                       push @branch_refs, split /\s+/, $value;
+               }
+       }
+
+       return @branch_refs;
+}
+
 # checking HEAD file with -e is fragile if the repository was
 # initialized long time ago (i.e. symlink HEAD) and was pack-ref'ed
 # and then pruned.
@@ -660,6 +693,18 @@ sub filter_snapshot_fmts {
                !$known_snapshot_formats{$_}{'disabled'}} @fmts;
 }
 
+sub filter_and_validate_refs {
+       my @refs = @_;
+       my %unique_refs = ();
+
+       foreach my $ref (@refs) {
+               die_error(500, "Invalid ref '$ref' in 'extra-branch-refs' feature") unless (is_valid_ref_format($ref));
+               # 'heads' are added implicitly in get_branch_refs().
+               $unique_refs{$ref} = 1 if ($ref ne 'heads');
+       }
+       return sort keys %unique_refs;
+}
+
 # If it is set to code reference, it is code that it is to be run once per
 # request, allowing updating configurations that change with each request,
 # while running other code in config file only once.
@@ -834,7 +879,7 @@ sub evaluate_query_params {
 
        while (my ($name, $symbol) = each %cgi_param_mapping) {
                if ($symbol eq 'opt') {
-                       $input_params{$name} = [ map { decode_utf8($_) } $cgi->param($symbol) ];
+                       $input_params{$name} = [ map { decode_utf8($_) } $cgi->multi_param($symbol) ];
                } else {
                        $input_params{$name} = decode_utf8($cgi->param($symbol));
                }
@@ -998,7 +1043,7 @@ our ($action, $project, $file_name, $file_parent, $hash, $hash_parent, $hash_bas
 sub evaluate_and_validate_params {
        our $action = $input_params{'action'};
        if (defined $action) {
-               if (!validate_action($action)) {
+               if (!is_valid_action($action)) {
                        die_error(400, "Invalid action parameter");
                }
        }
@@ -1006,7 +1051,7 @@ sub evaluate_and_validate_params {
        # parameters which are pathnames
        our $project = $input_params{'project'};
        if (defined $project) {
-               if (!validate_project($project)) {
+               if (!is_valid_project($project)) {
                        undef $project;
                        die_error(404, "No such project");
                }
@@ -1014,21 +1059,21 @@ sub evaluate_and_validate_params {
 
        our $project_filter = $input_params{'project_filter'};
        if (defined $project_filter) {
-               if (!validate_pathname($project_filter)) {
+               if (!is_valid_pathname($project_filter)) {
                        die_error(404, "Invalid project_filter parameter");
                }
        }
 
        our $file_name = $input_params{'file_name'};
        if (defined $file_name) {
-               if (!validate_pathname($file_name)) {
+               if (!is_valid_pathname($file_name)) {
                        die_error(400, "Invalid file parameter");
                }
        }
 
        our $file_parent = $input_params{'file_parent'};
        if (defined $file_parent) {
-               if (!validate_pathname($file_parent)) {
+               if (!is_valid_pathname($file_parent)) {
                        die_error(400, "Invalid file parent parameter");
                }
        }
@@ -1036,21 +1081,21 @@ sub evaluate_and_validate_params {
        # parameters which are refnames
        our $hash = $input_params{'hash'};
        if (defined $hash) {
-               if (!validate_refname($hash)) {
+               if (!is_valid_refname($hash)) {
                        die_error(400, "Invalid hash parameter");
                }
        }
 
        our $hash_parent = $input_params{'hash_parent'};
        if (defined $hash_parent) {
-               if (!validate_refname($hash_parent)) {
+               if (!is_valid_refname($hash_parent)) {
                        die_error(400, "Invalid hash parent parameter");
                }
        }
 
        our $hash_base = $input_params{'hash_base'};
        if (defined $hash_base) {
-               if (!validate_refname($hash_base)) {
+               if (!is_valid_refname($hash_base)) {
                        die_error(400, "Invalid hash base parameter");
                }
        }
@@ -1070,7 +1115,7 @@ sub evaluate_and_validate_params {
 
        our $hash_parent_base = $input_params{'hash_parent_base'};
        if (defined $hash_parent_base) {
-               if (!validate_refname($hash_parent_base)) {
+               if (!is_valid_refname($hash_parent_base)) {
                        die_error(400, "Invalid hash parent base parameter");
                }
        }
@@ -1117,7 +1162,7 @@ sub evaluate_git_dir {
        our $git_dir = "$projectroot/$project" if $project;
 }
 
-our (@snapshot_fmts, $git_avatar);
+our (@snapshot_fmts, $git_avatar, @extra_branch_refs);
 sub configure_gitweb_features {
        # list of supported snapshot formats
        our @snapshot_fmts = gitweb_get_feature('snapshot');
@@ -1135,6 +1180,13 @@ sub configure_gitweb_features {
        } else {
                $git_avatar = '';
        }
+
+       our @extra_branch_refs = gitweb_get_feature('extra-branch-refs');
+       @extra_branch_refs = filter_and_validate_refs (@extra_branch_refs);
+}
+
+sub get_branch_refs {
+       return ('heads', @extra_branch_refs);
 }
 
 # custom error handler: 'die <message>' is Internal Server Error
@@ -1422,28 +1474,31 @@ sub href {
 ## ======================================================================
 ## validation, quoting/unquoting and escaping
 
-sub validate_action {
-       my $input = shift || return undef;
+sub is_valid_action {
+       my $input = shift;
        return undef unless exists $actions{$input};
-       return $input;
+       return 1;
 }
 
-sub validate_project {
-       my $input = shift || return undef;
-       if (!validate_pathname($input) ||
+sub is_valid_project {
+       my $input = shift;
+
+       return unless defined $input;
+       if (!is_valid_pathname($input) ||
                !(-d "$projectroot/$input") ||
                !check_export_ok("$projectroot/$input") ||
                ($strict_export && !project_in_list($input))) {
                return undef;
        } else {
-               return $input;
+               return 1;
        }
 }
 
-sub validate_pathname {
-       my $input = shift || return undef;
+sub is_valid_pathname {
+       my $input = shift;
 
-       # no '.' or '..' as elements of path, i.e. no '.' nor '..'
+       return undef unless defined $input;
+       # no '.' or '..' as elements of path, i.e. no '.' or '..'
        # at the beginning, at the end, and between slashes.
        # also this catches doubled slashes
        if ($input =~ m!(^|/)(|\.|\.\.)(/|$)!) {
@@ -1453,24 +1508,33 @@ sub validate_pathname {
        if ($input =~ m!\0!) {
                return undef;
        }
-       return $input;
+       return 1;
 }
 
-sub validate_refname {
-       my $input = shift || return undef;
+sub is_valid_ref_format {
+       my $input = shift;
 
-       # textual hashes are O.K.
-       if ($input =~ m/^[0-9a-fA-F]{40}$/) {
-               return $input;
-       }
-       # it must be correct pathname
-       $input = validate_pathname($input)
-               or return undef;
+       return undef unless defined $input;
        # restrictions on ref name according to git-check-ref-format
        if ($input =~ m!(/\.|\.\.|[\000-\040\177 ~^:?*\[]|/$)!) {
                return undef;
        }
-       return $input;
+       return 1;
+}
+
+sub is_valid_refname {
+       my $input = shift;
+
+       return undef unless defined $input;
+       # textual hashes are O.K.
+       if ($input =~ m/^[0-9a-fA-F]{40}$/) {
+               return 1;
+       }
+       # it must be correct pathname
+       is_valid_pathname($input) or return undef;
+       # check git-check-ref-format restrictions
+       is_valid_ref_format($input) or return undef;
+       return 1;
 }
 
 # decode sequences of octets in utf8 into Perl's internal form,
@@ -2519,6 +2583,7 @@ sub format_snapshot_links {
 sub get_feed_info {
        my $format = shift || 'Atom';
        my %res = (action => lc($format));
+       my $matched_ref = 0;
 
        # feed links are possible only for project views
        return unless (defined $project);
@@ -2526,12 +2591,17 @@ sub get_feed_info {
        # or don't have specific feed yet (so they should use generic)
        return if (!$action || $action =~ /^(?:tags|heads|forks|tag|search)$/x);
 
-       my $branch;
-       # branches refs uses 'refs/heads/' prefix (fullname) to differentiate
-       # from tag links; this also makes possible to detect branch links
-       if ((defined $hash_base && $hash_base =~ m!^refs/heads/(.*)$!) ||
-           (defined $hash      && $hash      =~ m!^refs/heads/(.*)$!)) {
-               $branch = $1;
+       my $branch = undef;
+       # branches refs uses 'refs/' + $get_branch_refs()[x] + '/' prefix
+       # (fullname) to differentiate from tag links; this also makes
+       # possible to detect branch links
+       for my $ref (get_branch_refs()) {
+               if ((defined $hash_base && $hash_base =~ m!^refs/\Q$ref\E/(.*)$!) ||
+                   (defined $hash      && $hash      =~ m!^refs/\Q$ref\E/(.*)$!)) {
+                       $branch = $1;
+                       $matched_ref = $ref;
+                       last;
+               }
        }
        # find log type for feed description (title)
        my $type = 'log';
@@ -2544,7 +2614,7 @@ sub get_feed_info {
        }
 
        $res{-title} = $type;
-       $res{'hash'} = (defined $branch ? "refs/heads/$branch" : undef);
+       $res{'hash'} = (defined $branch ? "refs/$matched_ref/$branch" : undef);
        $res{'file_name'} = $file_name;
 
        return %res;
@@ -3197,7 +3267,7 @@ sub git_get_last_activity {
             '--format=%(committer)',
             '--sort=-committerdate',
             '--count=1',
-            'refs/heads') or return;
+            map { "refs/$_" } get_branch_refs ()) or return;
        my $most_recent = <$fd>;
        close $fd or return;
        if (defined $most_recent &&
@@ -3648,7 +3718,7 @@ sub parse_from_to_diffinfo {
 
 sub git_get_heads_list {
        my ($limit, @classes) = @_;
-       @classes = ('heads') unless @classes;
+       @classes = get_branch_refs() unless @classes;
        my @patterns = map { "refs/$_" } @classes;
        my @headslist;
 
@@ -3666,9 +3736,16 @@ sub git_get_heads_list {
                my ($committer, $epoch, $tz) =
                        ($committerinfo =~ /^(.*) ([0-9]+) (.*)$/);
                $ref_item{'fullname'}  = $name;
-               $name =~ s!^refs/(?:head|remote)s/!!;
+               my $strip_refs = join '|', map { quotemeta } get_branch_refs();
+               $name =~ s!^refs/($strip_refs|remotes)/!!;
+               $ref_item{'name'} = $name;
+               # for refs neither in 'heads' nor 'remotes' we want to
+               # show their ref dir
+               my $ref_dir = (defined $1) ? $1 : '';
+               if ($ref_dir ne '' and $ref_dir ne 'heads' and $ref_dir ne 'remotes') {
+                   $ref_item{'name'} .= ' (' . $ref_dir . ')';
+               }
 
-               $ref_item{'name'}  = $name;
                $ref_item{'id'}    = $hash;
                $ref_item{'title'} = $title || '(no commit message)';
                $ref_item{'epoch'} = $epoch;
@@ -4031,7 +4108,7 @@ sub print_search_form {
        if ($use_pathinfo) {
                $action .= "/".esc_url($project);
        }
-       print $cgi->startform(-method => "get", -action => $action) .
+       print $cgi->start_form(-method => "get", -action => $action) .
              "<div class=\"search\">\n" .
              (!$use_pathinfo &&
              $cgi->input({-name=>"p", -value=>$project, -type=>"hidden"}) . "\n") .
@@ -4136,6 +4213,7 @@ sub git_footer_html {
                my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime;
                $year += 1900;
                print "<div class=\"page_footer_text\">Copyright &copy; $year, <a href=\"$footer_url\">$footer_label</a></div>\n";
+               
                print $cgi->a({-href => href(project=>undef, action=>"opml",
                                             project_filter => $project_filter),
                              -class => $feed_class}, "OPML") . " ";
@@ -5444,7 +5522,7 @@ sub git_project_search_form {
        }
 
        print "<div class=\"projsearch\">\n";
-       print $cgi->startform(-method => 'get', -action => $my_uri) .
+       print $cgi->start_form(-method => 'get', -action => $my_uri) .
              $cgi->hidden(-name => 'a', -value => 'project_list')  . "\n";
        print $cgi->hidden(-name => 'pf', -value => $project_filter). "\n"
                if (defined $project_filter);
@@ -5805,7 +5883,6 @@ sub git_shortlog_body {
        $from = 0 unless defined $from;
        $to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
 
-
        print "<table class=\"shortlog\" cellspacing=\"0\" cellpadding=\"0\">\n";
        my $alternate = 1;
        my $graph_rand = int(rand(99999));
@@ -5825,7 +5902,7 @@ sub git_shortlog_body {
        if (!defined $page) {
                $page = 0;
        }
-       
+
        print "<tr class=\"header\">\n";
        print "<td colspan=\"2\"><img class=\"graph\" src=\"git_graph.php?r=".$graph_rand.";p=".$project.";h=".$graph_hash.";from=".($from + (100 * $page)).";to=".($to + (100 * $page)).";c=header\" /></td>\n";
        print "<td valign=\"bottom\"><b>Author</b></td>\n";
@@ -6221,7 +6298,7 @@ sub git_search_changes {
                        $alternate ^= 1;
                        %co = parse_commit($set{'commit'});
                        my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
-                       print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+                       print "<td class=\"". age_class($co{'age'}) . "\" title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
                              "<td><i>$author</i></td>\n" .
                              "<td>" .
                              $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
@@ -6355,7 +6432,7 @@ sub git_search_grep_body {
                        print "<tr class=\"light\">\n";
                }
                $alternate ^= 1;
-               print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+               print "<td class=\"". age_class($co{'age'}) . "\" title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
                      format_author_html('td', \%co, 15, 5) .
                      "<td>" .
                      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
@@ -7057,7 +7134,7 @@ sub git_blob {
        git_print_page_path($file_name, "blob", $hash_base);
        print "<div class=\"page_body\">\n";
        if ($mimetype =~ m!^image/!) {
-               print qq!<img type="!.esc_attr($mimetype).qq!"!;
+               print qq!<img class="blob" type="!.esc_attr($mimetype).qq!"!;
                if ($file_name) {
                        print qq! alt="!.esc_attr($file_name).qq!" title="!.esc_attr($file_name).qq!"!;
                }
@@ -7192,6 +7269,15 @@ sub git_tree {
        git_footer_html();
 }
 
+sub sanitize_for_filename {
+    my $name = shift;
+
+    $name =~ s!/!-!g;
+    $name =~ s/[^[:alnum:]_.-]//g;
+
+    return $name;
+}
+
 sub snapshot_name {
        my ($project, $hash) = @_;
 
@@ -7199,9 +7285,7 @@ sub snapshot_name {
        # path/to/project/.git -> project
        my $name = to_utf8($project);
        $name =~ s,([^/])/*\.git$,$1,;
-       $name = basename($name);
-       # sanitize name
-       $name =~ s/[[:cntrl:]]/?/g;
+       $name = sanitize_for_filename(basename($name));
 
        my $ver = $hash;
        if ($hash =~ /^[0-9a-fA-F]+$/) {
@@ -7215,13 +7299,25 @@ sub snapshot_name {
                $ver = $1;
        } else {
                # branches and other need shortened SHA-1 hash
-               if ($hash =~ m!^refs/(?:heads|remotes)/(.*)$!) {
-                       $ver = $1;
+               my $strip_refs = join '|', map { quotemeta } get_branch_refs();
+               if ($hash =~ m!^refs/($strip_refs|remotes)/(.*)$!) {
+                       my $ref_dir = (defined $1) ? $1 : '';
+                       $ver = $2;
+
+                       $ref_dir = sanitize_for_filename($ref_dir);
+                       # for refs neither in heads nor remotes we want to
+                       # add a ref dir to archive name
+                       if ($ref_dir ne '' and $ref_dir ne 'heads' and $ref_dir ne 'remotes') {
+                               $ver = $ref_dir . '-' . $ver;
+                       }
                }
                $ver .= '-' . git_get_short_hash($project, $hash);
        }
+       # special case of sanitization for filename - we change
+       # slashes to dots instead of dashes
        # in case of hierarchical branch names
        $ver =~ s!/!.!g;
+       $ver =~ s/[^[:alnum:]_.-]//g;
 
        # name = project-version_string
        $name = "$name-$ver";
@@ -7310,7 +7406,7 @@ sub git_snapshot {
 }
 
 sub git_log_generic {
-       my ($fmt_name, $body_subr, $base, $parent, $file_name, $file_hash) = @_;
+       my ($fmt_name, $body_subr, $base, $parent, $file_name, $file_hash, $allrefs) = @_;
 
        my $head = git_get_head_hash($project);
        my $allrefs;
@@ -7318,9 +7414,6 @@ sub git_log_generic {
                $base = $head;
                $allrefs = 1;
        }
-       if (!defined $base) {
-               $base = $head;
-       }
        if (!defined $page) {
                $page = 0;
        }
@@ -7329,7 +7422,7 @@ sub git_log_generic {
        my $commit_hash = $base;
        if (defined $allrefs) {
                $commit_hash = "--all";
-       } 
+       }
        if (defined $parent) {
                $commit_hash = "$parent..$base";
        }