{"thread":{"id":"47192","subject":"[PATCH V2] config: add --expiry-date","startedAt":"2017-11-13T17:53:56Z","lastAt":"2017-11-13T17:53:56Z","messageCount":1,"participants":["hsed@unimetic.com"],"isPatch":true,"patchVersion":2,"patchTotal":null},"messages":[{"id":"332428","messageId":"d4c5023dad8a7593373141a04a406b4d@unimetic.com","threadId":"47192","inReplyTo":"f9d6d1cfcad17c93a0131cf69c3d7705@unimetic.com","subject":"[PATCH V2] config: add --expiry-date","fromName":"","fromEmail":"hsed@unimetic.com","sentAt":"2017-11-13T17:53:46Z","receivedAt":"2017-11-13T17:53:56Z","isPatch":true,"sender":{"key":"hsed@unimetic.com","avatar":null},"body":"Description:\nThis patch adds a new option to the config command.\n\nEnables flag --expiry-date as a data-type to covert date-strings to\ntimestamps when reading from config files (GET).\nThis flag is ignored on write (SET) because the date-string is stored in\nconfig without performing any normalization.\n\nA few test cases are also created since this is a new feature.\n\nMotivation:\nA parse_expiry_date() function already existed for api calls,\nthis patch simply allows the function to be used from the command line.\n\nUpdate:\nAdded suggestions, documentation, relative time test case and test\nhelper function to print out timestamps for comparison. Updated reflog.c\nto avoid function duplication.\n\nSigned-off-by: Haaris <hsed@unimetic.com>\n---\n  Documentation/git-config.txt |  5 +++++\n  builtin/config.c             | 10 +++++++++-\n  builtin/reflog.c             | 14 ++------------\n  config.c                     |  9 +++++++++\n  config.h                     |  1 +\n  t/helper/test-date.c         | 12 ++++++++++++\n  t/t1300-repo-config.sh       | 30 ++++++++++++++++++++++++++++++\n  7 files changed, 68 insertions(+), 13 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex 4edd09fc6..14da5fc15 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -180,6 +180,11 @@ See also <<FILES>>.\n  \tvalue (but you can use `git config section.variable ~/`\n  \tfrom the command line to let your shell do the expansion).\n\n+--expiry-date::\n+\t`git config` will ensure that the output is converted from\n+\ta fixed or relative date-string to a timestamp. This option\n+\thas no effect when setting the value.\n+\n  -z::\n  --null::\n  \tFor all options that output values and/or keys, always\ndiff --git a/builtin/config.c b/builtin/config.c\nindex d13daeeb5..afdb02191 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -52,6 +52,7 @@ static int show_origin;\n  #define TYPE_INT (1<<1)\n  #define TYPE_BOOL_OR_INT (1<<2)\n  #define TYPE_PATH (1<<3)\n+#define TYPE_EXPIRY_DATE (1<<4)\n\n  static struct option builtin_config_options[] = {\n  \tOPT_GROUP(N_(\"Config file location\")),\n@@ -80,6 +81,7 @@ static struct option builtin_config_options[] = {\n  \tOPT_BIT(0, \"int\", &types, N_(\"value is decimal number\"), TYPE_INT),\n  \tOPT_BIT(0, \"bool-or-int\", &types, N_(\"value is --bool or --int\"), \nTYPE_BOOL_OR_INT),\n  \tOPT_BIT(0, \"path\", &types, N_(\"value is a path (file or directory \nname)\"), TYPE_PATH),\n+\tOPT_BIT(0, \"expiry-date\", &types, N_(\"value is an expiry date\"), \nTYPE_EXPIRY_DATE),\n  \tOPT_GROUP(N_(\"Other\")),\n  \tOPT_BOOL('z', \"null\", &end_null, N_(\"terminate values with NUL \nbyte\")),\n  \tOPT_BOOL(0, \"name-only\", &omit_values, N_(\"show variable names \nonly\")),\n@@ -159,6 +161,11 @@ static int format_config(struct strbuf *buf, const \nchar *key_, const char *value\n  \t\t\t\treturn -1;\n  \t\t\tstrbuf_addstr(buf, v);\n  \t\t\tfree((char *)v);\n+\t\t} else if (types == TYPE_EXPIRY_DATE) {\n+\t\t\ttimestamp_t t;\n+\t\t\tif(git_config_expiry_date(&t, key_, value_) < 0)\n+\t\t\t\treturn -1;\n+\t\t\tstrbuf_addf(buf, \"%\"PRItime, t);\n  \t\t} else if (value_) {\n  \t\t\tstrbuf_addstr(buf, value_);\n  \t\t} else {\n@@ -273,12 +280,13 @@ static char *normalize_value(const char *key, \nconst char *value)\n  \tif (!value)\n  \t\treturn NULL;\n\n-\tif (types == 0 || types == TYPE_PATH)\n+\tif (types == 0 || types == TYPE_PATH || types == TYPE_EXPIRY_DATE)\n  \t\t/*\n  \t\t * We don't do normalization for TYPE_PATH here: If\n  \t\t * the path is like ~/foobar/, we prefer to store\n  \t\t * \"~/foobar/\" in the config file, and to expand the ~\n  \t\t * when retrieving the value.\n+\t\t * Also don't do normalization for expiry dates.\n  \t\t */\n  \t\treturn xstrdup(value);\n  \tif (types == TYPE_INT)\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex ab31a3b6a..223372531 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -416,16 +416,6 @@ static struct reflog_expire_cfg *find_cfg_ent(const \nchar *pattern, size_t len)\n  \treturn ent;\n  }\n\n-static int parse_expire_cfg_value(const char *var, const char *value, \ntimestamp_t *expire)\n-{\n-\tif (!value)\n-\t\treturn config_error_nonbool(var);\n-\tif (parse_expiry_date(value, expire))\n-\t\treturn error(_(\"'%s' for '%s' is not a valid timestamp\"),\n-\t\t\t     value, var);\n-\treturn 0;\n-}\n-\n  /* expiry timer slot */\n  #define EXPIRE_TOTAL   01\n  #define EXPIRE_UNREACH 02\n@@ -443,11 +433,11 @@ static int reflog_expire_config(const char *var, \nconst char *value, void *cb)\n\n  \tif (!strcmp(key, \"reflogexpire\")) {\n  \t\tslot = EXPIRE_TOTAL;\n-\t\tif (parse_expire_cfg_value(var, value, &expire))\n+\t\tif (git_config_expiry_date(&expire, var, value))\n  \t\t\treturn -1;\n  \t} else if (!strcmp(key, \"reflogexpireunreachable\")) {\n  \t\tslot = EXPIRE_UNREACH;\n-\t\tif (parse_expire_cfg_value(var, value, &expire))\n+\t\tif (git_config_expiry_date(&expire, var, value))\n  \t\t\treturn -1;\n  \t} else\n  \t\treturn git_default_config(var, value, cb);\ndiff --git a/config.c b/config.c\nindex 903abf953..6ded9ce98 100644\n--- a/config.c\n+++ b/config.c\n@@ -990,6 +990,15 @@ int git_config_pathname(const char **dest, const \nchar *var, const char *value)\n  \treturn 0;\n  }\n\n+int git_config_expiry_date(timestamp_t *timestamp, const char *var, \nconst char *value)\n+{\n+\tif (!value)\n+\t\treturn config_error_nonbool(var);\n+\tif (parse_expiry_date(value, timestamp))\n+\t\tdie(_(\"failed to parse date_string in: '%s'\"), value);\n+\treturn 0;\n+}\n+\n  static int git_default_core_config(const char *var, const char *value)\n  {\n  \t/* This needs a better name */\ndiff --git a/config.h b/config.h\nindex a49d26441..fc66c5933 100644\n--- a/config.h\n+++ b/config.h\n@@ -58,6 +58,7 @@ extern int git_config_bool_or_int(const char *, const \nchar *, int *);\n  extern int git_config_bool(const char *, const char *);\n  extern int git_config_string(const char **, const char *, const char \n*);\n  extern int git_config_pathname(const char **, const char *, const char \n*);\n+extern int git_config_expiry_date(timestamp_t *, const char *, const \nchar *);\n  extern int git_config_set_in_file_gently(const char *, const char *, \nconst char *);\n  extern void git_config_set_in_file(const char *, const char *, const \nchar *);\n  extern int git_config_set_gently(const char *, const char *);\ndiff --git a/t/helper/test-date.c b/t/helper/test-date.c\nindex f414a3ac6..ac8368797 100644\n--- a/t/helper/test-date.c\n+++ b/t/helper/test-date.c\n@@ -5,6 +5,7 @@ static const char *usage_msg = \"\\n\"\n  \"  test-date show:<format> [time_t]...\\n\"\n  \"  test-date parse [date]...\\n\"\n  \"  test-date approxidate [date]...\\n\"\n+\"  test-date timestamp [date]...\\n\"\n  \"  test-date is64bit\\n\"\n  \"  test-date time_t-is64bit\\n\";\n\n@@ -71,6 +72,15 @@ static void parse_approxidate(const char **argv, \nstruct timeval *now)\n  \t}\n  }\n\n+static void parse_approx_timestamp(const char **argv, struct timeval \n*now)\n+{\n+\tfor (; *argv; argv++) {\n+\t\ttimestamp_t t;\n+\t\tt = approxidate_relative(*argv, now);\n+\t\tprintf(\"%s -> %\"PRItime\"\\n\", *argv, t);\n+\t}\n+}\n+\n  int cmd_main(int argc, const char **argv)\n  {\n  \tstruct timeval now;\n@@ -95,6 +105,8 @@ int cmd_main(int argc, const char **argv)\n  \t\tparse_dates(argv+1, &now);\n  \telse if (!strcmp(*argv, \"approxidate\"))\n  \t\tparse_approxidate(argv+1, &now);\n+\telse if (!strcmp(*argv, \"timestamp\"))\n+\t\tparse_approx_timestamp(argv+1, &now);\n  \telse if (!strcmp(*argv, \"is64bit\"))\n  \t\treturn sizeof(timestamp_t) == 8 ? 0 : 1;\n  \telse if (!strcmp(*argv, \"time_t-is64bit\"))\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 364a53700..cbeb9bebe 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -901,6 +901,36 @@ test_expect_success 'get --path barfs on boolean \nvariable' '\n  \ttest_must_fail git config --get --path path.bool\n  '\n\n+test_expect_success 'get --expiry-date' '\n+\trel=\"3.weeks.5.days.00:00\" &&\n+\trel_out=\"$rel ->\" &&\n+\tcat >.git/config <<-\\EOF &&\n+\t[date]\n+\tvalid1 = \"3.weeks.5.days 00:00\"\n+\tvalid2 = \"Fri Jun 4 15:46:55 2010\"\n+\tvalid3 = \"2017/11/11 11:11:11PM\"\n+\tvalid4 = \"2017/11/10 09:08:07 PM\"\n+\tvalid5 = \"never\"\n+\tinvalid1 = \"abc\"\n+\tEOF\n+\tcat >expect <<-EOF &&\n+\t$(test-date timestamp $rel)\n+\t1275666415\n+\t1510441871\n+\t1510348087\n+\t0\n+\tEOF\n+\t{\n+\t\techo \"$rel_out $(git config --expiry-date date.valid1)\"\n+\t\tgit config --expiry-date date.valid2 &&\n+\t\tgit config --expiry-date date.valid3 &&\n+\t\tgit config --expiry-date date.valid4 &&\n+\t\tgit config --expiry-date date.valid5\n+\t} >actual &&\n+\ttest_cmp expect actual &&\n+\ttest_must_fail git config --expiry-date date.invalid1\n+'\n+\n  cat > expect << EOF\n  [quote]\n  \tleading = \" test\"\n"}]}