Merge branch 'jn/gitweb-time-hires-comes-with-5.8'
authorJunio C Hamano <gitster@pobox.com>
Wed, 8 Dec 2010 19:25:04 +0000 (11:25 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 8 Dec 2010 19:25:04 +0000 (11:25 -0800)
* jn/gitweb-time-hires-comes-with-5.8:
gitweb: Time::HiRes is in core for Perl 5.8

1  2 
gitweb/gitweb.perl
diff --combined gitweb/gitweb.perl
index a1a3797b6d4fcd1a282f42ae7f63a76bb471ea90,23f9e79932f97d2f21000dd2e81591f97be00d2c..75306ca967c5e978da5504276051738338be7537
@@@ -17,12 -17,10 +17,10 @@@ use Encode
  use Fcntl ':mode';
  use File::Find qw();
  use File::Basename qw(basename);
+ use Time::HiRes qw(gettimeofday tv_interval);
  binmode STDOUT, ':utf8';
  
- our $t0;
- if (eval { require Time::HiRes; 1; }) {
-       $t0 = [Time::HiRes::gettimeofday()];
- }
+ our $t0 = [ gettimeofday() ];
  our $number_of_git_cmds = 0;
  
  BEGIN {
@@@ -493,18 -491,6 +491,18 @@@ our %feature = 
                'sub' => sub { feature_bool('highlight', @_) },
                'override' => 0,
                'default' => [0]},
 +
 +      # Enable displaying of remote heads in the heads list
 +
 +      # To enable system wide have in $GITWEB_CONFIG
 +      # $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;
 +      'remote_heads' => {
 +              'sub' => sub { feature_bool('remote_heads', @_) },
 +              'override' => 0,
 +              'default' => [0]},
  );
  
  sub gitweb_get_feature {
@@@ -719,7 -705,6 +717,7 @@@ our %actions = 
        "log" => \&git_log,
        "patch" => \&git_patch,
        "patches" => \&git_patches,
 +      "remotes" => \&git_remotes,
        "rss" => \&git_rss,
        "atom" => \&git_atom,
        "search" => \&git_search,
@@@ -1078,7 -1063,7 +1076,7 @@@ sub dispatch 
  }
  
  sub reset_timer {
-       our $t0 = [Time::HiRes::gettimeofday()]
+       our $t0 = [ gettimeofday() ]
                if defined $t0;
        our $number_of_git_cmds = 0;
  }
@@@ -1088,7 -1073,6 +1086,7 @@@ sub run_request 
  
        evaluate_uri();
        evaluate_gitweb_config();
 +      evaluate_git_version();
        check_loadavg();
  
        # $projectroot and $projects_list might be set in gitweb config file
@@@ -1141,6 -1125,7 +1139,6 @@@ sub evaluate_argv 
  
  sub run {
        evaluate_argv();
 -      evaluate_git_version();
  
        $pre_listen_hook->()
                if $pre_listen_hook;
@@@ -2772,44 -2757,6 +2770,44 @@@ sub git_get_last_activity 
        return (undef, undef);
  }
  
 +# Implementation note: when a single remote is wanted, we cannot use 'git
 +# remote show -n' because that command always work (assuming it's a remote URL
 +# if it's not defined), and we cannot use 'git remote show' because that would
 +# try to make a network roundtrip. So the only way to find if that particular
 +# remote is defined is to walk the list provided by 'git remote -v' and stop if
 +# and when we find what we want.
 +sub git_get_remotes_list {
 +      my $wanted = shift;
 +      my %remotes = ();
 +
 +      open my $fd, '-|' , git_cmd(), 'remote', '-v';
 +      return unless $fd;
 +      while (my $remote = <$fd>) {
 +              chomp $remote;
 +              $remote =~ s!\t(.*?)\s+\((\w+)\)$!!;
 +              next if $wanted and not $remote eq $wanted;
 +              my ($url, $key) = ($1, $2);
 +
 +              $remotes{$remote} ||= { 'heads' => () };
 +              $remotes{$remote}{$key} = $url;
 +      }
 +      close $fd or return;
 +      return wantarray ? %remotes : \%remotes;
 +}
 +
 +# Takes a hash of remotes as first parameter and fills it by adding the
 +# available remote heads for each of the indicated remotes.
 +sub fill_remote_heads {
 +      my $remotes = shift;
 +      my @heads = map { "remotes/$_" } keys %$remotes;
 +      my @remoteheads = git_get_heads_list(undef, @heads);
 +      foreach my $remote (keys %$remotes) {
 +              $remotes->{$remote}{'heads'} = [ grep {
 +                      $_->{'name'} =~ s!^$remote/!!
 +                      } @remoteheads ];
 +      }
 +}
 +
  sub git_get_references {
        my $type = shift || "";
        my %refs;
@@@ -3208,15 -3155,13 +3206,15 @@@ sub parse_from_to_diffinfo 
  ## parse to array of hashes functions
  
  sub git_get_heads_list {
 -      my $limit = shift;
 +      my ($limit, @classes) = @_;
 +      @classes = ('heads') unless @classes;
 +      my @patterns = map { "refs/$_" } @classes;
        my @headslist;
  
        open my $fd, '-|', git_cmd(), 'for-each-ref',
                ($limit ? '--count='.($limit+1) : ()), '--sort=-committerdate',
                '--format=%(objectname) %(refname) %(subject)%00%(committer)',
 -              'refs/heads'
 +              @patterns
                or return;
        while (my $line = <$fd>) {
                my %ref_item;
                my ($committer, $epoch, $tz) =
                        ($committerinfo =~ /^(.*) ([0-9]+) (.*)$/);
                $ref_item{'fullname'}  = $name;
 -              $name =~ s!^refs/heads/!!;
 +              $name =~ s!^refs/(?:head|remote)s/!!;
  
                $ref_item{'name'}  = $name;
                $ref_item{'id'}    = $hash;
        if (defined $project) {
                print $cgi->a({-href => href(action=>"summary")}, esc_html($project));
                if (defined $action) {
 -                      print " / $action";
 +                      my $action_print = $action ;
 +                      if (defined $opts{-action_extra}) {
 +                              $action_print = $cgi->a({-href => href(action=>$action)},
 +                                      $action);
 +                      }
 +                      print " / $action_print";
 +              }
 +              if (defined $opts{-action_extra}) {
 +                      print " / $opts{-action_extra}";
                }
                print "\n";
        }
@@@ -3651,7 -3588,7 +3649,7 @@@ sub git_footer_html 
                print "<div id=\"generating_info\">\n";
                print 'This page took '.
                      '<span id="generating_time" class="time_span">'.
-                     Time::HiRes::tv_interval($t0, [Time::HiRes::gettimeofday()]).
+                     tv_interval($t0, [ gettimeofday() ]).
                      ' seconds </span>'.
                      ' and '.
                      '<span id="generating_cmd">'.
@@@ -3779,19 -3716,6 +3777,19 @@@ sub git_print_page_nav 
              "</div>\n";
  }
  
 +# returns a submenu for the nagivation of the refs views (tags, heads,
 +# remotes) with the current view disabled and the remotes view only
 +# available if the feature is enabled
 +sub format_ref_views {
 +      my ($current) = @_;
 +      my @ref_views = qw{tags heads};
 +      push @ref_views, 'remotes' if gitweb_check_feature('remote_heads');
 +      return join " | ", map {
 +              $_ eq $current ? $_ :
 +              $cgi->a({-href => href(action=>$_)}, $_)
 +      } @ref_views
 +}
 +
  sub format_paging_nav {
        my ($action, $page, $has_next_link) = @_;
        my $paging_nav;
@@@ -3835,49 -3759,6 +3833,49 @@@ sub git_print_header_div 
              "\n</div>\n";
  }
  
 +sub format_repo_url {
 +      my ($name, $url) = @_;
 +      return "<tr class=\"metadata_url\"><td>$name</td><td>$url</td></tr>\n";
 +}
 +
 +# Group output by placing it in a DIV element and adding a header.
 +# Options for start_div() can be provided by passing a hash reference as the
 +# first parameter to the function.
 +# Options to git_print_header_div() can be provided by passing an array
 +# reference. This must follow the options to start_div if they are present.
 +# The content can be a scalar, which is output as-is, a scalar reference, which
 +# is output after html escaping, an IO handle passed either as *handle or
 +# *handle{IO}, or a function reference. In the latter case all following
 +# parameters will be taken as argument to the content function call.
 +sub git_print_section {
 +      my ($div_args, $header_args, $content);
 +      my $arg = shift;
 +      if (ref($arg) eq 'HASH') {
 +              $div_args = $arg;
 +              $arg = shift;
 +      }
 +      if (ref($arg) eq 'ARRAY') {
 +              $header_args = $arg;
 +              $arg = shift;
 +      }
 +      $content = $arg;
 +
 +      print $cgi->start_div($div_args);
 +      git_print_header_div(@$header_args);
 +
 +      if (ref($content) eq 'CODE') {
 +              $content->(@_);
 +      } elsif (ref($content) eq 'SCALAR') {
 +              print esc_html($$content);
 +      } elsif (ref($content) eq 'GLOB' or ref($content) eq 'IO::Handle') {
 +              print <$content>;
 +      } elsif (!ref($content) && defined($content)) {
 +              print $content;
 +      }
 +
 +      print $cgi->end_div;
 +}
 +
  sub print_local_time {
        print format_local_time(@_);
  }
@@@ -5077,7 -4958,7 +5075,7 @@@ sub git_heads_body 
                      "<td class=\"link\">" .
                      $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'fullname'})}, "shortlog") . " | " .
                      $cgi->a({-href => href(action=>"log", hash=>$ref{'fullname'})}, "log") . " | " .
 -                    $cgi->a({-href => href(action=>"tree", hash=>$ref{'fullname'}, hash_base=>$ref{'name'})}, "tree") .
 +                    $cgi->a({-href => href(action=>"tree", hash=>$ref{'fullname'}, hash_base=>$ref{'fullname'})}, "tree") .
                      "</td>\n" .
                      "</tr>";
        }
        print "</table>\n";
  }
  
 +# Display a single remote block
 +sub git_remote_block {
 +      my ($remote, $rdata, $limit, $head) = @_;
 +
 +      my $heads = $rdata->{'heads'};
 +      my $fetch = $rdata->{'fetch'};
 +      my $push = $rdata->{'push'};
 +
 +      my $urls_table = "<table class=\"projects_list\">\n" ;
 +
 +      if (defined $fetch) {
 +              if ($fetch eq $push) {
 +                      $urls_table .= format_repo_url("URL", $fetch);
 +              } else {
 +                      $urls_table .= format_repo_url("Fetch URL", $fetch);
 +                      $urls_table .= format_repo_url("Push URL", $push) if defined $push;
 +              }
 +      } elsif (defined $push) {
 +              $urls_table .= format_repo_url("Push URL", $push);
 +      } else {
 +              $urls_table .= format_repo_url("", "No remote URL");
 +      }
 +
 +      $urls_table .= "</table>\n";
 +
 +      my $dots;
 +      if (defined $limit && $limit < @$heads) {
 +              $dots = $cgi->a({-href => href(action=>"remotes", hash=>$remote)}, "...");
 +      }
 +
 +      print $urls_table;
 +      git_heads_body($heads, $head, 0, $limit, $dots);
 +}
 +
 +# Display a list of remote names with the respective fetch and push URLs
 +sub git_remotes_list {
 +      my ($remotedata, $limit) = @_;
 +      print "<table class=\"heads\">\n";
 +      my $alternate = 1;
 +      my @remotes = sort keys %$remotedata;
 +
 +      my $limited = $limit && $limit < @remotes;
 +
 +      $#remotes = $limit - 1 if $limited;
 +
 +      while (my $remote = shift @remotes) {
 +              my $rdata = $remotedata->{$remote};
 +              my $fetch = $rdata->{'fetch'};
 +              my $push = $rdata->{'push'};
 +              if ($alternate) {
 +                      print "<tr class=\"dark\">\n";
 +              } else {
 +                      print "<tr class=\"light\">\n";
 +              }
 +              $alternate ^= 1;
 +              print "<td>" .
 +                    $cgi->a({-href=> href(action=>'remotes', hash=>$remote),
 +                             -class=> "list name"},esc_html($remote)) .
 +                    "</td>";
 +              print "<td class=\"link\">" .
 +                    (defined $fetch ? $cgi->a({-href=> $fetch}, "fetch") : "fetch") .
 +                    " | " .
 +                    (defined $push ? $cgi->a({-href=> $push}, "push") : "push") .
 +                    "</td>";
 +
 +              print "</tr>\n";
 +      }
 +
 +      if ($limited) {
 +              print "<tr>\n" .
 +                    "<td colspan=\"3\">" .
 +                    $cgi->a({-href => href(action=>"remotes")}, "...") .
 +                    "</td>\n" . "</tr>\n";
 +      }
 +
 +      print "</table>";
 +}
 +
 +# Display remote heads grouped by remote, unless there are too many
 +# remotes, in which case we only display the remote names
 +sub git_remotes_body {
 +      my ($remotedata, $limit, $head) = @_;
 +      if ($limit and $limit < keys %$remotedata) {
 +              git_remotes_list($remotedata, $limit);
 +      } else {
 +              fill_remote_heads($remotedata);
 +              while (my ($remote, $rdata) = each %$remotedata) {
 +                      git_print_section({-class=>"remote", -id=>$remote},
 +                              ["remotes", $remote, $remote], sub {
 +                                      git_remote_block($remote, $rdata, $limit, $head);
 +                              });
 +              }
 +      }
 +}
 +
  sub git_search_grep_body {
        my ($commitlist, $from, $to, $extra) = @_;
        $from = 0 unless defined $from;
@@@ -5321,7 -5107,6 +5319,7 @@@ sub git_summary 
        my %co = parse_commit("HEAD");
        my %cd = %co ? parse_date($co{'committer_epoch'}, $co{'committer_tz'}) : ();
        my $head = $co{'id'};
 +      my $remote_heads = gitweb_check_feature('remote_heads');
  
        my $owner = git_get_project_owner($project);
  
        # there are more ...
        my @taglist  = git_get_tags_list(16);
        my @headlist = git_get_heads_list(16);
 +      my %remotedata = $remote_heads ? git_get_remotes_list() : ();
        my @forklist;
        my $check_forks = gitweb_check_feature('forks');
  
        @url_list = map { "$_/$project" } @git_base_url_list unless @url_list;
        foreach my $git_url (@url_list) {
                next unless $git_url;
 -              print "<tr class=\"metadata_url\"><td>$url_tag</td><td>$git_url</td></tr>\n";
 +              print format_repo_url($url_tag, $git_url);
                $url_tag = "";
        }
  
                               $cgi->a({-href => href(action=>"heads")}, "..."));
        }
  
 +      if (%remotedata) {
 +              git_print_header_div('remotes');
 +              git_remotes_body(\%remotedata, 15, $head);
 +      }
 +
        if (@forklist) {
                git_print_header_div('forks');
                git_project_list_body(\@forklist, 'age', 0, 15,
@@@ -5517,7 -5296,7 +5515,7 @@@ sub git_blame_common 
                print 'END';
                if (defined $t0 && gitweb_check_feature('timed')) {
                        print ' '.
-                             Time::HiRes::tv_interval($t0, [Time::HiRes::gettimeofday()]).
+                             tv_interval($t0, [ gettimeofday() ]).
                              ' '.$number_of_git_cmds;
                }
                print "\n";
@@@ -5704,7 -5483,7 +5702,7 @@@ sub git_blame_data 
  sub git_tags {
        my $head = git_get_head_hash($project);
        git_header_html();
 -      git_print_page_nav('','', $head,undef,$head);
 +      git_print_page_nav('','', $head,undef,$head,format_ref_views('tags'));
        git_print_header_div('summary', $project);
  
        my @tagslist = git_get_tags_list();
  sub git_heads {
        my $head = git_get_head_hash($project);
        git_header_html();
 -      git_print_page_nav('','', $head,undef,$head);
 +      git_print_page_nav('','', $head,undef,$head,format_ref_views('heads'));
        git_print_header_div('summary', $project);
  
        my @headslist = git_get_heads_list();
        git_footer_html();
  }
  
 +# used both for single remote view and for list of all the remotes
 +sub git_remotes {
 +      gitweb_check_feature('remote_heads')
 +              or die_error(403, "Remote heads view is disabled");
 +
 +      my $head = git_get_head_hash($project);
 +      my $remote = $input_params{'hash'};
 +
 +      my $remotedata = git_get_remotes_list($remote);
 +      die_error(500, "Unable to get remote information") unless defined $remotedata;
 +
 +      unless (%$remotedata) {
 +              die_error(404, defined $remote ?
 +                      "Remote $remote not found" :
 +                      "No remotes found");
 +      }
 +
 +      git_header_html(undef, undef, -action_extra => $remote);
 +      git_print_page_nav('', '',  $head, undef, $head,
 +              format_ref_views($remote ? '' : 'remotes'));
 +
 +      fill_remote_heads($remotedata);
 +      if (defined $remote) {
 +              git_print_header_div('remotes', "$remote remote for $project");
 +              git_remote_block($remote, $remotedata->{$remote}, undef, $head);
 +      } else {
 +              git_print_header_div('summary', "$project remotes");
 +              git_remotes_body($remotedata, undef, $head);
 +      }
 +
 +      git_footer_html();
 +}
 +
  sub git_blob_plain {
        my $type = shift;
        my $expires;