Skip to content

Commit 116eb3a

Browse files
committed
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 "<seconds since epoch> <timezone>" 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 "<digits> +<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 <[email protected]>
1 parent 703f05a commit 116eb3a

File tree

1 file changed

+29
-0
lines changed

1 file changed

+29
-0
lines changed

date.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,33 @@ static int date_string(unsigned long date, int offset, char *buf, int len)
585585
return snprintf(buf, len, "%lu %c%02d%02d", date, sign, offset / 60, offset % 60);
586586
}
587587

588+
/*
589+
* Parse a string like "0 +0000" as ancient timestamp near epoch, but
590+
* only when it appears not as part of any other string.
591+
*/
592+
static int match_object_header_date(const char *date, unsigned long *timestamp, int *offset)
593+
{
594+
char *end;
595+
unsigned long stamp;
596+
int ofs;
597+
598+
if (*date < '0' || '9' <= *date)
599+
return -1;
600+
stamp = strtoul(date, &end, 10);
601+
if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-'))
602+
return -1;
603+
date = end + 2;
604+
ofs = strtol(date, &end, 10);
605+
if ((*end != '\0' && (*end != '\n')) || end != date + 4)
606+
return -1;
607+
ofs = (ofs / 100) * 60 + (ofs % 100);
608+
if (date[-1] == '-')
609+
ofs = -ofs;
610+
*timestamp = stamp;
611+
*offset = ofs;
612+
return 0;
613+
}
614+
588615
/* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
589616
(i.e. English) day/month names, and it doesn't work correctly with %z. */
590617
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)
610637
*offset = -1;
611638
tm_gmt = 0;
612639

640+
if (!match_object_header_date(date, timestamp, offset))
641+
return 0; /* success */
613642
for (;;) {
614643
int match = 0;
615644
unsigned char c = *date;

0 commit comments

Comments
 (0)