Merge branch 'gb/gitweb-opml'
authorJunio C Hamano <gitster@pobox.com>
Sun, 18 Jan 2009 07:07:19 +0000 (23:07 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sun, 18 Jan 2009 07:07:19 +0000 (23:07 -0800)
* gb/gitweb-opml:
gitweb: suggest name for OPML view
gitweb: don't use pathinfo for global actions

1  2 
gitweb/gitweb.perl
diff --combined gitweb/gitweb.perl
index 9a5cfb0cb1577710bab3a13ed96ef852e2956d6c,995bc1a6a9bb7ef955caba319670c290e369d200..931db4f7ebb7e789278ac3e20a8570d40873163e
@@@ -203,7 -203,7 +203,7 @@@ our %feature = 
        # $feature{'blame'}{'override'} = 1;
        # and in project config gitweb.blame = 0|1;
        'blame' => {
 -              'sub' => \&feature_blame,
 +              'sub' => sub { feature_bool('blame', @_) },
                'override' => 0,
                'default' => [0]},
  
        # $feature{'grep'}{'override'} = 1;
        # and in project config gitweb.grep = 0|1;
        'grep' => {
 -              'sub' => \&feature_grep,
 +              'sub' => sub { feature_bool('grep', @_) },
                'override' => 0,
                'default' => [1]},
  
        # $feature{'pickaxe'}{'override'} = 1;
        # and in project config gitweb.pickaxe = 0|1;
        'pickaxe' => {
 -              'sub' => \&feature_pickaxe,
 +              'sub' => sub { feature_bool('pickaxe', @_) },
                'override' => 0,
                'default' => [1]},
  
        'ctags' => {
                'override' => 0,
                'default' => [0]},
 +
 +      # The maximum number of patches in a patchset generated in patch
 +      # view. Set this to 0 or undef to disable patch view, or to a
 +      # negative number to remove any limit.
 +
 +      # To disable system wide have in $GITWEB_CONFIG
 +      # $feature{'patches'}{'default'} = [0];
 +      # To have project specific config enable override in $GITWEB_CONFIG
 +      # $feature{'patches'}{'override'} = 1;
 +      # and in project config gitweb.patches = 0|n;
 +      # where n is the maximum number of patches allowed in a patchset.
 +      'patches' => {
 +              'sub' => \&feature_patches,
 +              'override' => 0,
 +              'default' => [16]},
  );
  
  sub gitweb_get_feature {
@@@ -378,17 -363,16 +378,17 @@@ sub gitweb_check_feature 
  }
  
  
 -sub feature_blame {
 -      my ($val) = git_get_project_config('blame', '--bool');
 +sub feature_bool {
 +      my $key = shift;
 +      my ($val) = git_get_project_config($key, '--bool');
  
        if ($val eq 'true') {
 -              return 1;
 +              return (1);
        } elsif ($val eq 'false') {
 -              return 0;
 +              return (0);
        }
  
 -      return $_[0];
 +      return ($_[0]);
  }
  
  sub feature_snapshot {
        return @fmts;
  }
  
 -sub feature_grep {
 -      my ($val) = git_get_project_config('grep', '--bool');
 +sub feature_patches {
 +      my @val = (git_get_project_config('patches', '--int'));
  
 -      if ($val eq 'true') {
 -              return (1);
 -      } elsif ($val eq 'false') {
 -              return (0);
 -      }
 -
 -      return ($_[0]);
 -}
 -
 -sub feature_pickaxe {
 -      my ($val) = git_get_project_config('pickaxe', '--bool');
 -
 -      if ($val eq 'true') {
 -              return (1);
 -      } elsif ($val eq 'false') {
 -              return (0);
 +      if (@val) {
 +              return @val;
        }
  
        return ($_[0]);
@@@ -506,8 -504,6 +506,8 @@@ our %actions = 
        "heads" => \&git_heads,
        "history" => \&git_history,
        "log" => \&git_log,
 +      "patch" => \&git_patch,
 +      "patches" => \&git_patches,
        "rss" => \&git_rss,
        "atom" => \&git_atom,
        "search" => \&git_search,
@@@ -834,7 -830,7 +834,7 @@@ sub href (%) 
        }
  
        my $use_pathinfo = gitweb_check_feature('pathinfo');
-       if ($use_pathinfo) {
+       if ($use_pathinfo and defined $params{'project'}) {
                # try to put as many parameters as possible in PATH_INFO:
                #   - project name
                #   - action
                $href =~ s,/$,,;
  
                # Then add the project name, if present
-               $href .= "/".esc_url($params{'project'}) if defined $params{'project'};
+               $href .= "/".esc_url($params{'project'});
                delete $params{'project'};
  
                # since we destructively absorb parameters, we keep this
@@@ -4580,33 -4576,28 +4580,33 @@@ sub git_tag 
  }
  
  sub git_blame {
 -      my $fd;
 -      my $ftype;
 -
 +      # permissions
        gitweb_check_feature('blame')
 -          or die_error(403, "Blame view not allowed");
 +              or die_error(403, "Blame view not allowed");
  
 +      # error checking
        die_error(400, "No file name given") unless $file_name;
        $hash_base ||= git_get_head_hash($project);
 -      die_error(404, "Couldn't find base commit") unless ($hash_base);
 +      die_error(404, "Couldn't find base commit") unless $hash_base;
        my %co = parse_commit($hash_base)
                or die_error(404, "Commit not found");
 +      my $ftype = "blob";
        if (!defined $hash) {
                $hash = git_get_hash_by_path($hash_base, $file_name, "blob")
                        or die_error(404, "Error looking up file");
 +      } else {
 +              $ftype = git_get_type($hash);
 +              if ($ftype !~ "blob") {
 +                      die_error(400, "Object is not a blob");
 +              }
        }
 -      $ftype = git_get_type($hash);
 -      if ($ftype !~ "blob") {
 -              die_error(400, "Object is not a blob");
 -      }
 -      open ($fd, "-|", git_cmd(), "blame", '-p', '--',
 -            $file_name, $hash_base)
 +
 +      # run git-blame --porcelain
 +      open my $fd, "-|", git_cmd(), "blame", '-p',
 +              $hash_base, '--', $file_name
                or die_error(500, "Open git-blame failed");
 +
 +      # page header
        git_header_html();
        my $formats_nav =
                $cgi->a({-href => href(action=>"blob", -replay=>1)},
        git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
        git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
        git_print_page_path($file_name, $ftype, $hash_base);
 -      my @rev_color = (qw(light2 dark2));
 +
 +      # page body
 +      my @rev_color = qw(light2 dark2);
        my $num_colors = scalar(@rev_color);
        my $current_color = 0;
 -      my $last_rev;
 +      my %metainfo = ();
 +
        print <<HTML;
  <div class="page_body">
  <table class="blame">
  <tr><th>Commit</th><th>Line</th><th>Data</th></tr>
  HTML
 -      my %metainfo = ();
 -      while (1) {
 -              $_ = <$fd>;
 -              last unless defined $_;
 + LINE:
 +      while (my $line = <$fd>) {
 +              chomp $line;
 +              # the header: <SHA-1> <src lineno> <dst lineno> [<lines in group>]
 +              # no <lines in group> for subsequent lines in group of lines
                my ($full_rev, $orig_lineno, $lineno, $group_size) =
 -                  /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/;
 +                 ($line =~ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/);
                if (!exists $metainfo{$full_rev}) {
                        $metainfo{$full_rev} = {};
                }
                my $meta = $metainfo{$full_rev};
 -              while (<$fd>) {
 -                      last if (s/^\t//);
 -                      if (/^(\S+) (.*)$/) {
 +              my $data;
 +              while ($data = <$fd>) {
 +                      chomp $data;
 +                      last if ($data =~ s/^\t//); # contents of line
 +                      if ($data =~ /^(\S+) (.*)$/) {
                                $meta->{$1} = $2;
                        }
                }
 -              my $data = $_;
 -              chomp $data;
 -              my $rev = substr($full_rev, 0, 8);
 +              my $short_rev = substr($full_rev, 0, 8);
                my $author = $meta->{'author'};
 -              my %date = parse_date($meta->{'author-time'},
 -                                    $meta->{'author-tz'});
 +              my %date =
 +                      parse_date($meta->{'author-time'}, $meta->{'author-tz'});
                my $date = $date{'iso-tz'};
                if ($group_size) {
 -                      $current_color = ++$current_color % $num_colors;
 +                      $current_color = ($current_color + 1) % $num_colors;
                }
 -              print "<tr class=\"$rev_color[$current_color]\">\n";
 +              print "<tr id=\"l$lineno\" class=\"$rev_color[$current_color]\">\n";
                if ($group_size) {
                        print "<td class=\"sha1\"";
                        print " title=\"". esc_html($author) . ", $date\"";
                        print $cgi->a({-href => href(action=>"commit",
                                                     hash=>$full_rev,
                                                     file_name=>$file_name)},
 -                                    esc_html($rev));
 +                                    esc_html($short_rev));
                        print "</td>\n";
                }
 -              open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^")
 -                      or die_error(500, "Open git-rev-parse failed");
 -              my $parent_commit = <$dd>;
 -              close $dd;
 -              chomp($parent_commit);
 +              my $parent_commit;
 +              if (!exists $meta->{'parent'}) {
 +                      open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^")
 +                              or die_error(500, "Open git-rev-parse failed");
 +                      $parent_commit = <$dd>;
 +                      close $dd;
 +                      chomp($parent_commit);
 +                      $meta->{'parent'} = $parent_commit;
 +              } else {
 +                      $parent_commit = $meta->{'parent'};
 +              }
                my $blamed = href(action => 'blame',
                                  file_name => $meta->{'filename'},
                                  hash_base => $parent_commit);
                print "<td class=\"linenr\">";
                print $cgi->a({ -href => "$blamed#l$orig_lineno",
 -                              -id => "l$lineno",
                                -class => "linenr" },
                              esc_html($lineno));
                print "</td>";
        print "</div>";
        close $fd
                or print "Reading blob failed\n";
 +
 +      # page footer
        git_footer_html();
  }
  
@@@ -5018,15 -4998,6 +5018,15 @@@ sub git_log 
  
        my $paging_nav = format_paging_nav('log', $hash, $head, $page, $#commitlist >= 100);
  
 +      my ($patch_max) = gitweb_get_feature('patches');
 +      if ($patch_max) {
 +              if ($patch_max < 0 || @commitlist <= $patch_max) {
 +                      $paging_nav .= " &sdot; " .
 +                              $cgi->a({-href => href(action=>"patches", -replay=>1)},
 +                                      "patches");
 +              }
 +      }
 +
        git_header_html();
        git_print_page_nav('log','', $hash,undef,undef, $paging_nav);
  
@@@ -5106,11 -5077,6 +5106,11 @@@ sub git_commit 
                        } @$parents ) .
                        ')';
        }
 +      if (gitweb_check_feature('patches')) {
 +              $formats_nav .= " | " .
 +                      $cgi->a({-href => href(action=>"patch", -replay=>1)},
 +                              "patch");
 +      }
  
        if (!defined $parent) {
                $parent = "--root";
@@@ -5387,14 -5353,7 +5387,14 @@@ sub git_blobdiff_plain 
  }
  
  sub git_commitdiff {
 -      my $format = shift || 'html';
 +      my %params = @_;
 +      my $format = $params{-format} || 'html';
 +
 +      my ($patch_max) = gitweb_get_feature('patches');
 +      if ($format eq 'patch') {
 +              die_error(403, "Patch view not allowed") unless $patch_max;
 +      }
 +
        $hash ||= $hash_base || "HEAD";
        my %co = parse_commit($hash)
            or die_error(404, "Unknown commit object");
                $formats_nav =
                        $cgi->a({-href => href(action=>"commitdiff_plain", -replay=>1)},
                                "raw");
 +              if ($patch_max) {
 +                      $formats_nav .= " | " .
 +                              $cgi->a({-href => href(action=>"patch", -replay=>1)},
 +                                      "patch");
 +              }
  
                if (defined $hash_parent &&
                    $hash_parent ne '-c' && $hash_parent ne '--cc') {
                open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
                        '-p', $hash_parent_param, $hash, "--"
                        or die_error(500, "Open git-diff-tree failed");
 -
 +      } elsif ($format eq 'patch') {
 +              # For commit ranges, we limit the output to the number of
 +              # patches specified in the 'patches' feature.
 +              # For single commits, we limit the output to a single patch,
 +              # diverging from the git-format-patch default.
 +              my @commit_spec = ();
 +              if ($hash_parent) {
 +                      if ($patch_max > 0) {
 +                              push @commit_spec, "-$patch_max";
 +                      }
 +                      push @commit_spec, '-n', "$hash_parent..$hash";
 +              } else {
 +                      if ($params{-single}) {
 +                              push @commit_spec, '-1';
 +                      } else {
 +                              if ($patch_max > 0) {
 +                                      push @commit_spec, "-$patch_max";
 +                              }
 +                              push @commit_spec, "-n";
 +                      }
 +                      push @commit_spec, '--root', $hash;
 +              }
 +              open $fd, "-|", git_cmd(), "format-patch", '--encoding=utf8',
 +                      '--stdout', @commit_spec
 +                      or die_error(500, "Open git-format-patch failed");
        } else {
                die_error(400, "Unknown commitdiff format");
        }
                        print to_utf8($line) . "\n";
                }
                print "---\n\n";
 +      } elsif ($format eq 'patch') {
 +              my $filename = basename($project) . "-$hash.patch";
 +
 +              print $cgi->header(
 +                      -type => 'text/plain',
 +                      -charset => 'utf-8',
 +                      -expires => $expires,
 +                      -content_disposition => 'inline; filename="' . "$filename" . '"');
        }
  
        # write patch
                print <$fd>;
                close $fd
                        or print "Reading git-diff-tree failed\n";
 +      } elsif ($format eq 'patch') {
 +              local $/ = undef;
 +              print <$fd>;
 +              close $fd
 +                      or print "Reading git-format-patch failed\n";
        }
  }
  
  sub git_commitdiff_plain {
 -      git_commitdiff('plain');
 +      git_commitdiff(-format => 'plain');
 +}
 +
 +# format-patch-style patches
 +sub git_patch {
 +      git_commitdiff(-format => 'patch', -single=> 1);
 +}
 +
 +sub git_patches {
 +      git_commitdiff(-format => 'patch');
  }
  
  sub git_history {
@@@ -5970,14 -5878,6 +5970,14 @@@ sub git_shortlog 
                        $cgi->a({-href => href(-replay=>1, page=>$page+1),
                                 -accesskey => "n", -title => "Alt-n"}, "next");
        }
 +      my $patch_max = gitweb_check_feature('patches');
 +      if ($patch_max) {
 +              if ($patch_max < 0 || @commitlist <= $patch_max) {
 +                      $paging_nav .= " &sdot; " .
 +                              $cgi->a({-href => href(action=>"patches", -replay=>1)},
 +                                      "patches");
 +              }
 +      }
  
        git_header_html();
        git_print_page_nav('shortlog','', $hash,$hash,$hash, $paging_nav);
@@@ -6222,7 -6122,11 +6222,11 @@@ sub git_atom 
  sub git_opml {
        my @list = git_get_projects_list();
  
-       print $cgi->header(-type => 'text/xml', -charset => 'utf-8');
+       print $cgi->header(
+               -type => 'text/xml',
+               -charset => 'utf-8',
+               -content_disposition => 'inline; filename="opml.xml"');
        print <<XML;
  <?xml version="1.0" encoding="utf-8"?>
  <opml version="1.0">