{"thread":{"id":"25709","subject":"[PATCHv7 00/11] gitweb: remote_heads feature","startedAt":"2010-11-11T12:26:07Z","lastAt":"2010-11-12T08:13:54Z","messageCount":16,"participants":["Giuseppe Bilotta","Jakub Narebski"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"155669","messageId":"1289478378-15604-1-git-send-email-giuseppe.bilotta@gmail.com","threadId":"25709","inReplyTo":null,"subject":"[PATCHv7 00/11] gitweb: remote_heads feature","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2010-11-11T12:26:07Z","receivedAt":"2010-11-11T12:26:07Z","isPatch":false,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"This 7th version of the remote heads feature for gitweb differs from the\nprevious mostly my small tune-ups, renames of internal functions, some\npatch reordering and a final addition.\n\nThe first patch is a bugfix that is only exposed by some subsequent\ncode. It's independent from the rest of the patchset, and can just go\nin.\n\nThe next 4 patches are rather straightforward too, and they introduce\nthe remote heads feature in a rather primitive way. They are ready for\ninclusion.\n\nPatch 6 and 7 introduce the 'single remote' view with an improved page\nheader visualization.\n\nPatches 8 and 9 provide the infrastructure for the grouped remote heads\nview introduced by the next patch.\n\nFinally, an 11th patch was added to the series to enable remote heads\nview from git instaweb.\n\nGiuseppe Bilotta (11):\n  gitweb: use fullname as hash_base in heads link\n  gitweb: introduce remote_heads feature\n  gitweb: git_get_heads_list accepts an optional list of refs\n  gitweb: separate heads and remotes lists\n  gitweb: nagivation menu for tags, heads and remotes\n  gitweb: allow action specialization in page header\n  gitweb: remotes view for a single remote\n  gitweb: refactor repository URL printing\n  gitweb: provide a routine to display (sub)sections\n  gitweb: group remote heads by remote\n  git instaweb: enable remote_heads\n\n git-instaweb.sh          |    2 +\n gitweb/gitweb.perl       |  275 ++++++++++++++++++++++++++++++++++++++++++++--\n gitweb/static/gitweb.css |    6 +\n 3 files changed, 275 insertions(+), 8 deletions(-)\n\n-- \n1.7.3.68.g6ec8\n"},{"id":"155672","messageId":"1289478378-15604-2-git-send-email-giuseppe.bilotta@gmail.com","threadId":"25709","inReplyTo":"1289478378-15604-1-git-send-email-giuseppe.bilotta@gmail.com","subject":"[PATCHv7 01/11] gitweb: use fullname as hash_base in heads link","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2010-11-11T12:26:08Z","receivedAt":"2010-11-11T12:26:08Z","isPatch":false,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"Otherwise, if names are manipulated for display, the link will point to\nthe wrong head.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n gitweb/gitweb.perl |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 253f41a..77693ab 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -4960,7 +4960,7 @@ sub git_heads_body {\n \t\t      \"<td class=\\\"link\\\">\" .\n \t\t      $cgi->a({-href => href(action=>\"shortlog\", hash=>$ref{'fullname'})}, \"shortlog\") . \" | \" .\n \t\t      $cgi->a({-href => href(action=>\"log\", hash=>$ref{'fullname'})}, \"log\") . \" | \" .\n-\t\t      $cgi->a({-href => href(action=>\"tree\", hash=>$ref{'fullname'}, hash_base=>$ref{'name'})}, \"tree\") .\n+\t\t      $cgi->a({-href => href(action=>\"tree\", hash=>$ref{'fullname'}, hash_base=>$ref{'fullname'})}, \"tree\") .\n \t\t      \"</td>\\n\" .\n \t\t      \"</tr>\";\n \t}\n-- \n1.7.3.68.g6ec8\n"},{"id":"155673","messageId":"1289478378-15604-3-git-send-email-giuseppe.bilotta@gmail.com","threadId":"25709","inReplyTo":"1289478378-15604-1-git-send-email-giuseppe.bilotta@gmail.com","subject":"[PATCHv7 02/11] gitweb: introduce remote_heads feature","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2010-11-11T12:26:09Z","receivedAt":"2010-11-11T12:26:09Z","isPatch":false,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"With this feature enabled, remote heads are retrieved (and displayed)\nwhen getting (and displaying) the heads list. Typical usage would be for\nlocal repository browsing, e.g. by using git-instaweb (or even a more\npermanent gitweb setup), to check the repository status and the relation\nbetween tracking branches and the originating remotes.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n gitweb/gitweb.perl |   18 ++++++++++++++++--\n 1 files changed, 16 insertions(+), 2 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 77693ab..e1787c2 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -493,6 +493,18 @@ our %feature = (\n \t\t'sub' => sub { feature_bool('highlight', @_) },\n \t\t'override' => 0,\n \t\t'default' => [0]},\n+\n+\t# Enable displaying of remote heads in the heads list\n+\n+\t# To enable system wide have in $GITWEB_CONFIG\n+\t# $feature{'remote_heads'}{'default'} = [1];\n+\t# To have project specific config enable override in $GITWEB_CONFIG\n+\t# $feature{'remote_heads'}{'override'} = 1;\n+\t# and in project config gitweb.remote_heads = 0|1;\n+\t'remote_heads' => {\n+\t\t'sub' => sub { feature_bool('remote_heads', @_) },\n+\t\t'override' => 0,\n+\t\t'default' => [0]},\n );\n \n sub gitweb_get_feature {\n@@ -3160,10 +3172,12 @@ sub git_get_heads_list {\n \tmy $limit = shift;\n \tmy @headslist;\n \n+\tmy $remote_heads = gitweb_check_feature('remote_heads');\n+\n \topen my $fd, '-|', git_cmd(), 'for-each-ref',\n \t\t($limit ? '--count='.($limit+1) : ()), '--sort=-committerdate',\n \t\t'--format=%(objectname) %(refname) %(subject)%00%(committer)',\n-\t\t'refs/heads'\n+\t\t'refs/heads', ($remote_heads ? 'refs/remotes' : ())\n \t\tor return;\n \twhile (my $line = <$fd>) {\n \t\tmy %ref_item;\n@@ -3174,7 +3188,7 @@ sub git_get_heads_list {\n \t\tmy ($committer, $epoch, $tz) =\n \t\t\t($committerinfo =~ /^(.*) ([0-9]+) (.*)$/);\n \t\t$ref_item{'fullname'}  = $name;\n-\t\t$name =~ s!^refs/heads/!!;\n+\t\t$name =~ s!^refs/(?:head|remote)s/!!;\n \n \t\t$ref_item{'name'}  = $name;\n \t\t$ref_item{'id'}    = $hash;\n-- \n1.7.3.68.g6ec8\n"},{"id":"155670","messageId":"1289478378-15604-4-git-send-email-giuseppe.bilotta@gmail.com","threadId":"25709","inReplyTo":"1289478378-15604-1-git-send-email-giuseppe.bilotta@gmail.com","subject":"[PATCHv7 03/11] gitweb: git_get_heads_list accepts an optional list of refs","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2010-11-11T12:26:10Z","receivedAt":"2010-11-11T12:26:10Z","isPatch":false,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"git_get_heads_list(limit, class1, class2, ...) can now be used to retrieve\nrefs/class1, refs/class2 etc. Defaults to ('heads', 'remotes') or ('heads')\ndepending on whether the 'remote_heads' feature is enabled or not.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n gitweb/gitweb.perl |   11 +++++++----\n 1 files changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex e1787c2..951bb0d 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -3169,15 +3169,18 @@ sub parse_from_to_diffinfo {\n ## parse to array of hashes functions\n \n sub git_get_heads_list {\n-\tmy $limit = shift;\n+\tmy ($limit, @classes) = @_;\n+\tunless (@classes) {\n+\t\tmy $remote_heads = gitweb_check_feature('remote_heads');\n+\t\t@classes = ('heads', $remote_heads ? 'remotes' : ());\n+\t}\n+\tmy @patterns = map { \"refs/$_\" } @classes;\n \tmy @headslist;\n \n-\tmy $remote_heads = gitweb_check_feature('remote_heads');\n-\n \topen my $fd, '-|', git_cmd(), 'for-each-ref',\n \t\t($limit ? '--count='.($limit+1) : ()), '--sort=-committerdate',\n \t\t'--format=%(objectname) %(refname) %(subject)%00%(committer)',\n-\t\t'refs/heads', ($remote_heads ? 'refs/remotes' : ())\n+\t\t@patterns\n \t\tor return;\n \twhile (my $line = <$fd>) {\n \t\tmy %ref_item;\n-- \n1.7.3.68.g6ec8\n"},{"id":"155671","messageId":"1289478378-15604-5-git-send-email-giuseppe.bilotta@gmail.com","threadId":"25709","inReplyTo":"1289478378-15604-1-git-send-email-giuseppe.bilotta@gmail.com","subject":"[PATCHv7 04/11] gitweb: separate heads and remotes lists","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2010-11-11T12:26:11Z","receivedAt":"2010-11-11T12:26:11Z","isPatch":false,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"We specialize the 'heads' action to only display local branches, and\nintroduce a 'remotes' action to display the remote branches (only\navailable when the remotes_head feature is enabled).\n\nMirroring this, we also split the heads list in summary view into\nlocal and remote lists, each linking to the appropriate action.\n\nThe git_get_heads_list now defaults to 'heads' only, regardless of\nwhether the remote heads feature is active or not.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n gitweb/gitweb.perl |   31 +++++++++++++++++++++++++++----\n 1 files changed, 27 insertions(+), 4 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 951bb0d..9fcbbb2 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -719,6 +719,7 @@ our %actions = (\n \t\"log\" => \\&git_log,\n \t\"patch\" => \\&git_patch,\n \t\"patches\" => \\&git_patches,\n+\t\"remotes\" => \\&git_remotes,\n \t\"rss\" => \\&git_rss,\n \t\"atom\" => \\&git_atom,\n \t\"search\" => \\&git_search,\n@@ -3170,10 +3171,7 @@ sub parse_from_to_diffinfo {\n \n sub git_get_heads_list {\n \tmy ($limit, @classes) = @_;\n-\tunless (@classes) {\n-\t\tmy $remote_heads = gitweb_check_feature('remote_heads');\n-\t\t@classes = ('heads', $remote_heads ? 'remotes' : ());\n-\t}\n+\t@classes = ('heads') unless @classes;\n \tmy @patterns = map { \"refs/$_\" } @classes;\n \tmy @headslist;\n \n@@ -5126,6 +5124,7 @@ sub git_summary {\n \tmy %co = parse_commit(\"HEAD\");\n \tmy %cd = %co ? parse_date($co{'committer_epoch'}, $co{'committer_tz'}) : ();\n \tmy $head = $co{'id'};\n+\tmy $remote_heads = gitweb_check_feature('remote_heads');\n \n \tmy $owner = git_get_project_owner($project);\n \n@@ -5134,6 +5133,7 @@ sub git_summary {\n \t# there are more ...\n \tmy @taglist  = git_get_tags_list(16);\n \tmy @headlist = git_get_heads_list(16);\n+\tmy @remotelist = $remote_heads ? git_get_heads_list(16, 'remotes') : ();\n \tmy @forklist;\n \tmy $check_forks = gitweb_check_feature('forks');\n \n@@ -5211,6 +5211,13 @@ sub git_summary {\n \t\t               $cgi->a({-href => href(action=>\"heads\")}, \"...\"));\n \t}\n \n+\tif (@remotelist) {\n+\t\tgit_print_header_div('remotes');\n+\t\tgit_heads_body(\\@remotelist, $head, 0, 15,\n+\t\t               $#remotelist <= 15 ? undef :\n+\t\t               $cgi->a({-href => href(action=>\"remotes\")}, \"...\"));\n+\t}\n+\n \tif (@forklist) {\n \t\tgit_print_header_div('forks');\n \t\tgit_project_list_body(\\@forklist, 'age', 0, 15,\n@@ -5525,6 +5532,22 @@ sub git_heads {\n \tgit_footer_html();\n }\n \n+sub git_remotes {\n+\tgitweb_check_feature('remote_heads')\n+\t\tor die_error(403, \"Remote heads view is disabled\");\n+\n+\tmy $head = git_get_head_hash($project);\n+\tgit_header_html();\n+\tgit_print_page_nav('','', $head,undef,$head);\n+\tgit_print_header_div('summary', $project);\n+\n+\tmy @remotelist = git_get_heads_list(undef, 'remotes');\n+\tif (@remotelist) {\n+\t\tgit_heads_body(\\@remotelist, $head);\n+\t}\n+\tgit_footer_html();\n+}\n+\n sub git_blob_plain {\n \tmy $type = shift;\n \tmy $expires;\n-- \n1.7.3.68.g6ec8\n"},{"id":"155676","messageId":"1289478378-15604-6-git-send-email-giuseppe.bilotta@gmail.com","threadId":"25709","inReplyTo":"1289478378-15604-1-git-send-email-giuseppe.bilotta@gmail.com","subject":"[PATCHv7 05/11] gitweb: nagivation menu for tags, heads and remotes","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2010-11-11T12:26:12Z","receivedAt":"2010-11-11T12:26:12Z","isPatch":false,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"tags, heads and remotes are all views that inspect a (particular class\nof) refs, so allow the user to easily switch between them by adding\nthe appropriate navigation submenu to each view.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n gitweb/gitweb.perl |   19 ++++++++++++++++---\n 1 files changed, 16 insertions(+), 3 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 9fcbbb2..7cd50d4 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -3733,6 +3733,19 @@ sub git_print_page_nav {\n \t      \"</div>\\n\";\n }\n \n+# returns a submenu for the nagivation of the refs views (tags, heads,\n+# remotes) with the current view disabled and the remotes view only\n+# available if the feature is enabled\n+sub format_ref_views {\n+\tmy ($current) = @_;\n+\tmy @ref_views = qw{tags heads};\n+\tpush @ref_views, 'remotes' if gitweb_check_feature('remote_heads');\n+\treturn join \" | \", map {\n+\t\t$_ eq $current ? $_ :\n+\t\t$cgi->a({-href => href(action=>$_)}, $_)\n+\t} @ref_views\n+}\n+\n sub format_paging_nav {\n \tmy ($action, $page, $has_next_link) = @_;\n \tmy $paging_nav;\n@@ -5509,7 +5522,7 @@ sub git_blame_data {\n sub git_tags {\n \tmy $head = git_get_head_hash($project);\n \tgit_header_html();\n-\tgit_print_page_nav('','', $head,undef,$head);\n+\tgit_print_page_nav('','', $head,undef,$head,format_ref_views('tags'));\n \tgit_print_header_div('summary', $project);\n \n \tmy @tagslist = git_get_tags_list();\n@@ -5522,7 +5535,7 @@ sub git_tags {\n sub git_heads {\n \tmy $head = git_get_head_hash($project);\n \tgit_header_html();\n-\tgit_print_page_nav('','', $head,undef,$head);\n+\tgit_print_page_nav('','', $head,undef,$head,format_ref_views('heads'));\n \tgit_print_header_div('summary', $project);\n \n \tmy @headslist = git_get_heads_list();\n@@ -5538,7 +5551,7 @@ sub git_remotes {\n \n \tmy $head = git_get_head_hash($project);\n \tgit_header_html();\n-\tgit_print_page_nav('','', $head,undef,$head);\n+\tgit_print_page_nav('','', $head,undef,$head,format_ref_views('remotes'));\n \tgit_print_header_div('summary', $project);\n \n \tmy @remotelist = git_get_heads_list(undef, 'remotes');\n-- \n1.7.3.68.g6ec8\n"},{"id":"155674","messageId":"1289478378-15604-7-git-send-email-giuseppe.bilotta@gmail.com","threadId":"25709","inReplyTo":"1289478378-15604-1-git-send-email-giuseppe.bilotta@gmail.com","subject":"[PATCHv7 06/11] gitweb: allow action specialization in page header","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2010-11-11T12:26:13Z","receivedAt":"2010-11-11T12:26:13Z","isPatch":false,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"An optional -action_extra parameter is given to git_header_html() to\nidentify a variant of the action that is being displayed. For example,\nthis can be used to specify that the remotes view is being used for a\nspecific remote and not to display all remotes.\n\nWhen -action_extra is provided, the action name in the header will be\nturned into a link to the action without any arguments or parameters, to\nprovide a quick link to the non-specific variant of the action.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n gitweb/gitweb.perl |   10 +++++++++-\n 1 files changed, 9 insertions(+), 1 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 7cd50d4..c3b8966 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -3526,7 +3526,15 @@ EOF\n \tif (defined $project) {\n \t\tprint $cgi->a({-href => href(action=>\"summary\")}, esc_html($project));\n \t\tif (defined $action) {\n-\t\t\tprint \" / $action\";\n+\t\t\tmy $action_print = $action ;\n+\t\t\tif (defined $opts{-action_extra}) {\n+\t\t\t\t$action_print = $cgi->a({-href => href(action=>$action)},\n+\t\t\t\t\t$action);\n+\t\t\t}\n+\t\t\tprint \" / $action_print\";\n+\t\t}\n+\t\tif (defined $opts{-action_extra}) {\n+\t\t\tprint \" / $opts{-action_extra}\";\n \t\t}\n \t\tprint \"\\n\";\n \t}\n-- \n1.7.3.68.g6ec8\n"},{"id":"155677","messageId":"1289478378-15604-8-git-send-email-giuseppe.bilotta@gmail.com","threadId":"25709","inReplyTo":"1289478378-15604-1-git-send-email-giuseppe.bilotta@gmail.com","subject":"[PATCHv7 07/11] gitweb: remotes view for a single remote","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2010-11-11T12:26:14Z","receivedAt":"2010-11-11T12:26:14Z","isPatch":false,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"When 'remotes' view is passed the 'hash' parameter, interpret it as the\nname of a remote and limit the view the the heads of that remote.\n\nIn single-remote view we let the user switch easily to the default\nremotes view by specifying an -action_extra for the page header and by\nenabling the 'remotes' link in the reference navigation submenu.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n gitweb/gitweb.perl |   30 ++++++++++++++++++++++++++----\n 1 files changed, 26 insertions(+), 4 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex c3b8966..bf38757 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -5558,14 +5558,36 @@ sub git_remotes {\n \t\tor die_error(403, \"Remote heads view is disabled\");\n \n \tmy $head = git_get_head_hash($project);\n-\tgit_header_html();\n-\tgit_print_page_nav('','', $head,undef,$head,format_ref_views('remotes'));\n-\tgit_print_header_div('summary', $project);\n+\tmy $remote = $input_params{'hash'};\n+\n+\tmy @remotelist;\n+\n+\tif (defined $remote) {\n+\t\t# only display the heads in a given remote, stripping the\n+\t\t# remote name which is already visible elsewhere\n+\t\t@remotelist = map {\n+\t\t\tmy $ref = $_ ;\n+\t\t\t$ref->{'name'} =~ s!^$remote/!!;\n+\t\t\t$ref\n+\t\t} git_get_heads_list(undef, \"remotes/$remote\");\n+\t} else {\n+\t\t@remotelist = git_get_heads_list(undef, 'remotes');\n+\t}\n+\n+\tgit_header_html(undef, undef, -action_extra => $remote);\n+\tgit_print_page_nav('', '',  $head, undef, $head,\n+\t\tformat_ref_views($remote ? '' : 'remotes'));\n+\n+\tif (defined $remote) {\n+\t\tgit_print_header_div('remotes', \"$remote remote for $project\");\n+\t} else {\n+\t\tgit_print_header_div('summary', \"$project remotes\");\n+\t}\n \n-\tmy @remotelist = git_get_heads_list(undef, 'remotes');\n \tif (@remotelist) {\n \t\tgit_heads_body(\\@remotelist, $head);\n \t}\n+\n \tgit_footer_html();\n }\n \n-- \n1.7.3.68.g6ec8\n"},{"id":"155680","messageId":"1289478378-15604-9-git-send-email-giuseppe.bilotta@gmail.com","threadId":"25709","inReplyTo":"1289478378-15604-1-git-send-email-giuseppe.bilotta@gmail.com","subject":"[PATCHv7 08/11] gitweb: refactor repository URL printing","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2010-11-11T12:26:15Z","receivedAt":"2010-11-11T12:26:15Z","isPatch":false,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"Factor out the code to display the repository URL(s) from summary view\ninto a format_rep_url() routine.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n gitweb/gitweb.perl |    7 ++++++-\n 1 files changed, 6 insertions(+), 1 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex bf38757..6e7a663 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -3797,6 +3797,11 @@ sub git_print_header_div {\n \t      \"\\n</div>\\n\";\n }\n \n+sub format_repo_url {\n+\tmy ($name, $url) = @_;\n+\treturn \"<tr class=\\\"metadata_url\\\"><td>$name</td><td>$url</td></tr>\\n\";\n+}\n+\n sub print_local_time {\n \tprint format_local_time(@_);\n }\n@@ -5180,7 +5185,7 @@ sub git_summary {\n \t@url_list = map { \"$_/$project\" } @git_base_url_list unless @url_list;\n \tforeach my $git_url (@url_list) {\n \t\tnext unless $git_url;\n-\t\tprint \"<tr class=\\\"metadata_url\\\"><td>$url_tag</td><td>$git_url</td></tr>\\n\";\n+\t\tprint format_repo_url($url_tag, $git_url);\n \t\t$url_tag = \"\";\n \t}\n \n-- \n1.7.3.68.g6ec8\n"},{"id":"155675","messageId":"1289478378-15604-10-git-send-email-giuseppe.bilotta@gmail.com","threadId":"25709","inReplyTo":"1289478378-15604-1-git-send-email-giuseppe.bilotta@gmail.com","subject":"[PATCHv7 09/11] gitweb: provide a routine to display (sub)sections","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2010-11-11T12:26:16Z","receivedAt":"2010-11-11T12:26:16Z","isPatch":false,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"The routine puts the given contento into a DIV element, automatically\nadding a header div. The content can be provided as a standard scalar\nvalue (which is used as-is), as a scalar ref (which is HTML-escaped), as\na function reference to be executed, or as a file handle to be dumped.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n gitweb/gitweb.perl |   38 ++++++++++++++++++++++++++++++++++++++\n 1 files changed, 38 insertions(+), 0 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 6e7a663..64da0cc 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -3802,6 +3802,44 @@ sub format_repo_url {\n \treturn \"<tr class=\\\"metadata_url\\\"><td>$name</td><td>$url</td></tr>\\n\";\n }\n \n+# Group output by placing it in a DIV element and adding a header.\n+# Options for start_div() can be provided by passing a hash reference as the\n+# first parameter to the function.\n+# Options to git_print_header_div() can be provided by passing an array\n+# reference. This must follow the options to start_div if they are present.\n+# The content can be a scalar, which is output as-is, a scalar reference, which\n+# is output after html escaping, an IO handle passed either as *handle or\n+# *handle{IO}, or a function reference. In the latter case all following\n+# parameters will be taken as argument to the content function call.\n+sub git_print_section {\n+\tmy ($div_args, $header_args, $content);\n+\tmy $arg = shift;\n+\tif (ref($arg) eq 'HASH') {\n+\t\t$div_args = $arg;\n+\t\t$arg = shift;\n+\t}\n+\tif (ref($arg) eq 'ARRAY') {\n+\t\t$header_args = $arg;\n+\t\t$arg = shift;\n+\t}\n+\t$content = $arg;\n+\n+\tprint $cgi->start_div($div_args);\n+\tgit_print_header_div(@$header_args);\n+\n+\tif (ref($content) eq 'CODE') {\n+\t\t$content->(@_);\n+\t} elsif (ref($content) eq 'SCALAR') {\n+\t\tprint esc_html($$content);\n+\t} elsif (ref($content) eq 'GLOB' or ref($content) eq 'IO::Handle') {\n+\t\tprint <$content>;\n+\t} elsif (!ref($content) && defined($content)) {\n+\t\tprint $content;\n+\t}\n+\n+\tprint $cgi->end_div;\n+}\n+\n sub print_local_time {\n \tprint format_local_time(@_);\n }\n-- \n1.7.3.68.g6ec8\n"},{"id":"155678","messageId":"1289478378-15604-11-git-send-email-giuseppe.bilotta@gmail.com","threadId":"25709","inReplyTo":"1289478378-15604-1-git-send-email-giuseppe.bilotta@gmail.com","subject":"[PATCHv7 10/11] gitweb: group remote heads by remote","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2010-11-11T12:26:17Z","receivedAt":"2010-11-11T12:26:17Z","isPatch":false,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"In remote and summary view, display a block for each remote, with the\nfetch and push URL(s) as well as the list of the remote heads.\n\nIn summary view, if the number of remotes is higher than a prescribed\nlimit, only display the first <limit> remotes and their fetch and push\nurls, without any heads information and without grouping.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n gitweb/gitweb.perl       |  173 ++++++++++++++++++++++++++++++++++++++++-----\n gitweb/static/gitweb.css |    6 ++\n 2 files changed, 159 insertions(+), 20 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 64da0cc..8df4a00 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -2772,6 +2772,50 @@ sub git_get_last_activity {\n \treturn (undef, undef);\n }\n \n+# Returns a hash ref mapping remote names to their fetch and push URLs.\n+# We return a hash ref rather than a hash so that a simple check with defined\n+# can be used to tell apart the \"no remotes\" case from other kinds of\n+# failures. The mapping can be restriced to a single remote by specifying its\n+# name as optional parameter to the call.\n+#\n+# Implementation note: when a single remote is wanted, we cannot use 'git\n+# remote show -n' because that command always work (assuming it's a remote URL\n+# if it's not defined), and we cannot use 'git remote show' because that would\n+# try to make a network roundtrip. So the only way to find if that particular\n+# remote is defined is to walk the list provided by 'git remote -v' and stop if\n+# and when we find what we want.\n+sub git_get_remotes_list {\n+\tmy $wanted = shift;\n+\tmy %remotes = ();\n+\n+\topen my $fd, '-|' , git_cmd(), 'remote', '-v';\n+\treturn unless $fd;\n+\twhile (my $remote = <$fd>) {\n+\t\tchomp $remote;\n+\t\t$remote =~ s!\\t(.*?)\\s+\\((\\w+)\\)$!!;\n+\t\tnext if $wanted and not $remote eq $wanted;\n+\t\tmy ($url, $key) = ($1, $2);\n+\n+\t\t$remotes{$remote} ||= { 'heads' => () };\n+\t\t$remotes{$remote}{$key} = $url;\n+\t}\n+\tclose $fd or return;\n+\treturn wantarray ? %remotes : \\%remotes;\n+}\n+\n+# Takes a hash of remotes as first parameter and fills it by adding the\n+# available remote heads for each of the indicated remotes.\n+sub fill_remote_heads {\n+\tmy $remotes = shift;\n+\tmy @heads = map { \"remotes/$_\" } keys %$remotes;\n+\tmy @remoteheads = git_get_heads_list(undef, @heads);\n+\tforeach my $remote (keys %$remotes) {\n+\t\t$remotes->{$remote}{'heads'} = [ grep {\n+\t\t\t$_->{'name'} =~ s!^$remote/!!\n+\t\t\t} @remoteheads ];\n+\t}\n+}\n+\n sub git_get_references {\n \tmy $type = shift || \"\";\n \tmy %refs;\n@@ -5051,6 +5095,102 @@ sub git_heads_body {\n \tprint \"</table>\\n\";\n }\n \n+# Display a single remote block\n+sub git_remote_block {\n+\tmy ($remote, $rdata, $limit, $head) = @_;\n+\n+\tmy $heads = $rdata->{'heads'};\n+\tmy $fetch = $rdata->{'fetch'};\n+\tmy $push = $rdata->{'push'};\n+\n+\tmy $urls_table = \"<table class=\\\"projects_list\\\">\\n\" ;\n+\n+\tif (defined $fetch) {\n+\t\tif ($fetch eq $push) {\n+\t\t\t$urls_table .= format_repo_url(\"URL\", $fetch);\n+\t\t} else {\n+\t\t\t$urls_table .= format_repo_url(\"Fetch URL\", $fetch);\n+\t\t\t$urls_table .= format_repo_url(\"Push URL\", $push) if defined $push;\n+\t\t}\n+\t} elsif (defined $push) {\n+\t\t$urls_table .= format_repo_url(\"Push URL\", $push);\n+\t} else {\n+\t\t$urls_table .= format_repo_url(\"\", \"No remote URL\");\n+\t}\n+\n+\t$urls_table .= \"</table>\\n\";\n+\n+\tmy $dots;\n+\tif (defined $limit && $limit < @$heads) {\n+\t\t$dots = $cgi->a({-href => href(action=>\"remotes\", hash=>$remote)}, \"...\");\n+\t}\n+\n+\tprint $urls_table;\n+\tgit_heads_body($heads, $head, 0, $limit, $dots);\n+}\n+\n+# Display a list of remote names with the respective fetch and push URLs\n+sub git_remotes_list {\n+\tmy ($remotedata, $limit) = @_;\n+\tprint \"<table class=\\\"heads\\\">\\n\";\n+\tmy $alternate = 1;\n+\tmy @remotes = sort keys %$remotedata;\n+\n+\tmy $limited = $limit && $limit < @remotes;\n+\n+\t$#remotes = $limit - 1 if $limited;\n+\n+\twhile (my $remote = shift @remotes) {\n+\t\tmy $rdata = $remotedata->{$remote};\n+\t\tmy $fetch = $rdata->{'fetch'};\n+\t\tmy $push = $rdata->{'push'};\n+\t\tif ($alternate) {\n+\t\t\tprint \"<tr class=\\\"dark\\\">\\n\";\n+\t\t} else {\n+\t\t\tprint \"<tr class=\\\"light\\\">\\n\";\n+\t\t}\n+\t\t$alternate ^= 1;\n+\t\tprint \"<td>\" .\n+\t\t      $cgi->a({-href=> href(action=>'remotes', hash=>$remote),\n+\t\t\t       -class=> \"list name\"},esc_html($remote)) .\n+\t\t      \"</td>\";\n+\t\tprint \"<td class=\\\"link\\\">\" .\n+\t\t      (defined $fetch ? $cgi->a({-href=> $fetch}, \"fetch\") : \"fetch\") .\n+\t\t      \" | \" .\n+\t\t      (defined $push ? $cgi->a({-href=> $push}, \"push\") : \"push\") .\n+\t\t      \"</td>\";\n+\n+\t\tprint \"</tr>\\n\";\n+\t}\n+\n+\tif ($limited) {\n+\t\tprint \"<tr>\\n\" .\n+\t\t      \"<td colspan=\\\"3\\\">\" .\n+\t\t      $cgi->a({-href => href(action=>\"remotes\")}, \"...\") .\n+\t\t      \"</td>\\n\" . \"</tr>\\n\";\n+\t}\n+\n+\tprint \"</table>\";\n+}\n+\n+# Display remote heads grouped by remote, unless there are too many\n+# remotes ($have_all is false), in which case we only display the remote\n+# names\n+sub git_remotes_body {\n+\tmy ($remotedata, $limit, $head) = @_;\n+\tif ($limit and $limit < keys %$remotedata) {\n+\t\tgit_remotes_list($remotedata, $limit);\n+\t} else {\n+\t\tfill_remote_heads($remotedata);\n+\t\twhile (my ($remote, $rdata) = each %$remotedata) {\n+\t\t\tgit_print_section({-class=>\"remote\", -id=>$remote},\n+\t\t\t\t[\"remotes\", $remote, $remote], sub {\n+\t\t\t\t\tgit_remote_block($remote, $rdata, $limit, $head);\n+\t\t\t\t});\n+\t\t}\n+\t}\n+}\n+\n sub git_search_grep_body {\n \tmy ($commitlist, $from, $to, $extra) = @_;\n \t$from = 0 unless defined $from;\n@@ -5197,7 +5337,7 @@ sub git_summary {\n \t# there are more ...\n \tmy @taglist  = git_get_tags_list(16);\n \tmy @headlist = git_get_heads_list(16);\n-\tmy @remotelist = $remote_heads ? git_get_heads_list(16, 'remotes') : ();\n+\tmy %remotedata = $remote_heads ? git_get_remotes_list() : ();\n \tmy @forklist;\n \tmy $check_forks = gitweb_check_feature('forks');\n \n@@ -5275,11 +5415,9 @@ sub git_summary {\n \t\t               $cgi->a({-href => href(action=>\"heads\")}, \"...\"));\n \t}\n \n-\tif (@remotelist) {\n+\tif (%remotedata) {\n \t\tgit_print_header_div('remotes');\n-\t\tgit_heads_body(\\@remotelist, $head, 0, 15,\n-\t\t               $#remotelist <= 15 ? undef :\n-\t\t               $cgi->a({-href => href(action=>\"remotes\")}, \"...\"));\n+\t\tgit_remotes_body(\\%remotedata, 15, $head);\n \t}\n \n \tif (@forklist) {\n@@ -5596,6 +5734,7 @@ sub git_heads {\n \tgit_footer_html();\n }\n \n+# used both for single remote view and for list of all the remotes\n sub git_remotes {\n \tgitweb_check_feature('remote_heads')\n \t\tor die_error(403, \"Remote heads view is disabled\");\n@@ -5603,32 +5742,26 @@ sub git_remotes {\n \tmy $head = git_get_head_hash($project);\n \tmy $remote = $input_params{'hash'};\n \n-\tmy @remotelist;\n+\tmy $remotedata = git_get_remotes_list($remote);\n+\tdie_error(500, \"Unable to get remote information\") unless defined $remotedata;\n \n-\tif (defined $remote) {\n-\t\t# only display the heads in a given remote, stripping the\n-\t\t# remote name which is already visible elsewhere\n-\t\t@remotelist = map {\n-\t\t\tmy $ref = $_ ;\n-\t\t\t$ref->{'name'} =~ s!^$remote/!!;\n-\t\t\t$ref\n-\t\t} git_get_heads_list(undef, \"remotes/$remote\");\n-\t} else {\n-\t\t@remotelist = git_get_heads_list(undef, 'remotes');\n+\tunless (%$remotedata) {\n+\t\tdie_error(404, defined $remote ?\n+\t\t\t\"Remote $remote not found\" :\n+\t\t\t\"No remotes found\");\n \t}\n \n \tgit_header_html(undef, undef, -action_extra => $remote);\n \tgit_print_page_nav('', '',  $head, undef, $head,\n \t\tformat_ref_views($remote ? '' : 'remotes'));\n \n+\tfill_remote_heads($remotedata);\n \tif (defined $remote) {\n \t\tgit_print_header_div('remotes', \"$remote remote for $project\");\n+\t\tgit_remote_block($remote, $remotedata->{$remote}, undef, $head);\n \t} else {\n \t\tgit_print_header_div('summary', \"$project remotes\");\n-\t}\n-\n-\tif (@remotelist) {\n-\t\tgit_heads_body(\\@remotelist, $head);\n+\t\tgit_remotes_body($remotedata, undef, $head);\n \t}\n \n \tgit_footer_html();\ndiff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css\nindex 4132aab..79d7eeb 100644\n--- a/gitweb/static/gitweb.css\n+++ b/gitweb/static/gitweb.css\n@@ -573,6 +573,12 @@ div.binary {\n \tfont-style: italic;\n }\n \n+div.remote {\n+\tmargin: .5em;\n+\tborder: 1px solid #d9d8d1;\n+\tdisplay: inline-block;\n+}\n+\n /* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */\n \n /* Highlighting theme definition: */\n-- \n1.7.3.68.g6ec8\n"},{"id":"155679","messageId":"1289478378-15604-12-git-send-email-giuseppe.bilotta@gmail.com","threadId":"25709","inReplyTo":"1289478378-15604-1-git-send-email-giuseppe.bilotta@gmail.com","subject":"[PATCHv7 11/11] git instaweb: enable remote_heads","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2010-11-11T12:26:18Z","receivedAt":"2010-11-11T12:26:18Z","isPatch":false,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"When using git instaweb to browse a local repository, performance is\nmuch less of an issue, and providing as much information as possible has\na higher priority, so it makes sense to enable remote_heads.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n git-instaweb.sh |    2 ++\n 1 files changed, 2 insertions(+), 0 deletions(-)\n\ndiff --git a/git-instaweb.sh b/git-instaweb.sh\nindex e6f6ecd..10fcebb 100755\n--- a/git-instaweb.sh\n+++ b/git-instaweb.sh\n@@ -580,6 +580,8 @@ gitweb_conf() {\n our \\$projectroot = \"$(dirname \"$fqgitdir\")\";\n our \\$git_temp = \"$fqgitdir/gitweb/tmp\";\n our \\$projects_list = \\$projectroot;\n+\n+\\$feature{'remote_heads'}{'default'} = [1];\n EOF\n }\n \n-- \n1.7.3.68.g6ec8\n"},{"id":"155732","messageId":"201011112025.30095.jnareb@gmail.com","threadId":"25709","inReplyTo":"1289478378-15604-11-git-send-email-giuseppe.bilotta@gmail.com","subject":"Re: [PATCHv7 10/11] gitweb: group remote heads by remote","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-11-11T19:25:29Z","receivedAt":"2010-11-11T19:25:29Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Thu, 11 Nov 2010 at 13:26, Giuseppe Bilotta wrote:\n\n> +# Returns a hash ref mapping remote names to their fetch and push URLs.\n> +# We return a hash ref rather than a hash so that a simple check with defined\n> +# can be used to tell apart the \"no remotes\" case from other kinds of\n> +# failures. The mapping can be restriced to a single remote by specifying its\n> +# name as optional parameter to the call.\n\nErrr... it is now hash or hash ref.\n\n[...]\n> +sub git_get_remotes_list {\n[...]\n> +\tclose $fd or return;\n> +\treturn wantarray ? %remotes : \\%remotes;\n> +}\n\n[...]\n\n> +# Display remote heads grouped by remote, unless there are too many\n> +# remotes ($have_all is false), in which case we only display the remote\n> +# names\n> +sub git_remotes_body {\n> +\tmy ($remotedata, $limit, $head) = @_;\n[...]\n\nWhat $have_all?\n\n\nBut with exception of those out-of-date comments I like this version.\nThanks.\n-- \nJakub Narebski\nPoland\n"},{"id":"155734","messageId":"201011112029.22373.jnareb@gmail.com","threadId":"25709","inReplyTo":"1289478378-15604-1-git-send-email-giuseppe.bilotta@gmail.com","subject":"Re: [PATCHv7 00/11] gitweb: remote_heads feature","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-11-11T19:29:21Z","receivedAt":"2010-11-11T19:29:21Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Thu, 11 Nov 2010, Giuseppe Bilotta wrote:\n\n> This 7th version of the remote heads feature for gitweb differs from the\n> previous mostly my small tune-ups, renames of internal functions, some\n> patch reordering and a final addition.\n[...]\n\nI like this version very much.\n\n> Giuseppe Bilotta (11):\n>   gitweb: use fullname as hash_base in heads link\n>   gitweb: introduce remote_heads feature\n>   gitweb: git_get_heads_list accepts an optional list of refs\n>   gitweb: separate heads and remotes lists\n>   gitweb: nagivation menu for tags, heads and remotes\n>   gitweb: allow action specialization in page header\n>   gitweb: remotes view for a single remote\n>   gitweb: refactor repository URL printing\n>   gitweb: provide a routine to display (sub)sections\n>   gitweb: group remote heads by remote\n>   git instaweb: enable remote_heads\n\nWith exception of next to last patch, which contains a few of outdated\ncomments (see my response for this commit), I don't see any problems.\n\nSo, for the whole series:\n\n  Acked-by: Jakub Narebski <jnareb@gmail.com>\n\n\nThanks for working diligently on this series, Giuseppe!\n-- \nJakub Narebski\nPoland\n"},{"id":"155765","messageId":"AANLkTimbnwi8B3YtOgxe=qATxOA+baGe=igAa2qQ1e30@mail.gmail.com","threadId":"25709","inReplyTo":"201011112029.22373.jnareb@gmail.com","subject":"Re: [PATCHv7 00/11] gitweb: remote_heads feature","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2010-11-12T08:05:59Z","receivedAt":"2010-11-12T08:05:59Z","isPatch":false,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"2010/11/11 Jakub Narebski <jnareb@gmail.com>:\n> On Thu, 11 Nov 2010, Giuseppe Bilotta wrote:\n>\n>> This 7th version of the remote heads feature for gitweb differs from the\n>> previous mostly my small tune-ups, renames of internal functions, some\n>> patch reordering and a final addition.\n> [...]\n>\n> I like this version very much.\n\nThanks.\n\n>> Giuseppe Bilotta (11):\n>>   gitweb: use fullname as hash_base in heads link\n>>   gitweb: introduce remote_heads feature\n>>   gitweb: git_get_heads_list accepts an optional list of refs\n>>   gitweb: separate heads and remotes lists\n>>   gitweb: nagivation menu for tags, heads and remotes\n>>   gitweb: allow action specialization in page header\n>>   gitweb: remotes view for a single remote\n>>   gitweb: refactor repository URL printing\n>>   gitweb: provide a routine to display (sub)sections\n>>   gitweb: group remote heads by remote\n>>   git instaweb: enable remote_heads\n>\n> With exception of next to last patch, which contains a few of outdated\n> comments (see my response for this commit), I don't see any problems.\n\nI will send a -bis for that patch, with the amended comments.\n\n> So, for the whole series:\n>\n>  Acked-by: Jakub Narebski <jnareb@gmail.com>\n>\n>\n> Thanks for working diligently on this series, Giuseppe!\n\nThank _you_ for the thorough review!\n\n-- \nGiuseppe \"Oblomov\" Bilotta\n"},{"id":"155766","messageId":"1289549634-19149-1-git-send-email-giuseppe.bilotta@gmail.com","threadId":"25709","inReplyTo":"201011112025.30095.jnareb@gmail.com","subject":"[PATCHv7bis 10/11] gitweb: group remote heads by remote","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2010-11-12T08:13:54Z","receivedAt":"2010-11-12T08:13:54Z","isPatch":false,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"In remote and summary view, display a block for each remote, with the\nfetch and push URL(s) as well as the list of the remote heads.\n\nIn summary view, if the number of remotes is higher than a prescribed\nlimit, only display the first <limit> remotes and their fetch and push\nurls, without any heads information and without grouping.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n gitweb/gitweb.perl       |  172 ++++++++++++++++++++++++++++++++++++++++------\n gitweb/static/gitweb.css |    6 ++\n 2 files changed, 158 insertions(+), 20 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 64da0cc..fc177f7 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -2772,6 +2772,50 @@ sub git_get_last_activity {\n \treturn (undef, undef);\n }\n \n+# Returns a hash mapping remote names to their fetch and push URLs. In scalar\n+# context, a hash ref is returned, allowing callers to tell apart the \"no\n+# remotes\" case (ref to empty hash is returned) from other kinds of failures\n+# (undef is returned). The mapping can be restriced to a single remote by\n+# specifying its name as optional parameter to the call.\n+#\n+# Implementation note: when a single remote is wanted, we cannot use 'git\n+# remote show -n' because that command always work (assuming it's a remote URL\n+# if it's not defined), and we cannot use 'git remote show' because that would\n+# try to make a network roundtrip. So the only way to find if that particular\n+# remote is defined is to walk the list provided by 'git remote -v' and stop if\n+# and when we find what we want.\n+sub git_get_remotes_list {\n+\tmy $wanted = shift;\n+\tmy %remotes = ();\n+\n+\topen my $fd, '-|' , git_cmd(), 'remote', '-v';\n+\treturn unless $fd;\n+\twhile (my $remote = <$fd>) {\n+\t\tchomp $remote;\n+\t\t$remote =~ s!\\t(.*?)\\s+\\((\\w+)\\)$!!;\n+\t\tnext if $wanted and not $remote eq $wanted;\n+\t\tmy ($url, $key) = ($1, $2);\n+\n+\t\t$remotes{$remote} ||= { 'heads' => () };\n+\t\t$remotes{$remote}{$key} = $url;\n+\t}\n+\tclose $fd or return;\n+\treturn wantarray ? %remotes : \\%remotes;\n+}\n+\n+# Takes a hash of remotes as first parameter and fills it by adding the\n+# available remote heads for each of the indicated remotes.\n+sub fill_remote_heads {\n+\tmy $remotes = shift;\n+\tmy @heads = map { \"remotes/$_\" } keys %$remotes;\n+\tmy @remoteheads = git_get_heads_list(undef, @heads);\n+\tforeach my $remote (keys %$remotes) {\n+\t\t$remotes->{$remote}{'heads'} = [ grep {\n+\t\t\t$_->{'name'} =~ s!^$remote/!!\n+\t\t\t} @remoteheads ];\n+\t}\n+}\n+\n sub git_get_references {\n \tmy $type = shift || \"\";\n \tmy %refs;\n@@ -5051,6 +5095,101 @@ sub git_heads_body {\n \tprint \"</table>\\n\";\n }\n \n+# Display a single remote block\n+sub git_remote_block {\n+\tmy ($remote, $rdata, $limit, $head) = @_;\n+\n+\tmy $heads = $rdata->{'heads'};\n+\tmy $fetch = $rdata->{'fetch'};\n+\tmy $push = $rdata->{'push'};\n+\n+\tmy $urls_table = \"<table class=\\\"projects_list\\\">\\n\" ;\n+\n+\tif (defined $fetch) {\n+\t\tif ($fetch eq $push) {\n+\t\t\t$urls_table .= format_repo_url(\"URL\", $fetch);\n+\t\t} else {\n+\t\t\t$urls_table .= format_repo_url(\"Fetch URL\", $fetch);\n+\t\t\t$urls_table .= format_repo_url(\"Push URL\", $push) if defined $push;\n+\t\t}\n+\t} elsif (defined $push) {\n+\t\t$urls_table .= format_repo_url(\"Push URL\", $push);\n+\t} else {\n+\t\t$urls_table .= format_repo_url(\"\", \"No remote URL\");\n+\t}\n+\n+\t$urls_table .= \"</table>\\n\";\n+\n+\tmy $dots;\n+\tif (defined $limit && $limit < @$heads) {\n+\t\t$dots = $cgi->a({-href => href(action=>\"remotes\", hash=>$remote)}, \"...\");\n+\t}\n+\n+\tprint $urls_table;\n+\tgit_heads_body($heads, $head, 0, $limit, $dots);\n+}\n+\n+# Display a list of remote names with the respective fetch and push URLs\n+sub git_remotes_list {\n+\tmy ($remotedata, $limit) = @_;\n+\tprint \"<table class=\\\"heads\\\">\\n\";\n+\tmy $alternate = 1;\n+\tmy @remotes = sort keys %$remotedata;\n+\n+\tmy $limited = $limit && $limit < @remotes;\n+\n+\t$#remotes = $limit - 1 if $limited;\n+\n+\twhile (my $remote = shift @remotes) {\n+\t\tmy $rdata = $remotedata->{$remote};\n+\t\tmy $fetch = $rdata->{'fetch'};\n+\t\tmy $push = $rdata->{'push'};\n+\t\tif ($alternate) {\n+\t\t\tprint \"<tr class=\\\"dark\\\">\\n\";\n+\t\t} else {\n+\t\t\tprint \"<tr class=\\\"light\\\">\\n\";\n+\t\t}\n+\t\t$alternate ^= 1;\n+\t\tprint \"<td>\" .\n+\t\t      $cgi->a({-href=> href(action=>'remotes', hash=>$remote),\n+\t\t\t       -class=> \"list name\"},esc_html($remote)) .\n+\t\t      \"</td>\";\n+\t\tprint \"<td class=\\\"link\\\">\" .\n+\t\t      (defined $fetch ? $cgi->a({-href=> $fetch}, \"fetch\") : \"fetch\") .\n+\t\t      \" | \" .\n+\t\t      (defined $push ? $cgi->a({-href=> $push}, \"push\") : \"push\") .\n+\t\t      \"</td>\";\n+\n+\t\tprint \"</tr>\\n\";\n+\t}\n+\n+\tif ($limited) {\n+\t\tprint \"<tr>\\n\" .\n+\t\t      \"<td colspan=\\\"3\\\">\" .\n+\t\t      $cgi->a({-href => href(action=>\"remotes\")}, \"...\") .\n+\t\t      \"</td>\\n\" . \"</tr>\\n\";\n+\t}\n+\n+\tprint \"</table>\";\n+}\n+\n+# Display remote heads grouped by remote, unless there are too many\n+# remotes, in which case we only display the remote names\n+sub git_remotes_body {\n+\tmy ($remotedata, $limit, $head) = @_;\n+\tif ($limit and $limit < keys %$remotedata) {\n+\t\tgit_remotes_list($remotedata, $limit);\n+\t} else {\n+\t\tfill_remote_heads($remotedata);\n+\t\twhile (my ($remote, $rdata) = each %$remotedata) {\n+\t\t\tgit_print_section({-class=>\"remote\", -id=>$remote},\n+\t\t\t\t[\"remotes\", $remote, $remote], sub {\n+\t\t\t\t\tgit_remote_block($remote, $rdata, $limit, $head);\n+\t\t\t\t});\n+\t\t}\n+\t}\n+}\n+\n sub git_search_grep_body {\n \tmy ($commitlist, $from, $to, $extra) = @_;\n \t$from = 0 unless defined $from;\n@@ -5197,7 +5336,7 @@ sub git_summary {\n \t# there are more ...\n \tmy @taglist  = git_get_tags_list(16);\n \tmy @headlist = git_get_heads_list(16);\n-\tmy @remotelist = $remote_heads ? git_get_heads_list(16, 'remotes') : ();\n+\tmy %remotedata = $remote_heads ? git_get_remotes_list() : ();\n \tmy @forklist;\n \tmy $check_forks = gitweb_check_feature('forks');\n \n@@ -5275,11 +5414,9 @@ sub git_summary {\n \t\t               $cgi->a({-href => href(action=>\"heads\")}, \"...\"));\n \t}\n \n-\tif (@remotelist) {\n+\tif (%remotedata) {\n \t\tgit_print_header_div('remotes');\n-\t\tgit_heads_body(\\@remotelist, $head, 0, 15,\n-\t\t               $#remotelist <= 15 ? undef :\n-\t\t               $cgi->a({-href => href(action=>\"remotes\")}, \"...\"));\n+\t\tgit_remotes_body(\\%remotedata, 15, $head);\n \t}\n \n \tif (@forklist) {\n@@ -5596,6 +5733,7 @@ sub git_heads {\n \tgit_footer_html();\n }\n \n+# used both for single remote view and for list of all the remotes\n sub git_remotes {\n \tgitweb_check_feature('remote_heads')\n \t\tor die_error(403, \"Remote heads view is disabled\");\n@@ -5603,32 +5741,26 @@ sub git_remotes {\n \tmy $head = git_get_head_hash($project);\n \tmy $remote = $input_params{'hash'};\n \n-\tmy @remotelist;\n+\tmy $remotedata = git_get_remotes_list($remote);\n+\tdie_error(500, \"Unable to get remote information\") unless defined $remotedata;\n \n-\tif (defined $remote) {\n-\t\t# only display the heads in a given remote, stripping the\n-\t\t# remote name which is already visible elsewhere\n-\t\t@remotelist = map {\n-\t\t\tmy $ref = $_ ;\n-\t\t\t$ref->{'name'} =~ s!^$remote/!!;\n-\t\t\t$ref\n-\t\t} git_get_heads_list(undef, \"remotes/$remote\");\n-\t} else {\n-\t\t@remotelist = git_get_heads_list(undef, 'remotes');\n+\tunless (%$remotedata) {\n+\t\tdie_error(404, defined $remote ?\n+\t\t\t\"Remote $remote not found\" :\n+\t\t\t\"No remotes found\");\n \t}\n \n \tgit_header_html(undef, undef, -action_extra => $remote);\n \tgit_print_page_nav('', '',  $head, undef, $head,\n \t\tformat_ref_views($remote ? '' : 'remotes'));\n \n+\tfill_remote_heads($remotedata);\n \tif (defined $remote) {\n \t\tgit_print_header_div('remotes', \"$remote remote for $project\");\n+\t\tgit_remote_block($remote, $remotedata->{$remote}, undef, $head);\n \t} else {\n \t\tgit_print_header_div('summary', \"$project remotes\");\n-\t}\n-\n-\tif (@remotelist) {\n-\t\tgit_heads_body(\\@remotelist, $head);\n+\t\tgit_remotes_body($remotedata, undef, $head);\n \t}\n \n \tgit_footer_html();\ndiff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css\nindex 4132aab..79d7eeb 100644\n--- a/gitweb/static/gitweb.css\n+++ b/gitweb/static/gitweb.css\n@@ -573,6 +573,12 @@ div.binary {\n \tfont-style: italic;\n }\n \n+div.remote {\n+\tmargin: .5em;\n+\tborder: 1px solid #d9d8d1;\n+\tdisplay: inline-block;\n+}\n+\n /* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */\n \n /* Highlighting theme definition: */\n-- \n1.7.3.68.g6ec8\n"}]}