git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v3 2/2] odb: ignore refs of shallow and grafted alternates

From
IWIgor Wiedler <iwiedler@gitlab.com>
Date
Oct 19, 2026, 11:09 UTC
Message-ID
<20261019110918.71193-3-iwiedler@gitlab.com>
In-Reply-To
<20261014181134.56220-1-iwiedler@gitlab.com>

odb_for_each_alternate_ref() reports the refs of every alternate object store that is a repository. Its three callers use them as commits whose history can be assumed to be present: "rev-list --alternate-refs" (and through it the connectivity check after a fetch), fetch-pack, which sends them as "have"s so that the server does not resend what the alternate has, and receive-pack, which advertises them as ".have".

That assumption does not hold for a shallow repository, whose refs point at commits with no history behind them below its shallow boundary, nor for a grafted one. A repository borrowing objects from such an alternate does not see the alternate's shallow file, so the history that is missing there is missing here as well. The fetch then succeeds, the connectivity check stops at the alternate's refs, and a later "git log" fails with "Could not read <oid>".

"git clone --reference" already rejects a shallow or grafted reference repository for this reason, but nothing prevents such a repository from being added via objects/info/alternates or GIT_ALTERNATE_OBJECT_DIRECTORIES, or from being made shallow after other repositories started borrowing from it.

Skip the refs of an alternate that has a "shallow" file or an "info/grafts" file, the same checks clone uses. Its objects are still available through the object store. Only its refs are no longer used as a boundary.

Partial clones have the same problem for the objects they lack, but detecting one requires reading the alternate's config, so this patch does not handle them.

Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Igor Wiedler <iwiedler@gitlab.com>
---
 Documentation/rev-list-options.adoc |  7 ++++---
 odb.c                               | 11 +++++++++++
 t/t5618-alternate-refs.sh           | 13 +++++++++++++
 3 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/Documentation/rev-list-options.adoc b/Documentation/rev-list-options.adoc
index bd9f345690..982a53c694 100644
--- a/Documentation/rev-list-options.adoc
+++ b/Documentation/rev-list-options.adoc
@@ -223,9 +223,10 @@ explicitly.
 	Pretend as if all objects mentioned as ref tips of alternate
 	repositories were listed on the command line. An alternate
 	repository is any repository whose object directory is specified
-	in `objects/info/alternates`.  The set of included objects may
-	be modified by `core.alternateRefsCommand`, etc. See
-	linkgit:git-config[1].
+	in `objects/info/alternates`.  The refs of shallow and grafted
+	alternate repositories are ignored, as the history behind them
+	may be missing.  The set of included objects may be modified by
+	`core.alternateRefsCommand`, etc. See linkgit:git-config[1].
 
 `--single-worktree`::
 	By default, all working trees will be examined by the
diff --git a/odb.c b/odb.c
index 0200e26f21..e6bb7ce6e6 100644
--- a/odb.c
+++ b/odb.c
@@ -458,6 +458,17 @@ static int refs_from_alternate_cb(struct odb_source *alternate,
 		goto out;
 	strbuf_setlen(&path, base_len);
 
+	/* A shallow or grafted repository may lack the history behind its refs. */
+	strbuf_addstr(&path, "/shallow");
+	if (!access(path.buf, F_OK))
+		goto out;
+	strbuf_setlen(&path, base_len);
+
+	strbuf_addstr(&path, "/info/grafts");
+	if (!access(path.buf, F_OK))
+		goto out;
+	strbuf_setlen(&path, base_len);
+
 	read_alternate_refs(alternate->odb->repo, path.buf, cb->fn, cb->payload);
 
 out:
diff --git a/t/t5618-alternate-refs.sh b/t/t5618-alternate-refs.sh
index 3c5fbef463..68139a1b3e 100755
--- a/t/t5618-alternate-refs.sh
+++ b/t/t5618-alternate-refs.sh
@@ -70,4 +70,17 @@ test_expect_success 'connectivity check of a deepening fetch excludes alternate
 		--alternate-refs --quiet <deepen.trace
 '
 
+test_expect_success 'refs of a shallow alternate are ignored' '
+	git clone --depth=1 --no-local . shallow &&
+	test_commit -C shallow shallow-only &&
+	shallow_tip=$(git -C shallow rev-parse HEAD) &&
+	cp .git/objects/info/alternates alternates.bak &&
+	test_when_finished "mv alternates.bak .git/objects/info/alternates" &&
+	echo "$(pwd)/shallow/.git/objects" >>.git/objects/info/alternates &&
+	git cat-file -e $shallow_tip &&
+	git rev-list --remotes=origin >expect &&
+	git rev-list --alternate-refs >actual &&
+	test_cmp expect actual
+'
+
 test_done
-- 
2.56.0
Previous: Igor WiedlerNext: Patrick Steinhardt
Message 13 of 21 in “connected: do not walk alternate refs in deepening fetches”
  1. connected: do not walk alternate refs in deepening fetchesIgor Wiedler, Oct 14, 2026
  2. Patrick SteinhardtOct 14, 2026
  3. Junio C HamanoOct 14, 2026
  4. Jeff KingOct 15, 2026
  5. Justin ToblerOct 14, 2026
  6. 0/2 connected: fix the connectivity check with alternates in deepening fetchesIgor Wiedler, Oct 14, 2026
  7. Junio C HamanoOct 14, 2026
  8. 0/2 connected: fix the connectivity check with alternates in deepening fetchesIgor Wiedler, Oct 19, 2026
  9. Junio C HamanoOct 19, 2026
  10. Igor WiedlerOct 20, 2026
  11. Patrick SteinhardtOct 23, 2026
  12. 1/2 connected: negate alternate refs in deepening fetches tooIgor Wiedler, Oct 19, 2026
  13. 2/2 odb: ignore refs of shallow and grafted alternatesIgor Wiedler, Oct 19, 2026
  14. Patrick SteinhardtOct 23, 2026
  15. 1/2 connected: negate alternate refs in deepening fetches tooIgor Wiedler, Oct 14, 2026
  16. 2/2 odb: ignore refs of shallow and grafted alternatesIgor Wiedler, Oct 14, 2026
  17. Jeff KingOct 15, 2026
  18. Junio C HamanoOct 15, 2026
  19. Jeff KingOct 16, 2026
  20. Igor WiedlerOct 19, 2026
  21. Junio C HamanoOct 18, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.