]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'maint'
authorJunio C Hamano <gitster@pobox.com>
Mon, 20 Dec 2010 01:49:42 +0000 (17:49 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 20 Dec 2010 01:49:42 +0000 (17:49 -0800)
* maint:
  gitweb: Include links to feeds in HTML header only for '200 OK' response
  fsck docs: remove outdated and useless diagnostic
  userdiff: fix typo in ruby and python word regexes
  trace.c: mark file-local function static
  Fix typo in git-gc document.

1  2 
Documentation/config.txt
Documentation/git-gc.txt
gitweb/gitweb.perl

diff --combined Documentation/config.txt
index bf9479e4eb49c971b9502d072dd9be18c838ab21,4f1e9799321380739afe96dd7ec841b768ae988b..54597f18979fb376e2f1d9ea29f5ac247e9df230
@@@ -374,15 -374,6 +374,15 @@@ core.warnAmbiguousRefs:
        If true, git will warn you if the ref name you passed it is ambiguous
        and might match multiple refs in the .git/refs/ tree. True by default.
  
 +core.abbrevguard::
 +      Even though git makes sure that it uses enough hexdigits to show
 +      an abbreviated object name unambiguously, as more objects are
 +      added to the repository over time, a short name that used to be
 +      unique will stop being unique.  Git uses this many extra hexdigits
 +      that are more than necessary to make the object name currently
 +      unique, in the hope that its output will stay unique a bit longer.
 +      Defaults to 0.
 +
  core.compression::
        An integer -1..9, indicating a default compression level.
        -1 is the zlib default. 0 means no compression,
@@@ -468,12 -459,6 +468,12 @@@ core.askpass:
        prompt. The external program shall be given a suitable prompt as
        command line argument and write the password on its STDOUT.
  
 +core.attributesfile::
 +      In addition to '.gitattributes' (per-directory) and
 +      '.git/info/attributes', git looks into this file for attributes
 +      (see linkgit:gitattributes[5]). Path expansions are made the same
 +      way as for `core.excludesfile`.
 +
  core.editor::
        Commands such as `commit` and `tag` that lets you edit
        messages by launching an editor uses the value of this
@@@ -522,9 -507,6 +522,9 @@@ core.whitespace:
    part of the line terminator, i.e. with it, `trailing-space`
    does not trigger if the character before such a carriage-return
    is not a whitespace (not enabled by default).
 +* `tabwidth=<n>` tells how many character positions a tab occupies; this
 +  is relevant for `indent-with-non-tab` and when git fixes `tab-in-indent`
 +  errors. The default tab width is 8. Allowed values are 1 to 63.
  
  core.fsyncobjectfiles::
        This boolean will enable 'fsync()' when writing object files.
@@@ -617,9 -599,8 +617,9 @@@ branch.autosetupmerge:
        this behavior can be chosen per-branch using the `--track`
        and `--no-track` options. The valid settings are: `false` -- no
        automatic setup is done; `true` -- automatic setup is done when the
 -      starting point is a remote branch; `always` -- automatic setup is
 -      done when the starting point is either a local branch or remote
 +      starting point is a remote-tracking branch; `always` --
 +      automatic setup is done when the starting point is either a
 +      local branch or remote-tracking
        branch. This option defaults to true.
  
  branch.autosetuprebase::
        When `local`, rebase is set to true for tracked branches of
        other local branches.
        When `remote`, rebase is set to true for tracked branches of
 -      remote branches.
 +      remote-tracking branches.
        When `always`, rebase will be set to true for all tracking
        branches.
        See "branch.autosetupmerge" for details on how to set up a
@@@ -697,7 -678,7 +697,7 @@@ color.branch:
  color.branch.<slot>::
        Use customized color for branch coloration. `<slot>` is one of
        `current` (the current branch), `local` (a local branch),
 -      `remote` (a tracking branch in refs/remotes/), `plain` (other
 +      `remote` (a remote-tracking branch in refs/remotes/), `plain` (other
        refs).
  +
  The value for these configuration variables is a list of colors (at most
@@@ -725,7 -706,7 +725,7 @@@ color.diff.<slot>:
  color.decorate.<slot>::
        Use customized color for 'git log --decorate' output.  `<slot>` is one
        of `branch`, `remoteBranch`, `tag`, `stash` or `HEAD` for local
 -      branches, remote tracking branches, tags, stash and HEAD, respectively.
 +      branches, remote-tracking branches, tags, stash and HEAD, respectively.
  
  color.grep::
        When set to `always`, always highlight matches.  When `false` (or
@@@ -790,8 -771,7 +790,8 @@@ color.status.<slot>:
        one of `header` (the header text of the status message),
        `added` or `updated` (files which are added but not committed),
        `changed` (files which are changed but not added in the index),
 -      `untracked` (files which are not tracked by git), or
 +      `untracked` (files which are not tracked by git),
 +      `branch` (the current branch), or
        `nobranch` (the color the 'no branch' warning is shown in, defaulting
        to red). The values of these variables may be specified as in
        color.branch.<slot>.
@@@ -897,11 -877,6 +897,11 @@@ diff.wordRegex:
        sequences that match the regular expression are "words", all other
        characters are *ignorable* whitespace.
  
 +fetch.recurseSubmodules::
 +      A boolean value which changes the behavior for fetch and pull, the
 +      default is to not recursively fetch populated sumodules unless
 +      configured otherwise.
 +
  fetch.unpackLimit::
        If the number of objects fetched over the git native
        transfer is below this
@@@ -996,7 -971,7 +996,7 @@@ gc.packrefs:
        Running `git pack-refs` in a repository renders it
        unclonable by Git versions prior to 1.5.1.2 over dumb
        transports such as HTTP.  This variable determines whether
-       'git gc' runs `git pack-refs`. This can be set to `nobare`
+       'git gc' runs `git pack-refs`. This can be set to `notbare`
        to enable it within all non-bare repos or it can be set to a
        boolean value.  The default is `true`.
  
@@@ -1125,7 -1100,7 +1125,7 @@@ gui.newbranchtemplate:
        linkgit:git-gui[1].
  
  gui.pruneduringfetch::
 -      "true" if linkgit:git-gui[1] should prune tracking branches when
 +      "true" if linkgit:git-gui[1] should prune remote-tracking branches when
        performing a fetch. The default value is "false".
  
  gui.trustmtime::
@@@ -1554,13 -1529,11 +1554,13 @@@ pack.packSizeLimit:
        supported.
  
  pager.<cmd>::
 -      Allows turning on or off pagination of the output of a
 -      particular git subcommand when writing to a tty.  If
 -      `\--paginate` or `\--no-pager` is specified on the command line,
 -      it takes precedence over this option.  To disable pagination for
 -      all commands, set `core.pager` or `GIT_PAGER` to `cat`.
 +      If the value is boolean, turns on or off pagination of the
 +      output of a particular git subcommand when writing to a tty.
 +      Otherwise, turns on pagination for the subcommand using the
 +      pager specified by the value of `pager.<cmd>`.  If `\--paginate`
 +      or `\--no-pager` is specified on the command line, it takes
 +      precedence over this option.  To disable pagination for all
 +      commands, set `core.pager` or `GIT_PAGER` to `cat`.
  
  pretty.<name>::
        Alias for a --pretty= format string, as specified in
@@@ -1762,7 -1735,6 +1762,7 @@@ sendemail.to:
  sendemail.smtpdomain::
  sendemail.smtpserver::
  sendemail.smtpserverport::
 +sendemail.smtpserveroption::
  sendemail.smtpuser::
  sendemail.thread::
  sendemail.validate::
@@@ -1816,13 -1788,6 +1816,13 @@@ submodule.<name>.update:
        URL and other values found in the `.gitmodules` file.  See
        linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
  
 +submodule.<name>.fetchRecurseSubmodules::
 +      This option can be used to enable/disable recursive fetching of this
 +      submodule. It can be overriden by using the --[no-]recurse-submodules
 +      command line option to "git fetch" and "git pull".
 +      This setting will override that from in the linkgit:gitmodules[5]
 +      file.
 +
  submodule.<name>.ignore::
        Defines under what circumstances "git status" and the diff family show
        a submodule as modified. When set to "all", it will never be considered
diff --combined Documentation/git-gc.txt
index 801aede6094e40b5bfb1f4c5d09393e7011f500e,a01eef6763d8c0383121019e9c33c869f0300acf..26632414b2646ba80a17fa861d422a9c8c957810
@@@ -89,7 -89,7 +89,7 @@@ are not part of the current project mos
  them sooner.  This option defaults to '30 days'.
  
  The above two configuration variables can be given to a pattern.  For
 -example, this sets non-default expiry values only to remote tracking
 +example, this sets non-default expiry values only to remote-tracking
  branches:
  
  ------------
@@@ -107,7 -107,7 +107,7 @@@ how long records of conflicted merge yo
  kept.  This defaults to 15 days.
  
  The optional configuration variable 'gc.packrefs' determines if
- 'git gc' runs 'git pack-refs'. This can be set to "nobare" to enable
+ 'git gc' runs 'git pack-refs'. This can be set to "notbare" to enable
  it within all non-bare repos or it can be set to a boolean value.
  This defaults to true.
  
@@@ -128,8 -128,8 +128,8 @@@ Note
  
  'git gc' tries very hard to be safe about the garbage it collects. In
  particular, it will keep not only objects referenced by your current set
 -of branches and tags, but also objects referenced by the index, remote
 -tracking branches, refs saved by 'git filter-branch' in
 +of branches and tags, but also objects referenced by the index,
 +remote-tracking branches, refs saved by 'git filter-branch' in
  refs/original/, or reflogs (which may reference commits in branches
  that were later amended or rewound).
  
diff --combined gitweb/gitweb.perl
index d521c93761b187a16eb270d7c231c497236377d5,42cc1da7ff85bca5c23a59b321cd4a6a7aa45d46..d965cda92a8b21f1cfc5b043f4dd56fbb4dd3b3f
@@@ -17,10 -17,12 +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 {
@@@ -164,12 -166,6 +164,12 @@@ our @diff_opts = ('-M'); # taken from g
  # the gitweb domain.
  our $prevent_xss = 0;
  
 +# Path to the highlight executable to use (must be the one from
 +# http://www.andre-simon.de due to assumptions about parameters and output).
 +# Useful if highlight is not installed on your webserver's PATH.
 +# [Default: highlight]
 +our $highlight_bin = "++HIGHLIGHT_BIN++";
 +
  # information about snapshot formats that gitweb is capable of serving
  our %known_snapshot_formats = (
        # name => {
@@@ -491,18 -487,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 {
@@@ -611,14 -595,6 +611,14 @@@ sub filter_snapshot_fmts 
                !$known_snapshot_formats{$_}{'disabled'}} @fmts;
  }
  
 +# 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.
 +#
 +# Otherwise, if it is false then gitweb would process config file only once;
 +# if it is true then gitweb config would be run for each request.
 +our $per_request_config = 1;
 +
  our ($GITWEB_CONFIG, $GITWEB_CONFIG_SYSTEM);
  sub evaluate_gitweb_config {
        our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
@@@ -725,7 -701,6 +725,7 @@@ our %actions = 
        "log" => \&git_log,
        "patch" => \&git_patch,
        "patches" => \&git_patches,
 +      "remotes" => \&git_remotes,
        "rss" => \&git_rss,
        "atom" => \&git_atom,
        "search" => \&git_search,
@@@ -800,10 -775,10 +800,10 @@@ sub evaluate_path_info 
                'history',
        );
  
 -      # we want to catch
 +      # we want to catch, among others
        # [$hash_parent_base[:$file_parent]..]$hash_parent[:$file_name]
        my ($parentrefname, $parentpathname, $refname, $pathname) =
 -              ($path_info =~ /^(?:(.+?)(?::(.+))?\.\.)?(.+?)(?::(.+))?$/);
 +              ($path_info =~ /^(?:(.+?)(?::(.+))?\.\.)?([^:]+?)?(?::(.+))?$/);
  
        # first, analyze the 'current' part
        if (defined $pathname) {
                # hash_base instead. It should also be noted that hand-crafted
                # links having 'history' as an action and no pathname or hash
                # set will fail, but that happens regardless of PATH_INFO.
 -              $input_params{'action'} ||= "shortlog";
 -              if (grep { $_ eq $input_params{'action'} } @wants_base) {
 +              if (defined $parentrefname) {
 +                      # if there is parent let the default be 'shortlog' action
 +                      # (for http://git.example.com/repo.git/A..B links); if there
 +                      # is no parent, dispatch will detect type of object and set
 +                      # action appropriately if required (if action is not set)
 +                      $input_params{'action'} ||= "shortlog";
 +              }
 +              if ($input_params{'action'} &&
 +                  grep { $_ eq $input_params{'action'} } @wants_base) {
                        $input_params{'hash_base'} ||= $refname;
                } else {
                        $input_params{'hash'} ||= $refname;
@@@ -1084,27 -1052,16 +1084,27 @@@ sub dispatch 
  }
  
  sub reset_timer {
 -      our $t0 = [Time::HiRes::gettimeofday()]
 +      our $t0 = [ gettimeofday() ]
                if defined $t0;
        our $number_of_git_cmds = 0;
  }
  
 +our $first_request = 1;
  sub run_request {
        reset_timer();
  
        evaluate_uri();
 -      evaluate_gitweb_config();
 +      if ($first_request) {
 +              evaluate_gitweb_config();
 +              evaluate_git_version();
 +      }
 +      if ($per_request_config) {
 +              if (ref($per_request_config) eq 'CODE') {
 +                      $per_request_config->();
 +              } elsif (!$first_request) {
 +                      evaluate_gitweb_config();
 +              }
 +      }
        check_loadavg();
  
        # $projectroot and $projects_list might be set in gitweb config file
@@@ -1157,8 -1114,8 +1157,8 @@@ sub evaluate_argv 
  
  sub run {
        evaluate_argv();
 -      evaluate_git_version();
  
 +      $first_request = 1;
        $pre_listen_hook->()
                if $pre_listen_hook;
  
  
                $post_dispatch_hook->()
                        if $post_dispatch_hook;
 +              $first_request = 0;
  
                last REQUEST if ($is_last_request->());
        }
@@@ -2797,44 -2753,6 +2797,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;
@@@ -3233,15 -3151,13 +3233,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;
@@@ -3452,8 -3368,7 +3452,8 @@@ sub run_highlighter 
        close $fd
                or die_error(404, "Reading blob failed");
        open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ".
 -                "highlight --xhtml --fragment --syntax $syntax |"
 +                quote_command($highlight_bin).
 +                " --xhtml --fragment --syntax $syntax |"
                or die_error(500, "Couldn't open file or run syntax highlighter");
        return $fd;
  }
@@@ -3479,6 -3394,51 +3479,51 @@@ sub get_page_title 
        return $title;
  }
  
+ sub print_feed_meta {
+       if (defined $project) {
+               my %href_params = get_feed_info();
+               if (!exists $href_params{'-title'}) {
+                       $href_params{'-title'} = 'log';
+               }
+               foreach my $format qw(RSS Atom) {
+                       my $type = lc($format);
+                       my %link_attr = (
+                               '-rel' => 'alternate',
+                               '-title' => esc_attr("$project - $href_params{'-title'} - $format feed"),
+                               '-type' => "application/$type+xml"
+                       );
+                       $href_params{'action'} = $type;
+                       $link_attr{'-href'} = href(%href_params);
+                       print "<link ".
+                             "rel=\"$link_attr{'-rel'}\" ".
+                             "title=\"$link_attr{'-title'}\" ".
+                             "href=\"$link_attr{'-href'}\" ".
+                             "type=\"$link_attr{'-type'}\" ".
+                             "/>\n";
+                       $href_params{'extra_options'} = '--no-merges';
+                       $link_attr{'-href'} = href(%href_params);
+                       $link_attr{'-title'} .= ' (no merges)';
+                       print "<link ".
+                             "rel=\"$link_attr{'-rel'}\" ".
+                             "title=\"$link_attr{'-title'}\" ".
+                             "href=\"$link_attr{'-href'}\" ".
+                             "type=\"$link_attr{'-type'}\" ".
+                             "/>\n";
+               }
+       } else {
+               printf('<link rel="alternate" title="%s projects list" '.
+                      'href="%s" type="text/plain; charset=utf-8" />'."\n",
+                      esc_attr($site_name), href(project=>undef, action=>"project_index"));
+               printf('<link rel="alternate" title="%s projects feeds" '.
+                      'href="%s" type="text/x-opml" />'."\n",
+                      esc_attr($site_name), href(project=>undef, action=>"opml"));
+       }
+ }
  sub git_header_html {
        my $status = shift || "200 OK";
        my $expires = shift;
                        print '<link rel="stylesheet" type="text/css" href="'.esc_url($stylesheet).'"/>'."\n";
                }
        }
-       if (defined $project) {
-               my %href_params = get_feed_info();
-               if (!exists $href_params{'-title'}) {
-                       $href_params{'-title'} = 'log';
-               }
-               foreach my $format qw(RSS Atom) {
-                       my $type = lc($format);
-                       my %link_attr = (
-                               '-rel' => 'alternate',
-                               '-title' => esc_attr("$project - $href_params{'-title'} - $format feed"),
-                               '-type' => "application/$type+xml"
-                       );
-                       $href_params{'action'} = $type;
-                       $link_attr{'-href'} = href(%href_params);
-                       print "<link ".
-                             "rel=\"$link_attr{'-rel'}\" ".
-                             "title=\"$link_attr{'-title'}\" ".
-                             "href=\"$link_attr{'-href'}\" ".
-                             "type=\"$link_attr{'-type'}\" ".
-                             "/>\n";
-                       $href_params{'extra_options'} = '--no-merges';
-                       $link_attr{'-href'} = href(%href_params);
-                       $link_attr{'-title'} .= ' (no merges)';
-                       print "<link ".
-                             "rel=\"$link_attr{'-rel'}\" ".
-                             "title=\"$link_attr{'-title'}\" ".
-                             "href=\"$link_attr{'-href'}\" ".
-                             "type=\"$link_attr{'-type'}\" ".
-                             "/>\n";
-               }
-       } else {
-               printf('<link rel="alternate" title="%s projects list" '.
-                      'href="%s" type="text/plain; charset=utf-8" />'."\n",
-                      esc_attr($site_name), href(project=>undef, action=>"project_index"));
-               printf('<link rel="alternate" title="%s projects feeds" '.
-                      'href="%s" type="text/x-opml" />'."\n",
-                      esc_attr($site_name), href(project=>undef, action=>"opml"));
-       }
+       print_feed_meta()
+               if ($status eq '200 OK');
        if (defined $favicon) {
                print qq(<link rel="shortcut icon" href=").esc_url($favicon).qq(" type="image/png" />\n);
        }
        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";
        }
@@@ -3676,7 -3588,7 +3681,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">'.
@@@ -3804,19 -3716,6 +3809,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;
@@@ -3860,49 -3759,6 +3865,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(@_);
  }
@@@ -5102,7 -4958,7 +5107,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;
@@@ -5346,7 -5107,6 +5351,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,
@@@ -5542,7 -5296,7 +5547,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";
@@@ -5729,7 -5483,7 +5734,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;