|
11 | 11 | #include "builtin.h" |
12 | 12 | #include "advice.h" |
13 | 13 | #include "config.h" |
| 14 | +#include "date.h" |
14 | 15 | #include "lockfile.h" |
15 | 16 | #include "cache-tree.h" |
16 | 17 | #include "color.h" |
|
21 | 22 | #include "commit.h" |
22 | 23 | #include "add-interactive.h" |
23 | 24 | #include "gettext.h" |
| 25 | +#include "ident.h" |
24 | 26 | #include "revision.h" |
25 | 27 | #include "wt-status.h" |
26 | 28 | #include "run-command.h" |
@@ -1666,6 +1668,63 @@ struct repository *repo UNUSED) |
1666 | 1668 | return 0; |
1667 | 1669 | } |
1668 | 1670 |
|
| 1671 | +/* |
| 1672 | + * Warn when the commit we are about to write is dated earlier than a parent. |
| 1673 | + * |
| 1674 | + * Git stores whatever the clock says, and history traversal assumes commit |
| 1675 | + * dates do not decrease: "git log --since", for one, stops walking at the |
| 1676 | + * first commit older than the cutoff, so an out-of-order date silently hides |
| 1677 | + * the commits behind it. Only the person committing can tell whether their |
| 1678 | + * clock or the parent's is the wrong one, so warn rather than refuse. |
| 1679 | + * |
| 1680 | + * This deliberately looks at nothing but the commit being created and its |
| 1681 | + * parents. Skew between machines is normal in a distributed system and is not |
| 1682 | + * something to complain about at commit time. |
| 1683 | + */ |
| 1684 | +static void warn_if_dated_before_parents(struct commit_list *parents) |
| 1685 | +{ |
| 1686 | + struct ident_split committer; |
| 1687 | + struct strbuf ours = STRBUF_INIT; |
| 1688 | + const char *info; |
| 1689 | + timestamp_t date, newest = 0; |
| 1690 | + |
| 1691 | + if (!advice_enabled(ADVICE_CLOCK_SKEW)) |
| 1692 | + return; |
| 1693 | + |
| 1694 | + info = git_committer_info(IDENT_STRICT); |
| 1695 | + if (split_ident_line(&committer, info, strlen(info)) || |
| 1696 | + !committer.date_begin) |
| 1697 | + return; |
| 1698 | + date = parse_timestamp(committer.date_begin, NULL, 10); |
| 1699 | + |
| 1700 | + for (; parents; parents = parents->next) { |
| 1701 | + struct commit *parent = parents->item; |
| 1702 | + |
| 1703 | + if (repo_parse_commit(the_repository, parent)) |
| 1704 | + continue; |
| 1705 | + if (parent->date > newest) |
| 1706 | + newest = parent->date; |
| 1707 | + } |
| 1708 | + |
| 1709 | + if (!newest || date >= newest) |
| 1710 | + return; |
| 1711 | + |
| 1712 | + /* show_date() reuses one buffer, so keep a copy of the first result. */ |
| 1713 | + strbuf_addstr(&ours, show_date(date, atoi(committer.date_end + 1), |
| 1714 | + DATE_MODE(ISO8601))); |
| 1715 | + |
| 1716 | + advise_if_enabled(ADVICE_CLOCK_SKEW, |
| 1717 | + _("the new commit is dated %s,\n" |
| 1718 | + "which is earlier than its parent, dated %s.\n" |
| 1719 | + "This usually means the system clock is wrong.\n" |
| 1720 | + "Commands that walk history in date order, such as\n" |
| 1721 | + "\"git log --since\", may skip commits as a result."), |
| 1722 | + ours.buf, |
| 1723 | + /* A parsed commit keeps no timezone, so show UTC. */ |
| 1724 | + show_date(newest, 0, DATE_MODE(ISO8601))); |
| 1725 | + strbuf_release(&ours); |
| 1726 | +} |
| 1727 | + |
1669 | 1728 | static int git_commit_config(const char *k, const char *v, |
1670 | 1729 | const struct config_context *ctx, void *cb) |
1671 | 1730 | { |
@@ -1935,6 +1994,8 @@ int cmd_commit(int argc, |
1935 | 1994 | append_merge_tag_headers(parents, &tail); |
1936 | 1995 | } |
1937 | 1996 |
|
| 1997 | + warn_if_dated_before_parents(parents); |
| 1998 | + |
1938 | 1999 | if (commit_tree_extended(sb.buf, sb.len, &the_repository->index->cache_tree->oid, |
1939 | 2000 | parents, &oid, author_ident.buf, NULL, |
1940 | 2001 | sign_commit, extra)) { |
|
0 commit comments