From 4701026352ada1ebc36532272ca1e12897b1de11 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 1 May 2014 21:06:57 -0400 Subject: [PATCH 1/4] commit: use split_ident_line to compare author/committer Instead of string-wise comparing the author/committer lines with their timestamps truncated, we can use split_ident_line and ident_cmp. These functions are more robust than our ad-hoc parsing, though in practice it should not matter, as we just generated these ident lines ourselves. However, this will also allow us easy access to the timestamp and tz fields in future patches. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/commit.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/builtin/commit.c b/builtin/commit.c index 9cfef6c6cc..728cc9bbfc 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -585,13 +585,11 @@ static void determine_author_info(struct strbuf *author_ident) } } -static char *cut_ident_timestamp_part(char *string) +static void split_ident_or_die(struct ident_split *id, const struct strbuf *buf) { - char *ket = strrchr(string, '>'); - if (!ket || ket[1] != ' ') - die(_("Malformed ident string: '%s'"), string); - *++ket = '\0'; - return ket; + if (split_ident_line(id, buf->buf, buf->len) || + !sane_ident_split(id)) + die(_("Malformed ident string: '%s'"), buf->buf); } static int prepare_to_commit(const char *index_file, const char *prefix, @@ -755,7 +753,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (use_editor && include_status) { int ident_shown = 0; int saved_color_setting; - char *ai_tmp, *ci_tmp; + struct ident_split ci, ai; + if (whence != FROM_COMMIT) { if (cleanup_mode == CLEANUP_SCISSORS) wt_status_add_cut_line(s->fp); @@ -795,21 +794,24 @@ static int prepare_to_commit(const char *index_file, const char *prefix, status_printf_ln(s, GIT_COLOR_NORMAL, "%s", only_include_assumed); - ai_tmp = cut_ident_timestamp_part(author_ident->buf); - ci_tmp = cut_ident_timestamp_part(committer_ident.buf); - if (strcmp(author_ident->buf, committer_ident.buf)) + split_ident_or_die(&ai, author_ident); + split_ident_or_die(&ci, &committer_ident); + + if (ident_cmp(&ai, &ci)) status_printf_ln(s, GIT_COLOR_NORMAL, _("%s" - "Author: %s"), + "Author: %.*s <%.*s>"), ident_shown++ ? "" : "\n", - author_ident->buf); + (int)(ai.name_end - ai.name_begin), ai.name_begin, + (int)(ai.mail_end - ai.mail_begin), ai.mail_begin); if (!committer_ident_sufficiently_given()) status_printf_ln(s, GIT_COLOR_NORMAL, _("%s" - "Committer: %s"), + "Committer: %.*s <%.*s>"), ident_shown++ ? "" : "\n", - committer_ident.buf); + (int)(ci.name_end - ci.name_begin), ci.name_begin, + (int)(ci.mail_end - ci.mail_begin), ci.mail_begin); if (ident_shown) status_printf_ln(s, GIT_COLOR_NORMAL, ""); @@ -818,9 +820,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix, s->use_color = 0; commitable = run_status(s->fp, index_file, prefix, 1, s); s->use_color = saved_color_setting; - - *ai_tmp = ' '; - *ci_tmp = ' '; } else { unsigned char sha1[20]; const char *parent = "HEAD"; From d1053246554d176173893a5283dc0c0fb563ed03 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 1 May 2014 21:07:22 -0400 Subject: [PATCH 2/4] pretty: make show_ident_date public We use this function internally to format "Date" lines in commit logs, but other parts of the code will want it, too. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- cache.h | 7 +++++++ pretty.c | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/cache.h b/cache.h index 107ac61b68..dd9e689daf 100644 --- a/cache.h +++ b/cache.h @@ -1045,6 +1045,13 @@ struct ident_split { */ extern int split_ident_line(struct ident_split *, const char *, int); +/* + * Like show_date, but pull the timestamp and tz parameters from + * the ident_split. It will also sanity-check the values and produce + * a well-known sentinel date if they appear bogus. + */ +const char *show_ident_date(const struct ident_split *id, enum date_mode mode); + /* * Compare split idents for equality or strict ordering. Note that we * compare only the ident part of the line, ignoring any timestamp. diff --git a/pretty.c b/pretty.c index 3c43db558a..e1e2cad36d 100644 --- a/pretty.c +++ b/pretty.c @@ -393,8 +393,8 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len, strbuf_addstr(sb, "?="); } -static const char *show_ident_date(const struct ident_split *ident, - enum date_mode mode) +const char *show_ident_date(const struct ident_split *ident, + enum date_mode mode) { unsigned long date = 0; long tz = 0; From b7242b8c9e4b3c57a07c2a76d0337389605aadcc Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 1 May 2014 21:10:01 -0400 Subject: [PATCH 3/4] commit: print "Date" line when the user has set date When we make a commit and the author is not the same as the committer (e.g., because you used "-c $commit" or "--author=$somebody"), we print the author's name and email in both the commit-message template and as part of the commit summary. This is a safety check to give the user a chance to confirm that we are doing what they expect. This patch brings the same safety for the "date" field, which may be set by "-c" or by using "--date". Note that we explicitly do not set it for $GIT_AUTHOR_DATE, as it is probably not of interest when "git commit" is being fed its parameters by a script. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/commit.c | 19 +++++++++++++++++++ t/t3508-cherry-pick-many-commits.sh | 6 ++++++ t/t7501-commit.sh | 5 +++++ t/t7502-commit.sh | 7 +++++++ 4 files changed, 37 insertions(+) diff --git a/builtin/commit.c b/builtin/commit.c index 728cc9bbfc..a25661f343 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -592,6 +592,11 @@ static void split_ident_or_die(struct ident_split *id, const struct strbuf *buf) die(_("Malformed ident string: '%s'"), buf->buf); } +static int author_date_is_interesting(void) +{ + return author_message || force_date; +} + static int prepare_to_commit(const char *index_file, const char *prefix, struct commit *current_head, struct wt_status *s, @@ -805,6 +810,13 @@ static int prepare_to_commit(const char *index_file, const char *prefix, (int)(ai.name_end - ai.name_begin), ai.name_begin, (int)(ai.mail_end - ai.mail_begin), ai.mail_begin); + if (author_date_is_interesting()) + status_printf_ln(s, GIT_COLOR_NORMAL, + _("%s" + "Date: %s"), + ident_shown++ ? "" : "\n", + show_ident_date(&ai, DATE_NORMAL)); + if (!committer_ident_sufficiently_given()) status_printf_ln(s, GIT_COLOR_NORMAL, _("%s" @@ -1355,6 +1367,13 @@ static void print_summary(const char *prefix, const unsigned char *sha1, strbuf_addstr(&format, "\n Author: "); strbuf_addbuf_percentquote(&format, &author_ident); } + if (author_date_is_interesting()) { + struct strbuf date = STRBUF_INIT; + format_commit_message(commit, "%ad", &date, &pctx); + strbuf_addstr(&format, "\n Date: "); + strbuf_addbuf_percentquote(&format, &date); + strbuf_release(&date); + } if (!committer_ident_sufficiently_given()) { strbuf_addstr(&format, "\n Committer: "); strbuf_addbuf_percentquote(&format, &committer_ident); diff --git a/t/t3508-cherry-pick-many-commits.sh b/t/t3508-cherry-pick-many-commits.sh index 19c99d7ef1..b457333e18 100755 --- a/t/t3508-cherry-pick-many-commits.sh +++ b/t/t3508-cherry-pick-many-commits.sh @@ -65,12 +65,15 @@ test_expect_success 'output to keep user entertained during multi-pick' ' cat <<-\EOF >expected && [master OBJID] second Author: A U Thor + Date: Thu Apr 7 15:14:13 2005 -0700 1 file changed, 1 insertion(+) [master OBJID] third Author: A U Thor + Date: Thu Apr 7 15:15:13 2005 -0700 1 file changed, 1 insertion(+) [master OBJID] fourth Author: A U Thor + Date: Thu Apr 7 15:16:13 2005 -0700 1 file changed, 1 insertion(+) EOF @@ -98,14 +101,17 @@ test_expect_success 'output during multi-pick indicates merge strategy' ' Trying simple merge. [master OBJID] second Author: A U Thor + Date: Thu Apr 7 15:14:13 2005 -0700 1 file changed, 1 insertion(+) Trying simple merge. [master OBJID] third Author: A U Thor + Date: Thu Apr 7 15:15:13 2005 -0700 1 file changed, 1 insertion(+) Trying simple merge. [master OBJID] fourth Author: A U Thor + Date: Thu Apr 7 15:16:13 2005 -0700 1 file changed, 1 insertion(+) EOF diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index d58b097ff3..5a76823d4c 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -346,6 +346,11 @@ test_expect_success 'amend commit to fix date' ' ' +test_expect_success 'commit mentions forced date in output' ' + git commit --amend --date=2010-01-02T03:04:05 >output && + grep "Date: *Sat Jan 2 03:04:05 2010" output +' + test_expect_success 'commit complains about bogus date' ' test_must_fail git commit --amend --date=10.11.2010 ' diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh index 9a3f3a1b41..6465cd59af 100755 --- a/t/t7502-commit.sh +++ b/t/t7502-commit.sh @@ -344,6 +344,13 @@ test_expect_success 'message shows author when it is not equal to committer' ' .git/COMMIT_EDITMSG ' +test_expect_success 'message shows date when it is explicitly set' ' + git commit --allow-empty -e -m foo --date="2010-01-02T03:04:05" && + test_i18ngrep \ + "^# Date: *Sat Jan 2 03:04:05 2010 +0000" \ + .git/COMMIT_EDITMSG +' + test_expect_success AUTOIDENT 'message shows committer when it is automatic' ' echo >>negative && From 14ac2864dcd566a025e449ab9db6b5dc57744a32 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 1 May 2014 21:12:42 -0400 Subject: [PATCH 4/4] commit: accept more date formats for "--date" Right now we pass off the string found by "--date" straight to the fmt_ident function, which will use our strict parse_date to normalize it. However, this means obvious things like "--date=now" or "--date=2.days.ago" will not work. Instead, let's fallback to the approxidate function to handle this for us. Note that we must try parse_date ourselves first, even though approxidate will try strict parsing itself. The reason is that approxidate throws away any timezone information it sees from the strict parsing, and we want to preserve it. So asking for: git commit --date="@1234567890 -0700" continues to set the date in -0700, regardless of what the local timezone is. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/commit.c | 27 +++++++++++++++++++++++++-- t/t7501-commit.sh | 12 ++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/builtin/commit.c b/builtin/commit.c index a25661f343..d1c90db95d 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -526,10 +526,29 @@ static int sane_ident_split(struct ident_split *person) return 1; } +static int parse_force_date(const char *in, char *out, int len) +{ + if (len < 1) + return -1; + *out++ = '@'; + len--; + + if (parse_date(in, out, len) < 0) { + int errors = 0; + unsigned long t = approxidate_careful(in, &errors); + if (errors) + return -1; + snprintf(out, len, "%lu", t); + } + + return 0; +} + static void determine_author_info(struct strbuf *author_ident) { char *name, *email, *date; struct ident_split author; + char date_buf[64]; name = getenv("GIT_AUTHOR_NAME"); email = getenv("GIT_AUTHOR_EMAIL"); @@ -574,8 +593,12 @@ static void determine_author_info(struct strbuf *author_ident) email = xstrndup(lb + 2, rb - (lb + 2)); } - if (force_date) - date = force_date; + if (force_date) { + if (parse_force_date(force_date, date_buf, sizeof(date_buf))) + die(_("invalid date format: %s"), force_date); + date = date_buf; + } + strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT)); if (!split_ident_line(&author, author_ident->buf, author_ident->len) && sane_ident_split(&author)) { diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index 5a76823d4c..63e04277f9 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -351,8 +351,16 @@ test_expect_success 'commit mentions forced date in output' ' grep "Date: *Sat Jan 2 03:04:05 2010" output ' -test_expect_success 'commit complains about bogus date' ' - test_must_fail git commit --amend --date=10.11.2010 +test_expect_success 'commit complains about completely bogus dates' ' + test_must_fail git commit --amend --date=seventeen +' + +test_expect_success 'commit --date allows approxidate' ' + git commit --amend \ + --date="midnight the 12th of october, anno domini 1979" && + echo "Fri Oct 12 00:00:00 1979 +0000" >expect && + git log -1 --format=%ad >actual && + test_cmp expect actual ' test_expect_success 'sign off (1)' '