{"thread":{"id":"43144","subject":"[PATCH 1/2] for-each-ref: \"creator\" and \"creatordate\" fields","startedAt":"2006-10-28T17:30:05Z","lastAt":"2006-11-03T03:27:49Z","messageCount":15,"participants":["Jakub Narebski","Junio C Hamano","Andreas Ericsson","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"294715","messageId":"200610281930.05889.jnareb@gmail.com","threadId":"43144","inReplyTo":null,"subject":"[PATCH] Documentation: Update information about <format> in git-for-each-ref","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-10-28T17:30:05Z","receivedAt":"2006-10-28T17:30:05Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Update information about value of <format> used when it is left\nunspecified.  Add information about `%%` and `%xx` interpolation\n(URL encoding).\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nCould any of you review this, fix wording (if needed)?\n\n\nBy the way one thing missing from git-for-each-ref to use it\nin gitweb without changing its output is ablility to sort on\n\"epoch\", i.e. taggerdate for tags and committerdate for commits.\nThis is needed to sort heavyweight and lightweight tags together\non epoch in \"summary\" and \"tags\" views in gitweb. (It is not\nneeded for heads/branches, but it is the tags sorting that\ncripples gitweb \"summary\" view performance.)\n\nWe could either add \"fake\" field `date` which will be `taggerdate`\nfor tags and `committerdate` for commits, and empty for trees\nand blobs, or allow in <key> for more than one field to be\nspecified.\n\n\nI also wish for change of %(field) to %{field}, adding %nn{field}\nand %-nn{field} field width specifiers, committerepoch and\ncommittertz fields (and equivalent for tagger and author) or\n%{committer:date} like field-format specifiers. And for peace\non Earth while at it... ;-)\n\n Documentation/git-for-each-ref.txt |    6 +++++-\n 1 files changed, 5 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex d5fdcef..4af1ebf 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -38,7 +38,11 @@ OPTIONS\n \tis prefixed with an asterisk (`*`) and the ref points\n \tat a tag object, the value for the field in the object\n \ttag refers is used.  When unspecified, defaults to\n-\t`%(refname)`.\n+\t`%(objectname) SPC %(objecttype) TAB %(refname)`.\n+\tIt also interpolates `%%` to `%`, and `%xx` where `xx`\n+\tare hex digits interpolates to character with hex code\n+\t`xx`; for example `%00` interpolates to `\\0` (NUL),\n+\t`%09` to `\\t` (TAB) and `%0a` to `\\n` (LF).\n \n <pattern>::\n \tIf given, the name of the ref is matched against this\n-- \n1.4.3.3\n"},{"id":"298041","messageId":"7vslh86uz9.fsf@assigned-by-dhcp.cox.net","threadId":"43144","inReplyTo":"200610281930.05889.jnareb@gmail.com","subject":"Re: [PATCH] Documentation: Update information about <format> in git-for-each-ref","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-10-28T20:33:46Z","receivedAt":"2006-10-28T20:33:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n> By the way one thing missing from git-for-each-ref to use it\n> in gitweb without changing its output is ablility to sort on\n> \"epoch\", i.e. taggerdate for tags and committerdate for commits.\n> This is needed to sort heavyweight and lightweight tags together\n> on epoch in \"summary\" and \"tags\" views in gitweb. (It is not\n> needed for heads/branches, but it is the tags sorting that\n> cripples gitweb \"summary\" view performance.)\n\nI guess something like this, but I haven't tested it heavily.\nIf you want it in \"next\" please ack (with necessary fix-up\npatches if any).\n\n-- >8 --\n[PATCH] for-each-ref: epoch and epochdate\n\nThis adds \"epoch\" (which is parallel to \"tagger\" or \"committer\")\nand \"epochdate\" (corresponds to \"taggerdate\" and\n\"committerdate\").\n\nAs other \"date\" fields, \"epochdate\" sorts numerically\nand displays human readably\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\ndiff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\nindex 698618b..3dc68cc 100644\n--- a/builtin-for-each-ref.c\n+++ b/builtin-for-each-ref.c\n@@ -59,6 +59,8 @@ static struct {\n \t{ \"taggername\" },\n \t{ \"taggeremail\" },\n \t{ \"taggerdate\", FIELD_TIME },\n+\t{ \"epoch\" },\n+\t{ \"epochdate\", FIELD_TIME },\n \t{ \"subject\" },\n \t{ \"body\" },\n \t{ \"contents\" },\n@@ -401,6 +403,29 @@ static void grab_person(const char *who,\n \t\telse if (!strcmp(name + wholen, \"date\"))\n \t\t\tgrab_date(wholine, v);\n \t}\n+\n+\t/* For a tag or a commit object, if \"epoch\" or \"epochdate\" is\n+\t * requested, do something special.\n+\t */\n+\tif (strcmp(who, \"tagger\") && strcmp(who, \"committer\"))\n+\t\treturn; /* \"author\" for commit object is not wanted */\n+\tif (!wholine)\n+\t\twholine = find_wholine(who, wholen, buf, sz);\n+\tif (!wholine)\n+\t\treturn;\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\n+\t\tif (!strcmp(name, \"epochdate\"))\n+\t\t\tgrab_date(wholine, v);\n+\t\telse if (!strcmp(name, \"epoch\"))\n+\t\t\tv->s = copy_line(wholine);\n+\t}\n }\n \n static void find_subpos(const char *buf, unsigned long sz, const char **sub, const char **body)\n"},{"id":"294199","messageId":"7v7iyk6ule.fsf@assigned-by-dhcp.cox.net","threadId":"43144","inReplyTo":"200610281930.05889.jnareb@gmail.com","subject":"Re: [PATCH] Documentation: Update information about <format> in git-for-each-ref","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-10-28T20:42:05Z","receivedAt":"2006-10-28T20:42:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n> Update information about value of <format> used when it is left\n> unspecified.  Add information about `%%` and `%xx` interpolation\n> (URL encoding).\n>\n> Signed-off-by: Jakub Narebski <jnareb@gmail.com>\n> ---\n> Could any of you review this, fix wording (if needed)?\n\nLooks fine; thanks.\n"},{"id":"297308","messageId":"200610282323.57797.jnareb@gmail.com","threadId":"43144","inReplyTo":"7vslh86uz9.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Documentation: Update information about <format> in git-for-each-ref","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-10-28T21:23:57Z","receivedAt":"2006-10-28T21:23:57Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n> [PATCH] for-each-ref: epoch and epochdate\n> \n> This adds \"epoch\" (which is parallel to \"tagger\" or \"committer\")\n> and \"epochdate\" (corresponds to \"taggerdate\" and\n> \"committerdate\").\n> \n> As other \"date\" fields, \"epochdate\" sorts numerically\n> and displays human readably\n\nI was thinking about having only \"epochdate\" (corresponding to either \n\"taggerdate\" or \"committerdate\"), only named \"epoch\". There is I think \nno need for field which would be \"tagger\" or \"committer\", and \nespecially not named \"epoch\" ;-).\n\nOtherwise looks fine, thanks a lot.\n\n\nBTW. I had to translate\n+       if (strcmp(who, \"tagger\") && strcmp(who, \"committer\"))\nto\n+       if (strcmp(who, \"tagger\") == 0 || strcmp(who, \"committer\") == 0)\nto understand it. But this is probably my lack of contact with such\nC idioms.\n-- \nJakub Narebski\n"},{"id":"298729","messageId":"45485A0F.3040807@op5.se","threadId":"43144","inReplyTo":"200610282323.57797.jnareb@gmail.com","subject":"Re: [PATCH] Documentation: Update information about <format> in git-for-each-ref","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2006-11-01T08:25:51Z","receivedAt":"2006-11-01T08:25:51Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"Jakub Narebski wrote:\n> Junio C Hamano wrote:\n>> [PATCH] for-each-ref: epoch and epochdate\n>>\n>> This adds \"epoch\" (which is parallel to \"tagger\" or \"committer\")\n>> and \"epochdate\" (corresponds to \"taggerdate\" and\n>> \"committerdate\").\n>>\n>> As other \"date\" fields, \"epochdate\" sorts numerically\n>> and displays human readably\n> \n> I was thinking about having only \"epochdate\" (corresponding to either \n> \"taggerdate\" or \"committerdate\"), only named \"epoch\". There is I think \n> no need for field which would be \"tagger\" or \"committer\", and \n> especially not named \"epoch\" ;-).\n> \n> Otherwise looks fine, thanks a lot.\n> \n> \n> BTW. I had to translate\n> +       if (strcmp(who, \"tagger\") && strcmp(who, \"committer\"))\n> to\n> +       if (strcmp(who, \"tagger\") == 0 || strcmp(who, \"committer\") == 0)\n> to understand it. But this is probably my lack of contact with such\n> C idioms.\n\n\nBut this does the exact opposite. The condition will now be true if the \n'who' variable holds a pointer to a string that is either \"tagger\" or \n\"committer\", whereas it used to be true for strings that were anything \n*but* any of those.\n\n\"Compare\" (as in \"strcmp\") also translates to \"are equal to\" and isn't \nonly a verb. This is unfortunate for people who aren't natively english \nand has had me confused on many a long night. I once ended up doing a \nmacro called \"string_matches\" just to wrap my head around an insanely \nlong conditional with too many strcmp() with about half of them negated \nby !.\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\n"},{"id":"295232","messageId":"7vd5877duh.fsf@assigned-by-dhcp.cox.net","threadId":"43144","inReplyTo":"45485A0F.3040807@op5.se","subject":"Re: [PATCH] Documentation: Update information about <format> in git-for-each-ref","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-11-01T08:47:34Z","receivedAt":"2006-11-01T08:47:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andreas Ericsson <ae@op5.se> writes:\n\n> Jakub Narebski wrote:\n>\n>> BTW. I had to translate\n>> +       if (strcmp(who, \"tagger\") && strcmp(who, \"committer\"))\n>> to\n>> +       if (strcmp(who, \"tagger\") == 0 || strcmp(who, \"committer\") == 0)\n>> to understand it. But this is probably my lack of contact with such\n>> C idioms.\n>\n> But this does the exact opposite....\n> \"Compare\" (as in \"strcmp\") also translates to \"are equal to\" and isn't\n> only a verb. This is unfortunate for people who aren't natively\n> english and has had me confused on many a long night...\n\nBeing a non-English speaker, I always pronounce xxxcmp() used as\nboolean 'is different' in my head.  The (correct version of the)\nabove example expression is read as 'if it is different from\n\"tagger\" and it is different from \"committer\", then do this'.\n\nOtherwise I pronounce xxxcmp(a,b) \"difference\"; it's sign\nmatches (a-b).\n\nif (xxxcmp(a,b) < 0) is similar to if ((a-b) < 0) which is\nequivalent to if (a < b).\n\n"},{"id":"297896","messageId":"454875AC.6060300@op5.se","threadId":"43144","inReplyTo":"7vd5877duh.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Documentation: Update information about <format> in git-for-each-ref","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2006-11-01T10:23:40Z","receivedAt":"2006-11-01T10:23:40Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"Junio C Hamano wrote:\n> Andreas Ericsson <ae@op5.se> writes:\n> \n>> Jakub Narebski wrote:\n>>\n>>> BTW. I had to translate\n>>> +       if (strcmp(who, \"tagger\") && strcmp(who, \"committer\"))\n>>> to\n>>> +       if (strcmp(who, \"tagger\") == 0 || strcmp(who, \"committer\") == 0)\n>>> to understand it. But this is probably my lack of contact with such\n>>> C idioms.\n>> But this does the exact opposite....\n>> \"Compare\" (as in \"strcmp\") also translates to \"are equal to\" and isn't\n>> only a verb. This is unfortunate for people who aren't natively\n>> english and has had me confused on many a long night...\n> \n> Being a non-English speaker, I always pronounce xxxcmp() used as\n> boolean 'is different' in my head.  The (correct version of the)\n> above example expression is read as 'if it is different from\n> \"tagger\" and it is different from \"committer\", then do this'.\n> \n\nWithout any slight intended toward your english proficiency, I'd say \nyour programming is better than your english. For me it's the other way \naround, so I think of the above as \"if it doesn't compare to this or \nthat, then do this\", but the \"are equal to\" meaning of \"compare\" isn't \nintuitive to me as I spent the first several years of my \nenglish-speaking life using \"compare\" exclusively as a verb.\n\nAnd yes. There was a compliment hidden in there. Soak it up. You've \nearned it. ;-)\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\n"},{"id":"298329","messageId":"7vac3b5gn8.fsf@assigned-by-dhcp.cox.net","threadId":"43144","inReplyTo":"454875AC.6060300@op5.se","subject":"Re: [PATCH] Documentation: Update information about <format> in git-for-each-ref","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-11-01T15:30:03Z","receivedAt":"2006-11-01T15:30:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andreas Ericsson <ae@op5.se> writes:\n\n> ... For me it's the other\n> way around, so I think of the above as \"if it doesn't compare to this\n> or that, then do this\", but the \"are equal to\" meaning of \"compare\"\n> isn't intuitive to me as I spent the first several years of my\n> english-speaking life using \"compare\" exclusively as a verb.\n\n<offtopic>\n\nI think I should have said that \"I force me to pronounce\" and I\nam trying to say the same thing as you said.\n\nxxxcmp(a, b) literally is \"compare a and b and give me the\nresult\"; the result coming back as \"true\" when a and b are\ndifferent is counter-intuitive and I think it is so to\neverybody, not limited to non-English speaking people.  That's\nwhy I force me to ignore the etymology of the \"cmp\" in the\nfunction name and call that \"a and b differs\" or \"subtract b\nfrom a\" depending on the context.\n\nBy the way, I think \"a compares to b\" is also a verb, not just\n\"i compare a and b and found these differences\".\n\n</offtopic>\n"},{"id":"298528","messageId":"Pine.LNX.4.63.0611011646070.1670@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"43144","inReplyTo":"7vac3b5gn8.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Documentation: Update information about <format> in git-for-each-ref","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-11-01T15:48:37Z","receivedAt":"2006-11-01T15:48:37Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 1 Nov 2006, Junio C Hamano wrote:\n\n> By the way, I think \"a compares to b\" is also a verb, not just\n> \"i compare a and b and found these differences\".\n\nIn mathematics, \"a compares to b\" means that \"a\" and \"b\" are comparable. \nThere are uncomparable things like \"0\" and \"sky\". What is greater?\n\nOf course, there is also a song with the title \"Nothing compares to you\" \nwhich is sung by a native speaker, so go figure.\n\nCiao,\nDscho\n"},{"id":"293914","messageId":"200611022017.31351.jnareb@gmail.com","threadId":"43144","inReplyTo":"7vslh86uz9.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 1/2] for-each-ref: \"creator\" and \"creatordate\" fields","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-11-02T19:17:30Z","receivedAt":"2006-11-02T19:17:30Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"From fa1a32c9a7c8a31b122df7d07f4a8885cbe120d0 Mon Sep 17 00:00:00 2001\nFrom: Junio C Hamano <junkio@cox.net>\nDate: Sat, 28 Oct 2006 13:33:46 -0700\nSubject: [PATCH 1/2] for-each-ref: \"creator\" and \"creatordate\" fields\n\nThis adds \"creator\" (which is parallel to \"tagger\" or \"committer\")\nand \"creatordate\" (corresponds to \"taggerdate\" and\n\"committerdate\").\n\nAs other \"date\" fields, \"creatordate\" sorts numerically\nand displays human readably. This allows for example for\nsorting together heavyweigth and lightweight tags.\n\n[jn: originally fields were named \"epoch\" and \"epochdate\".]\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\nAcked-by: Jakub Narebski <jnareb@gmail.com>\n---\n builtin-for-each-ref.c |   25 +++++++++++++++++++++++++\n 1 files changed, 25 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\nindex 93d3d7e..173bf38 100644\n--- a/builtin-for-each-ref.c\n+++ b/builtin-for-each-ref.c\n@@ -59,6 +59,8 @@ static struct {\n \t{ \"taggername\" },\n \t{ \"taggeremail\" },\n \t{ \"taggerdate\", FIELD_TIME },\n+\t{ \"creator\" },\n+\t{ \"creatordate\", FIELD_TIME },\n \t{ \"subject\" },\n \t{ \"body\" },\n \t{ \"contents\" },\n@@ -401,6 +403,29 @@ static void grab_person(const char *who,\n \t\telse if (!strcmp(name + wholen, \"date\"))\n \t\t\tgrab_date(wholine, v);\n \t}\n+\n+\t/* For a tag or a commit object, if \"creator\" or \"creatordate\" is\n+\t * requested, do something special.\n+\t */\n+\tif (strcmp(who, \"tagger\") && strcmp(who, \"committer\"))\n+\t\treturn; /* \"author\" for commit object is not wanted */\n+\tif (!wholine)\n+\t\twholine = find_wholine(who, wholen, buf, sz);\n+\tif (!wholine)\n+\t\treturn;\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\n+\t\tif (!strcmp(name, \"creatordate\"))\n+\t\t\tgrab_date(wholine, v);\n+\t\telse if (!strcmp(name, \"creator\"))\n+\t\t\tv->s = copy_line(wholine);\n+\t}\n }\n \n static void find_subpos(const char *buf, unsigned long sz, const char **sub, const char **body)\n-- \n1.4.3.3\n"},{"id":"295890","messageId":"200611022023.12246.jnareb@gmail.com","threadId":"43144","inReplyTo":"200611022017.31351.jnareb@gmail.com","subject":"[PATCH 2/2] gitweb: Use git-for-each-ref to generate list of heads and/or tags","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-11-02T19:23:11Z","receivedAt":"2006-11-02T19:23:11Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"From 4d86b34d49dd1f18da465952be8306348fef5150 Mon Sep 17 00:00:00 2001\nFrom: Jakub Narebski <jnareb@gmail.com>\nDate: Thu, 2 Nov 2006 20:14:15 +0100\nSubject: [PATCH 2/2] gitweb: Use git-for-each-ref to generate list of heads and/or tags\n\nAdd two subroutines: git_get_heads_list and git_get_refs_list, which\nfill out needed parts of refs info (heads and tags respectively) info\nusing single call to git-for-each-ref, instead of using\ngit-peek-remote to get list of references and using parse_ref for each\nref to get ref info, which in turn uses at least one call of git\ncommand.\n\nReplace call to git_get_refs_list in git_summary by call to\ngit_get_references, git_get_heads_list and git_get_tags_list\n(simplifying this subroutine a bit). Use git_get_heads_list in\ngit_heads and git_get_tags_list in git_tags. Modify git_tags_body\nslightly to accept output from git_get_tags_list.\n\nRemove no longer used, and a bit hackish, git_get_refs_list.\nparse_ref is no longer used, but is left for now.\n\nGenerating \"summary\" and \"tags\" views should be much faster for\nprojects which have large number of tags.\n\nCHANGES IN OUTPUT: Before, if ref in refs/tags was tag pointing to\ncommit we used committer epoch as epoch for ref, and used tagger epoch\nas epoch only for tag pointing to object of other type. If ref in\nrefs/tags was commit, we used committer epoch as epoch for ref (see\nparse_ref; we sorted in gitweb by 'epoch' field).\n\nCurrently we use committer epoch for refs pointing to commit objects,\nand tagger epoch for refs pointing to tag object, even if tag points\nto commit. Going back to old behavior needs additional changes to\ngit-for-each-ref sorting (sort by given field and use other if first\nis not available, --sort=<key>|<key>).\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nPasky, you asked for faster \"summary\" and \"tags\" view. Simple ab benchmark\nbefore and after this patch for my git.git repository (git/jnareb-git.git)\nwith some heads and tags added as compared to git.git repository, shows\naround 2.4-3.0 times speedup for \"summary\" and \"tags\" views:\n\n summary   3134 +/- 24.2 ms  -->   1081 +/- 30.2 ms\n tags      2886 +/- 18.9 ms  -->   1196 +/- 15.6 ms\n\n gitweb/gitweb.perl |  153 +++++++++++++++++++++++++++++++---------------------\n 1 files changed, 92 insertions(+), 61 deletions(-)\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex bf5f829..7710cc2 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -1294,47 +1294,88 @@ sub parse_ls_tree_line ($;%) {\n ## ......................................................................\n ## parse to array of hashes functions\n \n-sub git_get_refs_list {\n-\tmy $type = shift || \"\";\n-\tmy %refs;\n-\tmy @reflist;\n-\n-\tmy @refs;\n-\topen my $fd, \"-|\", $GIT, \"peek-remote\", \"$projectroot/$project/\"\n+sub git_get_heads_list {\n+\tmy $limit = shift;\n+\tmy @headslist;\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\tor return;\n \twhile (my $line = <$fd>) {\n-\t\tchomp $line;\n-\t\tif ($line =~ m/^([0-9a-fA-F]{40})\\trefs\\/($type\\/?([^\\^]+))(\\^\\{\\})?$/) {\n-\t\t\tif (defined $refs{$1}) {\n-\t\t\t\tpush @{$refs{$1}}, $2;\n-\t\t\t} else {\n-\t\t\t\t$refs{$1} = [ $2 ];\n-\t\t\t}\n+\t\tmy %ref_item;\n \n-\t\t\tif (! $4) { # unpeeled, direct reference\n-\t\t\t\tpush @refs, { hash => $1, name => $3 }; # without type\n-\t\t\t} elsif ($3 eq $refs[-1]{'name'}) {\n-\t\t\t\t# most likely a tag is followed by its peeled\n-\t\t\t\t# (deref) one, and when that happens we know the\n-\t\t\t\t# previous one was of type 'tag'.\n-\t\t\t\t$refs[-1]{'type'} = \"tag\";\n-\t\t\t}\n+\t\tchomp $line;\n+\t\tmy ($refinfo, $committerinfo) = split(/\\0/, $line);\n+\t\tmy ($hash, $name, $title) = split(' ', $refinfo, 3);\n+\t\tmy ($committer, $epoch, $tz) =\n+\t\t\t($committerinfo =~ /^(.*) ([0-9]+) (.*)$/);\n+\t\t$name =~ s!^refs/heads/!!;\n+\n+\t\t$ref_item{'name'}  = $name;\n+\t\t$ref_item{'id'}    = $hash;\n+\t\t$ref_item{'title'} = $title || '(no commit message)';\n+\t\t$ref_item{'epoch'} = $epoch;\n+\t\tif ($epoch) {\n+\t\t\t$ref_item{'age'} = age_string(time - $ref_item{'epoch'});\n+\t\t} else {\n+\t\t\t$ref_item{'age'} = \"unknown\";\n \t\t}\n+\n+\t\tpush @headslist, \\%ref_item;\n \t}\n \tclose $fd;\n \n-\tforeach my $ref (@refs) {\n-\t\tmy $ref_file = $ref->{'name'};\n-\t\tmy $ref_id   = $ref->{'hash'};\n+\treturn wantarray ? @headslist : \\@headslist;\n+}\n+\n+sub git_get_tags_list {\n+\tmy $limit = shift;\n+\tmy @tagslist;\n \n-\t\tmy $type = $ref->{'type'} || git_get_type($ref_id) || next;\n-\t\tmy %ref_item = parse_ref($ref_file, $ref_id, $type);\n+\topen my $fd, '-|', git_cmd(), 'for-each-ref',\n+\t\t($limit ? '--count='.($limit+1) : ()), '--sort=-creatordate',\n+\t\t'--format=%(objectname) %(objecttype) %(refname) '.\n+\t\t'%(*objectname) %(*objecttype) %(subject)%00%(creator)',\n+\t\t'refs/tags'\n+\t\tor return;\n+\twhile (my $line = <$fd>) {\n+\t\tmy %ref_item;\n \n-\t\tpush @reflist, \\%ref_item;\n+\t\tchomp $line;\n+\t\tmy ($refinfo, $creatorinfo) = split(/\\0/, $line);\n+\t\tmy ($id, $type, $name, $refid, $reftype, $title) = split(' ', $refinfo, 6);\n+\t\tmy ($creator, $epoch, $tz) =\n+\t\t\t($creatorinfo =~ /^(.*) ([0-9]+) (.*)$/);\n+\t\t$name =~ s!^refs/tags/!!;\n+\n+\t\t$ref_item{'type'} = $type;\n+\t\t$ref_item{'id'} = $id;\n+\t\t$ref_item{'name'} = $name;\n+\t\tif ($type eq \"tag\") {\n+\t\t\t$ref_item{'subject'} = $title;\n+\t\t\t$ref_item{'reftype'} = $reftype;\n+\t\t\t$ref_item{'refid'}   = $refid;\n+\t\t} else {\n+\t\t\t$ref_item{'reftype'} = $type;\n+\t\t\t$ref_item{'refid'}   = $id;\n+\t\t}\n+\n+\t\tif ($type eq \"tag\" || $type eq \"commit\") {\n+\t\t\t$ref_item{'epoch'} = $epoch;\n+\t\t\tif ($epoch) {\n+\t\t\t\t$ref_item{'age'} = age_string(time - $ref_item{'epoch'});\n+\t\t\t} else {\n+\t\t\t\t$ref_item{'age'} = \"unknown\";\n+\t\t\t}\n+\t\t}\n+\n+\t\tpush @tagslist, \\%ref_item;\n \t}\n-\t# sort refs by age\n-\t@reflist = sort {$b->{'epoch'} <=> $a->{'epoch'}} @reflist;\n-\treturn (\\@reflist, \\%refs);\n+\tclose $fd;\n+\n+\treturn wantarray ? @tagslist : \\@tagslist;\n }\n \n ## ----------------------------------------------------------------------\n@@ -2264,8 +2305,7 @@ sub git_tags_body {\n \tfor (my $i = $from; $i <= $to; $i++) {\n \t\tmy $entry = $taglist->[$i];\n \t\tmy %tag = %$entry;\n-\t\tmy $comment_lines = $tag{'comment'};\n-\t\tmy $comment = shift @$comment_lines;\n+\t\tmy $comment = $tag{'subject'};\n \t\tmy $comment_short;\n \t\tif (defined $comment) {\n \t\t\t$comment_short = chop_str($comment, 30, 5);\n@@ -2298,7 +2338,7 @@ sub git_tags_body {\n \t\t      $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'})}, $tag{'reftype'});\n \t\tif ($tag{'reftype'} eq \"commit\") {\n \t\t\tprint \" | \" . $cgi->a({-href => href(action=>\"shortlog\", hash=>$tag{'name'})}, \"shortlog\") .\n-\t\t\t      \" | \" . $cgi->a({-href => href(action=>\"log\", hash=>$tag{'refid'})}, \"log\");\n+\t\t\t      \" | \" . $cgi->a({-href => href(action=>\"log\", hash=>$tag{'name'})}, \"log\");\n \t\t} elsif ($tag{'reftype'} eq \"blob\") {\n \t\t\tprint \" | \" . $cgi->a({-href => href(action=>\"blob_plain\", hash=>$tag{'refid'})}, \"raw\");\n \t\t}\n@@ -2323,23 +2363,23 @@ sub git_heads_body {\n \tmy $alternate = 1;\n \tfor (my $i = $from; $i <= $to; $i++) {\n \t\tmy $entry = $headlist->[$i];\n-\t\tmy %tag = %$entry;\n-\t\tmy $curr = $tag{'id'} eq $head;\n+\t\tmy %ref = %$entry;\n+\t\tmy $curr = $ref{'id'} eq $head;\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><i>$tag{'age'}</i></td>\\n\" .\n-\t\t      ($tag{'id'} eq $head ? \"<td class=\\\"current_head\\\">\" : \"<td>\") .\n-\t\t      $cgi->a({-href => href(action=>\"shortlog\", hash=>$tag{'name'}),\n-\t\t               -class => \"list name\"},esc_html($tag{'name'})) .\n+\t\tprint \"<td><i>$ref{'age'}</i></td>\\n\" .\n+\t\t      ($curr ? \"<td class=\\\"current_head\\\">\" : \"<td>\") .\n+\t\t      $cgi->a({-href => href(action=>\"shortlog\", hash=>$ref{'name'}),\n+\t\t               -class => \"list name\"},esc_html($ref{'name'})) .\n \t\t      \"</td>\\n\" .\n \t\t      \"<td class=\\\"link\\\">\" .\n-\t\t      $cgi->a({-href => href(action=>\"shortlog\", hash=>$tag{'name'})}, \"shortlog\") . \" | \" .\n-\t\t      $cgi->a({-href => href(action=>\"log\", hash=>$tag{'name'})}, \"log\") . \" | \" .\n-\t\t      $cgi->a({-href => href(action=>\"tree\", hash=>$tag{'name'}, hash_base=>$tag{'name'})}, \"tree\") .\n+\t\t      $cgi->a({-href => href(action=>\"shortlog\", hash=>$ref{'name'})}, \"shortlog\") . \" | \" .\n+\t\t      $cgi->a({-href => href(action=>\"log\", hash=>$ref{'name'})}, \"log\") . \" | \" .\n+\t\t      $cgi->a({-href => href(action=>\"tree\", hash=>$ref{'name'}, hash_base=>$ref{'name'})}, \"tree\") .\n \t\t      \"</td>\\n\" .\n \t\t      \"</tr>\";\n \t}\n@@ -2489,18 +2529,9 @@ sub git_summary {\n \n \tmy $owner = git_get_project_owner($project);\n \n-\tmy ($reflist, $refs) = git_get_refs_list();\n-\n-\tmy @taglist;\n-\tmy @headlist;\n-\tforeach my $ref (@$reflist) {\n-\t\tif ($ref->{'name'} =~ s!^heads/!!) {\n-\t\t\tpush @headlist, $ref;\n-\t\t} else {\n-\t\t\t$ref->{'name'} =~ s!^tags/!!;\n-\t\t\tpush @taglist, $ref;\n-\t\t}\n-\t}\n+\tmy $refs = git_get_references();\n+\tmy @taglist  = git_get_tags_list(15);\n+\tmy @headlist = git_get_heads_list(15);\n \n \tgit_header_html();\n \tgit_print_page_nav('summary','', $head);\n@@ -2792,9 +2823,9 @@ sub git_tags {\n \tgit_print_page_nav('','', $head,undef,$head);\n \tgit_print_header_div('summary', $project);\n \n-\tmy ($taglist) = git_get_refs_list(\"tags\");\n-\tif (@$taglist) {\n-\t\tgit_tags_body($taglist);\n+\tmy @tagslist = git_get_tags_list();\n+\tif (@tagslist) {\n+\t\tgit_tags_body(\\@tagslist);\n \t}\n \tgit_footer_html();\n }\n@@ -2805,9 +2836,9 @@ sub git_heads {\n \tgit_print_page_nav('','', $head,undef,$head);\n \tgit_print_header_div('summary', $project);\n \n-\tmy ($headlist) = git_get_refs_list(\"heads\");\n-\tif (@$headlist) {\n-\t\tgit_heads_body($headlist, $head);\n+\tmy @headslist = git_get_heads_list();\n+\tif (@headslist) {\n+\t\tgit_heads_body(\\@headslist, $head);\n \t}\n \tgit_footer_html();\n }\n-- \n1.4.3.3\n"},{"id":"296340","messageId":"7vmz79l0bi.fsf@assigned-by-dhcp.cox.net","threadId":"43144","inReplyTo":"200611022017.31351.jnareb@gmail.com","subject":"Re: [PATCH 1/2] for-each-ref: \"creator\" and \"creatordate\" fields","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-11-03T02:40:33Z","receivedAt":"2006-11-03T02:40:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n> From fa1a32c9a7c8a31b122df7d07f4a8885cbe120d0 Mon Sep 17 00:00:00 2001\n> From: Junio C Hamano <junkio@cox.net>\n> Date: Sat, 28 Oct 2006 13:33:46 -0700\n> Subject: [PATCH 1/2] for-each-ref: \"creator\" and \"creatordate\" fields\n>\n> This adds \"creator\" (which is parallel to \"tagger\" or \"committer\")\n> and \"creatordate\" (corresponds to \"taggerdate\" and\n> \"committerdate\").\n\nThe first line should not be in the message.  I agree that it is\nsensible not to call this \"epoch\", and \"creator\" is fine by me.\n\nThanks.  Will apply.\n"},{"id":"295792","messageId":"7vhcxhl0bh.fsf@assigned-by-dhcp.cox.net","threadId":"43144","inReplyTo":"200611022023.12246.jnareb@gmail.com","subject":"Re: [PATCH 2/2] gitweb: Use git-for-each-ref to generate list of heads and/or tags","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-11-03T02:40:34Z","receivedAt":"2006-11-03T02:40:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n> From 4d86b34d49dd1f18da465952be8306348fef5150 Mon Sep 17 00:00:00 2001\n> From: Jakub Narebski <jnareb@gmail.com>\n> Date: Thu, 2 Nov 2006 20:14:15 +0100\n> Subject: [PATCH 2/2] gitweb: Use git-for-each-ref to generate list of heads and/or tags\n\nThe first line should not be in the body of the message.\n\n> CHANGES IN OUTPUT: Before, if ref in refs/tags was tag pointing to\n> commit we used committer epoch as epoch for ref, and used tagger epoch\n> as epoch only for tag pointing to object of other type. If ref in\n> refs/tags was commit, we used committer epoch as epoch for ref (see\n> parse_ref; we sorted in gitweb by 'epoch' field).\n\nI think that behaviour was inconsistent and just silly.  Using\ncreatordate consistently is better so if the code generates what\nthe commit log claims to, I think that is fine (I haven't looked\nat it deeply yet).\n\nThanks.  Will apply.\n\n"},{"id":"298730","messageId":"200611030426.32734.jnareb@gmail.com","threadId":"43144","inReplyTo":"7vhcxhl0bh.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 2/2] gitweb: Use git-for-each-ref to generate list of heads and/or tags","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-11-03T03:26:32Z","receivedAt":"2006-11-03T03:26:32Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n> Jakub Narebski <jnareb@gmail.com> writes:\n \n>> CHANGES IN OUTPUT: Before, if ref in refs/tags was tag pointing to\n>> commit we used committer epoch as epoch for ref, and used tagger epoch\n>> as epoch only for tag pointing to object of other type. If ref in\n>> refs/tags was commit, we used committer epoch as epoch for ref (see\n>> parse_ref; we sorted in gitweb by 'epoch' field).\n> \n> I think that behaviour was inconsistent and just silly.  Using\n> creatordate consistently is better so if the code generates what\n> the commit log claims to, I think that is fine (I haven't looked\n> at it deeply yet).\n\nWell, I _could_ argue on the former behaviour, namely that we use\ncommitterdate when available (when ref points to commit, or to tag\npointing to commit), and if it is not available we use taggerdate\nif possible.\n\nThe latter is that we use always \"creation\" date.\n\n-- \nJakub Narebski\n"},{"id":"296222","messageId":"200611030427.49617.jnareb@gmail.com","threadId":"43144","inReplyTo":"7vmz79l0bi.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 1/2] for-each-ref: \"creator\" and \"creatordate\" fields","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-11-03T03:27:49Z","receivedAt":"2006-11-03T03:27:49Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Dnia piątek 3. listopada 2006 03:40, Junio C Hamano napisał:\n> Jakub Narebski <jnareb@gmail.com> writes:\n> \n>> From fa1a32c9a7c8a31b122df7d07f4a8885cbe120d0 Mon Sep 17 00:00:00 2001\n>> From: Junio C Hamano <junkio@cox.net>\n>> Date: Sat, 28 Oct 2006 13:33:46 -0700\n>> Subject: [PATCH 1/2] for-each-ref: \"creator\" and \"creatordate\" fields\n>>\n>> This adds \"creator\" (which is parallel to \"tagger\" or \"committer\")\n>> and \"creatordate\" (corresponds to \"taggerdate\" and\n>> \"committerdate\").\n> \n> The first line should not be in the message.\n\nI'm sorry. I just tried to send patch without changing authorship\n(from yours).\n\n-- \nJakub Narebski\n"}]}