From 93cfa7c7a85efbdb00daade4ad0afc11bd2fdf37 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 26 Jan 2010 11:58:00 -0800 Subject: approxidate_careful() reports errorneous date string For a long time, the time based reflog syntax (e.g. master@{yesterday}) didn't complain when the "human readable" timestamp was misspelled, as the underlying mechanism tried to be as lenient as possible. The funny thing was that parsing of "@{now}" even relied on the fact that anything not recognized by the machinery returned the current timestamp. Introduce approxidate_careful() that takes an optional pointer to an integer, that gets assigned 1 when the input does not make sense as a timestamp. As I am too lazy to fix all the callers that use approxidate(), most of the callers do not take advantage of the error checking, but convert the code to parse reflog to use it as a demonstration. Tests are mostly from Jeff King. Signed-off-by: Junio C Hamano diff --git a/cache.h b/cache.h index b90f671..0f163db 100644 --- a/cache.h +++ b/cache.h @@ -740,7 +740,8 @@ const char *show_date_relative(unsigned long time, int tz, size_t timebuf_size); int parse_date(const char *date, char *buf, int bufsize); void datestamp(char *buf, int bufsize); -unsigned long approxidate(const char *); +#define approxidate(s) approxidate_careful((s), NULL) +unsigned long approxidate_careful(const char *, int *); unsigned long approxidate_relative(const char *date, const struct timeval *now); enum date_mode parse_date_format(const char *format); diff --git a/date.c b/date.c index 5d05ef6..17385a9 100644 --- a/date.c +++ b/date.c @@ -696,6 +696,11 @@ static unsigned long update_tm(struct tm *tm, struct tm *now, unsigned long sec) return n; } +static void date_now(struct tm *tm, struct tm *now, int *num) +{ + update_tm(tm, now, 0); +} + static void date_yesterday(struct tm *tm, struct tm *now, int *num) { update_tm(tm, now, 24*60*60); @@ -770,6 +775,7 @@ static const struct special { { "PM", date_pm }, { "AM", date_am }, { "never", date_never }, + { "now", date_now }, { NULL } }; @@ -790,7 +796,7 @@ static const struct typelen { { NULL } }; -static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num) +static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num, int *touched) { const struct typelen *tl; const struct special *s; @@ -804,6 +810,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm int match = match_string(date, month_names[i]); if (match >= 3) { tm->tm_mon = i; + *touched = 1; return end; } } @@ -812,6 +819,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm int len = strlen(s->name); if (match_string(date, s->name) == len) { s->fn(tm, now, num); + *touched = 1; return end; } } @@ -821,11 +829,14 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm int len = strlen(number_name[i]); if (match_string(date, number_name[i]) == len) { *num = i; + *touched = 1; return end; } } - if (match_string(date, "last") == 4) + if (match_string(date, "last") == 4) { *num = 1; + *touched = 1; + } return end; } @@ -835,6 +846,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm if (match_string(date, tl->type) >= len-1) { update_tm(tm, now, tl->length * *num); *num = 0; + *touched = 1; return end; } tl++; @@ -852,6 +864,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm diff += 7*n; update_tm(tm, now, diff * 24 * 60 * 60); + *touched = 1; return end; } } @@ -866,6 +879,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm tm->tm_year--; } tm->tm_mon = n; + *touched = 1; return end; } @@ -873,6 +887,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm update_tm(tm, now, 0); /* fill in date fields if needed */ tm->tm_year -= *num; *num = 0; + *touched = 1; return end; } @@ -929,9 +944,12 @@ static void pending_number(struct tm *tm, int *num) } } -static unsigned long approxidate_str(const char *date, const struct timeval *tv) +static unsigned long approxidate_str(const char *date, + const struct timeval *tv, + int *error_ret) { int number = 0; + int touched = 0; struct tm tm, now; time_t time_sec; @@ -951,33 +969,42 @@ static unsigned long approxidate_str(const char *date, const struct timeval *tv) if (isdigit(c)) { pending_number(&tm, &number); date = approxidate_digit(date-1, &tm, &number); + touched = 1; continue; } if (isalpha(c)) - date = approxidate_alpha(date-1, &tm, &now, &number); + date = approxidate_alpha(date-1, &tm, &now, &number, &touched); } pending_number(&tm, &number); + if (!touched) + *error_ret = 1; return update_tm(&tm, &now, 0); } unsigned long approxidate_relative(const char *date, const struct timeval *tv) { char buffer[50]; + int errors = 0; if (parse_date(date, buffer, sizeof(buffer)) > 0) return strtoul(buffer, NULL, 0); - return approxidate_str(date, tv); + return approxidate_str(date, tv, &errors); } -unsigned long approxidate(const char *date) +unsigned long approxidate_careful(const char *date, int *error_ret) { struct timeval tv; char buffer[50]; + int dummy = 0; + if (!error_ret) + error_ret = &dummy; - if (parse_date(date, buffer, sizeof(buffer)) > 0) + if (parse_date(date, buffer, sizeof(buffer)) > 0) { + *error_ret = 0; return strtoul(buffer, NULL, 0); + } gettimeofday(&tv, NULL); - return approxidate_str(date, &tv); + return approxidate_str(date, &tv, error_ret); } diff --git a/sha1_name.c b/sha1_name.c index 44bb62d..f4a74fe 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -395,8 +395,11 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) } else if (0 <= nth) at_time = 0; else { + int errors = 0; char *tmp = xstrndup(str + at + 2, reflog_len); - at_time = approxidate(tmp); + at_time = approxidate_careful(tmp, &errors); + if (errors) + die("Bogus timestamp '%s'", tmp); free(tmp); } if (read_ref_at(real_ref, at_time, nth, sha1, NULL, diff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh new file mode 100755 index 0000000..ccabc37 --- /dev/null +++ b/t/t0101-at-syntax.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +test_description='various @{whatever} syntax tests' +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit one && + test_commit two +' + +check_at() { + echo "$2" >expect && + git log -1 --format=%s "$1" >actual && + test_cmp expect actual +} + +test_expect_success '@{0} shows current' ' + check_at @{0} two +' + +test_expect_success '@{1} shows old' ' + check_at @{1} one +' + +test_expect_success '@{now} shows current' ' + check_at @{now} two +' + +test_expect_success '@{30.years.ago} shows old' ' + check_at @{30.years.ago} one +' + +test_expect_success 'silly approxidates work' ' + check_at @{3.hot.dogs.and.30.years.ago} one +' + +test_expect_success 'notice misspelled upstream' ' + test_must_fail git log -1 --format=%s @{usptream} +' + +test_expect_success 'complain about total nonsense' ' + test_must_fail git log -1 --format=%s @{utter.bogosity} +' + +test_done -- cgit v0.10.2-6-g49f6 From a5e10acbb946305ca42f510cdb196d171020238e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 27 Jan 2010 10:53:09 -0800 Subject: Update @{bogus.timestamp} fix not to die() The caller will say "It is not a valid object name" if it wants to, and some callers may even try to see if it names an object and otherwise try to see if it is a path. Pointed out by Jeff King. Signed-off-by: Junio C Hamano diff --git a/sha1_name.c b/sha1_name.c index f4a74fe..04fb3b8 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -398,9 +398,9 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) int errors = 0; char *tmp = xstrndup(str + at + 2, reflog_len); at_time = approxidate_careful(tmp, &errors); - if (errors) - die("Bogus timestamp '%s'", tmp); free(tmp); + if (errors) + return -1; } if (read_ref_at(real_ref, at_time, nth, sha1, NULL, &co_time, &co_tz, &co_cnt)) { -- cgit v0.10.2-6-g49f6 From 6c647af3060b8dd20da0e0b21dcd0eb95ec70841 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 27 Jan 2010 10:54:22 -0800 Subject: t0101: use a fixed timestamp when searching in the reflog That will give us a better reproducibility during tests. Signed-off-by: Junio C Hamano diff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh index ccabc37..5e298c5 100755 --- a/t/t0101-at-syntax.sh +++ b/t/t0101-at-syntax.sh @@ -26,8 +26,8 @@ test_expect_success '@{now} shows current' ' check_at @{now} two ' -test_expect_success '@{30.years.ago} shows old' ' - check_at @{30.years.ago} one +test_expect_success '@{2001-09-17} (before the first commit) shows old' ' + check_at @{2001-09-17} one ' test_expect_success 'silly approxidates work' ' -- cgit v0.10.2-6-g49f6