config: add --expiry-date

Add --expiry-date as a data-type for config files when 'git config --get' is used. This will return any relative or fixed dates from config files as timestamps. This is useful for scripts (e.g. gc.reflogexpire) that work with timestamps so that '2.weeks' can be converted to a format acceptable by those scripts/functions. Following the convention of git_config_pathname(), move the helper function required for this feature from builtin/reflog.c to builtin/config.c where other similar functions exist (e.g. for --bool or --path), and match the order of parameters with other functions (i.e. output pointer as first parameter). Signed-off-by: Haaris Mehmood <hsed@unimetic.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Haaris Mehmood committed Nov 18, 2017 at 02:27 UTC 5f9674243d0341519e5031681c941b0e1ad7a9e3
7 files changed +69 -13
Documentation/git-config.txt
+5
@@ -180,6 +180,11 @@ See also <<FILES>>.
180 value (but you can use `git config section.variable ~/`
181 from the command line to let your shell do the expansion).
182
183 +--expiry-date::
184 + `git config` will ensure that the output is converted from
185 + a fixed or relative date-string to a timestamp. This option
186 + has no effect when setting the value.
187 +
188 -z::
189 --null::
190 For all options that output values and/or keys, always
builtin/config.c
+9 -1
@@ -52,6 +52,7 @@ static int show_origin;
52 #define TYPE_INT (1<<1)
53 #define TYPE_BOOL_OR_INT (1<<2)
54 #define TYPE_PATH (1<<3)
55 +#define TYPE_EXPIRY_DATE (1<<4)
56
57 static struct option builtin_config_options[] = {
58 OPT_GROUP(N_("Config file location")),
@@ -80,6 +81,7 @@ static struct option builtin_config_options[] = {
81 OPT_BIT(0, "int", &types, N_("value is decimal number"), TYPE_INT),
82 OPT_BIT(0, "bool-or-int", &types, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
83 OPT_BIT(0, "path", &types, N_("value is a path (file or directory name)"), TYPE_PATH),
84 + OPT_BIT(0, "expiry-date", &types, N_("value is an expiry date"), TYPE_EXPIRY_DATE),
85 OPT_GROUP(N_("Other")),
86 OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")),
87 OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
@@ -159,6 +161,11 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value
161 return -1;
162 strbuf_addstr(buf, v);
163 free((char *)v);
164 + } else if (types == TYPE_EXPIRY_DATE) {
165 + timestamp_t t;
166 + if (git_config_expiry_date(&t, key_, value_) < 0)
167 + return -1;
168 + strbuf_addf(buf, "%"PRItime, t);
169 } else if (value_) {
170 strbuf_addstr(buf, value_);
171 } else {
@@ -273,12 +280,13 @@ static char *normalize_value(const char *key, const char *value)
280 if (!value)
281 return NULL;
282
276 - if (types == 0 || types == TYPE_PATH)
283 + if (types == 0 || types == TYPE_PATH || types == TYPE_EXPIRY_DATE)
284 /*
285 * We don't do normalization for TYPE_PATH here: If
286 * the path is like ~/foobar/, we prefer to store
287 * "~/foobar/" in the config file, and to expand the ~
288 * when retrieving the value.
289 + * Also don't do normalization for expiry dates.
290 */
291 return xstrdup(value);
292 if (types == TYPE_INT)
builtin/reflog.c
+2 -12
@@ -416,16 +416,6 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
416 return ent;
417 }
418
419 -static int parse_expire_cfg_value(const char *var, const char *value, timestamp_t *expire)
420 -{
421 - if (!value)
422 - return config_error_nonbool(var);
423 - if (parse_expiry_date(value, expire))
424 - return error(_("'%s' for '%s' is not a valid timestamp"),
425 - value, var);
426 - return 0;
427 -}
428 -
419 /* expiry timer slot */
420 #define EXPIRE_TOTAL 01
421 #define EXPIRE_UNREACH 02
@@ -443,11 +433,11 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
433
434 if (!strcmp(key, "reflogexpire")) {
435 slot = EXPIRE_TOTAL;
446 - if (parse_expire_cfg_value(var, value, &expire))
436 + if (git_config_expiry_date(&expire, var, value))
437 return -1;
438 } else if (!strcmp(key, "reflogexpireunreachable")) {
439 slot = EXPIRE_UNREACH;
450 - if (parse_expire_cfg_value(var, value, &expire))
440 + if (git_config_expiry_date(&expire, var, value))
441 return -1;
442 } else
443 return git_default_config(var, value, cb);
config.c
+10
@@ -990,6 +990,16 @@ int git_config_pathname(const char **dest, const char *var, const char *value)
990 return 0;
991 }
992
993 +int git_config_expiry_date(timestamp_t *timestamp, const char *var, const char *value)
994 +{
995 + if (!value)
996 + return config_error_nonbool(var);
997 + if (parse_expiry_date(value, timestamp))
998 + return error(_("'%s' for '%s' is not a valid timestamp"),
999 + value, var);
1000 + return 0;
1001 +}
1002 +
1003 static int git_default_core_config(const char *var, const char *value)
1004 {
1005 /* This needs a better name */
config.h
+1
@@ -58,6 +58,7 @@ extern int git_config_bool_or_int(const char *, const char *, int *);
58 extern int git_config_bool(const char *, const char *);
59 extern int git_config_string(const char **, const char *, const char *);
60 extern int git_config_pathname(const char **, const char *, const char *);
61 +extern int git_config_expiry_date(timestamp_t *, const char *, const char *);
62 extern int git_config_set_in_file_gently(const char *, const char *, const char *);
63 extern void git_config_set_in_file(const char *, const char *, const char *);
64 extern int git_config_set_gently(const char *, const char *);
t/helper/test-date.c
+12
@@ -5,6 +5,7 @@ static const char *usage_msg = "\n"
5 " test-date show:<format> [time_t]...\n"
6 " test-date parse [date]...\n"
7 " test-date approxidate [date]...\n"
8 +" test-date timestamp [date]...\n"
9 " test-date is64bit\n"
10 " test-date time_t-is64bit\n";
11
@@ -71,6 +72,15 @@ static void parse_approxidate(const char **argv, struct timeval *now)
72 }
73 }
74
75 +static void parse_approx_timestamp(const char **argv, struct timeval *now)
76 +{
77 + for (; *argv; argv++) {
78 + timestamp_t t;
79 + t = approxidate_relative(*argv, now);
80 + printf("%s -> %"PRItime"\n", *argv, t);
81 + }
82 +}
83 +
84 int cmd_main(int argc, const char **argv)
85 {
86 struct timeval now;
@@ -95,6 +105,8 @@ int cmd_main(int argc, const char **argv)
105 parse_dates(argv+1, &now);
106 else if (!strcmp(*argv, "approxidate"))
107 parse_approxidate(argv+1, &now);
108 + else if (!strcmp(*argv, "timestamp"))
109 + parse_approx_timestamp(argv+1, &now);
110 else if (!strcmp(*argv, "is64bit"))
111 return sizeof(timestamp_t) == 8 ? 0 : 1;
112 else if (!strcmp(*argv, "time_t-is64bit"))
t/t1300-repo-config.sh
+30
@@ -901,6 +901,36 @@ test_expect_success 'get --path barfs on boolean variable' '
901 test_must_fail git config --get --path path.bool
902 '
903
904 +test_expect_success 'get --expiry-date' '
905 + rel="3.weeks.5.days.00:00" &&
906 + rel_out="$rel ->" &&
907 + cat >.git/config <<-\EOF &&
908 + [date]
909 + valid1 = "3.weeks.5.days 00:00"
910 + valid2 = "Fri Jun 4 15:46:55 2010"
911 + valid3 = "2017/11/11 11:11:11PM"
912 + valid4 = "2017/11/10 09:08:07 PM"
913 + valid5 = "never"
914 + invalid1 = "abc"
915 + EOF
916 + cat >expect <<-EOF &&
917 + $(test-date timestamp $rel)
918 + 1275666415
919 + 1510441871
920 + 1510348087
921 + 0
922 + EOF
923 + {
924 + echo "$rel_out $(git config --expiry-date date.valid1)"
925 + git config --expiry-date date.valid2 &&
926 + git config --expiry-date date.valid3 &&
927 + git config --expiry-date date.valid4 &&
928 + git config --expiry-date date.valid5
929 + } >actual &&
930 + test_cmp expect actual &&
931 + test_must_fail git config --expiry-date date.invalid1
932 +'
933 +
934 cat > expect << EOF
935 [quote]
936 leading = " test"