How to use git log --since to produce reliable results

Viewed 278

I need to run git log to show all commits after a specific date and time, and not leave out any commits, and without running git log to show all commits, as the latter is too slow (because this should work within git IMO).

However, before you mark this as a duplicate, consider I've read, as carefully as I can, through all of the following:

  1. What is the format for --date parameter of git commit
  2. git log --since=<date> options
  3. Strange behavior from git log --since
  4. How does git log --since count?

This is what I tried:

$ git --version
git version 2.31.1
$ date
Tue May 18 08:55:45 PDT 2021
$ git log --date=iso-local --format='%ad %<(20)%ar %H' --since='2021-04-01' | grep 2021-04- | tail -1
$ git log --date=iso-local --format='%ad %<(20)%ar %H' --since='2021-04-01 00:00' | grep 2021-04- | tail -1
$ git log --date=iso-local --format='%ad %<(20)%ar %H' --since='2021-04-01 00:00:00' | grep 2021-04- | tail -1
$ git log --date=iso-local --format='%ad %<(20)%ar %H' --since='2021-04-01T00:00:00' | grep 2021-04- | tail -1
$ git log --date=iso-local --format='%ad %<(20)%ar %H' --since='1 week ago' | grep 2021-04- | tail -1
$ git log --date=iso-local --format='%ad %<(20)%ar %H' --since='2 weeks ago' | grep 2021-04- | tail -1
$ git log --date=iso-local --format='%ad %<(20)%ar %H' --since='3 weeks ago' | grep 2021-04- | tail -1
$ git log --date=iso-local --format='%ad %<(20)%ar %H' --since='4 weeks ago' | grep 2021-04- | tail -1
$ git log --date=iso-local --format='%ad %<(20)%ar %H' --since='666 weeks ago' | grep 2021-04- | tail -1
2021-04-23 11:51:30 -0700 4 weeks ago          76044d3f08087add08ad1012ae1ee9a569679268
$ git log --date=local --format='%ad %<(20)%ar %H' --since='2021-04-01' | grep 2021-04- | tail -1
$ git log --date=local --format='%ad %<(20)%ar %H' --since='2021-04-01 00:00' | grep 2021-04- | tail -1
$ git log --date=local --format='%ad %<(20)%ar %H' --since='2021-04-01 00:00:00' | grep 2021-04- | tail -1
$ git log --date=local --format='%ad %<(20)%ar %H' --since='2021-04-01T00:00:00' | grep 2021-04- | tail -1
$ git log --date=local --format='%ad %<(20)%ar %H' --since='1 week ago' | grep 2021-04- | tail -1
$ git log --date=local --format='%ad %<(20)%ar %H' --since='2 weeks ago' | grep 2021-04- | tail -1
$ git log --date=local --format='%ad %<(20)%ar %H' --since='3 weeks ago' | grep 2021-04- | tail -1
$ git log --date=local --format='%ad %<(20)%ar %H' --since='4 weeks ago' | grep 2021-04- | tail -1
$ git log --date=local --format='%ad %<(20)%ar %H' --since='666 weeks ago' | grep 2021-04- | tail -1
$ git log --date=local --format='%ad %<(20)%ar %H' --since='666 weeks ago' | grep 2021-04- | tail -1
$ git log --date=default --format='%ad %<(20)%ar %H' --since='2021-04-01' | grep 2021-04- | tail -1
$ git log --date=default --format='%ad %<(20)%ar %H' --since='2021-04-01 00:00' | grep 2021-04- | tail -1
$ git log --date=default --format='%ad %<(20)%ar %H' --since='2021-04-01 00:00:00' | grep 2021-04- | tail -1
$ git log --date=default --format='%ad %<(20)%ar %H' --since='2021-04-01T00:00:00' | grep 2021-04- | tail -1
$ git log --date=default --format='%ad %<(20)%ar %H' --since='1 week ago' | grep 2021-04- | tail -1
$ git log --date=default --format='%ad %<(20)%ar %H' --since='2 weeks ago' | grep 2021-04- | tail -1
$ git log --date=default --format='%ad %<(20)%ar %H' --since='3 weeks ago' | grep 2021-04- | tail -1
$ git log --date=default --format='%ad %<(20)%ar %H' --since='4 weeks ago' | grep 2021-04- | tail -1
$ git log --date=default --format='%ad %<(20)%ar %H' --since='666 weeks ago' | grep 2021-04- | tail -1
$ git log --date=iso-local --format='%ad %<(20)%ar %H' | grep 2021-04- | tail -1
2021-04-01 00:00:13 -0700 7 weeks ago          657fa6a40cfd93c6d48a8aee4cc8190df753f6d7
$ git log --date=default --format='%ci %<(20)%ar %H' --since='2021-04-01' | grep 2021-04- | tail -1
$ git log --date=default --format='%ci %<(20)%ar %H' --since='2021-04-01 00:00' | grep 2021-04- | tail -1
$ git log --date=default --format='%ci %<(20)%ar %H' --since='2021-04-01 00:00:00' | grep 2021-04- | tail -1
$ git log --date=default --format='%ci %<(20)%ar %H' --since='2021-04-01T00:00:00' | grep 2021-04- | tail -1
$ git log --date=default --format='%ci %<(20)%ar %H' --since='1 week ago' | grep 2021-04- | tail -1
$ git log --date=default --format='%ci %<(20)%ar %H' --since='2 weeks ago' | grep 2021-04- | tail -1
$ git log --date=default --format='%ci %<(20)%ar %H' --since='3 weeks ago' | grep 2021-04- | tail -1
$ git log --date=default --format='%ci %<(20)%ar %H' --since='4 weeks ago' | grep 2021-04- | tail -1
$ git log --date=default --format='%ci %<(20)%ar %H' --since='666 weeks ago' | grep 2021-04- | tail -1
2021-04-23 18:51:30 +0000 4 weeks ago          76044d3f08087add08ad1012ae1ee9a569679268
$ git log --date=iso-local --format='%ci %<(20)%ar %H' | grep 2021-04- | tail -1
2021-04-01 00:00:13 -0700 7 weeks ago          657fa6a40cfd93c6d48a8aee4cc8190df753f6d7
$ 

I'm baffled as to why I get output about commit 76044d3f08087add08ad1012ae1ee9a569679268 at all, and the other --date=something variations do not show any output. And of course it is wrong output anyhow.

Puzzling.

Here is my slow workaround that I do not want to have to constantly resort to:

$ git log --date=iso-local --format='%ad %<(20)%ar %H' | grep 2021-04- | tail -1
2021-04-01 00:00:13 -0700 7 weeks ago          657fa6a40cfd93c6d48a8aee4cc8190df753f6d7
$

In the latter command, Git is dumping out the entire commit history, only for me to have to filter out the results I am looking for. In this case, git log is slow because my git repository is huge:

$ git log --date=iso-local --format='%ad %<(20)%ar %H' | wc -l
344841

What am I doing incorrectly here? Or is the --since option defective?

1 Answers

torek commented:

Since committer dates can be spoofed or incorrect, this can cut off a traversal incorrectly.

Not anymore, since Git 2.37 (Q3 2022):

Before 2.37, "git log --since=X"(man) was stopping traversal upon seeing a commit that is older than X.
But there may be commits behind it that is younger than X when the commit was created with a faulty clock.

A new option --since-as-filter is added to keep digging without stopping, and instead filter out commits with timestamp older than X.

See commit 9669778 (23 Apr 2022) by Miklos Vajna (vmiklos).
(Merged by Junio C Hamano -- gitster -- in commit 6f24da6, 20 May 2022)

log: "--since-as-filter" option is a non-terminating "--since" variant

Signed-off-by: Miklos Vajna

The "--since=<time>" option of "git log"(man) limits the commits displayed by the command by stopping the traversal once it sees a commit whose timestamp is older than the given time and not digging further into its parents.

This is OK in a history where a commit always has a newer timestamp than any of its parents'.
Once you see a commit older than the given <time>, all ancestor commits of it are even older than the time anyway.

It poses, however, a problem when there is a commit with a wrong timestamp that makes it appear older than its parents.
Stopping traversal at the "incorrectly old" commit will hide its ancestors that are newer than that wrong commit and are newer than the cut-off time given with the --since option.
--max-age and --after being the synonyms to --since, they share the same issue.

Add a new "--since-as-filter" option that is a variant of "--since=<time>".
Instead of stopping the traversal to hide an old enough commit and its all ancestors, exclude commits with an old timestamp from the output but still keep digging the history.

Without other traversal stopping options, this will force the command in "git log" family to dig down the history to the root.
It may be an acceptable cost for a small project with short history and many commits with screwy timestamps.

It is quite unlikely for us to add traversal stopper other than since, so have this as a --since-as-filter option, rather than a separate --as-filter, that would be probably more confusing.

rev-list-options now includes in its man page:

--since-as-filter=<date>

Show all commits more recent than a specific date.

This visits all commits in the range, rather than stopping at the first commit which is older than a specific date.

Related