date.c: add parse_expiry_date()

"git reflog --expire=all" tries to expire reflog entries up to the current second, because the approxidate() parser gives the current timestamp for anything it does not understand (and it does not know what time "all" means). When the user tells us to expire "all" (or set the expiration time to "now"), the user wants to remove all the reflog entries (no reflog entry should record future time). Just set it to ULONG_MAX and to let everything that is older that timestamp expire. While at it, allow "now" to be treated the same way for callers that parse expiry date timestamp with this function. Also use an error reporting version of approxidate() to report misspelled date. When the user says e.g. "--expire=mnoday" to delete entries two days or older on Wednesday, we wouldn't want the "unknown, default to now" logic to kick in. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Apr 17, 2013 at 15:38 UTC 3d27b9b005f26b107227fc18b6648df329daee97
3 files changed +30 -7
builtin/reflog.c
+7 -7
@@ -496,11 +496,9 @@ static int parse_expire_cfg_value(const char *var, const char *value, unsigned l
496 {
497 if (!value)
498 return config_error_nonbool(var);
499 - if (!strcmp(value, "never") || !strcmp(value, "false")) {
500 - *expire = 0;
501 - return 0;
502 - }
503 - *expire = approxidate(value);
499 + if (parse_expiry_date(value, expire))
500 + return error(_("%s' for '%s' is not a valid timestamp"),
501 + value, var);
502 return 0;
503 }
504
@@ -613,11 +611,13 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
611 if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
612 cb.dry_run = 1;
613 else if (!prefixcmp(arg, "--expire=")) {
616 - cb.expire_total = approxidate(arg + 9);
614 + if (parse_expiry_date(arg + 9, &cb.expire_total))
615 + die(_("'%s' is not a valid timestamp"), arg);
616 explicit_expiry |= EXPIRE_TOTAL;
617 }
618 else if (!prefixcmp(arg, "--expire-unreachable=")) {
620 - cb.expire_unreachable = approxidate(arg + 21);
619 + if (parse_expiry_date(arg + 21, &cb.expire_unreachable))
620 + die(_("'%s' is not a valid timestamp"), arg);
621 explicit_expiry |= EXPIRE_UNREACH;
622 }
623 else if (!strcmp(arg, "--stale-fix"))
cache.h
+1
@@ -878,6 +878,7 @@ void show_date_relative(unsigned long time, int tz, const struct timeval *now,
878 struct strbuf *timebuf);
879 int parse_date(const char *date, char *buf, int bufsize);
880 int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
881 +int parse_expiry_date(const char *date, unsigned long *timestamp);
882 void datestamp(char *buf, int bufsize);
883 #define approxidate(s) approxidate_careful((s), NULL)
884 unsigned long approxidate_careful(const char *, int *);
date.c
+22
@@ -705,6 +705,28 @@ int parse_date_basic(const char *date, unsigned long *timestamp, int *offset)
705 return 0; /* success */
706 }
707
708 +int parse_expiry_date(const char *date, unsigned long *timestamp)
709 +{
710 + int errors = 0;
711 +
712 + if (!strcmp(date, "never") || !strcmp(date, "false"))
713 + *timestamp = 0;
714 + else if (!strcmp(date, "all") || !strcmp(date, "now"))
715 + /*
716 + * We take over "now" here, which usually translates
717 + * to the current timestamp. This is because the user
718 + * really means to expire everything she has done in
719 + * the past, and by definition reflogs are the record
720 + * of the past, and there is nothing from the future
721 + * to be kept.
722 + */
723 + *timestamp = ULONG_MAX;
724 + else
725 + *timestamp = approxidate_careful(date, &errors);
726 +
727 + return errors;
728 +}
729 +
730 int parse_date(const char *date, char *result, int maxlen)
731 {
732 unsigned long timestamp;