summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2012-02-02 13:41:42 -0800
committerJunio C Hamano <gitster@pobox.com>2012-02-03 23:11:32 -0800
commit116eb3abfe4f79907f701317c2d905868941fff7 (patch)
tree09c4cafc205ee3cb2bb5fbe41b5a4f268668fa7c
parent703f05ad5835cff92b12c29aecf8d724c8c847e2 (diff)
downloadgit-116eb3abfe4f79907f701317c2d905868941fff7.tar.gz
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 <gitster@pobox.com>
-rw-r--r--date.c29
1 files changed, 29 insertions, 0 deletions
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;