{"thread":{"id":"10029","subject":"[PATCH] Add a --dateformat= option to git-for-each-ref","startedAt":"2007-09-26T09:09:18Z","lastAt":"2007-09-29T08:17:18Z","messageCount":15,"participants":["Andy Parkins","Jeff King","Johannes Schindelin","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"54090","messageId":"200709261009.18777.andyparkins@gmail.com","threadId":"10029","inReplyTo":null,"subject":"[PATCH] Add a --dateformat= option to git-for-each-ref","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-09-26T09:09:18Z","receivedAt":"2007-09-26T09:09:18Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"I wanted to get date information in RFC2822 format out of a tag using\ngit-for-each-ref; but there was no way to specify that.  This patch\naddresses that omission by adding a --dateformat option.\n\nFor example (I'm in BST, +0100 at present):\n\n $ git-for-each-ref --dateformat=normal --format='%(taggerdate)' refs/tags/v1.5.2\n Sun May 20 00:30:42 2007 -0700\n $ git-for-each-ref --dateformat=relative --format='%(taggerdate)' refs/tags/v1.5.2\n 4 months ago\n $ git-for-each-ref --dateformat=short --format='%(taggerdate)' refs/tags/v1.5.2\n 2007-05-20\n $ git-for-each-ref --dateformat=local --format='%(taggerdate)' refs/tags/v1.5.2\n Sun May 20 08:30:42 2007\n $ git-for-each-ref --dateformat=iso8601 --format='%(taggerdate)' refs/tags/v1.5.2\n 2007-05-20 00:30:42 -0700\n $ git-for-each-ref --dateformat=rfc2822 --format='%(taggerdate)' refs/tags/v1.5.2\n Sun, 20 May 2007 00:30:42 -0700\n\nThe default is to use 'normal', which leaves existing behaviour\nunchanged.\n\nSigned-off-by: Andy Parkins <andyparkins@gmail.com>\n---\n Documentation/git-for-each-ref.txt |    6 ++++++\n builtin-for-each-ref.c             |   18 +++++++++++++++++-\n 2 files changed, 23 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 6df8e85..1b8fdb8 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -10,6 +10,7 @@ SYNOPSIS\n [verse]\n 'git-for-each-ref' [--count=<count>]\\*\n                    [--shell|--perl|--python|--tcl]\n+                   [--dateformat=normal|relative|short|local|iso8601|rfc2822]\n                    [--sort=<key>]\\* [--format=<format>] [<pattern>]\n \n DESCRIPTION\n@@ -58,6 +59,11 @@ OPTIONS\n \tthe specified host language.  This is meant to produce\n \ta scriptlet that can directly be `eval`ed.\n \n+--dateformat::\n+\tIf given, all timestamp fields will be output in the specified\n+\tformat.  This is only really relevant for innvocations using the\n+\t--format option with a `%(date)`-type field.\n+\n \n FIELD NAMES\n -----------\ndiff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\nindex 0afa1c5..80e58fc 100644\n--- a/builtin-for-each-ref.c\n+++ b/builtin-for-each-ref.c\n@@ -80,6 +80,7 @@ static struct {\n static const char **used_atom;\n static cmp_type *used_atom_type;\n static int used_atom_cnt, sort_atom_limit, need_tagged;\n+static enum date_mode date_mode = DATE_NORMAL;\n \n /*\n  * Used to parse format string and sort specifiers\n@@ -362,7 +363,7 @@ static void grab_date(const char *buf, struct atom_value *v)\n \ttz = strtol(zone, NULL, 10);\n \tif ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)\n \t\tgoto bad;\n-\tv->s = xstrdup(show_date(timestamp, tz, 0));\n+\tv->s = xstrdup(show_date(timestamp, tz, date_mode));\n \tv->ul = timestamp;\n \treturn;\n  bad:\n@@ -870,6 +871,21 @@ int cmd_for_each_ref(int ac, const char **av, const char *prefix)\n \t\t\tsort->atom = parse_atom(arg, arg+len);\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!prefixcmp(arg, \"--dateformat=\")) {\n+\t\t\targ += 13;\n+\t\t\tif (!prefixcmp(arg,\"relative\")) {\n+\t\t\t\tdate_mode = DATE_RELATIVE;\n+\t\t\t} else if (!prefixcmp(arg,\"short\")) {\n+\t\t\t\tdate_mode = DATE_SHORT;\n+\t\t\t} else if (!prefixcmp(arg,\"local\")) {\n+\t\t\t\tdate_mode = DATE_LOCAL;\n+\t\t\t} else if (!prefixcmp(arg,\"iso8601\")) {\n+\t\t\t\tdate_mode = DATE_ISO8601;\n+\t\t\t} else if (!prefixcmp(arg,\"rfc2822\")) {\n+\t\t\t\tdate_mode = DATE_RFC2822;\n+\t\t\t}\n+\t\t\tcontinue;\n+\t\t}\n \t\tbreak;\n \t}\n \tif (quote_style < 0)\n-- \n1.5.3.1.5.g4e560-dirty\n"},{"id":"54099","messageId":"20070926125811.GC13739@coredump.intra.peff.net","threadId":"10029","inReplyTo":"200709261009.18777.andyparkins@gmail.com","subject":"Re: [PATCH] Add a --dateformat= option to git-for-each-ref","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2007-09-26T12:58:11Z","receivedAt":"2007-09-26T12:58:11Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Sep 26, 2007 at 10:09:18AM +0100, Andy Parkins wrote:\n\n> For example (I'm in BST, +0100 at present):\n> \n>  $ git-for-each-ref --dateformat=normal --format='%(taggerdate)' refs/tags/v1.5.2\n>  Sun May 20 00:30:42 2007 -0700\n>  $ git-for-each-ref --dateformat=relative --format='%(taggerdate)' refs/tags/v1.5.2\n>  4 months ago\n\nWhat if you want a format that contains two dates in different formats?\nSomething like:\n\n $ git-for-each-ref --format='%(committerdate:relative) %(authordate:normal)'\n\nwould be more flexible. Although perhaps that is a bit too unlikely to\nbe concerned with implementing, giving options to substitutions seems\nlike a sane way to implement these sorts of things (e.g.,\n\"%(objectsize:human)\", \"%(parent:1)\", etc).\n\n> +\t\tif (!prefixcmp(arg, \"--dateformat=\")) {\n> +\t\t\targ += 13;\n> +\t\t\tif (!prefixcmp(arg,\"relative\")) {\n> +\t\t\t\tdate_mode = DATE_RELATIVE;\n> +\t\t\t} else if (!prefixcmp(arg,\"short\")) {\n> +\t\t\t\tdate_mode = DATE_SHORT;\n> +\t\t\t} else if (!prefixcmp(arg,\"local\")) {\n> +\t\t\t\tdate_mode = DATE_LOCAL;\n> +\t\t\t} else if (!prefixcmp(arg,\"iso8601\")) {\n> +\t\t\t\tdate_mode = DATE_ISO8601;\n> +\t\t\t} else if (!prefixcmp(arg,\"rfc2822\")) {\n> +\t\t\t\tdate_mode = DATE_RFC2822;\n> +\t\t\t}\n> +\t\t\tcontinue;\n> +\t\t}\n\nSurely this same code exists elsewhere, and could be easily factored out\ninto a parse_date_type function.\n\n-Peff\n"},{"id":"54265","messageId":"200709281516.05438.andyparkins@gmail.com","threadId":"10029","inReplyTo":"20070926125811.GC13739@coredump.intra.peff.net","subject":"Re: [PATCH] Add a --dateformat= option to git-for-each-ref","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-09-28T14:15:58Z","receivedAt":"2007-09-28T14:15:58Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"On Wednesday 2007 September 26, Jeff King wrote:\n\n> would be more flexible. Although perhaps that is a bit too unlikely to\n> be concerned with implementing, giving options to substitutions seems\n> like a sane way to implement these sorts of things (e.g.,\n> \"%(objectsize:human)\", \"%(parent:1)\", etc).\n\nI'd thought about doing it like that, but imagined that there would objections \nthat it was overcomplicating git-for-each-ref.  As you think that's \nacceptable, I'll do it.\n\n> Surely this same code exists elsewhere, and could be easily factored out\n> into a parse_date_type function.\n\nIt was.  It was also in revisions.c.\n\nA patch series that implements both your requested changes to follow.\n\n\nAndy\n-- \nDr Andy Parkins, M Eng (hons), MIET\nandyparkins@gmail.com\n"},{"id":"54268","messageId":"200709281517.26627.andyparkins@gmail.com","threadId":"10029","inReplyTo":"200709281516.05438.andyparkins@gmail.com","subject":"[PATCH 1/4] Add parse_date_format() convenience function for converting a format string to an enum date_mode","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-09-28T14:17:26Z","receivedAt":"2007-09-28T14:17:26Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"parse_date_format() is passed a string that is compared against a\npre-defined list and converted to an enum date_format.  The table is as\nfollows:\n\n - \"relative\"         => DATE_RELATIVE\n - \"iso8601\" or \"iso\" => DATE_ISO8601\n - \"rfc2822\"          => DATE_RFC2822\n - \"short\"            => DATE_SHORT\n - \"local\"            => DATE_LOCAL\n - \"default\"          => DATE_NORMAL\n\nIn the event that none of these strings is found, the function die()s.\n\nSigned-off-by: Andy Parkins <andyparkins@gmail.com>\n---\n cache.h |    1 +\n date.c  |   20 ++++++++++++++++++++\n 2 files changed, 21 insertions(+), 0 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 8246500..5587f7e 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -432,6 +432,7 @@ const char *show_date(unsigned long time, int timezone, enum date_mode mode);\n int parse_date(const char *date, char *buf, int bufsize);\n void datestamp(char *buf, int bufsize);\n unsigned long approxidate(const char *);\n+enum date_mode parse_date_format(const char *format);\n \n extern const char *git_author_info(int);\n extern const char *git_committer_info(int);\ndiff --git a/date.c b/date.c\nindex 93bef6e..8f70500 100644\n--- a/date.c\n+++ b/date.c\n@@ -584,6 +584,26 @@ int parse_date(const char *date, char *result, int maxlen)\n \treturn date_string(then, offset, result, maxlen);\n }\n \n+enum date_mode parse_date_format(const char *format)\n+{\n+\tif (!strcmp(format, \"relative\"))\n+\t\treturn DATE_RELATIVE;\n+\telse if (!strcmp(format, \"iso8601\") ||\n+\t\t !strcmp(format, \"iso\"))\n+\t\treturn DATE_ISO8601;\n+\telse if (!strcmp(format, \"rfc2822\") ||\n+\t\t !strcmp(format, \"rfc\"))\n+\t\treturn DATE_RFC2822;\n+\telse if (!strcmp(format, \"short\"))\n+\t\treturn DATE_SHORT;\n+\telse if (!strcmp(format, \"local\"))\n+\t\treturn DATE_LOCAL;\n+\telse if (!strcmp(format, \"default\"))\n+\t\treturn DATE_NORMAL;\n+\telse\n+\t\tdie(\"unknown date format %s\", format);\n+}\n+\n void datestamp(char *buf, int bufsize)\n {\n \ttime_t now;\n-- \n1.5.3.2.105.gf47f2-dirty\n"},{"id":"54267","messageId":"200709281517.32030.andyparkins@gmail.com","threadId":"10029","inReplyTo":"200709281516.05438.andyparkins@gmail.com","subject":"[PATCH 2/4] Use parse_date_format() in revisions.c to parse the --date parameter","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-09-28T14:17:31Z","receivedAt":"2007-09-28T14:17:31Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"The --date parameter was previously handled in revisions.c with a list\nof if(strcmp()) calls; now parse_date_format() is called instead.\n\nSigned-off-by: Andy Parkins <andyparkins@gmail.com>\n---\n revision.c |   17 +----------------\n 1 files changed, 1 insertions(+), 16 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 33d092c..75cd0c6 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1134,22 +1134,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strncmp(arg, \"--date=\", 7)) {\n-\t\t\t\tif (!strcmp(arg + 7, \"relative\"))\n-\t\t\t\t\trevs->date_mode = DATE_RELATIVE;\n-\t\t\t\telse if (!strcmp(arg + 7, \"iso8601\") ||\n-\t\t\t\t\t !strcmp(arg + 7, \"iso\"))\n-\t\t\t\t\trevs->date_mode = DATE_ISO8601;\n-\t\t\t\telse if (!strcmp(arg + 7, \"rfc2822\") ||\n-\t\t\t\t\t !strcmp(arg + 7, \"rfc\"))\n-\t\t\t\t\trevs->date_mode = DATE_RFC2822;\n-\t\t\t\telse if (!strcmp(arg + 7, \"short\"))\n-\t\t\t\t\trevs->date_mode = DATE_SHORT;\n-\t\t\t\telse if (!strcmp(arg + 7, \"local\"))\n-\t\t\t\t\trevs->date_mode = DATE_LOCAL;\n-\t\t\t\telse if (!strcmp(arg + 7, \"default\"))\n-\t\t\t\t\trevs->date_mode = DATE_NORMAL;\n-\t\t\t\telse\n-\t\t\t\t\tdie(\"unknown date format %s\", arg);\n+\t\t\t\trevs->date_mode = parse_date_format(arg + 7);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--log-size\")) {\n-- \n1.5.3.2.105.gf47f2-dirty\n"},{"id":"54266","messageId":"200709281517.39245.andyparkins@gmail.com","threadId":"10029","inReplyTo":"200709281516.05438.andyparkins@gmail.com","subject":"[PATCH 3/4] Make for-each-ref allow atom names like \"<name>:<something>\"","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-09-28T14:17:39Z","receivedAt":"2007-09-28T14:17:39Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"In anticipation of supplying a per-field date format specifier, this\npatch makes parse_atom() in builtin-for-each-ref.c allow atoms that have\na valid atom name (as determined by the valid_atom[] table) followed by\na colon, followed by an arbitrary string.\n\nThe arbitrary string is where the format for the atom will be specified.\n\nNote, if different formats are specified for the same atom, multiple\nentries will be made in the used_atoms table to allow them to be\ndistinguished by the grab_XXXX() functions.\n\nSigned-off-by: Andy Parkins <andyparkins@gmail.com>\n---\n builtin-for-each-ref.c |    8 +++++++-\n 1 files changed, 7 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\nindex 0afa1c5..3280516 100644\n--- a/builtin-for-each-ref.c\n+++ b/builtin-for-each-ref.c\n@@ -106,7 +106,13 @@ static int parse_atom(const char *atom, const char *ep)\n \t/* Is the atom a valid one? */\n \tfor (i = 0; i < ARRAY_SIZE(valid_atom); i++) {\n \t\tint len = strlen(valid_atom[i].name);\n-\t\tif (len == ep - sp && !memcmp(valid_atom[i].name, sp, len))\n+\t\t/* If the atom name has a colon, strip it and everything after\n+\t\t * it off - it specifies the format for this entry, and\n+\t\t * shouldn't be used for checking against the valid_atom table */\n+\t\tconst char *formatp = strrchr(sp, ':' );\n+\t\tif (formatp == NULL )\n+\t\t\tformatp = ep;\n+\t\tif (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))\n \t\t\tbreak;\n \t}\n \n-- \n1.5.3.2.105.gf47f2-dirty\n"},{"id":"54269","messageId":"200709281517.45133.andyparkins@gmail.com","threadId":"10029","inReplyTo":"200709281516.05438.andyparkins@gmail.com","subject":"[PATCH 4/4] Make for-each-ref's grab_date() support per-atom formatting","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-09-28T14:17:45Z","receivedAt":"2007-09-28T14:17:45Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"grab_date() gets an extra parameter - atomname; this extra parameter is\nchecked to see if it has a \":<format>\" extra component in it, and if so\nthat \"<format>\" string is passed to parse_date_format() to produce an\nenum date_mode value which is then further passed to show_date().\n\nIn short it allows the user of git-for-each-ref to do things like this:\n\n $ git-for-each-ref --format='%(taggerdate:default)' refs/tags/v1.5.2\n Sun May 20 00:30:42 2007 -0700\n $ git-for-each-ref --format='%(taggerdate:relative)' refs/tags/v1.5.2\n 4 months ago\n $ git-for-each-ref --format='%(taggerdate:short)' refs/tags/v1.5.2\n 2007-05-20\n $ git-for-each-ref --format='%(taggerdate:local)' refs/tags/v1.5.2\n Sun May 20 08:30:42 2007\n $ git-for-each-ref --format='%(taggerdate:iso8601)' refs/tags/v1.5.2\n 2007-05-20 00:30:42 -0700\n $ git-for-each-ref --format='%(taggerdate:rfc2822)' refs/tags/v1.5.2\n Sun, 20 May 2007 00:30:42 -0700\n\nThe default, when no \":<format>\" is specified is \":default\", leaving the\nexisting behaviour unchanged.\n\nSigned-off-by: Andy Parkins <andyparkins@gmail.com>\n---\n Documentation/git-for-each-ref.txt |    5 +++++\n builtin-for-each-ref.c             |   26 +++++++++++++++++++-------\n 2 files changed, 24 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 6df8e85..f1f90cc 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -100,6 +100,11 @@ In any case, a field name that refers to a field inapplicable to\n the object referred by the ref does not cause an error.  It\n returns an empty string instead.\n \n+As a special case for the date-type fields, you may specify a format for\n+the date by adding one of `:default`, `:relative`, `:short`, `:local`,\n+`:iso8601` or `:rfc2822` to the end of the fieldname; e.g.\n+`%(taggerdate:relative)`.\n+\n \n EXAMPLES\n --------\ndiff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\nindex 3280516..2ca4fc6 100644\n--- a/builtin-for-each-ref.c\n+++ b/builtin-for-each-ref.c\n@@ -353,12 +353,24 @@ static const char *copy_email(const char *buf)\n \treturn line;\n }\n \n-static void grab_date(const char *buf, struct atom_value *v)\n+static void grab_date(const char *buf, struct atom_value *v, const char *atomname)\n {\n \tconst char *eoemail = strstr(buf, \"> \");\n \tchar *zone;\n \tunsigned long timestamp;\n \tlong tz;\n+\tenum date_mode date_mode = DATE_NORMAL;\n+\tconst char *formatp;\n+\n+\t/* We got here because atomname ends in \"date\" or \"date<something>\",\n+\t * it's not possible that <something> is not \":<format>\" because\n+\t * parse_atom() wouldn't have allowed it, so we can assume that no\n+\t * \":\" means no format is specified, use the default */\n+\tformatp = strrchr( atomname, ':' );\n+\tif (formatp != NULL) {\n+\t\tformatp++;\n+\t\tdate_mode = parse_date_format(formatp);\n+\t}\n \n \tif (!eoemail)\n \t\tgoto bad;\n@@ -368,7 +380,7 @@ static void grab_date(const char *buf, struct atom_value *v)\n \ttz = strtol(zone, NULL, 10);\n \tif ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)\n \t\tgoto bad;\n-\tv->s = xstrdup(show_date(timestamp, tz, 0));\n+\tv->s = xstrdup(show_date(timestamp, tz, date_mode));\n \tv->ul = timestamp;\n \treturn;\n  bad:\n@@ -395,7 +407,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru\n \t\tif (name[wholen] != 0 &&\n \t\t    strcmp(name + wholen, \"name\") &&\n \t\t    strcmp(name + wholen, \"email\") &&\n-\t\t    strcmp(name + wholen, \"date\"))\n+\t\t    prefixcmp(name + wholen, \"date\"))\n \t\t\tcontinue;\n \t\tif (!wholine)\n \t\t\twholine = find_wholine(who, wholen, buf, sz);\n@@ -407,8 +419,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru\n \t\t\tv->s = copy_name(wholine);\n \t\telse if (!strcmp(name + wholen, \"email\"))\n \t\t\tv->s = copy_email(wholine);\n-\t\telse if (!strcmp(name + wholen, \"date\"))\n-\t\t\tgrab_date(wholine, v);\n+\t\telse if (!prefixcmp(name + wholen, \"date\"))\n+\t\t\tgrab_date(wholine, v, name);\n \t}\n \n \t/* For a tag or a commit object, if \"creator\" or \"creatordate\" is\n@@ -428,8 +440,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru\n \t\tif (deref)\n \t\t\tname++;\n \n-\t\tif (!strcmp(name, \"creatordate\"))\n-\t\t\tgrab_date(wholine, v);\n+\t\tif (!prefixcmp(name, \"creatordate\"))\n+\t\t\tgrab_date(wholine, v, name);\n \t\telse if (!strcmp(name, \"creator\"))\n \t\t\tv->s = copy_line(wholine);\n \t}\n-- \n1.5.3.2.105.gf47f2-dirty\n"},{"id":"54271","messageId":"Pine.LNX.4.64.0709281622240.28395@racer.site","threadId":"10029","inReplyTo":"200709281517.32030.andyparkins@gmail.com","subject":"Re: [PATCH 2/4] Use parse_date_format() in revisions.c to parse the --date parameter","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-09-28T15:22:48Z","receivedAt":"2007-09-28T15:22:48Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 28 Sep 2007, Andy Parkins wrote:\n\n> The --date parameter was previously handled in revisions.c with a list\n> of if(strcmp()) calls; now parse_date_format() is called instead.\n\nSince this is really more like a code move, 1/4 and 2/4 should be \nsquashed.\n\nCiao,\nDscho\n"},{"id":"54283","messageId":"200709281900.25536.andyparkins@gmail.com","threadId":"10029","inReplyTo":"Pine.LNX.4.64.0709281622240.28395@racer.site","subject":"Re: [PATCH 2/4] Use parse_date_format() in revisions.c to parse the --date parameter","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-09-28T18:00:23Z","receivedAt":"2007-09-28T18:00:23Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"On Friday 2007, September 28, Johannes Schindelin wrote:\n\n> Since this is really more like a code move, 1/4 and 2/4 should be\n> squashed.\n\nI have no problem with that.\n\nJunio: would you like a resend?\n\n\n\nAndy\n-- \nDr Andy Parkins, M Eng (hons), MIET\nandyparkins@gmail.com\n"},{"id":"54285","messageId":"7v1wcipsn9.fsf@gitster.siamese.dyndns.org","threadId":"10029","inReplyTo":"200709281900.25536.andyparkins@gmail.com","subject":"Re: [PATCH 2/4] Use parse_date_format() in revisions.c to parse the --date parameter","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-09-28T18:11:06Z","receivedAt":"2007-09-28T18:11:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andy Parkins <andyparkins@gmail.com> writes:\n\n> On Friday 2007, September 28, Johannes Schindelin wrote:\n>\n>> Since this is really more like a code move, 1/4 and 2/4 should be\n>> squashed.\n>\n> I have no problem with that.\n>\n> Junio: would you like a resend?\n\nSounds like a good plan.\n"},{"id":"54286","messageId":"20070928184710.GA5560@coredump.intra.peff.net","threadId":"10029","inReplyTo":"200709281516.05438.andyparkins@gmail.com","subject":"Re: [PATCH] Add a --dateformat= option to git-for-each-ref","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2007-09-28T18:47:10Z","receivedAt":"2007-09-28T18:47:10Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Sep 28, 2007 at 03:15:58PM +0100, Andy Parkins wrote:\n\n> > like a sane way to implement these sorts of things (e.g.,\n> > \"%(objectsize:human)\", \"%(parent:1)\", etc).\n> \n> I'd thought about doing it like that, but imagined that there would\n> objections that it was overcomplicating git-for-each-ref.  As you\n> think that's acceptable, I'll do it.\n\nWell, I'm not sure my opinion counts for much, but at least there are\nnow two of us. :)\n\n> A patch series that implements both your requested changes to follow.\n\nPatches 1/2 look fine to me (but I agree with the squash suggestion).\n\n3/4 are not exactly what I had in mind, but I think are reasonable in\nthis case. Rather than treating it was \":format\", I had imagined more of\na \":attribute1:attribute2\" style, where some attributes may be\nunderstood by all substitutions (e.g., the moral equivalent of shell's\n\":-\" and \":+\"), and some only by some substitutions (such as date\nformats). And on top of that, these sorts of substitutions should be\nunified with the --pretty=format machinery.\n\nOf course, that is a much larger task and you probably just want to do\nyour date formatting and get your other work done. So I think your\nimplementation is reasonable, in that it accomplishes what you want in a\nreasonable amount of code, and its syntax doesn't prevent moving towards\nwhat I described above (since %(foo:bar:baz) is currently nonsensical,\nwe would be free to adapt its meaning later).\n\nSo in a very verbose way,\n\nAcked-by: Jeff King <peff@peff.net>\n\n-Peff\n"},{"id":"54312","messageId":"200709290839.45467.andyparkins@gmail.com","threadId":"10029","inReplyTo":"7v1wcipsn9.fsf@gitster.siamese.dyndns.org","subject":"[PATCH 1/3] Use parse_date_format() convenience function for converting a format string to an enum date_mode in revisions.c","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-09-29T07:39:44Z","receivedAt":"2007-09-29T07:39:44Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"parse_date_format() is passed a string that is compared against a\npre-defined list and converted to an enum date_format.  The table is as\nfollows:\n\n - \"relative\"         => DATE_RELATIVE\n - \"iso8601\" or \"iso\" => DATE_ISO8601\n - \"rfc2822\"          => DATE_RFC2822\n - \"short\"            => DATE_SHORT\n - \"local\"            => DATE_LOCAL\n - \"default\"          => DATE_NORMAL\n\nIn the event that none of these strings is found, the function die()s.\n\nThen we use parse_date_format() in revisions.c to parse the --date\nparameter.  The --date parameter was previously handled in revisions.c\nwith a list of if(strcmp()) calls; now parse_date_format() is called\ninstead.\n\nSigned-off-by: Andy Parkins <andyparkins@gmail.com>\n---\n cache.h    |    1 +\n date.c     |   20 ++++++++++++++++++++\n revision.c |   17 +----------------\n 3 files changed, 22 insertions(+), 16 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 8246500..5587f7e 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -432,6 +432,7 @@ const char *show_date(unsigned long time, int timezone, enum date_mode mode);\n int parse_date(const char *date, char *buf, int bufsize);\n void datestamp(char *buf, int bufsize);\n unsigned long approxidate(const char *);\n+enum date_mode parse_date_format(const char *format);\n \n extern const char *git_author_info(int);\n extern const char *git_committer_info(int);\ndiff --git a/date.c b/date.c\nindex 93bef6e..8f70500 100644\n--- a/date.c\n+++ b/date.c\n@@ -584,6 +584,26 @@ int parse_date(const char *date, char *result, int maxlen)\n \treturn date_string(then, offset, result, maxlen);\n }\n \n+enum date_mode parse_date_format(const char *format)\n+{\n+\tif (!strcmp(format, \"relative\"))\n+\t\treturn DATE_RELATIVE;\n+\telse if (!strcmp(format, \"iso8601\") ||\n+\t\t !strcmp(format, \"iso\"))\n+\t\treturn DATE_ISO8601;\n+\telse if (!strcmp(format, \"rfc2822\") ||\n+\t\t !strcmp(format, \"rfc\"))\n+\t\treturn DATE_RFC2822;\n+\telse if (!strcmp(format, \"short\"))\n+\t\treturn DATE_SHORT;\n+\telse if (!strcmp(format, \"local\"))\n+\t\treturn DATE_LOCAL;\n+\telse if (!strcmp(format, \"default\"))\n+\t\treturn DATE_NORMAL;\n+\telse\n+\t\tdie(\"unknown date format %s\", format);\n+}\n+\n void datestamp(char *buf, int bufsize)\n {\n \ttime_t now;\ndiff --git a/revision.c b/revision.c\nindex 33d092c..75cd0c6 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1134,22 +1134,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strncmp(arg, \"--date=\", 7)) {\n-\t\t\t\tif (!strcmp(arg + 7, \"relative\"))\n-\t\t\t\t\trevs->date_mode = DATE_RELATIVE;\n-\t\t\t\telse if (!strcmp(arg + 7, \"iso8601\") ||\n-\t\t\t\t\t !strcmp(arg + 7, \"iso\"))\n-\t\t\t\t\trevs->date_mode = DATE_ISO8601;\n-\t\t\t\telse if (!strcmp(arg + 7, \"rfc2822\") ||\n-\t\t\t\t\t !strcmp(arg + 7, \"rfc\"))\n-\t\t\t\t\trevs->date_mode = DATE_RFC2822;\n-\t\t\t\telse if (!strcmp(arg + 7, \"short\"))\n-\t\t\t\t\trevs->date_mode = DATE_SHORT;\n-\t\t\t\telse if (!strcmp(arg + 7, \"local\"))\n-\t\t\t\t\trevs->date_mode = DATE_LOCAL;\n-\t\t\t\telse if (!strcmp(arg + 7, \"default\"))\n-\t\t\t\t\trevs->date_mode = DATE_NORMAL;\n-\t\t\t\telse\n-\t\t\t\t\tdie(\"unknown date format %s\", arg);\n+\t\t\t\trevs->date_mode = parse_date_format(arg + 7);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--log-size\")) {\n-- \n1.5.3.rc5.11.g312e\n"},{"id":"54313","messageId":"200709290839.52436.andyparkins@gmail.com","threadId":"10029","inReplyTo":"7v1wcipsn9.fsf@gitster.siamese.dyndns.org","subject":"[PATCH 2/3] Make for-each-ref allow atom names like \"<name>:<something>\"","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-09-29T07:39:52Z","receivedAt":"2007-09-29T07:39:52Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"In anticipation of supplying a per-field date format specifier, this\npatch makes parse_atom() in builtin-for-each-ref.c allow atoms that have\na valid atom name (as determined by the valid_atom[] table) followed by\na colon, followed by an arbitrary string.\n\nThe arbitrary string is where the format for the atom will be specified.\n\nNote, if different formats are specified for the same atom, multiple\nentries will be made in the used_atoms table to allow them to be\ndistinguished by the grab_XXXX() functions.\n\nSigned-off-by: Andy Parkins <andyparkins@gmail.com>\n---\n builtin-for-each-ref.c |    8 +++++++-\n 1 files changed, 7 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\nindex 0afa1c5..3280516 100644\n--- a/builtin-for-each-ref.c\n+++ b/builtin-for-each-ref.c\n@@ -106,7 +106,13 @@ static int parse_atom(const char *atom, const char *ep)\n \t/* Is the atom a valid one? */\n \tfor (i = 0; i < ARRAY_SIZE(valid_atom); i++) {\n \t\tint len = strlen(valid_atom[i].name);\n-\t\tif (len == ep - sp && !memcmp(valid_atom[i].name, sp, len))\n+\t\t/* If the atom name has a colon, strip it and everything after\n+\t\t * it off - it specifies the format for this entry, and\n+\t\t * shouldn't be used for checking against the valid_atom table */\n+\t\tconst char *formatp = strrchr(sp, ':' );\n+\t\tif (formatp == NULL )\n+\t\t\tformatp = ep;\n+\t\tif (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))\n \t\t\tbreak;\n \t}\n \n-- \n1.5.3.rc5.11.g312e\n"},{"id":"54314","messageId":"200709290839.57507.andyparkins@gmail.com","threadId":"10029","inReplyTo":"7v1wcipsn9.fsf@gitster.siamese.dyndns.org","subject":"[PATCH 3/3] Make for-each-ref's grab_date() support per-atom formatting","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-09-29T07:39:57Z","receivedAt":"2007-09-29T07:39:57Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"grab_date() gets an extra parameter - atomname; this extra parameter is\nchecked to see if it has a \":<format>\" extra component in it, and if so\nthat \"<format>\" string is passed to parse_date_format() to produce an\nenum date_mode value which is then further passed to show_date().\n\nIn short it allows the user of git-for-each-ref to do things like this:\n\n $ git-for-each-ref --format='%(taggerdate:default)' refs/tags/v1.5.2\n Sun May 20 00:30:42 2007 -0700\n $ git-for-each-ref --format='%(taggerdate:relative)' refs/tags/v1.5.2\n 4 months ago\n $ git-for-each-ref --format='%(taggerdate:short)' refs/tags/v1.5.2\n 2007-05-20\n $ git-for-each-ref --format='%(taggerdate:local)' refs/tags/v1.5.2\n Sun May 20 08:30:42 2007\n $ git-for-each-ref --format='%(taggerdate:iso8601)' refs/tags/v1.5.2\n 2007-05-20 00:30:42 -0700\n $ git-for-each-ref --format='%(taggerdate:rfc2822)' refs/tags/v1.5.2\n Sun, 20 May 2007 00:30:42 -0700\n\nThe default, when no \":<format>\" is specified is \":default\", leaving the\nexisting behaviour unchanged.\n\nSigned-off-by: Andy Parkins <andyparkins@gmail.com>\n---\n Documentation/git-for-each-ref.txt |    5 +++++\n builtin-for-each-ref.c             |   26 +++++++++++++++++++-------\n 2 files changed, 24 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 6df8e85..f1f90cc 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -100,6 +100,11 @@ In any case, a field name that refers to a field inapplicable to\n the object referred by the ref does not cause an error.  It\n returns an empty string instead.\n \n+As a special case for the date-type fields, you may specify a format for\n+the date by adding one of `:default`, `:relative`, `:short`, `:local`,\n+`:iso8601` or `:rfc2822` to the end of the fieldname; e.g.\n+`%(taggerdate:relative)`.\n+\n \n EXAMPLES\n --------\ndiff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\nindex 3280516..2ca4fc6 100644\n--- a/builtin-for-each-ref.c\n+++ b/builtin-for-each-ref.c\n@@ -353,12 +353,24 @@ static const char *copy_email(const char *buf)\n \treturn line;\n }\n \n-static void grab_date(const char *buf, struct atom_value *v)\n+static void grab_date(const char *buf, struct atom_value *v, const char *atomname)\n {\n \tconst char *eoemail = strstr(buf, \"> \");\n \tchar *zone;\n \tunsigned long timestamp;\n \tlong tz;\n+\tenum date_mode date_mode = DATE_NORMAL;\n+\tconst char *formatp;\n+\n+\t/* We got here because atomname ends in \"date\" or \"date<something>\",\n+\t * it's not possible that <something> is not \":<format>\" because\n+\t * parse_atom() wouldn't have allowed it, so we can assume that no\n+\t * \":\" means no format is specified, use the default */\n+\tformatp = strrchr( atomname, ':' );\n+\tif (formatp != NULL) {\n+\t\tformatp++;\n+\t\tdate_mode = parse_date_format(formatp);\n+\t}\n \n \tif (!eoemail)\n \t\tgoto bad;\n@@ -368,7 +380,7 @@ static void grab_date(const char *buf, struct atom_value *v)\n \ttz = strtol(zone, NULL, 10);\n \tif ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)\n \t\tgoto bad;\n-\tv->s = xstrdup(show_date(timestamp, tz, 0));\n+\tv->s = xstrdup(show_date(timestamp, tz, date_mode));\n \tv->ul = timestamp;\n \treturn;\n  bad:\n@@ -395,7 +407,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru\n \t\tif (name[wholen] != 0 &&\n \t\t    strcmp(name + wholen, \"name\") &&\n \t\t    strcmp(name + wholen, \"email\") &&\n-\t\t    strcmp(name + wholen, \"date\"))\n+\t\t    prefixcmp(name + wholen, \"date\"))\n \t\t\tcontinue;\n \t\tif (!wholine)\n \t\t\twholine = find_wholine(who, wholen, buf, sz);\n@@ -407,8 +419,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru\n \t\t\tv->s = copy_name(wholine);\n \t\telse if (!strcmp(name + wholen, \"email\"))\n \t\t\tv->s = copy_email(wholine);\n-\t\telse if (!strcmp(name + wholen, \"date\"))\n-\t\t\tgrab_date(wholine, v);\n+\t\telse if (!prefixcmp(name + wholen, \"date\"))\n+\t\t\tgrab_date(wholine, v, name);\n \t}\n \n \t/* For a tag or a commit object, if \"creator\" or \"creatordate\" is\n@@ -428,8 +440,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru\n \t\tif (deref)\n \t\t\tname++;\n \n-\t\tif (!strcmp(name, \"creatordate\"))\n-\t\t\tgrab_date(wholine, v);\n+\t\tif (!prefixcmp(name, \"creatordate\"))\n+\t\t\tgrab_date(wholine, v, name);\n \t\telse if (!strcmp(name, \"creator\"))\n \t\t\tv->s = copy_line(wholine);\n \t}\n-- \n1.5.3.rc5.11.g312e\n"},{"id":"54317","messageId":"7v641tnawh.fsf@gitster.siamese.dyndns.org","threadId":"10029","inReplyTo":"200709281517.45133.andyparkins@gmail.com","subject":"Re: [PATCH 4/4] Make for-each-ref's grab_date() support per-atom formatting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-09-29T08:17:18Z","receivedAt":"2007-09-29T08:17:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"I squashed 1+2 together so there is no need to resend.  It would\nbe nice to see a few tests in the test suite though.\n"}]}