From: Junio C Hamano <gitster@pobox.com>
To: "Harald Nordgren via GitGitGadget" <gitgitgadget@gmail.com>
Cc: git@vger.kernel.org, "D. Ben Knoble" <ben.knoble@gmail.com>,
Harald Nordgren <haraldnordgren@gmail.com>
Subject: Re: [PATCH v2] shallow: advise when a walk stops at a shallow boundary
Date: Tue, 22 Sep 2026 14:20:05 -0700 [thread overview]
Message-ID: <xmqqo6dpc7ay.fsf@gitster.g> (raw)
In-Reply-To: <pull.2413.v2.git.git.1790084326913.gitgitgadget@gmail.com> (Harald Nordgren via GitGitGadget's message of "Tue, 22 Sep 2026 13:38:46 +0000")
"Harald Nordgren via GitGitGadget" <gitgitgadget@gmail.com> writes:
> diff --git a/builtin/log.c b/builtin/log.c
> index 350b35c556..22a40c7d28 100644
> --- a/builtin/log.c
> +++ b/builtin/log.c
> @@ -47,6 +47,7 @@
> #include "commit-reach.h"
> #include "promisor-remote.h"
> #include "range-diff.h"
> +#include "shallow.h"
> #include "tmp-objdir.h"
> #include "tree.h"
> #include "userdiff.h"
> @@ -396,9 +397,32 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
> cmd_log_init_finish(argc, argv, prefix, rev, opt, cfg);
> }
>
> +static void advise_if_log_stopped_at_shallow_boundary(struct rev_info *rev,
> + struct commit *last_shown)
> +{
> + if (!last_shown)
> + return;
> + /* a plain "git log" running out of history is expected */
> + if (rev->max_count < 0 && rev->max_age == (timestamp_t)-1)
> + return;
"git log -999" may run out of commits because the history genuinely
may only have 20 commits, or the clone was made shallowly and we
only happen to have 20 commits at hand. The same is true for "git
log" that does not get any count. So I do not quite see the reason
why we want to give an early return in this function.
> + if (!is_repository_shallow(the_repository))
> + return;
> + if (!commit_is_shallow_boundary(the_repository, &last_shown->object.oid))
> + return;
> + wait_for_pager();
> + advise_if_enabled(ADVICE_SHALLOW_HISTORY,
> + _("'%s' stopped at %s because this repository is a shallow\n"
> + "clone, and might have more history upstream that was never fetched."),
> + "git log",
> + repo_find_unique_abbrev(the_repository,
> + &last_shown->object.oid,
> + DEFAULT_ABBREV));
> +}
> +
Anyway, sorry, I regret opening this can of worms X-<. It is not
that your implementation and design is bad, it is the problem being
solved that is bad. But ...
> static int cmd_log_walk_no_free(struct rev_info *rev)
> {
> struct commit *commit;
> + struct commit *last_shown = NULL;
> int saved_nrl = 0;
> int saved_dcctc = 0;
> int result;
> @@ -412,6 +436,7 @@ static int cmd_log_walk_no_free(struct rev_info *rev)
> * retain that state information if replacing rev->diffopt in this loop
> */
> while ((commit = get_revision(rev)) != NULL) {
> + last_shown = commit;
> if (!log_tree_commit(rev, commit) && rev->max_count >= 0)
> /*
> * We decremented max_count in get_revision,
> @@ -437,6 +462,7 @@ static int cmd_log_walk_no_free(struct rev_info *rev)
> if (rev->diffopt.degraded_cc_to_c)
> saved_dcctc = 1;
> }
> + advise_if_log_stopped_at_shallow_boundary(rev, last_shown);
... the "last shown" commit may or may not be at shallow boundary.
It may be a normal root commit, yet there may be truncated side
history that we stopped traversing during the above loop. If for
example we had a history like this (time flows from left to right):
()---b---d---e (side branch)
\
\
a---------c--------f------g (trunk)
where a side branch is much denser than the trunk, and shallow clone
truncated the history, hiding the parents of 'b', we may see that
our traversal goes 'g', 'f', 'e', 'd', 'c', 'b', 'a' and the last
shown commit may be 'a', which is a genuine root commit. But behind
'b' there may be hundreds of commits on the side branch that
eventually leads down to 'a'. Doesn't the user want to be notified
that they are missing tons of history behind 'b' in such a case when
'b' is shown and we stop traversing its parents?
That was the original motivation behind the issue I raised during
the previous review, and that is why I say I regret opening this can
of worms. If the commit 'a' in the history had parentes hidden
behind a shallow boundary (in other words, 'a' is not root), then
from the same traversal, we would see the "traversal stopped at
shallow boundary" advise, which means that we sometimes see it and
sometimes we don't, even though in either case we are showing 'b' as
if it were a root when it is not.
I do not think of a good way to solve this, and showing "your
traversal happened to have ended at the shallow boundary" only
sometimes in an unreliable way is probably counter-productive, I am
afraid.
So please forget what I said in the previous review. Even though it
may be a good piece of information to have somehow for the user to
know which commit has its parents hidden beyond a shallow boundary,
a regular get_revision() traversal loop is probalby not a good place
to do so.
We might want to show the information by enriching "log --graph"
output but that is totally unrelated to what you are doing with this
<rev>~N topic.
> +test_expect_success 'shallowHistory advice accounts for depth already present' '
> + test_commit shallow_partial_1 &&
> + test_commit shallow_partial_2 &&
> + test_commit shallow_partial_3 &&
> + test_commit shallow_partial_4 &&
> + test_commit shallow_partial_5 &&
> + test_commit shallow_partial_6 &&
> + git clone --no-local --depth=3 --branch main --single-branch \
> + .git shallow-advice-partial &&
> + test_when_finished "rm -rf shallow-advice-partial" &&
> + (
> + cd shallow-advice-partial &&
> + oid=$(git rev-parse --short origin/main~2) &&
> + test_must_fail git rev-parse origin/main~5 2>err &&
> + check_shallow_history_advice origin/main "$oid" \
> + "git fetch --deepen=3 origin main" &&
Would wew see the same output if we asked for "origin/main^^^^^"?
Just being curious.
> + git fetch --deepen=3 origin &&
> + git rev-parse origin/main~5 &&
> + test_must_fail git rev-parse origin/main~6
> + )
> +'
Thanks, and sorry about the ill-defined feature request.
next prev parent reply other threads:[~2026-09-22 21:20 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-20 9:53 [PATCH] object-name: explain why <ref>~N fails in a shallow clone Harald Nordgren via GitGitGadget
2026-09-20 21:59 ` D. Ben Knoble
2026-09-21 16:50 ` Junio C Hamano
2026-09-21 22:17 ` Harald Nordgren
2026-09-21 23:00 ` Junio C Hamano
2026-09-22 13:38 ` [PATCH v2] shallow: advise when a walk stops at a shallow boundary Harald Nordgren via GitGitGadget
2026-09-22 21:20 ` Junio C Hamano [this message]
2026-09-23 18:55 ` [PATCH v3] object-name: explain why <rev>~N fails in a shallow clone Harald Nordgren via GitGitGadget
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=xmqqo6dpc7ay.fsf@gitster.g \
--to=gitster@pobox.com \
--cc=ben.knoble@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitgitgadget@gmail.com \
--cc=haraldnordgren@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox