add newest gitweb files
authorStricted <info@nexus-irc.de>
Sun, 23 Jun 2013 18:21:00 +0000 (20:21 +0200)
committerStricted <info@nexus-irc.de>
Sun, 23 Jun 2013 18:21:00 +0000 (20:21 +0200)
gitweb.css
index.cgi [changed mode: 0644->0755]

index 44a65daacce1153ee6c0a3b86de8b77a3be53121..8b0811d5324d139773113711d6e59d285ed25ba2 100644 (file)
@@ -195,18 +195,6 @@ table {
        border-spacing: 0;
 }
 
-table.shortlog td {
-    padding: 0px 8px;
-    vertical-align: middle;
-    height:20px;
-}
-
-img.graph {
-    padding: 0px;
-    margin: 0px;
-    display: block;
-}
-
 table.diff_tree {
        font-family: monospace;
 }
@@ -260,14 +248,6 @@ table.blame .dark:hover {
        background-color: #f6f6f0;
 }
 
-tr.header {
-    background-color: #d9d8d1;
-}
-
-tr.header td {
-    vertical-align: center;
-}
-
 /* currently both use the same, but it can change */
 tr.light:hover,
 tr.dark:hover {
@@ -287,7 +267,7 @@ tr.color2 { background-color: #f6f6ff; }
 tr.color3 { background-color: #fff6f6; }
 
 td {
-       padding: 2px 5px;
+       padding: 0px 5px;
        font-size: 100%;
        vertical-align: top;
 }
@@ -353,17 +333,22 @@ table.diff_tree span.file_status.copied {
 /* age1: 60*60*2 <= age < 60*60*24*2 */
 .age1 {
        color: #009900;
-       background: transparent;
+       font-style: italic;
 }
 
 /* age0: age < 60*60*2 */
-
 .age0 {
        color: #009900;
-       background: transparent;
+       font-style: italic;
        font-weight: bold;
 }
 
+img.graph {
+    padding: 0px;
+    margin: 0px;
+    display: block;
+}
+
 td.pre, div.pre, div.diff {
        font-family: monospace;
        font-size: 12px;
@@ -441,39 +426,47 @@ div.diff a.hash:hover {
 
 div.diff.to_file a.path,
 div.diff.to_file {
-       color: #000000;
+       color: #007000;
+}
+
+div.diff.add {
+       color: #008800;
+}
+
+div.diff.add span.marked {
+       background-color: #aaffaa;
 }
 
 div.diff.from_file a.path,
 div.diff.from_file {
-       color: #000000;
+       color: #aa0000;
 }
-.rem ,
-.from_file {
-  background-color: #ffdddd;
+
+div.diff.rem {
+       color: #cc0000;
 }
-.add ,
-.to_file{
-  background-color: #ddffdd;
+
+div.diff.rem span.marked {
+       background-color: #ffaaaa;
 }
 
 div.diff.chunk_header a,
 div.diff.chunk_header {
-       color: #999;
+       color: #990099;
 }
 
+div.diff.chunk_header {
+       border: dotted #ffe0ff;
+       border-width: 1px 0px 0px 0px;
+       margin-top: 2px;
+}
 
 div.diff.chunk_header span.chunk_info {
-  background: #eaf2f5;
-  color: #999;
-}
-.chunk_header {
-  background: #eaf2f5;
-  color: #999;
+       background-color: #ffeeff;
 }
 
 div.diff.chunk_header span.section {
-       color: #999;
+       color: #aa22aa;
 }
 
 div.diff.incomplete {
@@ -514,6 +507,7 @@ div.chunk_block.ctx div     div.diff.ctx {
        color: #404040;
 }
 
+
 div.index_include {
        border: solid #d9d8d1;
        border-width: 0px 0px 1px;
@@ -529,7 +523,7 @@ div.search {
        right: 12px
 }
 
-.projsearch {
+div.projsearch {
        text-align: center;
        margin: 20px 0px;
 }
@@ -662,6 +656,7 @@ div.remote {
        cursor: pointer;
 }
 
+
 /* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */
 
 /* Highlighting theme definition: */
old mode 100644 (file)
new mode 100755 (executable)
index aa1d860..735dfc8
--- a/index.cgi
+++ b/index.cgi
@@ -27,7 +27,7 @@ BEGIN {
        CGI->compile() if $ENV{'MOD_PERL'};
 }
 
-our $version = "1.7.10.4-Stricted";
+our $version = "1.8.1.2-Stricted";
 
 our ($my_url, $my_uri, $base_url, $path_info, $home_link);
 sub evaluate_uri {
@@ -54,6 +54,11 @@ sub evaluate_uri {
        # to build the base URL ourselves:
        our $path_info = decode_utf8($ENV{"PATH_INFO"});
        if ($path_info) {
+               # $path_info has already been URL-decoded by the web server, but
+               # $my_url and $my_uri have not. URL-decode them so we can properly
+               # strip $path_info.
+               $my_url = unescape($my_url);
+               $my_uri = unescape($my_uri);
                if ($my_url =~ s,\Q$path_info\E$,, &&
                    $my_uri =~ s,\Q$path_info\E$,, &&
                    defined $ENV{'SCRIPT_NAME'}) {
@@ -110,11 +115,9 @@ our $javascript = "static/gitweb.js";
 #our $logo_label = "git documentation";
 our $logo_url = "http://git-scm.com/";
 our $logo_label = "git homepage";
-
 # URI and label (title) of footer
 our $footer_url = "http://git-scm.com/";
 our $footer_label = "git homepage";
-
 # source of projects list
 our $projects_list = "";
 
@@ -137,6 +140,12 @@ our $default_projects_order = "project";
 # (only effective if this variable evaluates to true)
 our $export_ok = "";
 
+# don't generate age column on the projects list page
+our $omit_age_column = 0;
+
+# don't generate information about owners of repositories
+our $omit_owner=0;
+
 # show repository only if this subroutine returns true
 # when given the path to the project, for example:
 #    sub { return -e "$_[0]/git-daemon-export-ok"; }
@@ -162,7 +171,7 @@ our $mimetypes_file = undef;
 # for which encoding all byte sequences are valid, for example
 # 'iso-8859-1' aka 'latin1' (it is decoded without checking, so it
 # could be even 'utf-8' for the old behavior)
-our $fallback_encoding = 'iso-8859-1';
+our $fallback_encoding = 'latin1';
 
 # rename detection options for git-diff and git-diff-tree
 # - default is '-M', with the cost proportional to
@@ -263,16 +272,15 @@ our %highlight_basename = (
 our %highlight_ext = (
        # main extensions, defining name of syntax;
        # see files in /usr/share/highlight/langDefs/ directory
-       map { $_ => $_ }
-               qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl sql make),
+       (map { $_ => $_ } qw(py rb java css js tex bib xml awk bat ini spec tcl sql)),
        # alternate extensions, see /etc/highlight/filetypes.conf
-       'h' => 'c',
-       map { $_ => 'sh'  } qw(bash zsh ksh),
-       map { $_ => 'cpp' } qw(cxx c++ cc),
-       map { $_ => 'php' } qw(php3 php4 php5 phps),
-       map { $_ => 'pl'  } qw(perl pm cgi),
-       map { $_ => 'make'} qw(mak mk),
-       map { $_ => 'xml' } qw(xhtml html htm),
+       (map { $_ => 'c'   } qw(c h)),
+       (map { $_ => 'sh'  } qw(sh bash zsh ksh)),
+       (map { $_ => 'cpp' } qw(cpp cxx c++ cc)),
+       (map { $_ => 'php' } qw(php php3 php4 php5 phps)),
+       (map { $_ => 'pl'  } qw(pl perl pm)), # perhaps also 'cgi'
+       (map { $_ => 'make'} qw(make mak mk)),
+       (map { $_ => 'xml' } qw(xml xhtml html htm)),
 );
 
 # You define site-wide feature defaults here; override them with
@@ -522,6 +530,7 @@ our %feature = (
 
        # To enable system wide have in $GITWEB_CONFIG
        # $feature{'highlight'}{'default'} = [1];
+
        'highlight' => {
                'sub' => sub { feature_bool('highlight', @_) },
                'override' => 0,
@@ -533,7 +542,7 @@ our %feature = (
        # $feature{'remote_heads'}{'default'} = [1];
        # To have project specific config enable override in $GITWEB_CONFIG
        # $feature{'remote_heads'}{'override'} = 1;
-       # and in project config gitweb.remote_heads = 0|1;
+       # and in project config gitweb.remoteheads = 0|1;
        'remote_heads' => {
                'sub' => sub { feature_bool('remote_heads', @_) },
                'override' => 0,
@@ -1549,7 +1558,7 @@ sub sanitize {
        return undef unless defined $str;
 
        $str = to_utf8($str);
-       $str =~ s|([[:cntrl:]])|($1 =~ /[\t\n\r]/ ? $1 : quot_cec($1))|eg;
+       $str =~ s|([[:cntrl:]])|(index("\t\n\r", $1) != -1 ? $1 : quot_cec($1))|eg;
        return $str;
 }
 
@@ -1735,20 +1744,29 @@ sub chop_and_escape_str {
 # '<span class="mark">foo</span>bar'
 sub esc_html_hl_regions {
        my ($str, $css_class, @sel) = @_;
-       return esc_html($str) unless @sel;
+       my %opts = grep { ref($_) ne 'ARRAY' } @sel;
+       @sel     = grep { ref($_) eq 'ARRAY' } @sel;
+       return esc_html($str, %opts) unless @sel;
 
        my $out = '';
        my $pos = 0;
 
        for my $s (@sel) {
-               $out .= esc_html(substr($str, $pos, $s->[0] - $pos))
-                       if ($s->[0] - $pos > 0);
-               $out .= $cgi->span({-class => $css_class},
-                                  esc_html(substr($str, $s->[0], $s->[1] - $s->[0])));
+               my ($begin, $end) = @$s;
 
-               $pos = $s->[1];
+               # Don't create empty <span> elements.
+               next if $end <= $begin;
+
+               my $escaped = esc_html(substr($str, $begin, $end - $begin),
+                                      %opts);
+
+               $out .= esc_html(substr($str, $pos, $begin - $pos), %opts)
+                       if ($begin - $pos > 0);
+               $out .= $cgi->span({-class => $css_class}, $escaped);
+
+               $pos = $end;
        }
-       $out .= esc_html(substr($str, $pos))
+       $out .= esc_html(substr($str, $pos), %opts)
                if ($pos < length($str));
 
        return $out;
@@ -2424,26 +2442,32 @@ sub format_cc_diff_chunk_header {
 }
 
 # process patch (diff) line (not to be used for diff headers),
-# returning class and HTML-formatted (but not wrapped) line
-sub process_diff_line {
-       my $line = shift;
-       my ($from, $to) = @_;
-
-       my $diff_class = diff_line_class($line, $from, $to);
-
-       chomp $line;
-       $line = untabify($line);
+# returning HTML-formatted (but not wrapped) line.
+# If the line is passed as a reference, it is treated as HTML and not
+# esc_html()'ed.
+sub format_diff_line {
+       my ($line, $diff_class, $from, $to) = @_;
+
+       if (ref($line)) {
+               $line = $$line;
+       } else {
+               chomp $line;
+               $line = untabify($line);
 
-       if ($from && $to && $line =~ m/^\@{2} /) {
-               $line = format_unidiff_chunk_header($line, $from, $to);
-               return $diff_class, $line;
+               if ($from && $to && $line =~ m/^\@{2} /) {
+                       $line = format_unidiff_chunk_header($line, $from, $to);
+               } elsif ($from && $to && $line =~ m/^\@{3}/) {
+                       $line = format_cc_diff_chunk_header($line, $from, $to);
+               } else {
+                       $line = esc_html($line, -nbsp=>1);
+               }
+       }
 
-       } elsif ($from && $to && $line =~ m/^\@{3}/) {
-               $line = format_cc_diff_chunk_header($line, $from, $to);
-               return $diff_class, $line;
+       my $diff_classes = "diff";
+       $diff_classes .= " $diff_class" if ($diff_class);
+       $line = "<div class=\"$diff_classes\">$line</div>\n";
 
-       }
-       return $diff_class, esc_html($line, -nbsp=>1);
+       return $line;
 }
 
 # Generates undef or something like "_snapshot_" or "snapshot (_tbz2_ _zip_)",
@@ -2674,12 +2698,15 @@ sub git_get_project_config {
        # only subsection, if exists, is case sensitive,
        # and not lowercased by 'git config -z -l'
        if (my ($hi, $mi, $lo) = ($key =~ /^([^.]*)\.(.*)\.([^.]*)$/)) {
+               $lo =~ s/_//g;
                $key = join(".", lc($hi), $mi, lc($lo));
+               return if ($lo =~ /\W/ || $hi =~ /\W/);
        } else {
                $key = lc($key);
+               $key =~ s/_//g;
+               return if ($key =~ /\W/);
        }
        $key =~ s/^gitweb\.//;
-       return if ($key =~ m/\W/);
 
        # type sanity check
        if (defined $type) {
@@ -3000,9 +3027,11 @@ sub git_get_projects_list {
                        }
                        if (check_export_ok("$projectroot/$path")) {
                                my $pr = {
-                                       path => $path,
-                                       owner => to_utf8($owner),
+                                       path => $path
                                };
+                               if ($owner) {
+                                       $pr->{'owner'} = to_utf8($owner);
+                               }
                                push @list, $pr;
                        }
                }
@@ -3889,6 +3918,7 @@ sub print_feed_meta {
                                '-type' => "application/$type+xml"
                        );
 
+                       $href_params{'extra_options'} = undef;
                        $href_params{'action'} = $type;
                        $link_attr{'-href'} = href(%href_params);
                        print "<link ".
@@ -4466,30 +4496,33 @@ sub git_print_log {
        }
 
        # print log
-       my $signoff = 0;
-       my $empty = 0;
+       my $skip_blank_line = 0;
        foreach my $line (@$log) {
-               if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) {
-                       $signoff = 1;
-                       $empty = 0;
+               if ($line =~ m/^\s*([A-Z][-A-Za-z]*-[Bb]y|C[Cc]): /) {
                        if (! $opts{'-remove_signoff'}) {
                                print "<span class=\"signoff\">" . esc_html($line) . "</span><br/>\n";
-                               next;
-                       } else {
-                               # remove signoff lines
-                               next;
+                               $skip_blank_line = 1;
                        }
-               } else {
-                       $signoff = 0;
+                       next;
+               }
+
+               if ($line =~ m,\s*([a-z]*link): (https?://\S+),i) {
+                       if (! $opts{'-remove_signoff'}) {
+                               print "<span class=\"signoff\">" . esc_html($1) . ": " .
+                                       "<a href=\"" . esc_html($2) . "\">" . esc_html($2) . "</a>" .
+                                       "</span><br/>\n";
+                               $skip_blank_line = 1;
+                       }
+                       next;
                }
 
                # print only one empty line
                # do not print empty line after signoff
                if ($line eq "") {
-                       next if ($empty || $signoff);
-                       $empty = 1;
+                       next if ($skip_blank_line);
+                       $skip_blank_line = 1;
                } else {
-                       $empty = 0;
+                       $skip_blank_line = 0;
                }
 
                print format_log_line_html($line) . "<br/>\n";
@@ -4497,7 +4530,7 @@ sub git_print_log {
 
        if ($opts{'-final_empty_line'}) {
                # end with single empty line
-               print "<br/>\n" unless $empty;
+               print "<br/>\n" unless $skip_blank_line;
        }
 }
 
@@ -4999,10 +5032,186 @@ sub git_difftree_body {
        print "</table>\n";
 }
 
-sub print_sidebyside_diff_chunk {
-       my @chunk = @_;
+# Print context lines and then rem/add lines in a side-by-side manner.
+sub print_sidebyside_diff_lines {
+       my ($ctx, $rem, $add) = @_;
+
+       # print context block before add/rem block
+       if (@$ctx) {
+               print join '',
+                       '<div class="chunk_block ctx">',
+                               '<div class="old">',
+                               @$ctx,
+                               '</div>',
+                               '<div class="new">',
+                               @$ctx,
+                               '</div>',
+                       '</div>';
+       }
+
+       if (!@$add) {
+               # pure removal
+               print join '',
+                       '<div class="chunk_block rem">',
+                               '<div class="old">',
+                               @$rem,
+                               '</div>',
+                       '</div>';
+       } elsif (!@$rem) {
+               # pure addition
+               print join '',
+                       '<div class="chunk_block add">',
+                               '<div class="new">',
+                               @$add,
+                               '</div>',
+                       '</div>';
+       } else {
+               print join '',
+                       '<div class="chunk_block chg">',
+                               '<div class="old">',
+                               @$rem,
+                               '</div>',
+                               '<div class="new">',
+                               @$add,
+                               '</div>',
+                       '</div>';
+       }
+}
+
+# Print context lines and then rem/add lines in inline manner.
+sub print_inline_diff_lines {
+       my ($ctx, $rem, $add) = @_;
+
+       print @$ctx, @$rem, @$add;
+}
+
+# Format removed and added line, mark changed part and HTML-format them.
+# Implementation is based on contrib/diff-highlight
+sub format_rem_add_lines_pair {
+       my ($rem, $add, $num_parents) = @_;
+
+       # We need to untabify lines before split()'ing them;
+       # otherwise offsets would be invalid.
+       chomp $rem;
+       chomp $add;
+       $rem = untabify($rem);
+       $add = untabify($add);
+
+       my @rem = split(//, $rem);
+       my @add = split(//, $add);
+       my ($esc_rem, $esc_add);
+       # Ignore leading +/- characters for each parent.
+       my ($prefix_len, $suffix_len) = ($num_parents, 0);
+       my ($prefix_has_nonspace, $suffix_has_nonspace);
+
+       my $shorter = (@rem < @add) ? @rem : @add;
+       while ($prefix_len < $shorter) {
+               last if ($rem[$prefix_len] ne $add[$prefix_len]);
+
+               $prefix_has_nonspace = 1 if ($rem[$prefix_len] !~ /\s/);
+               $prefix_len++;
+       }
+
+       while ($prefix_len + $suffix_len < $shorter) {
+               last if ($rem[-1 - $suffix_len] ne $add[-1 - $suffix_len]);
+
+               $suffix_has_nonspace = 1 if ($rem[-1 - $suffix_len] !~ /\s/);
+               $suffix_len++;
+       }
+
+       # Mark lines that are different from each other, but have some common
+       # part that isn't whitespace.  If lines are completely different, don't
+       # mark them because that would make output unreadable, especially if
+       # diff consists of multiple lines.
+       if ($prefix_has_nonspace || $suffix_has_nonspace) {
+               $esc_rem = esc_html_hl_regions($rem, 'marked',
+                       [$prefix_len, @rem - $suffix_len], -nbsp=>1);
+               $esc_add = esc_html_hl_regions($add, 'marked',
+                       [$prefix_len, @add - $suffix_len], -nbsp=>1);
+       } else {
+               $esc_rem = esc_html($rem, -nbsp=>1);
+               $esc_add = esc_html($add, -nbsp=>1);
+       }
+
+       return format_diff_line(\$esc_rem, 'rem'),
+              format_diff_line(\$esc_add, 'add');
+}
+
+# HTML-format diff context, removed and added lines.
+sub format_ctx_rem_add_lines {
+       my ($ctx, $rem, $add, $num_parents) = @_;
+       my (@new_ctx, @new_rem, @new_add);
+       my $can_highlight = 0;
+       my $is_combined = ($num_parents > 1);
+
+       # Highlight if every removed line has a corresponding added line.
+       if (@$add > 0 && @$add == @$rem) {
+               $can_highlight = 1;
+
+               # Highlight lines in combined diff only if the chunk contains
+               # diff between the same version, e.g.
+               #
+               #    - a
+               #   -  b
+               #    + c
+               #   +  d
+               #
+               # Otherwise the highlightling would be confusing.
+               if ($is_combined) {
+                       for (my $i = 0; $i < @$add; $i++) {
+                               my $prefix_rem = substr($rem->[$i], 0, $num_parents);
+                               my $prefix_add = substr($add->[$i], 0, $num_parents);
+
+                               $prefix_rem =~ s/-/+/g;
+
+                               if ($prefix_rem ne $prefix_add) {
+                                       $can_highlight = 0;
+                                       last;
+                               }
+                       }
+               }
+       }
+
+       if ($can_highlight) {
+               for (my $i = 0; $i < @$add; $i++) {
+                       my ($line_rem, $line_add) = format_rem_add_lines_pair(
+                               $rem->[$i], $add->[$i], $num_parents);
+                       push @new_rem, $line_rem;
+                       push @new_add, $line_add;
+               }
+       } else {
+               @new_rem = map { format_diff_line($_, 'rem') } @$rem;
+               @new_add = map { format_diff_line($_, 'add') } @$add;
+       }
+
+       @new_ctx = map { format_diff_line($_, 'ctx') } @$ctx;
+
+       return (\@new_ctx, \@new_rem, \@new_add);
+}
+
+# Print context lines and then rem/add lines.
+sub print_diff_lines {
+       my ($ctx, $rem, $add, $diff_style, $num_parents) = @_;
+       my $is_combined = $num_parents > 1;
+
+       ($ctx, $rem, $add) = format_ctx_rem_add_lines($ctx, $rem, $add,
+               $num_parents);
+
+       if ($diff_style eq 'sidebyside' && !$is_combined) {
+               print_sidebyside_diff_lines($ctx, $rem, $add);
+       } else {
+               # default 'inline' style and unknown styles
+               print_inline_diff_lines($ctx, $rem, $add);
+       }
+}
+
+sub print_diff_chunk {
+       my ($diff_style, $num_parents, $from, $to, @chunk) = @_;
        my (@ctx, @rem, @add);
 
+       # The class of the previous line.
+       my $prev_class = '';
+
        return unless @chunk;
 
        # incomplete last line might be among removed or added lines,
@@ -5021,55 +5230,19 @@ sub print_sidebyside_diff_chunk {
 
                # print chunk headers
                if ($class && $class eq 'chunk_header') {
-                       print $line;
+                       print format_diff_line($line, $class, $from, $to);
                        next;
                }
 
-               ## print from accumulator when type of class of lines change
-               # empty contents block on start rem/add block, or end of chunk
-               if (@ctx && (!$class || $class eq 'rem' || $class eq 'add')) {
-                       print join '',
-                               '<div class="chunk_block ctx">',
-                                       '<div class="old">',
-                                       @ctx,
-                                       '</div>',
-                                       '<div class="new">',
-                                       @ctx,
-                                       '</div>',
-                               '</div>';
-                       @ctx = ();
-               }
-               # empty add/rem block on start context block, or end of chunk
-               if ((@rem || @add) && (!$class || $class eq 'ctx')) {
-                       if (!@add) {
-                               # pure removal
-                               print join '',
-                                       '<div class="chunk_block rem">',
-                                               '<div class="old">',
-                                               @rem,
-                                               '</div>',
-                                       '</div>';
-                       } elsif (!@rem) {
-                               # pure addition
-                               print join '',
-                                       '<div class="chunk_block add">',
-                                               '<div class="new">',
-                                               @add,
-                                               '</div>',
-                                       '</div>';
-                       } else {
-                               # assume that it is change
-                               print join '',
-                                       '<div class="chunk_block chg">',
-                                               '<div class="old">',
-                                               @rem,
-                                               '</div>',
-                                               '<div class="new">',
-                                               @add,
-                                               '</div>',
-                                       '</div>';
-                       }
-                       @rem = @add = ();
+               ## print from accumulator when have some add/rem lines or end
+               # of chunk (flush context lines), or when have add and rem
+               # lines and new block is reached (otherwise add/rem lines could
+               # be reordered)
+               if (!$class || ((@rem || @add) && $class eq 'ctx') ||
+                   (@rem && @add && $class ne $prev_class)) {
+                       print_diff_lines(\@ctx, \@rem, \@add,
+                                        $diff_style, $num_parents);
+                       @ctx = @rem = @add = ();
                }
 
                ## adding lines to accumulator
@@ -5085,6 +5258,8 @@ sub print_sidebyside_diff_chunk {
                if ($class eq 'ctx') {
                        push @ctx, $line;
                }
+
+               $prev_class = $class;
        }
 }
 
@@ -5206,27 +5381,19 @@ sub git_patchset_body {
 
                        next PATCH if ($patch_line =~ m/^diff /);
 
-                       my ($class, $line) = process_diff_line($patch_line, \%from, \%to);
-                       my $diff_classes = "diff";
-                       $diff_classes .= " $class" if ($class);
-                       $line = "<div class=\"$diff_classes\">$line</div>\n";
+                       my $class = diff_line_class($patch_line, \%from, \%to);
 
-                       if ($diff_style eq 'sidebyside' && !$is_combined) {
-                               if ($class eq 'chunk_header') {
-                                       print_sidebyside_diff_chunk(@chunk);
-                                       @chunk = ( [ $class, $line ] );
-                               } else {
-                                       push @chunk, [ $class, $line ];
-                               }
-                       } else {
-                               # default 'inline' style and unknown styles
-                               print $line;
+                       if ($class eq 'chunk_header') {
+                               print_diff_chunk($diff_style, scalar @hash_parents, \%from, \%to, @chunk);
+                               @chunk = ();
                        }
+
+                       push @chunk, [ $class, $patch_line ];
                }
 
        } continue {
                if (@chunk) {
-                       print_sidebyside_diff_chunk(@chunk);
+                       print_diff_chunk($diff_style, scalar @hash_parents, \%from, \%to, @chunk);
                        @chunk = ();
                }
                print "</div>\n"; # class="patch"
@@ -5276,7 +5443,7 @@ sub git_project_search_form {
                if (defined $project_filter);
        print $cgi->textfield(-name => 's', -value => $searchtext,
                              -title => "Search project by name and description$limit",
-                             -size => 20) . "\n" .
+                             -size => 60) . "\n" .
              "<span title=\"Extended regular expression\">" .
              $cgi->checkbox(-name => 'sr', -value => 1, -label => 're',
                             -checked => $search_use_regexp) .
@@ -5285,7 +5452,7 @@ sub git_project_search_form {
              $cgi->end_form() . "\n" .
              $cgi->a({-href => href(project => undef, searchtext => undef,
                                     project_filter => $project_filter)},
-                    ) . "<br />\n";
+                     esc_html("List all projects$limit")) . "<br />\n";
        print "</div>\n";
 }
 
@@ -5366,23 +5533,30 @@ sub fill_project_list_info {
 
 sub sort_projects_list {
        my ($projlist, $order) = @_;
-       my @projects;
 
-       my %order_info = (
-               project => { key => 'path', type => 'str' },
-               descr => { key => 'descr_long', type => 'str' },
-               owner => { key => 'owner', type => 'str' },
-               age => { key => 'age', type => 'num' }
-       );
-       my $oi = $order_info{$order};
-       return @$projlist unless defined $oi;
-       if ($oi->{'type'} eq 'str') {
-               @projects = sort {$a->{$oi->{'key'}} cmp $b->{$oi->{'key'}}} @$projlist;
-       } else {
-               @projects = sort {$a->{$oi->{'key'}} <=> $b->{$oi->{'key'}}} @$projlist;
+       sub order_str {
+               my $key = shift;
+               return sub { $a->{$key} cmp $b->{$key} };
        }
 
-       return @projects;
+       sub order_num_then_undef {
+               my $key = shift;
+               return sub {
+                       defined $a->{$key} ?
+                               (defined $b->{$key} ? $a->{$key} <=> $b->{$key} : -1) :
+                               (defined $b->{$key} ? 1 : 0)
+               };
+       }
+
+       my %orderings = (
+               project => order_str('path'),
+               descr => order_str('descr_long'),
+               owner => order_str('owner'),
+               age => order_num_then_undef('age'),
+       );
+
+       my $ordering = $orderings{$order};
+       return defined $ordering ? sort $ordering @$projlist : @$projlist;
 }
 
 # returns a hash of categories, containing the list of project
@@ -5430,9 +5604,11 @@ sub git_project_list_rows {
 
        $from = 0 unless defined $from;
        $to = $#$projlist if (!defined $to || $#$projlist < $to);
+
        my $alternate = 1;
        for (my $i = $from; $i <= $to; $i++) {
                my $pr = $projlist->[$i];
+
                if ($alternate) {
                        print "<tr class=\"dark\">\n";
                } else {
@@ -5464,11 +5640,15 @@ sub git_project_list_rows {
                                        ? esc_html_match_hl_chopped($pr->{'descr_long'},
                                                                    $pr->{'descr'}, $search_regexp)
                                        : esc_html($pr->{'descr'})) .
-                     "</td>\n" .
-                     "<td><i>" . chop_and_escape_str($pr->{'owner'}, 15) . "</i></td>\n";
-               print "<td class=\"". age_class($pr->{'age'}) . "\">" .
-                     (defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "</td>\n" .
-                     "<td class=\"link\">" .
+                     "</td>\n";
+               unless ($omit_owner) {
+                       print "<td><i>" . chop_and_escape_str($pr->{'owner'}, 15) . "</i></td>\n";
+               }
+               unless ($omit_age_column) {
+                       print "<td class=\"". age_class($pr->{'age'}) . "\">" .
+                           (defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "</td>\n";
+               }
+               print"<td class=\"link\">" .
                      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary")   . " | " .
                      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " .
                      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . " | " .
@@ -5477,7 +5657,7 @@ sub git_project_list_rows {
                      "</td>\n" .
                      "</tr>\n";
        }
-       print "<tr>";
+       print "<tr>";
        if ($check_forks) {
                print "<td colspan=\"2\">&nbsp;</td>";
        } else {
@@ -5506,7 +5686,10 @@ sub git_project_list_body {
                                         'tagfilter'  => $tagfilter)
                if ($tagfilter || $search_regexp);
        # fill the rest
-       @projects = fill_project_list_info(\@projects);
+       my @all_fields = ('descr', 'descr_long', 'ctags', 'category');
+       push @all_fields, ('age', 'age_string') unless($omit_age_column);
+       push @all_fields, 'owner' unless($omit_owner);
+       @projects = fill_project_list_info(\@projects, @all_fields);
 
        $order ||= $default_projects_order;
        $from = 0 unless defined $from;
@@ -5537,8 +5720,8 @@ sub git_project_list_body {
                }
                print_sort_th('project', $order, 'Project');
                print_sort_th('descr', $order, 'Description');
-               print_sort_th('owner', $order, 'Owner');
-               print_sort_th('age', $order, 'Last Change');
+               print_sort_th('owner', $order, 'Owner') unless $omit_owner;
+               print_sort_th('age', $order, 'Last Change') unless $omit_age_column;
                print "<th></th>\n" . # for links
                      "</tr>\n";
        }
@@ -5623,7 +5806,7 @@ sub git_shortlog_body {
        print "<table class=\"shortlog\" cellspacing=\"0\" cellpadding=\"0\">\n";
        my $alternate = 1;
        my $graph_rand = int(rand(99999));
-       my $head = git_get_head_hash($project); 
+       my $head = git_get_head_hash($project); 
        my $graph_hash;
        if (defined $allrefs && $allrefs == 1) {
                $graph_hash = "all";
@@ -6314,8 +6497,10 @@ sub git_summary {
 
        print "<div class=\"title\">&nbsp;</div>\n";
        print "<table class=\"projects_list\">\n" .
-             "<tr id=\"metadata_desc\"><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
-             "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
+             "<tr id=\"metadata_desc\"><td>description</td><td>" . esc_html($descr) . "</td></tr>\n";
+        unless ($omit_owner) {
+               print  "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
+        }
        if (defined $cd{'rfc2822'}) {
                print "<tr id=\"metadata_lchange\"><td>last change</td>" .
                      "<td>".format_timestamp_html(\%cd)."</td></tr>\n";
@@ -6364,7 +6549,7 @@ sub git_summary {
                git_print_header_div('shortlog');
                git_shortlog_body(\@commitlist, 0, 15, $refs,
                                  $#commitlist <=  15 ? undef :
-                                 $cgi->a({-href => href(action=>"shortlog")}, "..."), 0, 0, 0, 1);
+                                 $cgi->a({-href => href(action=>"shortlog")}, "..."), 0, 0, 0, 1);;
        }
 
        if (@taglist) {
@@ -7037,6 +7222,28 @@ sub snapshot_name {
        return wantarray ? ($name, $name) : $name;
 }
 
+sub exit_if_unmodified_since {
+       my ($latest_epoch) = @_;
+       our $cgi;
+
+       my $if_modified = $cgi->http('IF_MODIFIED_SINCE');
+       if (defined $if_modified) {
+               my $since;
+               if (eval { require HTTP::Date; 1; }) {
+                       $since = HTTP::Date::str2time($if_modified);
+               } elsif (eval { require Time::ParseDate; 1; }) {
+                       $since = Time::ParseDate::parsedate($if_modified, GMT => 1);
+               }
+               if (defined $since && $latest_epoch <= $since) {
+                       my %latest_date = parse_date($latest_epoch);
+                       print $cgi->header(
+                               -last_modified => $latest_date{'rfc2822'},
+                               -status => '304 Not Modified');
+                       goto DONE_GITWEB;
+               }
+       }
+}
+
 sub git_snapshot {
        my $format = $input_params{'snapshot_format'};
        if (!@snapshot_fmts) {
@@ -7063,6 +7270,10 @@ sub git_snapshot {
 
        my ($name, $prefix) = snapshot_name($project, $hash);
        my $filename = "$name$known_snapshot_formats{$format}{'suffix'}";
+
+       my %co = parse_commit($hash);
+       exit_if_unmodified_since($co{'committer_epoch'}) if %co;
+
        my $cmd = quote_command(
                git_cmd(), 'archive',
                "--format=$known_snapshot_formats{$format}{'format'}",
@@ -7072,9 +7283,15 @@ sub git_snapshot {
        }
 
        $filename =~ s/(["\\])/\\$1/g;
+       my %latest_date;
+       if (%co) {
+               %latest_date = parse_date($co{'committer_epoch'}, $co{'committer_tz'});
+       }
+
        print $cgi->header(
                -type => $known_snapshot_formats{$format}{'type'},
                -content_disposition => 'inline; filename="' . $filename . '"',
+               %co ? (-last_modified => $latest_date{'rfc2822'}) : (),
                -status => '200 OK');
 
        open my $fd, "-|", $cmd
@@ -7089,10 +7306,10 @@ sub git_log_generic {
        my ($fmt_name, $body_subr, $base, $parent, $file_name, $file_hash) = @_;
 
        my $head = git_get_head_hash($project);
-    my $allrefs;
+       my $allrefs;
        if (!defined $base) {
                $base = $head;
-        $allrefs = 1;
+               $allrefs = 1;
        }
        if (!defined $page) {
                $page = 0;
@@ -7100,10 +7317,10 @@ sub git_log_generic {
        my $refs = git_get_references();
 
        my $commit_hash = $base;
-    if (defined $allrefs) {
-        $commit_hash = "--all";
-    } 
-    if (defined $parent) {
+       if (defined $allrefs) {
+               $commit_hash = "--all";
+       
+       if (defined $parent) {
                $commit_hash = "$parent..$base";
        }
        my @commitlist =
@@ -7859,33 +8076,14 @@ sub git_feed {
        if (defined($commitlist[0])) {
                %latest_commit = %{$commitlist[0]};
                my $latest_epoch = $latest_commit{'committer_epoch'};
-               %latest_date   = parse_date($latest_epoch, $latest_commit{'comitter_tz'});
-               my $if_modified = $cgi->http('IF_MODIFIED_SINCE');
-               if (defined $if_modified) {
-                       my $since;
-                       if (eval { require HTTP::Date; 1; }) {
-                               $since = HTTP::Date::str2time($if_modified);
-                       } elsif (eval { require Time::ParseDate; 1; }) {
-                               $since = Time::ParseDate::parsedate($if_modified, GMT => 1);
-                       }
-                       if (defined $since && $latest_epoch <= $since) {
-                               print $cgi->header(
-                                       -type => $content_type,
-                                       -charset => 'utf-8',
-                                       -last_modified => $latest_date{'rfc2822'},
-                                       -status => '304 Not Modified');
-                               return;
-                       }
-               }
-               print $cgi->header(
-                       -type => $content_type,
-                       -charset => 'utf-8',
-                       -last_modified => $latest_date{'rfc2822'});
-       } else {
-               print $cgi->header(
-                       -type => $content_type,
-                       -charset => 'utf-8');
+               exit_if_unmodified_since($latest_epoch);
+               %latest_date = parse_date($latest_epoch, $latest_commit{'committer_tz'});
        }
+       print $cgi->header(
+               -type => $content_type,
+               -charset => 'utf-8',
+               %latest_date ? (-last_modified => $latest_date{'rfc2822'}) : (),
+               -status => '200 OK');
 
        # Optimization: skip generating the body if client asks only
        # for Last-Modified date.
@@ -7906,6 +8104,7 @@ sub git_feed {
                $feed_type = 'history';
        }
        $title .= " $feed_type";
+       $title = esc_html($title);
        my $descr = git_get_project_description($project);
        if (defined $descr) {
                $descr = esc_html($descr);