Volume XXII, number 279Tuesday, October 6, 2026Latest message 19 minutes ago

The Git List

News and archive of git@vger.kernel.org, since April 2005

patchcommit: warn when a new commit is dated before its parent

1 messages between Oct 5, 2026 and Oct 5, 2026, from Devi Srinivas Vasamsetti via GitGitGadget.

Plain Markdown or JSON for tools and agents. Diffs are folded; open one to read it.

Devi Srinivas Vasamsetti via GitGitGadgetOct 5, 2026, 15:09 UTC on lore
From: Devi Srinivas Vasamsetti <devisrinivas.vasamsetti@gmail.com>

Git writes whatever the clock says into the commit object. This can be problematic because history traversal assumes commit dates are non-decreasing. For example, "git log --since" stops walking at the first commit older than the cutoff, so an out-of-order date hides the commits behind it.

Add a check gated by a new advice.clockSkew setting that warns when the new commit's date precedes a parent's date. This allows users to catch a wrong clock at commit time when it is still easy to fix.

This check only looks at the new commit being created and its parents. Dates in replayed history or merges are not warned about.

Signed-off-by: Devi Srinivas Vasamsetti <devisrinivas.vasamsetti@gmail.com>
---
    commit: warn when a new commit is dated before its parent
    
    What does this PR do? This PR adds a helpful warning if you try to make
    a new commit with a date that is older than its parent commit.
    
    Why is this needed? Git just uses whatever time your computer's clock
    says when creating a commit. If your system clock is incorrect, you
    might accidentally create a commit dated in the past.
    
    This causes problems because commands like git log --since assume that
    commit dates always move forward. If a date goes backward in history,
    those commands can get confused and accidentally hide commits from the
    log.
    
    What is fixed? We added a new configuration setting called
    advice.clockSkew. Now, Git will warn you right away if your computer's
    clock seems to be wrong when you are making a new commit. This gives you
    a chance to fix your computer's time before you accidentally push
    out-of-order dates!
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2235%2Fsrinivas1591%2Ffeature%2Fwarn-older-commits-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2235/srinivas1591/feature/warn-older-commits-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/2235
 Documentation/config/advice.adoc |  7 +++++
 advice.c                         |  1 +
 advice.h                         |  1 +
 builtin/commit.c                 | 46 ++++++++++++++++++++++++++++++++
 t/t7502-commit-porcelain.sh      | 27 +++++++++++++++++++
 5 files changed, 82 insertions(+)
Show changes to 5 files +82 −0

Documentation/config/advice.adoc, advice.c, advice.h, builtin/commit.c, t/t7502-commit-porcelain.sh

diff --git a/Documentation/config/advice.adoc b/Documentation/config/advice.adoc
index 81f80a9274..59b643d9d0 100644
--- a/Documentation/config/advice.adoc
+++ b/Documentation/config/advice.adoc
@@ -38,6 +38,13 @@ all advice messages.
 		configuration variable for how to set a given remote
 		to be used by default in some situations where this
 		advice would be printed.
+	clockSkew::
+		Shown by linkgit:git-commit[1] when the commit being
+		created is dated earlier than one of its parents, which
+		usually means the system clock is wrong. History
+		traversal assumes commit dates do not decrease, so such
+		a commit can cause commands like `git log --since` to
+		skip the commits behind it.
 	commitBeforeMerge::
 		Shown when linkgit:git-merge[1] refuses to
 		merge to avoid overwriting local changes.
diff --git a/advice.c b/advice.c
index 63bf8b0c5f..3e14859de4 100644
--- a/advice.c
+++ b/advice.c
@@ -50,6 +50,7 @@ static struct {
 	[ADVICE_AMBIGUOUS_FETCH_REFSPEC]		= { "ambiguousFetchRefspec" },
 	[ADVICE_AM_WORK_DIR] 				= { "amWorkDir" },
 	[ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME] 	= { "checkoutAmbiguousRemoteBranchName" },
+	[ADVICE_CLOCK_SKEW]				= { "clockSkew" },
 	[ADVICE_COMMIT_BEFORE_MERGE]			= { "commitBeforeMerge" },
 	[ADVICE_DEFAULT_BRANCH_NAME]			= { "defaultBranchName" },
 	[ADVICE_DETACHED_HEAD]				= { "detachedHead" },
diff --git a/advice.h b/advice.h
index 66f6cd6a77..43de2b19a2 100644
--- a/advice.h
+++ b/advice.h
@@ -17,6 +17,7 @@ enum advice_type {
 	ADVICE_AMBIGUOUS_FETCH_REFSPEC,
 	ADVICE_AM_WORK_DIR,
 	ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME,
+	ADVICE_CLOCK_SKEW,
 	ADVICE_COMMIT_BEFORE_MERGE,
 	ADVICE_DEFAULT_BRANCH_NAME, /* To be retired sometime after Git 3.0 */
 	ADVICE_DETACHED_HEAD,
diff --git a/builtin/commit.c b/builtin/commit.c
index 205fbd57e3..78d1dde27e 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -11,6 +11,7 @@
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
+#include "date.h"
 #include "lockfile.h"
 #include "cache-tree.h"
 #include "color.h"
@@ -21,6 +22,7 @@
 #include "commit.h"
 #include "add-interactive.h"
 #include "gettext.h"
+#include "ident.h"
 #include "revision.h"
 #include "wt-status.h"
 #include "run-command.h"
@@ -1693,6 +1695,48 @@ struct repository *repo UNUSED)
 	return 0;
 }
 
+static void warn_if_dated_before_parents(struct commit_list *parents)
+{
+	struct ident_split committer;
+	struct strbuf ours = STRBUF_INIT;
+	const char *info;
+	timestamp_t date, newest = 0;
+
+	if (!advice_enabled(ADVICE_CLOCK_SKEW))
+		return;
+
+	info = git_committer_info(IDENT_STRICT);
+	if (split_ident_line(&committer, info, strlen(info)) ||
+	    !committer.date_begin)
+		return;
+	date = parse_timestamp(committer.date_begin, NULL, 10);
+
+	for (; parents; parents = parents->next) {
+		struct commit *parent = parents->item;
+
+		if (repo_parse_commit(the_repository, parent))
+			continue;
+		if (parent->date > newest)
+			newest = parent->date;
+	}
+
+	if (!newest || date >= newest)
+		return;
+
+	strbuf_addstr(&ours, show_date(date, atoi(committer.date_end + 1),
+				       DATE_MODE(ISO8601)));
+
+	advise_if_enabled(ADVICE_CLOCK_SKEW,
+			  _("the new commit is dated %s,\n"
+			    "which is earlier than its parent, dated %s.\n"
+			    "This usually means the system clock is wrong.\n"
+			    "Commands that walk history in date order, such as\n"
+			    "\"git log --since\", may skip commits as a result."),
+			  ours.buf,
+			  show_date(newest, 0, DATE_MODE(ISO8601)));
+	strbuf_release(&ours);
+}
+
 static int git_commit_config(const char *k, const char *v,
 			     const struct config_context *ctx, void *cb)
 {
@@ -1962,6 +2006,8 @@ int cmd_commit(int argc,
 		append_merge_tag_headers(parents, &tail);
 	}
 
+	warn_if_dated_before_parents(parents);
+
 	if (commit_tree_extended(sb.buf, sb.len, &the_repository->index->cache_tree->oid,
 				 parents, &oid, author_ident.buf, NULL,
 				 sign_commit, extra)) {
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 2adfe70b3d..fb611b191b 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -1003,4 +1003,31 @@ test_expect_success WITH_BREAKING_CHANGES 'core.commentChar=auto is rejected' '
 	test_cmp expect actual
 '
 
+test_expect_success 'warn when a commit is dated before its parent' '
+	test_when_finished "git checkout main 2>/dev/null || git checkout master" &&
+	git checkout -b clock-skew &&
+	test_commit --date "2026-09-25T10:00:00+0000" skew-parent &&
+	echo skew >skew-child &&
+	git add skew-child &&
+	GIT_COMMITTER_DATE="2026-09-13T06:00:00+0000" \
+		git commit -m "behind its parent" 2>actual &&
+	test_grep "earlier than its parent" actual
+'
+
+test_expect_success 'no warning when commit dates increase' '
+	echo forward >skew-forward &&
+	git add skew-forward &&
+	GIT_COMMITTER_DATE="2026-09-26T06:00:00+0000" \
+		git commit -m "after its parent" 2>actual &&
+	test_grep ! "earlier than its parent" actual
+'
+
+test_expect_success 'advice.clockSkew silences the warning' '
+	echo quiet >skew-quiet &&
+	git add skew-quiet &&
+	GIT_COMMITTER_DATE="2026-09-14T06:00:00+0000" \
+		git -c advice.clockSkew=false commit -m quiet 2>actual &&
+	test_grep ! "earlier than its parent" actual
+'
+
 test_done

base-commit: 3bc0341126508f78f5869cbfc0005e987efdf0c7
-- 
gitgitgadget

Back to recent threads