{"thread":{"id":"57647","subject":"git log --since to not stop after first old commit?","startedAt":"2022-04-01T08:21:53Z","lastAt":"2022-04-23T13:00:10Z","messageCount":24,"participants":["Miklos Vajna","Ævar Arnfjörð Bjarmason","Junio C Hamano","demerphq"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"452854","messageId":"Yka2GSGs3EIXm6Xt@vmiklos.hu","threadId":"57647","inReplyTo":null,"subject":"git log --since to not stop after first old commit?","fromName":"Miklos Vajna","fromEmail":"vmiklos@vmiklos.hu","sentAt":"2022-04-01T08:21:45Z","receivedAt":"2022-04-01T08:21:53Z","isPatch":false,"sender":{"key":"vmiklos@vmiklos.hu","avatar":"https://avatars.githubusercontent.com/u/13838?v=4"},"body":"Hi,\n\nI wanted to look at commits of a contributor from the last year, and\nnoticed that I only see commits from this year, not last year when I use:\n\n        git log --author=\"that person\" --since=\"1 year ago\"\n\nDigging around in the history, one other contributor pushed a mistake on\n1st Jan, where the author date was supposed to be 2022-01-01, but\nhappened to be 2021-01-01. Knowing that, it makes sense that 'git log'\nstopped at that commit by default.\n\nI wonder though, is there any option to \"force\" git log to walk all\nreachable commits from HEAD, but just show the ones which match the\n--since criteria?\n\nOr is this need so special that the best is to parse the output of 'git\nrev-list' and do my own filtering for author and date?\n\nThanks,\n\nMiklos\n"},{"id":"452858","messageId":"220401.86pmm1nmvh.gmgdl@evledraar.gmail.com","threadId":"57647","inReplyTo":"Yka2GSGs3EIXm6Xt@vmiklos.hu","subject":"Re: git log --since to not stop after first old commit?","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-01T09:57:33Z","receivedAt":"2022-04-01T10:01:28Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Apr 01 2022, Miklos Vajna wrote:\n\n> Hi,\n>\n> I wanted to look at commits of a contributor from the last year, and\n> noticed that I only see commits from this year, not last year when I use:\n>\n>         git log --author=\"that person\" --since=\"1 year ago\"\n>\n> Digging around in the history, one other contributor pushed a mistake on\n> 1st Jan, where the author date was supposed to be 2022-01-01, but\n> happened to be 2021-01-01. Knowing that, it makes sense that 'git log'\n> stopped at that commit by default.\n\nSo (just making sure I understand this) in this case the --since option\nis behaving as expected in the sense that the information in the commit\nitself matches what it's finding, but what you'd really like for it to\nconsider some \"adjusted\" commit date?\n\nI.e. to be smart enough to spot that it should include a commit from\n2021 if all the preceding commits are from 2022, or some other similar\nheuristic?\n\nOr...\n\n> I wonder though, is there any option to \"force\" git log to walk all\n> reachable commits from HEAD, but just show the ones which match the\n> --since criteria?\n\n...did we stop the walk as soon as we saw that 2021 commit?\n\n> Or is this need so special that the best is to parse the output of 'git\n> rev-list' and do my own filtering for author and date?\n\nI think this is somewhere between \"we could grow a new feature to be\nmore helpful\" (we adjust commit dates in other places, i.e. commit-graph\nreachability), or \"a bug\" depending on the answersto the above, but I\nobviously haven't dug much. Hope this helps!\n"},{"id":"452859","messageId":"YkbQnnB8GSzuAROh@vmiklos.hu","threadId":"57647","inReplyTo":"220401.86pmm1nmvh.gmgdl@evledraar.gmail.com","subject":"Re: git log --since to not stop after first old commit?","fromName":"Miklos Vajna","fromEmail":"vmiklos@vmiklos.hu","sentAt":"2022-04-01T10:14:54Z","receivedAt":"2022-04-01T10:15:03Z","isPatch":false,"sender":{"key":"vmiklos@vmiklos.hu","avatar":"https://avatars.githubusercontent.com/u/13838?v=4"},"body":"Hi Ævar,\n\nOn Fri, Apr 01, 2022 at 11:57:33AM +0200, Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n> So (just making sure I understand this) in this case the --since option\n> is behaving as expected in the sense that the information in the commit\n> itself matches what it's finding, but what you'd really like for it to\n> consider some \"adjusted\" commit date?\n> \n> I.e. to be smart enough to spot that it should include a commit from\n> 2021 if all the preceding commits are from 2022, or some other similar\n> heuristic?\n\nNo heuristics. Just a way to not stop at the first commit that doesn't\nmatch the --since criteria. Here is an example:\n\nGiven:\n\nrm -rf .git file\ngit init\necho a > file\ngit add file\ngit commit -m init\necho a >> file\ngit add file\nGIT_COMMITTER_DATE=\"2021-01-01 0:00\" git commit -m second\necho a >> file\ngit add file\ngit commit -m third\n\nWhen I do:\n\ngit log --pretty=oneline --since=\"2022-01-01\"\n\nThen current I get:\n\n91a24b6ccba6b1d26c3bd5bcea7ff86e6997b599 (HEAD -> master) third\n\nAnd I would like to have an opt-in way to instead get:\n\n91a24b6ccba6b1d26c3bd5bcea7ff86e6997b599 (HEAD -> master) third\ne259a40784d3d70f3878105adac380c8e8a8ae52 init\n\nArguing that both \"init\" and \"third\" was committed this year.\n\nThe question is if there is a way to do this already (perhaps I missed\nsomething in the docs or didn't notice it while I briefly researched the\ncommit walk code), or in case I want to do this, then would it make\nsense to have this feature in git or this is more a \"run git rev-list\nand do your own filtering\" case?\n\nThanks,\n\nMiklos\n"},{"id":"452868","messageId":"220401.86czi0oqfl.gmgdl@evledraar.gmail.com","threadId":"57647","inReplyTo":"YkbQnnB8GSzuAROh@vmiklos.hu","subject":"Re: git log --since to not stop after first old commit?","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-01T13:51:38Z","receivedAt":"2022-04-01T13:59:16Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Apr 01 2022, Miklos Vajna wrote:\n\n> Hi Ævar,\n>\n> On Fri, Apr 01, 2022 at 11:57:33AM +0200, Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>> So (just making sure I understand this) in this case the --since option\n>> is behaving as expected in the sense that the information in the commit\n>> itself matches what it's finding, but what you'd really like for it to\n>> consider some \"adjusted\" commit date?\n>> \n>> I.e. to be smart enough to spot that it should include a commit from\n>> 2021 if all the preceding commits are from 2022, or some other similar\n>> heuristic?\n>\n> No heuristics. Just a way to not stop at the first commit that doesn't\n> match the --since criteria. Here is an example:\n>\n> Given:\n>\n> rm -rf .git file\n> git init\n> echo a > file\n> git add file\n> git commit -m init\n> echo a >> file\n> git add file\n> GIT_COMMITTER_DATE=\"2021-01-01 0:00\" git commit -m second\n> echo a >> file\n> git add file\n> git commit -m third\n>\n> When I do:\n>\n> git log --pretty=oneline --since=\"2022-01-01\"\n>\n> Then current I get:\n>\n> 91a24b6ccba6b1d26c3bd5bcea7ff86e6997b599 (HEAD -> master) third\n>\n> And I would like to have an opt-in way to instead get:\n>\n> 91a24b6ccba6b1d26c3bd5bcea7ff86e6997b599 (HEAD -> master) third\n> e259a40784d3d70f3878105adac380c8e8a8ae52 init\n>\n> Arguing that both \"init\" and \"third\" was committed this year.\n\nIndeed.\n\n> The question is if there is a way to do this already (perhaps I missed\n> something in the docs or didn't notice it while I briefly researched the\n> commit walk code), or in case I want to do this, then would it make\n> sense to have this feature in git or this is more a \"run git rev-list\n> and do your own filtering\" case?\n\nI think it might make sense to have it as feature, but hopefully we\ncould piggy-back on the date adjustment that the commit-graph needs to\ndo already, I'm not sure if we save that information anywhere though...\n\nThe assumption with --since was that this sort of timestamp drift\nwouldn't be this bad, so mostly it works out. It could be made to work\nlike --grep, but then it needs to walk the whole history if no other\nlimit is provided.\n\nSo it'll be very slow if you just want --since=2.weeks.ago, but\naccurate.\n\nI think an alternate solution to this in the meantime is to use \"git\nreplace\" as a band-aid, I haven't tried, but you should be able to\nreplace the relevant commit with one that has adjusted dates.\n"},{"id":"452913","messageId":"xmqq1qygy9nd.fsf@gitster.g","threadId":"57647","inReplyTo":"Yka2GSGs3EIXm6Xt@vmiklos.hu","subject":"Re: git log --since to not stop after first old commit?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-01T17:51:34Z","receivedAt":"2022-04-01T17:51:42Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miklos Vajna <vmiklos@vmiklos.hu> writes:\n\n> I wanted to look at commits of a contributor from the last year, and\n> noticed that I only see commits from this year, not last year when I use:\n>\n>         git log --author=\"that person\" --since=\"1 year ago\"\n>\n> Digging around in the history, one other contributor pushed a mistake on\n> 1st Jan, where the author date was supposed to be 2022-01-01, but\n> happened to be 2021-01-01. Knowing that, it makes sense that 'git log'\n> stopped at that commit by default.\n>\n> I wonder though, is there any option to \"force\" git log to walk all\n> reachable commits from HEAD, but just show the ones which match the\n> --since criteria?\n>\n> Or is this need so special that the best is to parse the output of 'git\n> rev-list' and do my own filtering for author and date?\n\nCurrently yes.  I am not sure if it is (or is not) worth changing,\nthough.\n\nMany \"git log\" options make commits hidden from the output by\nfiltering each commit we find but keep digging the history further,\nbut some options make commits hidden by stopping the traversal.\n\"--until\" is the former (there is no way to implement it as the\nlatter) but \"--since\" is the latter.\n\nIf we can implement more of these \"commit hiding operations\" as\ntraversal stoppers, it allows \"log\" to avoid doing unnecessary work,\nand in a history without skewed timestamps, \"--since\" is a prime\ncandidate to take advantage of the fact that parent must be older\nthan any of its children (hence we can safely stop traversal once we\nsee an old enough commit) to be implemented as a \"traversal stopper\".\nBut in the presense of skewed timestamps, those commits behind one\ncommit with an incorrectly old timestamp will end up being hidden.\n\nWe could add a --since-as-filter= option or something, but then the\nuser needs to be careful when to stop (and digging down to the root\nof the history, i.e. \"never stop\", may be an acceptable answer to\nsome projects).  We may be able to, when commit-graph (v2) with\nadjusted timestamp data exist, stop before going down to the root,\nbut we would still need to add it as a different option because the\nexisting behaviour of \"--since\", to immediately stop upon seeing a\ncommit with a timestamp that is older than the given time, is so\nwell established and it would irritate users of existing scripts if\nit changes all of a sudden.\n\n\n"},{"id":"452922","messageId":"YkdwbUqM45T06R00@vmiklos.hu","threadId":"57647","inReplyTo":"xmqq1qygy9nd.fsf@gitster.g","subject":"[PATCH] git-log: add a --since-as-filter option","fromName":"Miklos Vajna","fromEmail":"vmiklos@vmiklos.hu","sentAt":"2022-04-01T21:36:45Z","receivedAt":"2022-04-01T21:36:54Z","isPatch":true,"sender":{"key":"vmiklos@vmiklos.hu","avatar":"https://avatars.githubusercontent.com/u/13838?v=4"},"body":"This is similar to --since, but it will filter out not matching commits,\nrather than stopping at the first not matching commit.\n\nThis is useful if you e.g. want to list the commits from the last year,\nbut one odd commit has a bad commit date and that would hide lots of\nearlier commits in that range.\n\nThe behavior of --since is left unchanged, since it's valid to depend on\nits current behavior.\n---\n\nHi,\n\nOn Fri, Apr 01, 2022 at 10:51:34AM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> We could add a --since-as-filter= option or something, but then the\n> user needs to be careful when to stop (and digging down to the root\n> of the history, i.e. \"never stop\", may be an acceptable answer to\n> some projects).\n\nHere is a patch that does this. As a somewhat arbitrary testcase, the\nLibreOffice core.git repo has 474064 commits and --since-as-filter\nfinishes in 688 ms for a sample query (and expected output), while I got\nempty output with --since previously. I would argue this is an\nacceptable trade-off.\n\nWhat do you think?\n\nThanks,\n\nMiklos\n\n Documentation/rev-list-options.txt |  5 +++++\n revision.c                         | 10 ++++++++++\n revision.h                         |  1 +\n t/t4217-log-limit.sh               | 32 ++++++++++++++++++++++++++++++\n 4 files changed, 48 insertions(+)\n create mode 100755 t/t4217-log-limit.sh\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex fd4f4e26c9..ba01b1ba06 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -25,6 +25,11 @@ ordering and formatting options, such as `--reverse`.\n --after=<date>::\n \tShow commits more recent than a specific date.\n \n+--since-as-filter=<date>::\n+\tShow all commits more recent than a specific date. This visits all\n+\tcommits in the range, rather than stopping at the first commit which is older\n+\tthan a specific date.\n+\n --until=<date>::\n --before=<date>::\n \tShow commits older than a specific date.\ndiff --git a/revision.c b/revision.c\nindex 2646b78990..ebc95319d6 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1440,6 +1440,9 @@ static int limit_list(struct rev_info *revs)\n \t\tif (revs->min_age != -1 && (commit->date > revs->min_age) &&\n \t\t    !revs->line_level_traverse)\n \t\t\tcontinue;\n+\t\tif (revs->max_age_as_filter != -1 && (commit->date < revs->max_age_as_filter) &&\n+\t\t    !revs->line_level_traverse)\n+\t\t\tcontinue;\n \t\tdate = commit->date;\n \t\tp = &commit_list_insert(commit, p)->next;\n \n@@ -1838,6 +1841,7 @@ void repo_init_revisions(struct repository *r,\n \trevs->dense = 1;\n \trevs->prefix = prefix;\n \trevs->max_age = -1;\n+\trevs->max_age_as_filter = -1;\n \trevs->min_age = -1;\n \trevs->skip_count = -1;\n \trevs->max_count = -1;\n@@ -2218,6 +2222,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t} else if ((argcount = parse_long_opt(\"since\", argv, &optarg))) {\n \t\trevs->max_age = approxidate(optarg);\n \t\treturn argcount;\n+\t} else if ((argcount = parse_long_opt(\"since-as-filter\", argv, &optarg))) {\n+\t\trevs->max_age_as_filter = approxidate(optarg);\n+\t\treturn argcount;\n \t} else if ((argcount = parse_long_opt(\"after\", argv, &optarg))) {\n \t\trevs->max_age = approxidate(optarg);\n \t\treturn argcount;\n@@ -3862,6 +3869,9 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi\n \tif (revs->min_age != -1 &&\n \t    comparison_date(revs, commit) > revs->min_age)\n \t\t\treturn commit_ignore;\n+\tif (revs->max_age_as_filter != -1 &&\n+\t    comparison_date(revs, commit) < revs->max_age_as_filter)\n+\t\t\treturn commit_ignore;\n \tif (revs->min_parents || (revs->max_parents >= 0)) {\n \t\tint n = commit_list_count(commit->parents);\n \t\tif ((n < revs->min_parents) ||\ndiff --git a/revision.h b/revision.h\nindex 5bc59c7bfe..e80c148b19 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -263,6 +263,7 @@ struct rev_info {\n \tint skip_count;\n \tint max_count;\n \ttimestamp_t max_age;\n+\ttimestamp_t max_age_as_filter;\n \ttimestamp_t min_age;\n \tint min_parents;\n \tint max_parents;\ndiff --git a/t/t4217-log-limit.sh b/t/t4217-log-limit.sh\nnew file mode 100755\nindex 0000000000..5b7d30d5ad\n--- /dev/null\n+++ b/t/t4217-log-limit.sh\n@@ -0,0 +1,32 @@\n+#!/bin/sh\n+\n+test_description='git log with filter options limiting the output'\n+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n+\n+. ./test-lib.sh\n+\n+GIT_TEST_COMMIT_GRAPH=0\n+GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0\n+\n+test_expect_success 'setup test' '\n+\tgit init &&\n+\techo a > file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2022-02-01 0:00\" git commit -m init &&\n+\techo a >> file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2021-01-01 0:00\" git commit -m second &&\n+\techo a >> file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2022-03-01 0:00\" git commit -m third\n+'\n+\n+test_expect_success 'git log --since-as-filter' '\n+\tgit log --since-as-filter=\"2022-01-01\" --pretty=\"format:%s\" > actual &&\n+\ttest_i18ngrep init actual &&\n+\t! test_i18ngrep second actual &&\n+\ttest_i18ngrep third actual\n+'\n+\n+test_done\n-- \n2.34.1\n\n"},{"id":"452926","messageId":"Ykggy4poryul8uyG@vmiklos.hu","threadId":"57647","inReplyTo":"YkdwbUqM45T06R00@vmiklos.hu","subject":"[PATCH v2] git-log: add a --since-as-filter option","fromName":"Miklos Vajna","fromEmail":"vmiklos@vmiklos.hu","sentAt":"2022-04-02T10:09:15Z","receivedAt":"2022-04-02T10:09:29Z","isPatch":true,"sender":{"key":"vmiklos@vmiklos.hu","avatar":"https://avatars.githubusercontent.com/u/13838?v=4"},"body":"This is similar to --since, but it will filter out not matching commits,\nrather than stopping at the first not matching commit.\n\nThis is useful if you e.g. want to list the commits from the last year,\nbut one odd commit has a bad commit date and that would hide lots of\nearlier commits in that range.\n\nThe behavior of --since is left unchanged, since it's valid to depend on\nits current behavior.\n\nSigned-off-by: Miklos Vajna <vmiklos@vmiklos.hu>\n---\n\nHi,\n\nOn Fri, Apr 01, 2022 at 11:36:48PM +0200, Miklos Vajna <vmiklos@vmiklos.hu> wrote:\n> Here is a patch that does this.\n\nOops, forgot the sign-off, fixed now.\n\nRegards,\n\nMiklos\n\n Documentation/rev-list-options.txt |  5 +++++\n revision.c                         | 10 ++++++++++\n revision.h                         |  1 +\n t/t4217-log-limit.sh               | 32 ++++++++++++++++++++++++++++++\n 4 files changed, 48 insertions(+)\n create mode 100755 t/t4217-log-limit.sh\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex fd4f4e26c9..ba01b1ba06 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -25,6 +25,11 @@ ordering and formatting options, such as `--reverse`.\n --after=<date>::\n \tShow commits more recent than a specific date.\n \n+--since-as-filter=<date>::\n+\tShow all commits more recent than a specific date. This visits all\n+\tcommits in the range, rather than stopping at the first commit which is older\n+\tthan a specific date.\n+\n --until=<date>::\n --before=<date>::\n \tShow commits older than a specific date.\ndiff --git a/revision.c b/revision.c\nindex 2646b78990..ebc95319d6 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1440,6 +1440,9 @@ static int limit_list(struct rev_info *revs)\n \t\tif (revs->min_age != -1 && (commit->date > revs->min_age) &&\n \t\t    !revs->line_level_traverse)\n \t\t\tcontinue;\n+\t\tif (revs->max_age_as_filter != -1 && (commit->date < revs->max_age_as_filter) &&\n+\t\t    !revs->line_level_traverse)\n+\t\t\tcontinue;\n \t\tdate = commit->date;\n \t\tp = &commit_list_insert(commit, p)->next;\n \n@@ -1838,6 +1841,7 @@ void repo_init_revisions(struct repository *r,\n \trevs->dense = 1;\n \trevs->prefix = prefix;\n \trevs->max_age = -1;\n+\trevs->max_age_as_filter = -1;\n \trevs->min_age = -1;\n \trevs->skip_count = -1;\n \trevs->max_count = -1;\n@@ -2218,6 +2222,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t} else if ((argcount = parse_long_opt(\"since\", argv, &optarg))) {\n \t\trevs->max_age = approxidate(optarg);\n \t\treturn argcount;\n+\t} else if ((argcount = parse_long_opt(\"since-as-filter\", argv, &optarg))) {\n+\t\trevs->max_age_as_filter = approxidate(optarg);\n+\t\treturn argcount;\n \t} else if ((argcount = parse_long_opt(\"after\", argv, &optarg))) {\n \t\trevs->max_age = approxidate(optarg);\n \t\treturn argcount;\n@@ -3862,6 +3869,9 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi\n \tif (revs->min_age != -1 &&\n \t    comparison_date(revs, commit) > revs->min_age)\n \t\t\treturn commit_ignore;\n+\tif (revs->max_age_as_filter != -1 &&\n+\t    comparison_date(revs, commit) < revs->max_age_as_filter)\n+\t\t\treturn commit_ignore;\n \tif (revs->min_parents || (revs->max_parents >= 0)) {\n \t\tint n = commit_list_count(commit->parents);\n \t\tif ((n < revs->min_parents) ||\ndiff --git a/revision.h b/revision.h\nindex 5bc59c7bfe..e80c148b19 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -263,6 +263,7 @@ struct rev_info {\n \tint skip_count;\n \tint max_count;\n \ttimestamp_t max_age;\n+\ttimestamp_t max_age_as_filter;\n \ttimestamp_t min_age;\n \tint min_parents;\n \tint max_parents;\ndiff --git a/t/t4217-log-limit.sh b/t/t4217-log-limit.sh\nnew file mode 100755\nindex 0000000000..5b7d30d5ad\n--- /dev/null\n+++ b/t/t4217-log-limit.sh\n@@ -0,0 +1,32 @@\n+#!/bin/sh\n+\n+test_description='git log with filter options limiting the output'\n+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n+\n+. ./test-lib.sh\n+\n+GIT_TEST_COMMIT_GRAPH=0\n+GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0\n+\n+test_expect_success 'setup test' '\n+\tgit init &&\n+\techo a > file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2022-02-01 0:00\" git commit -m init &&\n+\techo a >> file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2021-01-01 0:00\" git commit -m second &&\n+\techo a >> file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2022-03-01 0:00\" git commit -m third\n+'\n+\n+test_expect_success 'git log --since-as-filter' '\n+\tgit log --since-as-filter=\"2022-01-01\" --pretty=\"format:%s\" > actual &&\n+\ttest_i18ngrep init actual &&\n+\t! test_i18ngrep second actual &&\n+\ttest_i18ngrep third actual\n+'\n+\n+test_done\n-- \n2.34.1\n\n"},{"id":"453268","messageId":"Yk8Gvf/fjVca9hDB@vmiklos.hu","threadId":"57647","inReplyTo":"xmqq1qygy9nd.fsf@gitster.g","subject":"Re: git log --since to not stop after first old commit?","fromName":"Miklos Vajna","fromEmail":"vmiklos@vmiklos.hu","sentAt":"2022-04-07T15:43:57Z","receivedAt":"2022-04-07T15:44:25Z","isPatch":false,"sender":{"key":"vmiklos@vmiklos.hu","avatar":"https://avatars.githubusercontent.com/u/13838?v=4"},"body":"Hi Junio,\n\nOn Fri, Apr 01, 2022 at 10:51:34AM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> We could add a --since-as-filter= option or something, but then the\n> user needs to be careful when to stop (and digging down to the root\n> of the history, i.e. \"never stop\", may be an acceptable answer to\n> some projects).\n\nI sent a patch to add such an option (which picks the \"never stop\"\nbehavior) on 1st, did you see that?\n\nIf the idea is OK in principle, but the patch needs tweaking, please let\nme know.\n\nThanks,\n\nMiklos\n"},{"id":"453308","messageId":"xmqqv8vkpara.fsf@gitster.g","threadId":"57647","inReplyTo":"Yk8Gvf/fjVca9hDB@vmiklos.hu","subject":"Re: git log --since to not stop after first old commit?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-08T02:30:33Z","receivedAt":"2022-04-08T02:30:42Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miklos Vajna <vmiklos@vmiklos.hu> writes:\n\n> On Fri, Apr 01, 2022 at 10:51:34AM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n>> We could add a --since-as-filter= option or something, but then the\n>> user needs to be careful when to stop (and digging down to the root\n>> of the history, i.e. \"never stop\", may be an acceptable answer to\n>> some projects).\n>\n> I sent a patch to add such an option (which picks the \"never stop\"\n> behavior) on 1st, did you see that?\n>\n> If the idea is OK in principle, but the patch needs tweaking, please let\n> me know.\n\nAs a single-shot change, \"--since-as-filter\" is certainly an easy to\nexplain approach of least resistance.\n\nBut when viewed from a higher level as a general design problem, I\nam unsure if it is a good direction to go in.\n\nGiving \"--since\" the \"as-filter\" variant sets a precedent, and\ncloses the door for a better UI that we can extend more generally\nwithout having to add \"--X-as-filter\" for each and every conceivable\n\"--X\" that is a traversal stopper into a filtering kind.\n\n\n"},{"id":"453342","messageId":"xmqqtub3moa0.fsf@gitster.g","threadId":"57647","inReplyTo":"xmqqv8vkpara.fsf@gitster.g","subject":"Re: git log --since to not stop after first old commit?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-08T18:19:03Z","receivedAt":"2022-04-08T18:19:12Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Miklos Vajna <vmiklos@vmiklos.hu> writes:\n>\n>> On Fri, Apr 01, 2022 at 10:51:34AM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n>>> We could add a --since-as-filter= option or something, but then the\n>>> user needs to be careful when to stop (and digging down to the root\n>>> of the history, i.e. \"never stop\", may be an acceptable answer to\n>>> some projects).\n>>\n>> I sent a patch to add such an option (which picks the \"never stop\"\n>> behavior) on 1st, did you see that?\n>>\n>> If the idea is OK in principle, but the patch needs tweaking, please let\n>> me know.\n>\n> As a single-shot change, \"--since-as-filter\" is certainly an easy to\n> explain approach of least resistance.\n>\n> But when viewed from a higher level as a general design problem, I\n> am unsure if it is a good direction to go in.\n>\n> Giving \"--since\" the \"as-filter\" variant sets a precedent, and\n> closes the door for a better UI that we can extend more generally\n> without having to add \"--X-as-filter\" for each and every conceivable\n> \"--X\" that is a traversal stopper into a filtering kind.\n\nIf we pursue the possibility further, perhaps we may realize that\nthere isn't much room for us to add too many \"traversal stoppers\" in\nthe future, in which case giving \"as-filter\" to a very limited few\ntraversal stoppers may not be too bad.  I just do not think we have\nexplored that enough to decide that \"--since-as-filter\" is a good UI\n(and it is not a good timing for me to spend brain cycles on the\nissue).\n\nThanks.\n\n"},{"id":"453353","messageId":"YlCiqgO6rL908Zsi@vmiklos.hu","threadId":"57647","inReplyTo":"xmqqtub3moa0.fsf@gitster.g","subject":"[PATCH v3] git-log: add a --since=... --as-filter option","fromName":"Miklos Vajna","fromEmail":"vmiklos@vmiklos.hu","sentAt":"2022-04-08T21:01:30Z","receivedAt":"2022-04-08T21:01:41Z","isPatch":true,"sender":{"key":"vmiklos@vmiklos.hu","avatar":"https://avatars.githubusercontent.com/u/13838?v=4"},"body":"This is similar to --since, but it will filter out not matching commits,\nrather than stopping at the first not matching commit.\n\nThis is useful if you e.g. want to list the commits from the last year,\nbut one odd commit has a bad commit date and that would hide lots of\nearlier commits in that range.\n\nThe behavior of --since is left unchanged, since it's valid to depend on\nits current behavior.\n\nSigned-off-by: Miklos Vajna <vmiklos@vmiklos.hu>\n---\n\nHi Junio,\n\nOn Thu, Apr 07, 2022 at 07:30:33PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> As a single-shot change, \"--since-as-filter\" is certainly an easy to\n> explain approach of least resistance.\n> \n> But when viewed from a higher level as a general design problem, I\n> am unsure if it is a good direction to go in.\n> \n> Giving \"--since\" the \"as-filter\" variant sets a precedent, and\n> closes the door for a better UI that we can extend more generally\n> without having to add \"--X-as-filter\" for each and every conceivable\n> \"--X\" that is a traversal stopper into a filtering kind.\n\nI like the idea of doing this mode as \"--since=... --as-filter\". I can\nstill implement it just for --since=... initially, but it can be\nextended for other flags as well in the future if there is a need.\n\n> If we pursue the possibility further, perhaps we may realize that\n> there isn't much room for us to add too many \"traversal stoppers\" in\n> the future, in which case giving \"as-filter\" to a very limited few\n> traversal stoppers may not be too bad.  I just do not think we have\n> explored that enough to decide that \"--since-as-filter\" is a good UI\n\nMy understanding is that get_revision_1() has a special-case for the max\nage case to be a \"traversal stopper\", and all other options are just \nfiltering in limit_range(). But perhaps I missed something.\n\nHere is an updated patch to do the new syntax.\n\nThanks,\n\nMiklos\n\n Documentation/rev-list-options.txt |  5 +++++\n revision.c                         | 14 +++++++++++--\n revision.h                         |  1 +\n t/t4217-log-limit.sh               | 32 ++++++++++++++++++++++++++++++\n 4 files changed, 50 insertions(+), 2 deletions(-)\n create mode 100755 t/t4217-log-limit.sh\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex fd4f4e26c9..8565299264 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -25,6 +25,11 @@ ordering and formatting options, such as `--reverse`.\n --after=<date>::\n \tShow commits more recent than a specific date.\n \n+--as-filter::\n+\tWhen combined with `--since=<date>`, show all commits more recent than\n+\ta specific date. This visits all commits in the range, rather than stopping at\n+\tthe first commit which is older than a specific date.\n+\n --until=<date>::\n --before=<date>::\n \tShow commits older than a specific date.\ndiff --git a/revision.c b/revision.c\nindex 7d435f8048..41ea72e516 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1440,6 +1440,9 @@ static int limit_list(struct rev_info *revs)\n \t\tif (revs->min_age != -1 && (commit->date > revs->min_age) &&\n \t\t    !revs->line_level_traverse)\n \t\t\tcontinue;\n+\t\tif (revs->max_age != -1 && revs->as_filter && (commit->date < revs->max_age) &&\n+\t\t    !revs->line_level_traverse)\n+\t\t\tcontinue;\n \t\tdate = commit->date;\n \t\tp = &commit_list_insert(commit, p)->next;\n \n@@ -1838,6 +1841,7 @@ void repo_init_revisions(struct repository *r,\n \trevs->dense = 1;\n \trevs->prefix = prefix;\n \trevs->max_age = -1;\n+\trevs->as_filter = 0;\n \trevs->min_age = -1;\n \trevs->skip_count = -1;\n \trevs->max_count = -1;\n@@ -2218,6 +2222,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t} else if ((argcount = parse_long_opt(\"since\", argv, &optarg))) {\n \t\trevs->max_age = approxidate(optarg);\n \t\treturn argcount;\n+\t} else if (!strcmp(arg, \"--as-filter\")) {\n+\t\trevs->as_filter = 1;\n+\t\treturn argcount;\n \t} else if ((argcount = parse_long_opt(\"after\", argv, &optarg))) {\n \t\trevs->max_age = approxidate(optarg);\n \t\treturn argcount;\n@@ -3365,7 +3372,7 @@ static void explore_walk_step(struct rev_info *revs)\n \tif (revs->sort_order == REV_SORT_BY_AUTHOR_DATE)\n \t\trecord_author_date(&info->author_date, c);\n \n-\tif (revs->max_age != -1 && (c->date < revs->max_age))\n+\tif (revs->max_age != -1 && !revs->as_filter && (c->date < revs->max_age))\n \t\tc->object.flags |= UNINTERESTING;\n \n \tif (process_parents(revs, c, NULL, NULL) < 0)\n@@ -3862,6 +3869,9 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi\n \tif (revs->min_age != -1 &&\n \t    comparison_date(revs, commit) > revs->min_age)\n \t\t\treturn commit_ignore;\n+\tif (revs->max_age != -1 && revs->as_filter &&\n+\t    comparison_date(revs, commit) < revs->max_age)\n+\t\t\treturn commit_ignore;\n \tif (revs->min_parents || (revs->max_parents >= 0)) {\n \t\tint n = commit_list_count(commit->parents);\n \t\tif ((n < revs->min_parents) ||\n@@ -4019,7 +4029,7 @@ static struct commit *get_revision_1(struct rev_info *revs)\n \t\t * that we'd otherwise have done in limit_list().\n \t\t */\n \t\tif (!revs->limited) {\n-\t\t\tif (revs->max_age != -1 &&\n+\t\t\tif (revs->max_age != -1 && !revs->as_filter &&\n \t\t\t    comparison_date(revs, commit) < revs->max_age)\n \t\t\t\tcontinue;\n \ndiff --git a/revision.h b/revision.h\nindex 5bc59c7bfe..fe37ebd83d 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -263,6 +263,7 @@ struct rev_info {\n \tint skip_count;\n \tint max_count;\n \ttimestamp_t max_age;\n+\tint as_filter;\n \ttimestamp_t min_age;\n \tint min_parents;\n \tint max_parents;\ndiff --git a/t/t4217-log-limit.sh b/t/t4217-log-limit.sh\nnew file mode 100755\nindex 0000000000..a66830e3d7\n--- /dev/null\n+++ b/t/t4217-log-limit.sh\n@@ -0,0 +1,32 @@\n+#!/bin/sh\n+\n+test_description='git log with filter options limiting the output'\n+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n+\n+. ./test-lib.sh\n+\n+GIT_TEST_COMMIT_GRAPH=0\n+GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0\n+\n+test_expect_success 'setup test' '\n+\tgit init &&\n+\techo a > file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2022-02-01 0:00\" git commit -m init &&\n+\techo a >> file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2021-01-01 0:00\" git commit -m second &&\n+\techo a >> file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2022-03-01 0:00\" git commit -m third\n+'\n+\n+test_expect_success 'git log --since-as-filter' '\n+\tgit log --since=\"2022-01-01\" --as-filter --pretty=\"format:%s\" > actual &&\n+\ttest_i18ngrep init actual &&\n+\t! test_i18ngrep second actual &&\n+\ttest_i18ngrep third actual\n+'\n+\n+test_done\n-- \n2.34.1\n\n"},{"id":"453388","messageId":"YlPMuxhpngs+x9n8@vmiklos.hu","threadId":"57647","inReplyTo":"CANgJU+Wr+tKNPfeh4dst-E_LSnoYYmN1easqmkFUA9spp-rpKQ@mail.gmail.com","subject":"Re: git log --since to not stop after first old commit?","fromName":"Miklos Vajna","fromEmail":"vmiklos@vmiklos.hu","sentAt":"2022-04-11T06:37:47Z","receivedAt":"2022-04-11T06:37:56Z","isPatch":false,"sender":{"key":"vmiklos@vmiklos.hu","avatar":"https://avatars.githubusercontent.com/u/13838?v=4"},"body":"Hi Yves,\n\nOn Sat, Apr 09, 2022 at 06:02:44AM +0200, demerphq <demerphq@gmail.com> wrote:\n> When you do have the cycles perhaps it is worth considering whether\n> splitting it up, so that --as-filter is a modifier for traversal stoppers,\n> would avoid the problem of proliferating options.   Eg, instead of saying\n> --since-as-filter you would say --since ... --as-filter.\n\n[PATCH v3] in this thread is meant to implement this. I hope that helps.\n\nRegards,\n\nMiklos\n"},{"id":"453391","messageId":"CANgJU+U31g_27TdFGvZPy6wgk24OrnC8Z0+hZp2zVshEd7LgJg@mail.gmail.com","threadId":"57647","inReplyTo":"YlPMuxhpngs+x9n8@vmiklos.hu","subject":"Re: git log --since to not stop after first old commit?","fromName":"demerphq","fromEmail":"demerphq@gmail.com","sentAt":"2022-04-11T09:18:51Z","receivedAt":"2022-04-11T09:19:08Z","isPatch":false,"sender":{"key":"demerphq@gmail.com","avatar":null},"body":"On Mon, 11 Apr 2022 at 08:37, Miklos Vajna <vmiklos@vmiklos.hu> wrote:\n>\n> Hi Yves,\n>\n> On Sat, Apr 09, 2022 at 06:02:44AM +0200, demerphq <demerphq@gmail.com> wrote:\n> > When you do have the cycles perhaps it is worth considering whether\n> > splitting it up, so that --as-filter is a modifier for traversal stoppers,\n> > would avoid the problem of proliferating options.   Eg, instead of saying\n> > --since-as-filter you would say --since ... --as-filter.\n>\n> [PATCH v3] in this thread is meant to implement this. I hope that helps.\n\nThat sounds good to me and IMO certainly preferable to a stack of\noptions with the suffix -as-filter. But please be aware that I am not\na person of significance to the git project and my opinion on this\nprobably isn't worth much. I just thought I would point out that it\nmight be a way to cleanly address Junio's concern about option\nproliferation while still giving you what you want.\n\nCheers,\nYves\n\n-- \nperl -Mre=debug -e \"/just|another|perl|hacker/\"\n"},{"id":"453415","messageId":"xmqqilrfk14q.fsf@gitster.g","threadId":"57647","inReplyTo":"CANgJU+Wr+tKNPfeh4dst-E_LSnoYYmN1easqmkFUA9spp-rpKQ@mail.gmail.com","subject":"Re: git log --since to not stop after first old commit?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-11T16:58:45Z","receivedAt":"2022-04-11T16:58:54Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"demerphq <demerphq@gmail.com> writes:\n\n> On Sat, 9 Apr 2022 at 02:43, Junio C Hamano <gitster@pobox.com> wrote:\n>\n>> > Giving \"--since\" the \"as-filter\" variant sets a precedent, and\n>> > closes the door for a better UI that we can extend more generally\n>> > without having to add \"--X-as-filter\" for each and every conceivable\n>> > \"--X\" that is a traversal stopper into a filtering kind.\n>>\n>> If we pursue the possibility further, perhaps we may realize that\n>> there isn't much room for us to add too many \"traversal stoppers\" in\n>> the future, in which case giving \"as-filter\" to a very limited few\n>> traversal stoppers may not be too bad.  I just do not think we have\n>> explored that enough to decide that \"--since-as-filter\" is a good UI\n>> (and it is not a good timing for me to spend brain cycles on the\n>> issue).\n>\n> When you do have the cycles perhaps it is worth considering whether\n> splitting it up, so that --as-filter is a modifier for traversal stoppers,\n> would avoid the problem of proliferating options.   Eg, instead of saying\n> --since-as-filter you would say --since ... --as-filter. That way the\n> stoppers where \"filter like behavior\" made sense could just check if the\n> --as-filter flag was set.\n\nYes, that has exactly the opposite problem I wanted to warn us about\nby sending an extra message (to which you are reponding to).  If we\nhave (or can have) very many traversal stopping option, it might\nmake sense to have --as-filter as a modifier and avoid doubling the\nnumber of options, but if we only have very few (and fundamentally\ncannot have more than very few), then giving each of these very few\n--X its own --X-as-filter variant would probably make more sense.\nBecause end users would probably not know which ones are inherently\nfilters and will not be affected with --as-filter modifier, it would\nhelp them understand if we give them independent --since-as-filter\noption and document it separately, if there aren't many of them.\n\nBesides, if we had very few but still multiple of them, --X and\n--Y-as-filter can be combined to say \"X stops as before, but Y is\napplied as filter\", which is strictly more expressive than a\nseparate --as-filter modifier.\n\nSo that is why I threw out the message for those interested in the\ntopic to first think about.  I know we agree that --since may be a\ngood candidate to have these two flavours of behaviour.  I do not\nthink anybody carefully thought about existing options to see if\nthere are many like --since that want two flavours, let alone\npossible options we have said in the past that we may want to have\nbut not yet added.\n\nThanks.\n"},{"id":"453437","messageId":"220412.86pmlmhe9a.gmgdl@evledraar.gmail.com","threadId":"57647","inReplyTo":"YlCiqgO6rL908Zsi@vmiklos.hu","subject":"Re: [PATCH v3] git-log: add a --since=... --as-filter option","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-12T08:47:15Z","receivedAt":"2022-04-12T09:59:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Apr 08 2022, Miklos Vajna wrote:\n\n> On Thu, Apr 07, 2022 at 07:30:33PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n>> As a single-shot change, \"--since-as-filter\" is certainly an easy to\n>> explain approach of least resistance.\n>> \n>> But when viewed from a higher level as a general design problem, I\n>> am unsure if it is a good direction to go in.\n>> \n>> Giving \"--since\" the \"as-filter\" variant sets a precedent, and\n>> closes the door for a better UI that we can extend more generally\n>> without having to add \"--X-as-filter\" for each and every conceivable\n>> \"--X\" that is a traversal stopper into a filtering kind.\n>\n> I like the idea of doing this mode as \"--since=... --as-filter\". I can\n> still implement it just for --since=... initially, but it can be\n> extended for other flags as well in the future if there is a need.\n\nYes, I think this is much better.\n\n>> If we pursue the possibility further, perhaps we may realize that\n>> there isn't much room for us to add too many \"traversal stoppers\" in\n>> the future, in which case giving \"as-filter\" to a very limited few\n>> traversal stoppers may not be too bad.  I just do not think we have\n>> explored that enough to decide that \"--since-as-filter\" is a good UI\n>\n> My understanding is that get_revision_1() has a special-case for the max\n> age case to be a \"traversal stopper\", and all other options are just \n> filtering in limit_range(). But perhaps I missed something.\n> [...]\n>  Documentation/rev-list-options.txt |  5 +++++\n>  revision.c                         | 14 +++++++++++--\n>  revision.h                         |  1 +\n>  t/t4217-log-limit.sh               | 32 ++++++++++++++++++++++++++++++\n>  4 files changed, 50 insertions(+), 2 deletions(-)\n>  create mode 100755 t/t4217-log-limit.sh\n>\n> diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\n> index fd4f4e26c9..8565299264 100644\n> --- a/Documentation/rev-list-options.txt\n> +++ b/Documentation/rev-list-options.txt\n> @@ -25,6 +25,11 @@ ordering and formatting options, such as `--reverse`.\n>  --after=<date>::\n>  \tShow commits more recent than a specific date.\n>  \n> +--as-filter::\n> +\tWhen combined with `--since=<date>`, show all commits more recent than\n> +\ta specific date. This visits all commits in the range, rather than stopping at\n> +\tthe first commit which is older than a specific date.\n\nI wonder if we should be more future-proof here and say that we'll run\nanything as a filter, and that --since is the one option currently\naffected.\n\nBut maybe there's no reason to do so...\n\nIn any case these docs are inaccurate because they cover --since, but if\nyou check revision.c we'll set \"max_age\" on other options too\n(synonyms?).\n\nAll in all I wonder if this wouldn't be much more understandable if we\nadvertised is as another option to do \"HISTORY SIMPLIFICATION\", which\nlooking at e.g. get_commit_action() and \"prune\" is kind of what we're\ndoing with the existing --since behavior.\n\n>  --until=<date>::\n>  --before=<date>::\n>  \tShow commits older than a specific date.\n> diff --git a/revision.c b/revision.c\n> index 7d435f8048..41ea72e516 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -1440,6 +1440,9 @@ static int limit_list(struct rev_info *revs)\n>  \t\tif (revs->min_age != -1 && (commit->date > revs->min_age) &&\n>  \t\t    !revs->line_level_traverse)\n>  \t\t\tcontinue;\n> +\t\tif (revs->max_age != -1 && revs->as_filter && (commit->date < revs->max_age) &&\n> +\t\t    !revs->line_level_traverse)\n> +\t\t\tcontinue;\n>  \t\tdate = commit->date;\n>  \t\tp = &commit_list_insert(commit, p)->next;\n>  \n> @@ -1838,6 +1841,7 @@ void repo_init_revisions(struct repository *r,\n>  \trevs->dense = 1;\n>  \trevs->prefix = prefix;\n>  \trevs->max_age = -1;\n> +\trevs->as_filter = 0;\n>  \trevs->min_age = -1;\n>  \trevs->skip_count = -1;\n>  \trevs->max_count = -1;\n> @@ -2218,6 +2222,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n>  \t} else if ((argcount = parse_long_opt(\"since\", argv, &optarg))) {\n>  \t\trevs->max_age = approxidate(optarg);\n>  \t\treturn argcount;\n> +\t} else if (!strcmp(arg, \"--as-filter\")) {\n> +\t\trevs->as_filter = 1;\n> +\t\treturn argcount;\n>  \t} else if ((argcount = parse_long_opt(\"after\", argv, &optarg))) {\n>  \t\trevs->max_age = approxidate(optarg);\n>  \t\treturn argcount;\n> @@ -3365,7 +3372,7 @@ static void explore_walk_step(struct rev_info *revs)\n>  \tif (revs->sort_order == REV_SORT_BY_AUTHOR_DATE)\n>  \t\trecord_author_date(&info->author_date, c);\n>  \n> -\tif (revs->max_age != -1 && (c->date < revs->max_age))\n> +\tif (revs->max_age != -1 && !revs->as_filter && (c->date < revs->max_age))\n>  \t\tc->object.flags |= UNINTERESTING;\n>  \n>  \tif (process_parents(revs, c, NULL, NULL) < 0)\n> @@ -3862,6 +3869,9 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi\n>  \tif (revs->min_age != -1 &&\n>  \t    comparison_date(revs, commit) > revs->min_age)\n>  \t\t\treturn commit_ignore;\n> +\tif (revs->max_age != -1 && revs->as_filter &&\n> +\t    comparison_date(revs, commit) < revs->max_age)\n> +\t\t\treturn commit_ignore;\n>  \tif (revs->min_parents || (revs->max_parents >= 0)) {\n>  \t\tint n = commit_list_count(commit->parents);\n>  \t\tif ((n < revs->min_parents) ||\n> @@ -4019,7 +4029,7 @@ static struct commit *get_revision_1(struct rev_info *revs)\n>  \t\t * that we'd otherwise have done in limit_list().\n>  \t\t */\n>  \t\tif (!revs->limited) {\n> -\t\t\tif (revs->max_age != -1 &&\n> +\t\t\tif (revs->max_age != -1 && !revs->as_filter &&\n>  \t\t\t    comparison_date(revs, commit) < revs->max_age)\n>  \t\t\t\tcontinue;\n\nI think it's good to do this as a general mechanism, but if you now\nremove the \"max_age\" field from \"struct rev_info\" and:\n\n\tmake -k\n\nYou'll see a bunch of callers who check \"max_age\" outside of revision.c,\nsince those will accept these revision options are they doing the right\nthing now too?\n\n...\n\n> diff --git a/revision.h b/revision.h\n> index 5bc59c7bfe..fe37ebd83d 100644\n> --- a/revision.h\n> +++ b/revision.h\n> @@ -263,6 +263,7 @@ struct rev_info {\n>  \tint skip_count;\n>  \tint max_count;\n>  \ttimestamp_t max_age;\n> +\tint as_filter;\n>  \ttimestamp_t min_age;\n>  \tint min_parents;\n>  \tint max_parents;\n> diff --git a/t/t4217-log-limit.sh b/t/t4217-log-limit.sh\n> new file mode 100755\n> index 0000000000..a66830e3d7\n> --- /dev/null\n> +++ b/t/t4217-log-limit.sh\n> @@ -0,0 +1,32 @@\n> +#!/bin/sh\n> +\n> +test_description='git log with filter options limiting the output'\n> +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n> +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n> +\n> +. ./test-lib.sh\n> +\n> +GIT_TEST_COMMIT_GRAPH=0\n> +GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0\n> +\n> +test_expect_success 'setup test' '\n> +\tgit init &&\n> +\techo a > file &&\n> +\tgit add file &&\n> +\tGIT_COMMITTER_DATE=\"2022-02-01 0:00\" git commit -m init &&\n> +\techo a >> file &&\n> +\tgit add file &&\n> +\tGIT_COMMITTER_DATE=\"2021-01-01 0:00\" git commit -m second &&\n> +\techo a >> file &&\n> +\tgit add file &&\n> +\tGIT_COMMITTER_DATE=\"2022-03-01 0:00\" git commit -m third\n> +'\n> +\n> +test_expect_success 'git log --since-as-filter' '\n> +\tgit log --since=\"2022-01-01\" --as-filter --pretty=\"format:%s\" > actual &&\n> +\ttest_i18ngrep init actual &&\n> +\t! test_i18ngrep second actual &&\n> +\ttest_i18ngrep third actual\n> +'\n> +\n> +test_done\n\nIn any case we should have tests for those callers, i.e. blame, bundle\netc.\n"},{"id":"453723","messageId":"YlnYDgZRzDI87b/z@vmiklos.hu","threadId":"57647","inReplyTo":"220412.86pmlmhe9a.gmgdl@evledraar.gmail.com","subject":"[PATCH v4] git-log: add a --since=... --as-filter option","fromName":"Miklos Vajna","fromEmail":"vmiklos@vmiklos.hu","sentAt":"2022-04-15T20:39:42Z","receivedAt":"2022-04-15T20:39:52Z","isPatch":true,"sender":{"key":"vmiklos@vmiklos.hu","avatar":"https://avatars.githubusercontent.com/u/13838?v=4"},"body":"This is similar to --since, but it will filter out not matching commits,\nrather than stopping at the first not matching commit.\n\nThis is useful if you e.g. want to list the commits from the last year,\nbut one odd commit has a bad commit date and that would hide lots of\nearlier commits in that range.\n\nThe behavior of --since is left unchanged, since it's valid to depend on\nits current behavior.\n\nSigned-off-by: Miklos Vajna <vmiklos@vmiklos.hu>\n---\n\nHi Ævar,\n\nOn Tue, Apr 12, 2022 at 10:47:15AM +0200, Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n> > +--as-filter::\n> > +\tWhen combined with `--since=<date>`, show all commits more recent than\n> > +\ta specific date. This visits all commits in the range, rather than stopping at\n> > +\tthe first commit which is older than a specific date.\n> \n> I wonder if we should be more future-proof here and say that we'll run\n> anything as a filter, and that --since is the one option currently\n> affected.\n> \n> But maybe there's no reason to do so...\n\nMy understanding is that in practice --since is the only option that\nterminates the revision walk on the first match, so I would argue there\nis no need for this.\n\n> In any case these docs are inaccurate because they cover --since, but if\n> you check revision.c we'll set \"max_age\" on other options too\n> (synonyms?).\n\nGood catch, I've added --max-age and --after as well.\n\n> All in all I wonder if this wouldn't be much more understandable if we\n> advertised is as another option to do \"HISTORY SIMPLIFICATION\", which\n> looking at e.g. get_commit_action() and \"prune\" is kind of what we're\n> doing with the existing --since behavior.\n\nMakes sense, we kind of simplify history by default here & then this\noption could be documented as one that modifies this terminating\nbehavior.\n\n> I think it's good to do this as a general mechanism, but if you now\n> remove the \"max_age\" field from \"struct rev_info\" and:\n> \n> \tmake -k\n> \n> You'll see a bunch of callers who check \"max_age\" outside of revision.c,\n> since those will accept these revision options are they doing the right\n> thing now too?\n\nI found the following callers:\n\n- some builtins that want to make sure that no history limiting is used,\n  an additional --as-filter doesn't change behavior there\n\n- blame: this has its own commit walking loop, so --as-filter doesn't\n  change any behavior here unintentionally.\n\n- bundle: --since is not used for revision walking here, just to check\n  what tags to include/exclude, so this is already not terminating\n\n> In any case we should have tests for those callers, i.e. blame, bundle\n> etc.\n\nt/t5607-clone-bundle.sh already tests bundle --since. I've added a new\nt/t4218-blame-limit.sh to test blame --since, it seems there were no\ntests for this so far.\n\nThanks,\n\nMiklos\n\n Documentation/rev-list-options.txt |  6 +++++\n revision.c                         | 13 +++++++++--\n revision.h                         |  1 +\n t/t4217-log-limit.sh               | 36 ++++++++++++++++++++++++++++++\n t/t4218-blame-limit.sh             | 36 ++++++++++++++++++++++++++++++\n 5 files changed, 90 insertions(+), 2 deletions(-)\n create mode 100755 t/t4217-log-limit.sh\n create mode 100755 t/t4218-blame-limit.sh\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex fd4f4e26c9..354bd29f10 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -350,6 +350,12 @@ The following options select the commits to be shown:\n <paths>::\n \tCommits modifying the given <paths> are selected.\n \n+--as-filter::\n+\tWhen combined with `--max-age=<date>`, `--since=<date>` or\n+\t`--after=<date>`, show all commits more recent than a specific date. This\n+\tvisits all commits in the range, rather than stopping at the first commit\n+\twhich is older than a specific date.\n+\n --simplify-by-decoration::\n \tCommits that are referred by some branch or tag are selected.\n \ndiff --git a/revision.c b/revision.c\nindex 7d435f8048..ff018c3976 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1440,6 +1440,9 @@ static int limit_list(struct rev_info *revs)\n \t\tif (revs->min_age != -1 && (commit->date > revs->min_age) &&\n \t\t    !revs->line_level_traverse)\n \t\t\tcontinue;\n+\t\tif (revs->max_age != -1 && revs->as_filter && (commit->date < revs->max_age) &&\n+\t\t    !revs->line_level_traverse)\n+\t\t\tcontinue;\n \t\tdate = commit->date;\n \t\tp = &commit_list_insert(commit, p)->next;\n \n@@ -1838,6 +1841,7 @@ void repo_init_revisions(struct repository *r,\n \trevs->dense = 1;\n \trevs->prefix = prefix;\n \trevs->max_age = -1;\n+\trevs->as_filter = 0;\n \trevs->min_age = -1;\n \trevs->skip_count = -1;\n \trevs->max_count = -1;\n@@ -2218,6 +2222,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t} else if ((argcount = parse_long_opt(\"since\", argv, &optarg))) {\n \t\trevs->max_age = approxidate(optarg);\n \t\treturn argcount;\n+\t} else if (!strcmp(arg, \"--as-filter\")) {\n+\t\trevs->as_filter = 1;\n \t} else if ((argcount = parse_long_opt(\"after\", argv, &optarg))) {\n \t\trevs->max_age = approxidate(optarg);\n \t\treturn argcount;\n@@ -3365,7 +3371,7 @@ static void explore_walk_step(struct rev_info *revs)\n \tif (revs->sort_order == REV_SORT_BY_AUTHOR_DATE)\n \t\trecord_author_date(&info->author_date, c);\n \n-\tif (revs->max_age != -1 && (c->date < revs->max_age))\n+\tif (revs->max_age != -1 && !revs->as_filter && (c->date < revs->max_age))\n \t\tc->object.flags |= UNINTERESTING;\n \n \tif (process_parents(revs, c, NULL, NULL) < 0)\n@@ -3862,6 +3868,9 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi\n \tif (revs->min_age != -1 &&\n \t    comparison_date(revs, commit) > revs->min_age)\n \t\t\treturn commit_ignore;\n+\tif (revs->max_age != -1 && revs->as_filter &&\n+\t    comparison_date(revs, commit) < revs->max_age)\n+\t\t\treturn commit_ignore;\n \tif (revs->min_parents || (revs->max_parents >= 0)) {\n \t\tint n = commit_list_count(commit->parents);\n \t\tif ((n < revs->min_parents) ||\n@@ -4019,7 +4028,7 @@ static struct commit *get_revision_1(struct rev_info *revs)\n \t\t * that we'd otherwise have done in limit_list().\n \t\t */\n \t\tif (!revs->limited) {\n-\t\t\tif (revs->max_age != -1 &&\n+\t\t\tif (revs->max_age != -1 && !revs->as_filter &&\n \t\t\t    comparison_date(revs, commit) < revs->max_age)\n \t\t\t\tcontinue;\n \ndiff --git a/revision.h b/revision.h\nindex 5bc59c7bfe..fe37ebd83d 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -263,6 +263,7 @@ struct rev_info {\n \tint skip_count;\n \tint max_count;\n \ttimestamp_t max_age;\n+\tint as_filter;\n \ttimestamp_t min_age;\n \tint min_parents;\n \tint max_parents;\ndiff --git a/t/t4217-log-limit.sh b/t/t4217-log-limit.sh\nnew file mode 100755\nindex 0000000000..2a3705c714\n--- /dev/null\n+++ b/t/t4217-log-limit.sh\n@@ -0,0 +1,36 @@\n+#!/bin/sh\n+\n+test_description='git log with filter options limiting the output'\n+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n+\n+. ./test-lib.sh\n+\n+GIT_TEST_COMMIT_GRAPH=0\n+GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0\n+\n+test_expect_success 'setup test' '\n+\tgit init &&\n+\techo a > file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2021-02-01 0:00\" git commit -m init &&\n+\techo a >> file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2022-02-01 0:00\" git commit -m first &&\n+\techo a >> file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2021-03-01 0:00\" git commit -m second &&\n+\techo a >> file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2022-03-01 0:00\" git commit -m third\n+'\n+\n+test_expect_success 'git log --since=... --as-filter' '\n+\tgit log --since=\"2022-01-01\" --as-filter --pretty=\"format:%s\" > actual &&\n+\t! test_i18ngrep init actual &&\n+\ttest_i18ngrep first actual &&\n+\t! test_i18ngrep second actual &&\n+\ttest_i18ngrep third actual\n+'\n+\n+test_done\ndiff --git a/t/t4218-blame-limit.sh b/t/t4218-blame-limit.sh\nnew file mode 100755\nindex 0000000000..03f513f331\n--- /dev/null\n+++ b/t/t4218-blame-limit.sh\n@@ -0,0 +1,36 @@\n+#!/bin/sh\n+\n+test_description='git blame with filter options limiting the output'\n+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n+\n+. ./test-lib.sh\n+\n+GIT_TEST_COMMIT_GRAPH=0\n+GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0\n+\n+test_expect_success 'setup test' '\n+\tgit init &&\n+\techo a > file &&\n+\tgit add file &&\n+\tGIT_AUTHOR_DATE=\"2020-01-01 0:00\" GIT_COMMITTER_DATE=\"2020-01-01 0:00\" git commit -m init &&\n+\techo a >> file &&\n+\tgit add file &&\n+\tGIT_AUTHOR_DATE=\"2020-02-01 0:00\" GIT_COMMITTER_DATE=\"2020-02-01 0:00\" git commit -m first &&\n+\techo a >> file &&\n+\tgit add file &&\n+\tGIT_AUTHOR_DATE=\"2020-03-01 0:00\" GIT_COMMITTER_DATE=\"2020-03-01 0:00\" git commit -m second &&\n+\techo a >> file &&\n+\tgit add file &&\n+\tGIT_AUTHOR_DATE=\"2020-04-01 0:00\" GIT_COMMITTER_DATE=\"2020-04-01 0:00\" git commit -m third\n+'\n+\n+test_expect_success 'git blame --since=...' '\n+\tgit blame --since=\"2020-02-15\" file > actual &&\n+\t! test_i18ngrep 2020-01-01 actual &&\n+\ttest_i18ngrep 2020-02-01 actual &&\n+\ttest_i18ngrep 2020-03-01 actual &&\n+\ttest_i18ngrep 2020-04-01 actual\n+'\n+\n+test_done\n-- \n2.34.1\n\n"},{"id":"453734","messageId":"xmqqmtgm9c01.fsf@gitster.g","threadId":"57647","inReplyTo":"YlnYDgZRzDI87b/z@vmiklos.hu","subject":"Re: [PATCH v4] git-log: add a --since=... --as-filter option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-15T23:13:02Z","receivedAt":"2022-04-15T23:13:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miklos Vajna <vmiklos@vmiklos.hu> writes:\n\n> This is similar to --since, but it will filter out not matching commits,\n> rather than stopping at the first not matching commit.\n>\n> This is useful if you e.g. want to list the commits from the last year,\n> but one odd commit has a bad commit date and that would hide lots of\n> earlier commits in that range.\n>\n> The behavior of --since is left unchanged, since it's valid to depend on\n> its current behavior.\n\nThe above is good if it were a new --since-as-filter option.  Adding\n\"--as-filter\" as a separate option to be used in conjunction with\n\"--since\" would fundamentally mean \"The behaviour of --since is left\nunchanged\" cannot possibly be true.\n\nA suggested rewrite.  I label each section and highlight its point,\nbut in the end product, you should just separate them with a blank\nline, making each of them into its own paragraph.\n\nTitle (single line, short and to the point)\n\n    log: \"--as-filter\" option adjusts how \"--since\" cut-off works\n\nIntro (observation of the current state)\n\n    The \"--since=<time>\" option of \"git log\" limits the commits\n    displayed by the command by stopping the traversal once it\n    sees a commit whose timestamp is older than the given time and\n    not digging further into its parents.\n\nProblem description (pros and cons of the current state)\n\n    This is OK in a history where a commit always has a newer\n    timestamp than any of its parents'.  Once you see a commit older\n    than the given <time>, all ancestor commits of it are even older\n    than the time anyway.  It poses, however, a problem when there\n    is a commit with a wrong timestamp that makes it appear older\n    than its parents.  Stopping traversal at the \"incorrectly old\"\n    commit will hide its ancestors that are newer than that wrong\n    commit and are newer than the cut-off time given with the --since\n    option.  --max-age and --after being the synonyms to --since,\n    they share the same issue.\n\nSolution (give orders to the codebase to \"be like so\")\n\n    Add a new \"--as-filter\" option that modifies how \"--since=<time>\"\n    is used.  Instead of stopping the traversal to hide an old\n    enough commit and its all ancestors, exclude commits with an old\n    timestamp from the output but still keep digging the history.\n\nOther comments (caveats, etc.)\n\n    Without other traversal stopping options, this will force the\n    command in \"git log\" family to dig down the history to the root.\n    It may be an acceptable cost for a small project with short\n    history and many commits with screwy timestamps.\n\n> diff --git a/revision.c b/revision.c\n> index 7d435f8048..ff018c3976 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -1440,6 +1440,9 @@ static int limit_list(struct rev_info *revs)\n>  \t\tif (revs->min_age != -1 && (commit->date > revs->min_age) &&\n>  \t\t    !revs->line_level_traverse)\n>  \t\t\tcontinue;\n> +\t\tif (revs->max_age != -1 && revs->as_filter && (commit->date < revs->max_age) &&\n> +\t\t    !revs->line_level_traverse)\n\nThat's an overly long line.\n\n> +\t\t\tcontinue;\n\nIn any case, isn't this too late in limit_list() to adjust --since?\nThere is this logic earlier in the loop:\n\n\twhile (original_list) {\n\t\tstruct commit *commit = pop_commit(&original_list);\n\t\tstruct object *obj = &commit->object;\n\t\tshow_early_output_fn_t show;\n\n\t\tif (commit == interesting_cache)\n\t\t\tinteresting_cache = NULL;\n\n\t\tif (revs->max_age != -1 && (commit->date < revs->max_age))\n\t\t\tobj->flags |= UNINTERESTING;\n\t\tif (process_parents(revs, commit, &original_list, NULL) < 0)\n\t\t\treturn -1;\n\nWe look at max_age and if it is set and newer than the timestamp of\nthe commit we are looking at, we immediately mark that commit\nuninteresting so that this and all its ancestors are excluded from\nthe output.  Don't you want to disable that logic so that the\ntraversal continues?\n\n> @@ -3862,6 +3868,9 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi\n>  \tif (revs->min_age != -1 &&\n>  \t    comparison_date(revs, commit) > revs->min_age)\n>  \t\t\treturn commit_ignore;\n> +\tif (revs->max_age != -1 && revs->as_filter &&\n> +\t    comparison_date(revs, commit) < revs->max_age)\n> +\t\t\treturn commit_ignore;\n>  \tif (revs->min_parents || (revs->max_parents >= 0)) {\n>  \t\tint n = commit_list_count(commit->parents);\n>  \t\tif ((n < revs->min_parents) ||\n\nI am not sure how --since should affect (or not affect) the history\nsimplification, but my gut feeling says this may cause unintended\nfallout.  I do not have time to dig deeper today, but something to\nkeep in mind...\n\n> diff --git a/t/t4217-log-limit.sh b/t/t4217-log-limit.sh\n> new file mode 100755\n> index 0000000000..2a3705c714\n> --- /dev/null\n> +++ b/t/t4217-log-limit.sh\n> @@ -0,0 +1,36 @@\n> +#!/bin/sh\n> +\n> +test_description='git log with filter options limiting the output'\n\nOK.\n\n> +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n> +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n\nDoes any of the tests in this file care?  The above is a mechanism\nprimarily for transitioning scripts that were written long time ago,\nand newly written tests that do not have to care about the initial\nbranch should not need it (they can instead explicitly create a\nbranch and work on it, if they really need to refer the initial\nbranch by name, but as far as I can tell, none your test in this\nfile even needs to refer to any branch with any name).\n\n> +\n> +. ./test-lib.sh\n> +\n> +GIT_TEST_COMMIT_GRAPH=0\n> +GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0\n\nDo these matter, and should these matter?  Does anybody export them\nto make any difference in this test?\n\n> +test_expect_success 'setup test' '\n> +\tgit init &&\n> +\techo a > file &&\n\nStyle:\n\n\techo a >file &&\n\n(cf. Documentation/CodingGuidelines)\n\n> +\tgit add file &&\n> +\tGIT_COMMITTER_DATE=\"2021-02-01 0:00\" git commit -m init &&\n\nIt is somewhat annoying to see 00:00 spelled as 0:00 here (and below).\n\n> +\techo a >> file &&\n> +\tgit add file &&\n> +\tGIT_COMMITTER_DATE=\"2022-02-01 0:00\" git commit -m first &&\n> +\techo a >> file &&\n> +\tgit add file &&\n> +\tGIT_COMMITTER_DATE=\"2021-03-01 0:00\" git commit -m second &&\n> +\techo a >> file &&\n> +\tgit add file &&\n> +\tGIT_COMMITTER_DATE=\"2022-03-01 0:00\" git commit -m third\n> +'\n> +\n> +test_expect_success 'git log --since=... --as-filter' '\n> +\tgit log --since=\"2022-01-01\" --as-filter --pretty=\"format:%s\" > actual &&\n\nI think you meant --format=%s (which is --pretty=\"tformat:%s\"), as\nyou do not want \"actual\" to end in an incomplete line.  The same\n\"no space between the redirection operator and its target\" applies\nhere as everywhere else.\n\n> +\t! test_i18ngrep init actual &&\n> +\ttest_i18ngrep first actual &&\n> +\t! test_i18ngrep second actual &&\n> +\ttest_i18ngrep third actual\n\ntest_i18ngrep -> grep\n\nBut stepping back a bit, do we or do we not care the order in which\nfirst and third appear in the \"actual\" file?  It's not like we have\na mergy history and two commits that cannot topologically be\ncompared.  We have a simple linear single strand of pearls here, and\nif you start traversing from the tip, you'll reliably see third\nfirst, then second, then first and then finally init, in that order\nand in no other order.  So, wouldn't we be better off writing it\nmore like ...\n\n\tgit log --since=2022-01-01 --as-filter --format=%s >actual &&\n\tcat >expect <<-\\EOF &&\n\tthird\n\tfirst\n\tEOF\n\ttest_cmp expect actual\n\n... i.e. explicitly spell out what we expect not to change?\n\nThanks.\n"},{"id":"453774","messageId":"YlrRT6NAgW6nK2fc@vmiklos.hu","threadId":"57647","inReplyTo":"xmqqmtgm9c01.fsf@gitster.g","subject":"[PATCH v5] log: \"--as-filter\" option adjusts how \"--since\" cut-off works","fromName":"Miklos Vajna","fromEmail":"vmiklos@vmiklos.hu","sentAt":"2022-04-16T14:23:11Z","receivedAt":"2022-04-16T14:23:20Z","isPatch":true,"sender":{"key":"vmiklos@vmiklos.hu","avatar":"https://avatars.githubusercontent.com/u/13838?v=4"},"body":"The \"--since=<time>\" option of \"git log\" limits the commits displayed by\nthe command by stopping the traversal once it sees a commit whose\ntimestamp is older than the given time and not digging further into its\nparents.\n\nThis is OK in a history where a commit always has a newer timestamp than\nany of its parents'.  Once you see a commit older than the given <time>,\nall ancestor commits of it are even older than the time anyway.  It\nposes, however, a problem when there is a commit with a wrong timestamp\nthat makes it appear older than its parents.  Stopping traversal at the\n\"incorrectly old\" commit will hide its ancestors that are newer than\nthat wrong commit and are newer than the cut-off time given with the\n--since option.  --max-age and --after being the synonyms to --since,\nthey share the same issue.\n\nAdd a new \"--as-filter\" option that modifies how \"--since=<time>\" is\nused.  Instead of stopping the traversal to hide an old enough commit\nand its all ancestors, exclude commits with an old timestamp from the\noutput but still keep digging the history.\n\nWithout other traversal stopping options, this will force the command in\n\"git log\" family to dig down the history to the root.  It may be an\nacceptable cost for a small project with short history and many commits\nwith screwy timestamps.\n\nSigned-off-by: Miklos Vajna <vmiklos@vmiklos.hu>\n---\n\nHi Junio,\n\nOn Fri, Apr 15, 2022 at 04:13:02PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> A suggested rewrite.\n\nThanks a lot, I've updated the commit message to match this.\n\n> > +\t\tif (revs->max_age != -1 && revs->as_filter && (commit->date < revs->max_age) &&\n> > +\t\t    !revs->line_level_traverse)\n> \n> That's an overly long line.\n\nFixed.\n\n> > +\t\t\tcontinue;\n> \n> In any case, isn't this too late in limit_list() to adjust --since?\n> There is this logic earlier in the loop:\n> \n> \twhile (original_list) {\n> \t\tstruct commit *commit = pop_commit(&original_list);\n> \t\tstruct object *obj = &commit->object;\n> \t\tshow_early_output_fn_t show;\n> \n> \t\tif (commit == interesting_cache)\n> \t\t\tinteresting_cache = NULL;\n> \n> \t\tif (revs->max_age != -1 && (commit->date < revs->max_age))\n> \t\t\tobj->flags |= UNINTERESTING;\n> \t\tif (process_parents(revs, commit, &original_list, NULL) < 0)\n> \t\t\treturn -1;\n> \n> We look at max_age and if it is set and newer than the timestamp of\n> the commit we are looking at, we immediately mark that commit\n> uninteresting so that this and all its ancestors are excluded from\n> the output.  Don't you want to disable that logic so that the\n> traversal continues?\n\nYes, a \"and not as-filter\" was missing there, I've added it now.\n\n> \n> > @@ -3862,6 +3868,9 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi\n> >  \tif (revs->min_age != -1 &&\n> >  \t    comparison_date(revs, commit) > revs->min_age)\n> >  \t\t\treturn commit_ignore;\n> > +\tif (revs->max_age != -1 && revs->as_filter &&\n> > +\t    comparison_date(revs, commit) < revs->max_age)\n> > +\t\t\treturn commit_ignore;\n> >  \tif (revs->min_parents || (revs->max_parents >= 0)) {\n> >  \t\tint n = commit_list_count(commit->parents);\n> >  \t\tif ((n < revs->min_parents) ||\n> \n> I am not sure how --since should affect (or not affect) the history\n> simplification, but my gut feeling says this may cause unintended\n> fallout.  I do not have time to dig deeper today, but something to\n> keep in mind...\n\nHm, this works exactly as --after (which is already filtering), this is the\nactual place where this patch implements the filtering way of --since.  Based\non this, it feels safe to me. But perhaps I missed something.\n\n> > +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n> > +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n> \n> Does any of the tests in this file care?  The above is a mechanism\n> primarily for transitioning scripts that were written long time ago,\n> and newly written tests that do not have to care about the initial\n> branch should not need it (they can instead explicitly create a\n> branch and work on it, if they really need to refer the initial\n> branch by name, but as far as I can tell, none your test in this\n> file even needs to refer to any branch with any name).\n\nIndeed not needed, I now removed these.\n\n> > +GIT_TEST_COMMIT_GRAPH=0\n> > +GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0\n> \n> Do these matter, and should these matter?  Does anybody export them\n> to make any difference in this test?\n\nAha, doesn't matter, removed.\n\n> > +test_expect_success 'setup test' '\n> > +\tgit init &&\n> > +\techo a > file &&\n> \n> Style:\n> \n> \techo a >file &&\n\nFixed.\n\n> > +\tGIT_COMMITTER_DATE=\"2021-02-01 0:00\" git commit -m init &&\n> \n> It is somewhat annoying to see 00:00 spelled as 0:00 here (and below).\n\nFixed.\n\n> > +test_expect_success 'git log --since=... --as-filter' '\n> > +\tgit log --since=\"2022-01-01\" --as-filter --pretty=\"format:%s\" > actual &&\n> \n> I think you meant --format=%s (which is --pretty=\"tformat:%s\"), as\n> you do not want \"actual\" to end in an incomplete line.  The same\n> \"no space between the redirection operator and its target\" applies\n> here as everywhere else.\n\nIndeed, fixed.\n\n> \n> > +\t! test_i18ngrep init actual &&\n> > +\ttest_i18ngrep first actual &&\n> > +\t! test_i18ngrep second actual &&\n> > +\ttest_i18ngrep third actual\n> \n> test_i18ngrep -> grep\n> \n> But stepping back a bit, do we or do we not care the order in which\n> first and third appear in the \"actual\" file?  It's not like we have\n> a mergy history and two commits that cannot topologically be\n> compared.  We have a simple linear single strand of pearls here, and\n> if you start traversing from the tip, you'll reliably see third\n> first, then second, then first and then finally init, in that order\n> and in no other order.  So, wouldn't we be better off writing it\n> more like ...\n> \n> \tgit log --since=2022-01-01 --as-filter --format=%s >actual &&\n> \tcat >expect <<-\\EOF &&\n> \tthird\n> \tfirst\n> \tEOF\n> \ttest_cmp expect actual\n> \n> ... i.e. explicitly spell out what we expect not to change?\n\nAha, I reworked the test to avoid the grep here.\n\nRegards,\n\nMiklos\n\n Documentation/rev-list-options.txt |  6 ++++++\n revision.c                         | 16 +++++++++++---\n revision.h                         |  1 +\n t/t4217-log-limit.sh               | 32 ++++++++++++++++++++++++++++\n t/t4218-blame-limit.sh             | 34 ++++++++++++++++++++++++++++++\n 5 files changed, 86 insertions(+), 3 deletions(-)\n create mode 100755 t/t4217-log-limit.sh\n create mode 100755 t/t4218-blame-limit.sh\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex fd4f4e26c9..354bd29f10 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -350,6 +350,12 @@ The following options select the commits to be shown:\n <paths>::\n \tCommits modifying the given <paths> are selected.\n \n+--as-filter::\n+\tWhen combined with `--max-age=<date>`, `--since=<date>` or\n+\t`--after=<date>`, show all commits more recent than a specific date. This\n+\tvisits all commits in the range, rather than stopping at the first commit\n+\twhich is older than a specific date.\n+\n --simplify-by-decoration::\n \tCommits that are referred by some branch or tag are selected.\n \ndiff --git a/revision.c b/revision.c\nindex 7d435f8048..f9970bd8ac 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1426,7 +1426,8 @@ static int limit_list(struct rev_info *revs)\n \t\tif (commit == interesting_cache)\n \t\t\tinteresting_cache = NULL;\n \n-\t\tif (revs->max_age != -1 && (commit->date < revs->max_age))\n+\t\tif (revs->max_age != -1 && !revs->as_filter &&\n+\t\t\t(commit->date < revs->max_age))\n \t\t\tobj->flags |= UNINTERESTING;\n \t\tif (process_parents(revs, commit, &original_list, NULL) < 0)\n \t\t\treturn -1;\n@@ -1440,6 +1441,9 @@ static int limit_list(struct rev_info *revs)\n \t\tif (revs->min_age != -1 && (commit->date > revs->min_age) &&\n \t\t    !revs->line_level_traverse)\n \t\t\tcontinue;\n+\t\tif (revs->max_age != -1 && revs->as_filter &&\n+\t\t\t(commit->date < revs->max_age) && !revs->line_level_traverse)\n+\t\t\tcontinue;\n \t\tdate = commit->date;\n \t\tp = &commit_list_insert(commit, p)->next;\n \n@@ -1838,6 +1842,7 @@ void repo_init_revisions(struct repository *r,\n \trevs->dense = 1;\n \trevs->prefix = prefix;\n \trevs->max_age = -1;\n+\trevs->as_filter = 0;\n \trevs->min_age = -1;\n \trevs->skip_count = -1;\n \trevs->max_count = -1;\n@@ -2218,6 +2223,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t} else if ((argcount = parse_long_opt(\"since\", argv, &optarg))) {\n \t\trevs->max_age = approxidate(optarg);\n \t\treturn argcount;\n+\t} else if (!strcmp(arg, \"--as-filter\")) {\n+\t\trevs->as_filter = 1;\n \t} else if ((argcount = parse_long_opt(\"after\", argv, &optarg))) {\n \t\trevs->max_age = approxidate(optarg);\n \t\treturn argcount;\n@@ -3365,7 +3372,7 @@ static void explore_walk_step(struct rev_info *revs)\n \tif (revs->sort_order == REV_SORT_BY_AUTHOR_DATE)\n \t\trecord_author_date(&info->author_date, c);\n \n-\tif (revs->max_age != -1 && (c->date < revs->max_age))\n+\tif (revs->max_age != -1 && !revs->as_filter && (c->date < revs->max_age))\n \t\tc->object.flags |= UNINTERESTING;\n \n \tif (process_parents(revs, c, NULL, NULL) < 0)\n@@ -3862,6 +3869,9 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi\n \tif (revs->min_age != -1 &&\n \t    comparison_date(revs, commit) > revs->min_age)\n \t\t\treturn commit_ignore;\n+\tif (revs->max_age != -1 && revs->as_filter &&\n+\t    comparison_date(revs, commit) < revs->max_age)\n+\t\t\treturn commit_ignore;\n \tif (revs->min_parents || (revs->max_parents >= 0)) {\n \t\tint n = commit_list_count(commit->parents);\n \t\tif ((n < revs->min_parents) ||\n@@ -4019,7 +4029,7 @@ static struct commit *get_revision_1(struct rev_info *revs)\n \t\t * that we'd otherwise have done in limit_list().\n \t\t */\n \t\tif (!revs->limited) {\n-\t\t\tif (revs->max_age != -1 &&\n+\t\t\tif (revs->max_age != -1 && !revs->as_filter &&\n \t\t\t    comparison_date(revs, commit) < revs->max_age)\n \t\t\t\tcontinue;\n \ndiff --git a/revision.h b/revision.h\nindex 5bc59c7bfe..fe37ebd83d 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -263,6 +263,7 @@ struct rev_info {\n \tint skip_count;\n \tint max_count;\n \ttimestamp_t max_age;\n+\tint as_filter;\n \ttimestamp_t min_age;\n \tint min_parents;\n \tint max_parents;\ndiff --git a/t/t4217-log-limit.sh b/t/t4217-log-limit.sh\nnew file mode 100755\nindex 0000000000..41df7f0f0f\n--- /dev/null\n+++ b/t/t4217-log-limit.sh\n@@ -0,0 +1,32 @@\n+#!/bin/sh\n+\n+test_description='git log with filter options limiting the output'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup test' '\n+\tgit init &&\n+\techo a >file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2021-02-01 00:00\" git commit -m init &&\n+\techo a >>file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2022-02-01 00:00\" git commit -m first &&\n+\techo a >>file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2021-03-01 00:00\" git commit -m second &&\n+\techo a >>file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2022-03-01 00:00\" git commit -m third\n+'\n+\n+test_expect_success 'git log --since=... --as-filter' '\n+\tgit log --since=\"2022-01-01\" --as-filter --format=%s >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tthird\n+\tfirst\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_done\ndiff --git a/t/t4218-blame-limit.sh b/t/t4218-blame-limit.sh\nnew file mode 100755\nindex 0000000000..bef7cf7d48\n--- /dev/null\n+++ b/t/t4218-blame-limit.sh\n@@ -0,0 +1,34 @@\n+#!/bin/sh\n+\n+test_description='git blame with filter options limiting the output'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup test' '\n+\tgit init &&\n+\techo a >file &&\n+\tgit add file &&\n+\tGIT_AUTHOR_DATE=\"2020-01-01 00:00\" GIT_COMMITTER_DATE=\"2020-01-01 00:00\" git commit -m init &&\n+\techo a >>file &&\n+\tgit add file &&\n+\tGIT_AUTHOR_DATE=\"2020-02-01 00:00\" GIT_COMMITTER_DATE=\"2020-02-01 00:00\" git commit -m first &&\n+\techo a >>file &&\n+\tgit add file &&\n+\tGIT_AUTHOR_DATE=\"2020-03-01 00:00\" GIT_COMMITTER_DATE=\"2020-03-01 00:00\" git commit -m second &&\n+\techo a >>file &&\n+\tgit add file &&\n+\tGIT_AUTHOR_DATE=\"2020-04-01 00:00\" GIT_COMMITTER_DATE=\"2020-04-01 00:00\" git commit -m third\n+'\n+\n+test_expect_success 'git blame --since=...' '\n+\tgit blame --since=\"2020-02-15\" file >actual &&\n+\tcat >expect <<-\\EOF &&\n+\t^c7bc5ce (A U Thor 2020-02-01 00:00:00 +0000 1) a\n+\t^c7bc5ce (A U Thor 2020-02-01 00:00:00 +0000 2) a\n+\t33fc0d13 (A U Thor 2020-03-01 00:00:00 +0000 3) a\n+\tec76e003 (A U Thor 2020-04-01 00:00:00 +0000 4) a\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n2.34.1\n\n"},{"id":"454224","messageId":"YmJQNKdMj3Bp2RJE@vmiklos.hu","threadId":"57647","inReplyTo":"YlrRT6NAgW6nK2fc@vmiklos.hu","subject":"Re: [PATCH v5] log: \"--as-filter\" option adjusts how \"--since\" cut-off works","fromName":"Miklos Vajna","fromEmail":"vmiklos@vmiklos.hu","sentAt":"2022-04-22T06:50:28Z","receivedAt":"2022-04-22T06:50:37Z","isPatch":true,"sender":{"key":"vmiklos@vmiklos.hu","avatar":"https://avatars.githubusercontent.com/u/13838?v=4"},"body":"Hi,\n\nOn Sat, Apr 16, 2022 at 04:23:14PM +0200, Miklos Vajna <vmiklos@vmiklos.hu> wrote:\n> Thanks a lot, I've updated the commit message to match this.\n\nIs there anything else I should tweak in v5 to get this into a topic\nbranch?\n\nThanks,\n\nMiklos\n"},{"id":"454273","messageId":"xmqqzgkd7y42.fsf@gitster.g","threadId":"57647","inReplyTo":"xmqqilrfk14q.fsf@gitster.g","subject":"Re: git log --since to not stop after first old commit?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-22T18:48:45Z","receivedAt":"2022-04-22T19:02:59Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> When you do have the cycles perhaps it is worth considering whether\n>> splitting it up, so that --as-filter is a modifier for traversal stoppers,\n>> would avoid the problem of proliferating options.   Eg, instead of saying\n>> --since-as-filter you would say --since ... --as-filter. That way the\n>> stoppers where \"filter like behavior\" made sense could just check if the\n>> --as-filter flag was set.\n>\n> Yes, that has exactly the opposite problem I wanted to warn us about\n> by sending an extra message (to which you are reponding to).  If we\n> have (or can have) very many traversal stopping option, it might\n> make sense to have --as-filter as a modifier and avoid doubling the\n> number of options, but if we only have very few (and fundamentally\n> cannot have more than very few), then giving each of these very few\n> --X its own --X-as-filter variant would probably make more sense.\n> Because end users would probably not know which ones are inherently\n> filters and will not be affected with --as-filter modifier, it would\n> help them understand if we give them independent --since-as-filter\n> option and document it separately, if there aren't many of them.\n>\n> Besides, if we had very few but still multiple of them, --X and\n> --Y-as-filter can be combined to say \"X stops as before, but Y is\n> applied as filter\", which is strictly more expressive than a\n> separate --as-filter modifier.\n>\n> So that is why I threw out the message for those interested in the\n> topic to first think about.  I know we agree that --since may be a\n> good candidate to have these two flavours of behaviour.  I do not\n> think anybody carefully thought about existing options to see if\n> there are many like --since that want two flavours, let alone\n> possible options we have said in the past that we may want to have\n> but not yet added.\n\nNow I had some time to think about it, I have a feeling that it is\nquite unlikely for us to add traversal stopper other than since, so\nhaving a separate \"--as-filter\" would probably be more confusing\nthan adding \"--since-as-filter\", stressing on \"only the 'show\ncommits with timestamp after this one' has two variants\".\n\nThanks.\n"},{"id":"454276","messageId":"YmMJqvKN6itSHEZW@vmiklos.hu","threadId":"57647","inReplyTo":"xmqqzgkd7y42.fsf@gitster.g","subject":"[PATCH v6] log: \"--since-as-filter\" option is a non-terminating \"--since\" variant","fromName":"Miklos Vajna","fromEmail":"vmiklos@vmiklos.hu","sentAt":"2022-04-22T20:01:46Z","receivedAt":"2022-04-22T21:07:41Z","isPatch":true,"sender":{"key":"vmiklos@vmiklos.hu","avatar":"https://avatars.githubusercontent.com/u/13838?v=4"},"body":"The \"--since=<time>\" option of \"git log\" limits the commits displayed by\nthe command by stopping the traversal once it sees a commit whose\ntimestamp is older than the given time and not digging further into its\nparents.\n\nThis is OK in a history where a commit always has a newer timestamp than\nany of its parents'.  Once you see a commit older than the given <time>,\nall ancestor commits of it are even older than the time anyway.  It\nposes, however, a problem when there is a commit with a wrong timestamp\nthat makes it appear older than its parents.  Stopping traversal at the\n\"incorrectly old\" commit will hide its ancestors that are newer than\nthat wrong commit and are newer than the cut-off time given with the\n--since option.  --max-age and --after being the synonyms to --since,\nthey share the same issue.\n\nAdd a new \"--since-as-filter\" option that is a variant of\n\"--since=<time>\".  Instead of stopping the traversal to hide an old\nenough commit and its all ancestors, exclude commits with an old\ntimestamp from the output but still keep digging the history.\n\nWithout other traversal stopping options, this will force the command in\n\"git log\" family to dig down the history to the root.  It may be an\nacceptable cost for a small project with short history and many commits\nwith screwy timestamps.\n\nIt is quite unlikely for us to add traversal stopper other than since,\nso have this as a --since-as-filter option, rather than a separate\n--as-filter, that would be probably more confusing.\n\nSigned-off-by: Miklos Vajna <vmiklos@vmiklos.hu>\n---\n\nHi Junio,\n\nOn Fri, Apr 22, 2022 at 11:48:45AM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> Now I had some time to think about it, I have a feeling that it is\n> quite unlikely for us to add traversal stopper other than since, so\n> having a separate \"--as-filter\" would probably be more confusing\n> than adding \"--since-as-filter\", stressing on \"only the 'show\n> commits with timestamp after this one' has two variants\".\n\nI'm fine with this approach, it goes back to the initial version. I \nrather reworked v5 in practice, to keep the other improvements.\n\nHere is a patch that does this.\n\nRegards,\n\nMiklos\n\n Documentation/rev-list-options.txt |  5 +++++\n revision.c                         | 10 +++++++++\n revision.h                         |  1 +\n t/t4217-log-limit.sh               | 32 ++++++++++++++++++++++++++++\n t/t4218-blame-limit.sh             | 34 ++++++++++++++++++++++++++++++\n 5 files changed, 82 insertions(+)\n create mode 100755 t/t4217-log-limit.sh\n create mode 100755 t/t4218-blame-limit.sh\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex fd4f4e26c9..195e74eec6 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -25,6 +25,11 @@ ordering and formatting options, such as `--reverse`.\n --after=<date>::\n \tShow commits more recent than a specific date.\n \n+--since-as-filter=<date>::\n+\tShow all commits more recent than a specific date. This visits\n+\tall commits in the range, rather than stopping at the first commit which\n+\tis older than a specific date.\n+\n --until=<date>::\n --before=<date>::\n \tShow commits older than a specific date.\ndiff --git a/revision.c b/revision.c\nindex 7d435f8048..ee933a11c7 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1440,6 +1440,9 @@ static int limit_list(struct rev_info *revs)\n \t\tif (revs->min_age != -1 && (commit->date > revs->min_age) &&\n \t\t    !revs->line_level_traverse)\n \t\t\tcontinue;\n+\t\tif (revs->max_age_as_filter != -1 &&\n+\t\t\t(commit->date < revs->max_age) && !revs->line_level_traverse)\n+\t\t\tcontinue;\n \t\tdate = commit->date;\n \t\tp = &commit_list_insert(commit, p)->next;\n \n@@ -1838,6 +1841,7 @@ void repo_init_revisions(struct repository *r,\n \trevs->dense = 1;\n \trevs->prefix = prefix;\n \trevs->max_age = -1;\n+\trevs->max_age_as_filter = -1;\n \trevs->min_age = -1;\n \trevs->skip_count = -1;\n \trevs->max_count = -1;\n@@ -2218,6 +2222,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t} else if ((argcount = parse_long_opt(\"since\", argv, &optarg))) {\n \t\trevs->max_age = approxidate(optarg);\n \t\treturn argcount;\n+\t} else if ((argcount = parse_long_opt(\"since-as-filter\", argv, &optarg))) {\n+\t\trevs->max_age_as_filter = approxidate(optarg);\n+\t\treturn argcount;\n \t} else if ((argcount = parse_long_opt(\"after\", argv, &optarg))) {\n \t\trevs->max_age = approxidate(optarg);\n \t\treturn argcount;\n@@ -3862,6 +3869,9 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi\n \tif (revs->min_age != -1 &&\n \t    comparison_date(revs, commit) > revs->min_age)\n \t\t\treturn commit_ignore;\n+\tif (revs->max_age_as_filter != -1 &&\n+\t    comparison_date(revs, commit) < revs->max_age_as_filter)\n+\t\t\treturn commit_ignore;\n \tif (revs->min_parents || (revs->max_parents >= 0)) {\n \t\tint n = commit_list_count(commit->parents);\n \t\tif ((n < revs->min_parents) ||\ndiff --git a/revision.h b/revision.h\nindex 5bc59c7bfe..e80c148b19 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -263,6 +263,7 @@ struct rev_info {\n \tint skip_count;\n \tint max_count;\n \ttimestamp_t max_age;\n+\ttimestamp_t max_age_as_filter;\n \ttimestamp_t min_age;\n \tint min_parents;\n \tint max_parents;\ndiff --git a/t/t4217-log-limit.sh b/t/t4217-log-limit.sh\nnew file mode 100755\nindex 0000000000..587cd0e386\n--- /dev/null\n+++ b/t/t4217-log-limit.sh\n@@ -0,0 +1,32 @@\n+#!/bin/sh\n+\n+test_description='git log with filter options limiting the output'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup test' '\n+\tgit init &&\n+\techo a >file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2021-02-01 00:00\" git commit -m init &&\n+\techo a >>file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2022-02-01 00:00\" git commit -m first &&\n+\techo a >>file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2021-03-01 00:00\" git commit -m second &&\n+\techo a >>file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2022-03-01 00:00\" git commit -m third\n+'\n+\n+test_expect_success 'git log --since-as-filter=...' '\n+\tgit log --since-as-filter=\"2022-01-01\" --format=%s >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tthird\n+\tfirst\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_done\ndiff --git a/t/t4218-blame-limit.sh b/t/t4218-blame-limit.sh\nnew file mode 100755\nindex 0000000000..bef7cf7d48\n--- /dev/null\n+++ b/t/t4218-blame-limit.sh\n@@ -0,0 +1,34 @@\n+#!/bin/sh\n+\n+test_description='git blame with filter options limiting the output'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup test' '\n+\tgit init &&\n+\techo a >file &&\n+\tgit add file &&\n+\tGIT_AUTHOR_DATE=\"2020-01-01 00:00\" GIT_COMMITTER_DATE=\"2020-01-01 00:00\" git commit -m init &&\n+\techo a >>file &&\n+\tgit add file &&\n+\tGIT_AUTHOR_DATE=\"2020-02-01 00:00\" GIT_COMMITTER_DATE=\"2020-02-01 00:00\" git commit -m first &&\n+\techo a >>file &&\n+\tgit add file &&\n+\tGIT_AUTHOR_DATE=\"2020-03-01 00:00\" GIT_COMMITTER_DATE=\"2020-03-01 00:00\" git commit -m second &&\n+\techo a >>file &&\n+\tgit add file &&\n+\tGIT_AUTHOR_DATE=\"2020-04-01 00:00\" GIT_COMMITTER_DATE=\"2020-04-01 00:00\" git commit -m third\n+'\n+\n+test_expect_success 'git blame --since=...' '\n+\tgit blame --since=\"2020-02-15\" file >actual &&\n+\tcat >expect <<-\\EOF &&\n+\t^c7bc5ce (A U Thor 2020-02-01 00:00:00 +0000 1) a\n+\t^c7bc5ce (A U Thor 2020-02-01 00:00:00 +0000 2) a\n+\t33fc0d13 (A U Thor 2020-03-01 00:00:00 +0000 3) a\n+\tec76e003 (A U Thor 2020-04-01 00:00:00 +0000 4) a\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n2.34.1\n\n"},{"id":"454309","messageId":"xmqqee1o6a68.fsf@gitster.g","threadId":"57647","inReplyTo":"YmMJqvKN6itSHEZW@vmiklos.hu","subject":"Re: [PATCH v6] log: \"--since-as-filter\" option is a non-terminating \"--since\" variant","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-22T22:11:11Z","receivedAt":"2022-04-22T22:48:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miklos Vajna <vmiklos@vmiklos.hu> writes:\n\n> The \"--since=<time>\" option of \"git log\" limits the commits displayed by\n> the command by stopping the traversal once it sees a commit whose\n> timestamp is older than the given time and not digging further into its\n> parents.\n>\n> This is OK in a history where a commit always has a newer timestamp than\n> any of its parents'.  Once you see a commit older than the given <time>,\n> all ancestor commits of it are even older than the time anyway.  It\n> poses, however, a problem when there is a commit with a wrong timestamp\n> that makes it appear older than its parents.  Stopping traversal at the\n> \"incorrectly old\" commit will hide its ancestors that are newer than\n> that wrong commit and are newer than the cut-off time given with the\n> --since option.  --max-age and --after being the synonyms to --since,\n> they share the same issue.\n>\n> Add a new \"--since-as-filter\" option that is a variant of\n> \"--since=<time>\".  Instead of stopping the traversal to hide an old\n> enough commit and its all ancestors, exclude commits with an old\n> timestamp from the output but still keep digging the history.\n>\n> Without other traversal stopping options, this will force the command in\n> \"git log\" family to dig down the history to the root.  It may be an\n> acceptable cost for a small project with short history and many commits\n> with screwy timestamps.\n>\n> It is quite unlikely for us to add traversal stopper other than since,\n> so have this as a --since-as-filter option, rather than a separate\n> --as-filter, that would be probably more confusing.\n>\n> Signed-off-by: Miklos Vajna <vmiklos@vmiklos.hu>\n> ---\n>\n> Hi Junio,\n>\n> On Fri, Apr 22, 2022 at 11:48:45AM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n>> Now I had some time to think about it, I have a feeling that it is\n>> quite unlikely for us to add traversal stopper other than since, so\n>> having a separate \"--as-filter\" would probably be more confusing\n>> than adding \"--since-as-filter\", stressing on \"only the 'show\n>> commits with timestamp after this one' has two variants\".\n>\n> I'm fine with this approach, it goes back to the initial version. I \n> rather reworked v5 in practice, to keep the other improvements.\n>\n> Here is a patch that does this.\n>\n> Regards,\n>\n> Miklos\n\nThanks.  Now 2.36 release is behind us, let's queue this in 'seen'\nand after getting reviewed by somebody else---I do not trust\nanything looked at by me and nobody else---merge it down, aiming for\ngraduation during this cycle.\n\n> +--since-as-filter=<date>::\n> +\tShow all commits more recent than a specific date. This visits\n> +\tall commits in the range, rather than stopping at the first commit which\n> +\tis older than a specific date.\n\n> diff --git a/revision.c b/revision.c\n> index 7d435f8048..ee933a11c7 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -1440,6 +1440,9 @@ static int limit_list(struct rev_info *revs)\n>  \t\tif (revs->min_age != -1 && (commit->date > revs->min_age) &&\n>  \t\t    !revs->line_level_traverse)\n>  \t\t\tcontinue;\n\nMuch ealrier than this part in the same loop there is\n\n\t\tif (revs->max_age != -1 && (commit->date < revs->max_age))\n\t\t\tobj->flags |= UNINTERESTING;\n\t\tif (process_parents(revs, commit, &original_list, NULL) < 0)\n\t\t\treturn -1;\n\nwhich taints the commit we are looking at as UNINTERESTING, cutting\nthe traversal down to its parents, if its timestamp is older than\nmax_age, and it would need to be taught not to do that, no?\n\nAh, OK, max_age_as_filter is *NOT* a boolean (false is -1 and true\nis something else) but is an independent timestamp and the\nexpectation is that max_age is left to be -1 when --since is not in\nuse.  --since-as-filter's timestamp is stored in max_age_as_filter\nso the earlier \"compare with max_age and use UNINTERESTING bit to\nstop traversal\" code does not have to be modified.\n\n> +\t\tif (revs->max_age_as_filter != -1 &&\n> +\t\t\t(commit->date < revs->max_age) && !revs->line_level_traverse)\n> +\t\t\tcontinue;\n>  \t\tdate = commit->date;\n>  \t\tp = &commit_list_insert(commit, p)->next;\n\nAnd if that is the assumption of this code, shouldn't this part be\nusing \"if max_age is set (i.e. not -1) and the commit is older than\nthat and we are not doing -La,b traversal\"?  \"--since-as-filter\"\nbeing used does not mean max_age must be set to a value that can be\ncompared to timestamps in commits in the world order of this version\nof the patch, right?\n\n> @@ -1838,6 +1841,7 @@ void repo_init_revisions(struct repository *r,\n>  \trevs->dense = 1;\n>  \trevs->prefix = prefix;\n>  \trevs->max_age = -1;\n> +\trevs->max_age_as_filter = -1;\n>  \trevs->min_age = -1;\n>  \trevs->skip_count = -1;\n>  \trevs->max_count = -1;\n> @@ -2218,6 +2222,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n>  \t} else if ((argcount = parse_long_opt(\"since\", argv, &optarg))) {\n>  \t\trevs->max_age = approxidate(optarg);\n>  \t\treturn argcount;\n> +\t} else if ((argcount = parse_long_opt(\"since-as-filter\", argv, &optarg))) {\n> +\t\trevs->max_age_as_filter = approxidate(optarg);\n> +\t\treturn argcount;\n\nOK, so max_age_as_filter is a timestamp to be compared with commits\nwhen --since-as-filter option is given.\n\n> @@ -3862,6 +3869,9 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi\n>  \tif (revs->min_age != -1 &&\n>  \t    comparison_date(revs, commit) > revs->min_age)\n>  \t\t\treturn commit_ignore;\n> +\tif (revs->max_age_as_filter != -1 &&\n> +\t    comparison_date(revs, commit) < revs->max_age_as_filter)\n> +\t\t\treturn commit_ignore;\n\nThis one does make sense.\n\n> diff --git a/revision.h b/revision.h\n> index 5bc59c7bfe..e80c148b19 100644\n> --- a/revision.h\n> +++ b/revision.h\n> @@ -263,6 +263,7 @@ struct rev_info {\n>  \tint skip_count;\n>  \tint max_count;\n>  \ttimestamp_t max_age;\n> +\ttimestamp_t max_age_as_filter;\n>  \ttimestamp_t min_age;\n\nSo does this.\n\nEverything except that \"why are we checking if --since-as-filter is\nset and then comparing with the value came from --since?\" part looks\ngreat to me.  If that indeed is a bug (it is very possible that I am\nmisreading the logic and the comparison with continue is perfectly\ncorrect), and if the tests added by this patch didn't catch it, then\nthe test script may need a bit more work to catch such a mistake.\n\nThanks.\n"},{"id":"454315","messageId":"xmqqsfq44rc6.fsf@gitster.g","threadId":"57647","inReplyTo":"YmMJqvKN6itSHEZW@vmiklos.hu","subject":"Re: [PATCH v6] log: \"--since-as-filter\" option is a non-terminating \"--since\" variant","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-22T23:43:21Z","receivedAt":"2022-04-22T23:43:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miklos Vajna <vmiklos@vmiklos.hu> writes:\n\n> +test_expect_success 'git blame --since=...' '\n> +\tgit blame --since=\"2020-02-15\" file >actual &&\n> +\tcat >expect <<-\\EOF &&\n> +\t^c7bc5ce (A U Thor 2020-02-01 00:00:00 +0000 1) a\n> +\t^c7bc5ce (A U Thor 2020-02-01 00:00:00 +0000 2) a\n> +\t33fc0d13 (A U Thor 2020-03-01 00:00:00 +0000 3) a\n> +\tec76e003 (A U Thor 2020-04-01 00:00:00 +0000 4) a\n> +\tEOF\n> +\ttest_cmp expect actual\n> +'\n\nHardcoding the object names like this does not pass our test suite.\nThese abbreviated object names hardcode the use of SHA-1, but the\ncode is tested in repositories that use SHA-256 as well.\n\nAs you are creating four commits with distinct timestamps, I think\nyou can simply filter out the object name part for comparison,\nperhaps like:\n\nredact_blame_output () {\n\tsed -e 's/\\([^]*\\)\\([0-9a-f]*\\) /\\1HASH /'\n}\n\ntest_expect_success 'git blame --since=...' '\n\tgit blame --since=2020-02-15 file >raw &&\n\tredact_blame_output <raw >actual &&\n\tredact_blame_output <<-\\EOF &&\n\t^c7bc5ce (A U Thor 2020-02-01 00:00:00 +0000 1) a\n\t^c7bc5ce (A U Thor 2020-02-01 00:00:00 +0000 2) a\n\t33fc0d13 (A U Thor 2020-03-01 00:00:00 +0000 3) a\n\tec76e003 (A U Thor 2020-04-01 00:00:00 +0000 4) a\n\tEOF\n\ttest_cmp expect actual\n'\n\nBut did you really mean to test how --since works with blame?  Given\nthat there does not seem to be any clock skew in the history being\ntested, I am wondering if this new test file should even be a part\nof the topic.\n\nThanks.\n\n\n"},{"id":"454333","messageId":"YmP4TaYmSEi6GeB4@vmiklos.hu","threadId":"57647","inReplyTo":"xmqqee1o6a68.fsf@gitster.g","subject":"[PATCH v7] log: \"--since-as-filter\" option is a non-terminating \"--since\" variant","fromName":"Miklos Vajna","fromEmail":"vmiklos@vmiklos.hu","sentAt":"2022-04-23T12:59:57Z","receivedAt":"2022-04-23T13:00:10Z","isPatch":true,"sender":{"key":"vmiklos@vmiklos.hu","avatar":"https://avatars.githubusercontent.com/u/13838?v=4"},"body":"The \"--since=<time>\" option of \"git log\" limits the commits displayed by\nthe command by stopping the traversal once it sees a commit whose\ntimestamp is older than the given time and not digging further into its\nparents.\n\nThis is OK in a history where a commit always has a newer timestamp than\nany of its parents'.  Once you see a commit older than the given <time>,\nall ancestor commits of it are even older than the time anyway.  It\nposes, however, a problem when there is a commit with a wrong timestamp\nthat makes it appear older than its parents.  Stopping traversal at the\n\"incorrectly old\" commit will hide its ancestors that are newer than\nthat wrong commit and are newer than the cut-off time given with the\n--since option.  --max-age and --after being the synonyms to --since,\nthey share the same issue.\n\nAdd a new \"--since-as-filter\" option that is a variant of\n\"--since=<time>\".  Instead of stopping the traversal to hide an old\nenough commit and its all ancestors, exclude commits with an old\ntimestamp from the output but still keep digging the history.\n\nWithout other traversal stopping options, this will force the command in\n\"git log\" family to dig down the history to the root.  It may be an\nacceptable cost for a small project with short history and many commits\nwith screwy timestamps.\n\nIt is quite unlikely for us to add traversal stopper other than since,\nso have this as a --since-as-filter option, rather than a separate\n--as-filter, that would be probably more confusing.\n\nSigned-off-by: Miklos Vajna <vmiklos@vmiklos.hu>\n---\n\nHi Junio,\n\nOn Fri, Apr 22, 2022 at 03:11:11PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> Thanks.  Now 2.36 release is behind us, let's queue this in 'seen'\n> and after getting reviewed by somebody else---I do not trust\n> anything looked at by me and nobody else---merge it down, aiming for\n> graduation during this cycle.\n\nÆvar: you had opinion in this thread -- are you interested in reviewing this patch?\n\n> Much ealrier than this part in the same loop there is\n> \n> \t\tif (revs->max_age != -1 && (commit->date < revs->max_age))\n> \t\t\tobj->flags |= UNINTERESTING;\n> \t\tif (process_parents(revs, commit, &original_list, NULL) < 0)\n> \t\t\treturn -1;\n> \n> which taints the commit we are looking at as UNINTERESTING, cutting\n> the traversal down to its parents, if its timestamp is older than\n> max_age, and it would need to be taught not to do that, no?\n> \n> Ah, OK, max_age_as_filter is *NOT* a boolean (false is -1 and true\n> is something else) but is an independent timestamp and the\n> expectation is that max_age is left to be -1 when --since is not in\n> use.  --since-as-filter's timestamp is stored in max_age_as_filter\n> so the earlier \"compare with max_age and use UNINTERESTING bit to\n> stop traversal\" code does not have to be modified.\n\nYes, max_age is the terminating timestamp and max_age_as_filter is the\nfiltering timestamp.\n\n> > +\t\tif (revs->max_age_as_filter != -1 &&\n> > +\t\t\t(commit->date < revs->max_age) && !revs->line_level_traverse)\n> > +\t\t\tcontinue;\n> >  \t\tdate = commit->date;\n> >  \t\tp = &commit_list_insert(commit, p)->next;\n> \n> And if that is the assumption of this code, shouldn't this part be\n> using \"if max_age is set (i.e. not -1) and the commit is older than\n> that and we are not doing -La,b traversal\"?  \"--since-as-filter\"\n> being used does not mean max_age must be set to a value that can be\n> compared to timestamps in commits in the world order of this version\n> of the patch, right?\n\nGood catch, commit->date < revs->max_age is meant to be commit->date <\nrevs->max_age_as_filter there, fixed.\n\n> Everything except that \"why are we checking if --since-as-filter is\n> set and then comparing with the value came from --since?\" part looks\n> great to me.  If that indeed is a bug (it is very possible that I am\n> misreading the logic and the comparison with continue is perfectly\n> correct), and if the tests added by this patch didn't catch it, then\n> the test script may need a bit more work to catch such a mistake.\n\nYes, it was a bug. For example git log --children --since-as-filter=...\ngave empty output due to this. I now added a test for this.\n\n> Hardcoding the object names like this does not pass our test suite.\n> These abbreviated object names hardcode the use of SHA-1, but the\n> code is tested in repositories that use SHA-256 as well.\n> \n> As you are creating four commits with distinct timestamps, I think\n> you can simply filter out the object name part for comparison,\n> perhaps like:\n> \n> redact_blame_output () {\n> \tsed -e 's/\\([^]*\\)\\([0-9a-f]*\\) /\\1HASH /'\n> }\n> \n> test_expect_success 'git blame --since=...' '\n> \tgit blame --since=2020-02-15 file >raw &&\n> \tredact_blame_output <raw >actual &&\n> \tredact_blame_output <<-\\EOF &&\n> \t^c7bc5ce (A U Thor 2020-02-01 00:00:00 +0000 1) a\n> \t^c7bc5ce (A U Thor 2020-02-01 00:00:00 +0000 2) a\n> \t33fc0d13 (A U Thor 2020-03-01 00:00:00 +0000 3) a\n> \tec76e003 (A U Thor 2020-04-01 00:00:00 +0000 4) a\n> \tEOF\n> \ttest_cmp expect actual\n> '\n> \n> But did you really mean to test how --since works with blame?  Given\n> that there does not seem to be any clock skew in the history being\n> tested, I am wondering if this new test file should even be a part\n> of the topic.\n\nI dropped t/t4218-blame-limit.sh from this topic. The idea was to\nincrease coverage for git blame --since=..., as it seems to have no\ntest. But we can get back to that in a separate topic, I agree to keep\nthis topic scoped.\n\nThanks,\n\nMiklos\n\n Documentation/rev-list-options.txt |  5 ++++\n revision.c                         | 10 ++++++++\n revision.h                         |  1 +\n t/t4217-log-limit.sh               | 41 ++++++++++++++++++++++++++++++\n 4 files changed, 57 insertions(+)\n create mode 100755 t/t4217-log-limit.sh\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex fd4f4e26c9..195e74eec6 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -25,6 +25,11 @@ ordering and formatting options, such as `--reverse`.\n --after=<date>::\n \tShow commits more recent than a specific date.\n \n+--since-as-filter=<date>::\n+\tShow all commits more recent than a specific date. This visits\n+\tall commits in the range, rather than stopping at the first commit which\n+\tis older than a specific date.\n+\n --until=<date>::\n --before=<date>::\n \tShow commits older than a specific date.\ndiff --git a/revision.c b/revision.c\nindex 7d435f8048..c367273c00 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1440,6 +1440,9 @@ static int limit_list(struct rev_info *revs)\n \t\tif (revs->min_age != -1 && (commit->date > revs->min_age) &&\n \t\t    !revs->line_level_traverse)\n \t\t\tcontinue;\n+\t\tif (revs->max_age_as_filter != -1 &&\n+\t\t\t(commit->date < revs->max_age_as_filter) && !revs->line_level_traverse)\n+\t\t\tcontinue;\n \t\tdate = commit->date;\n \t\tp = &commit_list_insert(commit, p)->next;\n \n@@ -1838,6 +1841,7 @@ void repo_init_revisions(struct repository *r,\n \trevs->dense = 1;\n \trevs->prefix = prefix;\n \trevs->max_age = -1;\n+\trevs->max_age_as_filter = -1;\n \trevs->min_age = -1;\n \trevs->skip_count = -1;\n \trevs->max_count = -1;\n@@ -2218,6 +2222,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t} else if ((argcount = parse_long_opt(\"since\", argv, &optarg))) {\n \t\trevs->max_age = approxidate(optarg);\n \t\treturn argcount;\n+\t} else if ((argcount = parse_long_opt(\"since-as-filter\", argv, &optarg))) {\n+\t\trevs->max_age_as_filter = approxidate(optarg);\n+\t\treturn argcount;\n \t} else if ((argcount = parse_long_opt(\"after\", argv, &optarg))) {\n \t\trevs->max_age = approxidate(optarg);\n \t\treturn argcount;\n@@ -3862,6 +3869,9 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi\n \tif (revs->min_age != -1 &&\n \t    comparison_date(revs, commit) > revs->min_age)\n \t\t\treturn commit_ignore;\n+\tif (revs->max_age_as_filter != -1 &&\n+\t    comparison_date(revs, commit) < revs->max_age_as_filter)\n+\t\t\treturn commit_ignore;\n \tif (revs->min_parents || (revs->max_parents >= 0)) {\n \t\tint n = commit_list_count(commit->parents);\n \t\tif ((n < revs->min_parents) ||\ndiff --git a/revision.h b/revision.h\nindex 5bc59c7bfe..e80c148b19 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -263,6 +263,7 @@ struct rev_info {\n \tint skip_count;\n \tint max_count;\n \ttimestamp_t max_age;\n+\ttimestamp_t max_age_as_filter;\n \ttimestamp_t min_age;\n \tint min_parents;\n \tint max_parents;\ndiff --git a/t/t4217-log-limit.sh b/t/t4217-log-limit.sh\nnew file mode 100755\nindex 0000000000..6e01e2629c\n--- /dev/null\n+++ b/t/t4217-log-limit.sh\n@@ -0,0 +1,41 @@\n+#!/bin/sh\n+\n+test_description='git log with filter options limiting the output'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup test' '\n+\tgit init &&\n+\techo a >file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2021-02-01 00:00\" git commit -m init &&\n+\techo a >>file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2022-02-01 00:00\" git commit -m first &&\n+\techo a >>file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2021-03-01 00:00\" git commit -m second &&\n+\techo a >>file &&\n+\tgit add file &&\n+\tGIT_COMMITTER_DATE=\"2022-03-01 00:00\" git commit -m third\n+'\n+\n+test_expect_success 'git log --since-as-filter=...' '\n+\tgit log --since-as-filter=\"2022-01-01\" --format=%s >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tthird\n+\tfirst\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'git log --children --since-as-filter=...' '\n+\tgit log --children --since-as-filter=\"2022-01-01\" --format=%s >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tthird\n+\tfirst\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n2.34.1\n\n"}]}