docs: drop asciidoc7compatible flag
[gitweb.git] / gitweb / gitweb.perl
index d5b3f04e118d1b86bf009a163bc9e7ade060b017..55e0e9ea38b3080e32467b6faf56f40d45386b96 100755 (executable)
@@ -133,6 +133,12 @@ sub evaluate_uri {
 # (only effective if this variable evaluates to true)
 our $export_ok = "++GITWEB_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"; }
@@ -3012,9 +3018,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;
                        }
                }
@@ -3901,6 +3909,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 ".
@@ -5064,7 +5073,7 @@ sub print_inline_diff_lines {
 # 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) = @_;
+       my ($rem, $add, $num_parents) = @_;
 
        # We need to untabify lines before split()'ing them;
        # otherwise offsets would be invalid.
@@ -5076,8 +5085,8 @@ sub format_rem_add_lines_pair {
        my @rem = split(//, $rem);
        my @add = split(//, $add);
        my ($esc_rem, $esc_add);
-       # Ignore +/- character, thus $prefix_len is set to 1.
-       my ($prefix_len, $suffix_len) = (1, 0);
+       # 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;
@@ -5115,15 +5124,43 @@ sub format_rem_add_lines_pair {
 
 # HTML-format diff context, removed and added lines.
 sub format_ctx_rem_add_lines {
-       my ($ctx, $rem, $add, $is_combined) = @_;
+       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.
-       # Combined diffs are not supported at this moment.
-       if (!$is_combined && @$add > 0 && @$add == @$rem) {
+       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]);
+                               $rem->[$i], $add->[$i], $num_parents);
                        push @new_rem, $line_rem;
                        push @new_add, $line_add;
                }
@@ -5139,10 +5176,11 @@ sub format_ctx_rem_add_lines {
 
 # Print context lines and then rem/add lines.
 sub print_diff_lines {
-       my ($ctx, $rem, $add, $diff_style, $is_combined) = @_;
+       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,
-               $is_combined);
+               $num_parents);
 
        if ($diff_style eq 'sidebyside' && !$is_combined) {
                print_sidebyside_diff_lines($ctx, $rem, $add);
@@ -5153,7 +5191,7 @@ sub print_diff_lines {
 }
 
 sub print_diff_chunk {
-       my ($diff_style, $is_combined, $from, $to, @chunk) = @_;
+       my ($diff_style, $num_parents, $from, $to, @chunk) = @_;
        my (@ctx, @rem, @add);
 
        # The class of the previous line.
@@ -5188,7 +5226,7 @@ sub print_diff_chunk {
                if (!$class || ((@rem || @add) && $class eq 'ctx') ||
                    (@rem && @add && $class ne $prev_class)) {
                        print_diff_lines(\@ctx, \@rem, \@add,
-                                        $diff_style, $is_combined);
+                                        $diff_style, $num_parents);
                        @ctx = @rem = @add = ();
                }
 
@@ -5331,7 +5369,7 @@ sub git_patchset_body {
                        my $class = diff_line_class($patch_line, \%from, \%to);
 
                        if ($class eq 'chunk_header') {
-                               print_diff_chunk($diff_style, $is_combined, \%from, \%to, @chunk);
+                               print_diff_chunk($diff_style, scalar @hash_parents, \%from, \%to, @chunk);
                                @chunk = ();
                        }
 
@@ -5340,7 +5378,7 @@ sub git_patchset_body {
 
        } continue {
                if (@chunk) {
-                       print_diff_chunk($diff_style, $is_combined, \%from, \%to, @chunk);
+                       print_diff_chunk($diff_style, scalar @hash_parents, \%from, \%to, @chunk);
                        @chunk = ();
                }
                print "</div>\n"; # class="patch"
@@ -5580,11 +5618,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") . " | " .
@@ -5615,7 +5657,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;
@@ -5646,8 +5691,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";
        }
@@ -6400,8 +6445,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";
@@ -7123,6 +7170,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) {
@@ -7149,6 +7218,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'}",
@@ -7158,9 +7231,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
@@ -7940,33 +8019,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{'comitter_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.