* [PATCH] commit: warn when a new commit is dated before its parent
@ 2026-10-05 15:09 Devi Srinivas Vasamsetti via GitGitGadget
2026-10-06 14:37 ` Junio C Hamano
0 siblings, 1 reply; 2+ messages in thread
From: Devi Srinivas Vasamsetti via GitGitGadget @ 2026-10-05 15:09 UTC (permalink / raw)
To: git; +Cc: Devi Srinivas Vasamsetti, Devi Srinivas Vasamsetti
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(+)
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
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH] commit: warn when a new commit is dated before its parent
2026-10-05 15:09 [PATCH] commit: warn when a new commit is dated before its parent Devi Srinivas Vasamsetti via GitGitGadget
@ 2026-10-06 14:37 ` Junio C Hamano
0 siblings, 0 replies; 2+ messages in thread
From: Junio C Hamano @ 2026-10-06 14:37 UTC (permalink / raw)
To: Devi Srinivas Vasamsetti via GitGitGadget; +Cc: git, Devi Srinivas Vasamsetti
"Devi Srinivas Vasamsetti via GitGitGadget" <gitgitgadget@gmail.com>
writes:
> 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.
It might be annoying, but the value of such a warning is unclear.
If you clone from an upstream repository, you might find that the
commit at the tip was made on a machine with a clock set far in the
future. When you try to make a commit on top of it, what are you
supposed to do? Wait for a year so your commit is newer than the
tip? Ask the committer to correct their clock, redo the commit, and
force-push?
It also does not help if the commit at the tip of the cloned branch
has a timestamp in the past, but is a child of a commit with an
incorrect timestamp. Nobody would receive a warning, yet --since
may still stop prematurely.
Stepping back a bit, suppose you clone from upstream and obtain a
HEAD dated 24 hours ago, HEAD~1 dated 72 hours ago, and HEAD~2 dated
48 hours ago. A command like git log --since=50.hours may stop
without showing HEAD~2, but if you suspect that some clocks are
skewed, there is no way to determine the correct output from these
timestamps anyway. It is possible that HEAD~1 has an incorrect
timestamp and was actually written 30 hours ago, meaning all three
commits should be shown. Alternatively, HEAD~2 might have an
incorrect timestamp and was written 80 hours ago, in which case
showing neither HEAD~1 nor HEAD~2 is correct.
The moral of the story is that --since or any other time-based
option cannot be fully reliable, as you cannot force everyone to
run with a correctly synchronized clock. If you truly need to
know the ancestral relationship between commits, you should avoid
these options and use topology-based ones instead.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-06 14:37 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-05 15:09 [PATCH] commit: warn when a new commit is dated before its parent Devi Srinivas Vasamsetti via GitGitGadget
2026-10-06 14:37 ` Junio C Hamano
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox