From 116eb3abfe4f79907f701317c2d905868941fff7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 2 Feb 2012 13:41:42 -0800 Subject: [PATCH] parse_date(): allow ancient git-timestamp The date-time parser parses out a human-readble datestring piece by piece, so that it could even parse a string in a rather strange notation like 'noon november 11, 2005', but restricts itself from parsing strings in " " format only for reasonably new timestamps (like 1974 or newer) with 10 or more digits. This is to prevent a string like "20100917" from getting interpreted as seconds since epoch (we want to treat it as September 17, 2010 instead) while doing so. The same codepath is used to read back the timestamp that we have already recorded in the headers of commit and tag objects; because of this, such a commit with timestamp "0 +0000" cannot be rebased or amended very easily. Teach parse_date() codepath to special case a string of the form " +<4-digits>" to work this issue around, but require that there is no other cruft around the string when parsing a timestamp of this format for safety. Note that this has a slight backward incompatibility implications. If somebody writes "git commit --date='20100917 +0900'" and wants it to mean a timestamp in September 2010 in Japan, this change will break such a use case. Signed-off-by: Junio C Hamano --- date.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/date.c b/date.c index 896fbb4806..099ddbe7fc 100644 --- a/date.c +++ b/date.c @@ -585,6 +585,33 @@ static int date_string(unsigned long date, int offset, char *buf, int len) return snprintf(buf, len, "%lu %c%02d%02d", date, sign, offset / 60, offset % 60); } +/* + * Parse a string like "0 +0000" as ancient timestamp near epoch, but + * only when it appears not as part of any other string. + */ +static int match_object_header_date(const char *date, unsigned long *timestamp, int *offset) +{ + char *end; + unsigned long stamp; + int ofs; + + if (*date < '0' || '9' <= *date) + return -1; + stamp = strtoul(date, &end, 10); + if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-')) + return -1; + date = end + 2; + ofs = strtol(date, &end, 10); + if ((*end != '\0' && (*end != '\n')) || end != date + 4) + return -1; + ofs = (ofs / 100) * 60 + (ofs % 100); + if (date[-1] == '-') + ofs = -ofs; + *timestamp = stamp; + *offset = ofs; + return 0; +} + /* Gr. strptime is crap for this; it doesn't have a way to require RFC2822 (i.e. English) day/month names, and it doesn't work correctly with %z. */ int parse_date_basic(const char *date, unsigned long *timestamp, int *offset) @@ -610,6 +637,8 @@ int parse_date_basic(const char *date, unsigned long *timestamp, int *offset) *offset = -1; tm_gmt = 0; + if (!match_object_header_date(date, timestamp, offset)) + return 0; /* success */ for (;;) { int match = 0; unsigned char c = *date;