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

[PATCH 3/3] promisor-remote: teach 'fromAccepted' to GIT_NO_LAZY_FETCH

From
Christian Couder <christian.couder@gmail.com>
Date
Jul 10, 2026, 08:51 UTC
Message-ID
<20260710085137.4171240-4-christian.couder@gmail.com>
In-Reply-To
<20260710085137.4171240-1-christian.couder@gmail.com>

The `GIT_NO_LAZY_FETCH` environment variable can be set to 'true' or 'false' to enable or disable lazy fetching. By default it is set to 'true' when calling `git upload-pack` to avoid security issues, see 7b70e9efb1 (upload-pack: disable lazy-fetching by default, 2024-04-16).

Recently though, the "promisor-remote" capability was introduced into protocol v2, which allows a server to advertise some promisor remotes and clients to accept them or not.

When promisor remotes are advertised by the server and accepted by the client, it means that they are quite trusted. So the security risks which come from lazy fetching from them could be considered much more acceptable.

Let's introduce a 'fromAccepted' option on top of 'true' and 'false' for `GIT_NO_LAZY_FETCH` to allow lazy fetching only from accepted promisor remotes.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
 Documentation/git-upload-pack.adoc    |  5 +++
 Documentation/git.adoc                |  6 ++--
 promisor-remote.c                     | 14 +++++++-
 promisor-remote.h                     |  1 +
 t/t5710-promisor-remote-capability.sh | 49 +++++++++++++++++++++++++++
 5 files changed, 71 insertions(+), 4 deletions(-)
diff --git a/Documentation/git-upload-pack.adoc b/Documentation/git-upload-pack.adoc
index 9167a321d0..1c2ed9d7ba 100644
--- a/Documentation/git-upload-pack.adoc
+++ b/Documentation/git-upload-pack.adoc
@@ -71,6 +71,11 @@ This is implemented by having `upload-pack` internally set the
 (because you are fetching from a partial clone, and you are sure
 you trust it), you can explicitly set `GIT_NO_LAZY_FETCH` to
 `0`.
++
+`GIT_NO_LAZY_FETCH` can also be set to 'fromAccepted' which allows
+lazy fetching only from remotes that are advertised and accepted using
+the "promisor-remote" protocol v2 capability. See
+linkgit:gitprotocol-v2[5]. This is safer than setting it to `0`.
 
 SECURITY
 --------
diff --git a/Documentation/git.adoc b/Documentation/git.adoc
index 8a5cdd3b3d..14a083bcdb 100644
--- a/Documentation/git.adoc
+++ b/Documentation/git.adoc
@@ -947,9 +947,9 @@ for full details.
 	pathspecs as case-insensitive.
 
 `GIT_NO_LAZY_FETCH`::
-	Setting this Boolean environment variable to true tells Git
-	not to lazily fetch missing objects from the promisor remote
-	on demand.
+	Setting this environment variable controls whether Git is
+	allowed to lazily fetch missing objects from a promisor remote
+	on demand. See linkgit:git-upload-pack[1].
 
 `GIT_REFLOG_ACTION`::
 	When a ref is updated, reflog entries are created to keep
diff --git a/promisor-remote.c b/promisor-remote.c
index 56f57c5267..c80319f966 100644
--- a/promisor-remote.c
+++ b/promisor-remote.c
@@ -35,9 +35,11 @@ enum allow_lazy_fetch parse_allow_lazy_fetch_env(void)
 		return LAZY_FETCH_ALL;
 	if (val > 0)
 		return LAZY_FETCH_NONE;
+	if (!strcasecmp(v, "fromAccepted"))
+		return LAZY_FETCH_ACCEPTED;
 
 	die(_("bad environment value '%s' for '%s'; "
-	      "only 'false/0' and 'true/1' are valid"),
+	      "only 'false/0', 'true/1' and 'fromAccepted' are valid"),
 	    v, NO_LAZY_FETCH_ENVIRONMENT);
 }
 
@@ -337,6 +339,16 @@ static bool lazy_fetch_objects(struct repository *repo,
 				 to_free, true))
 		return true;
 
+	if (lf == LAZY_FETCH_ACCEPTED) {
+		static int warning_shown;
+		if (!warning_shown) {
+			warning_shown = 1;
+			warning(_("lazy fetching from accepted promisor remotes only; "
+				  "some objects may not be available"));
+		}
+		return false;
+	}
+
 	return try_promisor_remotes(repo, remaining_oids, remaining_nr,
 				    to_free, false);
 }
diff --git a/promisor-remote.h b/promisor-remote.h
index 87fc24c9eb..0d05ff9d84 100644
--- a/promisor-remote.h
+++ b/promisor-remote.h
@@ -28,6 +28,7 @@ int repo_has_promisor_remote(struct repository *r);
 /* Enum for lazy fetching parsing */
 enum allow_lazy_fetch {
 	LAZY_FETCH_NONE    = 0,  /* No lazy fetching */
+	LAZY_FETCH_ACCEPTED,     /* Lazy fetching only from accepted promisor remotes */
 	LAZY_FETCH_ALL           /* Lazy fetch from any promisor remotes */
 };
 
diff --git a/t/t5710-promisor-remote-capability.sh b/t/t5710-promisor-remote-capability.sh
index 549acff23f..1c61b100b9 100755
--- a/t/t5710-promisor-remote-capability.sh
+++ b/t/t5710-promisor-remote-capability.sh
@@ -173,6 +173,55 @@ test_expect_success "clone with promisor.acceptfromserver set to 'None'" '
 	initialize_server 1 "$oid"
 '
 
+test_expect_success "clone with GIT_NO_LAZY_FETCH=fromAccepted and accepted promisor remote" '
+	git -C server config promisor.advertise true &&
+	test_when_finished "rm -rf client" &&
+
+	# Clone from server to create a client
+	GIT_NO_LAZY_FETCH=fromAccepted git clone -c remote.lop.promisor=true \
+		-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+		-c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \
+		-c promisor.acceptfromserver=All \
+		--no-local --filter="blob:limit=5k" server client &&
+
+	# Check that the largest object is still missing on the server
+	check_missing_objects server 1 "$oid"
+'
+
+test_expect_success "clone with GIT_NO_LAZY_FETCH=fromAccepted and no accepted promisor remote" '
+	git -C server config promisor.advertise true &&
+	test_when_finished "rm -rf client" &&
+
+	# Clone from server to create a client
+	# It should fail because the server cannot lazy fetch the missing blob
+	test_must_fail env GIT_NO_LAZY_FETCH=fromAccepted git clone -c remote.lop.promisor=true \
+		-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+		-c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \
+		-c promisor.acceptfromserver=None \
+		--no-local --filter="blob:limit=5k" server client 2>err &&
+
+	test_grep "lazy fetching from accepted promisor remotes only" err &&
+
+	# Check that the largest object is still missing on the server
+	check_missing_objects server 1 "$oid"
+'
+
+test_expect_success "clone failure with GIT_NO_LAZY_FETCH=bogus" '
+	git -C server config promisor.advertise true &&
+	test_when_finished "rm -rf client" &&
+
+	test_must_fail env GIT_NO_LAZY_FETCH=bogus git clone -c remote.lop.promisor=true \
+		-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+		-c remote.lop.url="$TRASH_DIRECTORY_URL/lop" \
+		-c promisor.acceptfromserver=All \
+		--no-local --filter="blob:limit=5k" server client 2>err &&
+
+	test_grep "bad environment value" err &&
+
+	# Check that the largest object is still missing on the server
+	check_missing_objects server 1 "$oid"
+'
+
 test_expect_success "init + fetch with promisor.advertise set to 'true'" '
 	git -C server config promisor.advertise true &&
 	test_when_finished "rm -rf client" &&
-- 
2.55.0.125.g395cd2c8ec.dirty
Previous: Christian CouderNext: brian m. carlson
Message 4 of 70 in “Introduce a 'fromAccepted' option to GIT_NO_LAZY_FETCH”
  1. 0/3 Introduce a 'fromAccepted' option to GIT_NO_LAZY_FETCHChristian Couder, Jul 10, 2026
  2. 1/3 promisor-remote: factor out lazy_fetch_objects()Christian Couder, Jul 10, 2026
  3. 2/3 promisor-remote: introduce enum allow_lazy_fetchChristian Couder, Jul 10, 2026
  4. 3/3 promisor-remote: teach 'fromAccepted' to GIT_NO_LAZY_FETCHChristian Couder, Jul 10, 2026
  5. brian m. carlsonJul 10, 2026
  6. Christian CouderJul 12, 2026
  7. 0/5 Introduce 'uploadpack.lazyFetchTrusted'Christian Couder, Aug 7, 2026
  8. 1/5 promisor-remote: factor out lazy_fetch_objects()Christian Couder, Aug 7, 2026
  9. Christian CouderAug 7, 2026
  10. 2/5 setup: extract path_allowlist_apply()Christian Couder, Aug 7, 2026
  11. 4/5 upload-pack: read uploadpack.lazyFetchTrustedChristian Couder, Aug 7, 2026
  12. 5/5 builtin/upload-pack: set GIT_NO_LAZY_FETCH to 0 on trusted repoChristian Couder, Aug 7, 2026
  13. 3/5 setup: add 'allow_dot' arg to path_allowlist_apply()Christian Couder, Aug 7, 2026
  14. Junio C HamanoAug 7, 2026
  15. Christian CouderAug 10, 2026
  16. Junio C HamanoAug 11, 2026
  17. 0/5 Introduce 'uploadpack.lazyFetchTrusted'Christian Couder, Aug 13, 2026
  18. Junio C HamanoAug 13, 2026
  19. Christian CouderAug 14, 2026
  20. Junio C HamanoAug 14, 2026
  21. 0/5 Introduce 'uploadpack.lazyFetchTrusted'Christian Couder, Sep 8, 2026
  22. 1/5 promisor-remote: factor out lazy_fetch_objects()Christian Couder, Sep 8, 2026
  23. Junio C HamanoSep 8, 2026
  24. Christian CouderSep 28, 2026
  25. 2/5 setup: extract path_allowlist_apply()Christian Couder, Sep 8, 2026
  26. Junio C HamanoSep 8, 2026
  27. Christian CouderSep 28, 2026
  28. 3/5 upload-pack: read uploadpack.lazyFetchTrustedChristian Couder, Sep 8, 2026
  29. 4/5 promisor-remote: prevent infinite recursion when lazy fetchingChristian Couder, Sep 8, 2026
  30. Junio C HamanoSep 8, 2026
  31. Christian CouderSep 9, 2026
  32. Junio C HamanoSep 9, 2026
  33. Christian CouderSep 28, 2026
  34. 5/5 builtin/upload-pack: set GIT_NO_LAZY_FETCH to 0 on trusted repoChristian Couder, Sep 8, 2026
  35. Junio C HamanoSep 8, 2026
  36. Christian CouderSep 28, 2026
  37. 0/5 Introduce 'uploadpack.lazyFetchTrusted'Christian Couder, Sep 28, 2026
  38. 1/5 promisor-remote: factor out lazy_fetch_objects()Christian Couder, Sep 28, 2026
  39. 2/5 setup: extract path_allowlist_apply()Christian Couder, Sep 28, 2026
  40. Junio C HamanoSep 29, 2026
  41. Christian CouderOct 2, 2026
  42. 3/5 upload-pack: read uploadpack.lazyFetchTrustedChristian Couder, Sep 28, 2026
  43. 4/5 promisor-remote: prevent infinite recursion when lazy fetchingChristian Couder, Sep 28, 2026
  44. 5/5 builtin/upload-pack: don't disable lazy fetching on trusted repoChristian Couder, Sep 28, 2026
  45. Junio C HamanoSep 29, 2026
  46. Christian CouderOct 2, 2026
  47. Christian CouderOct 2, 2026
  48. 0/5 Introduce 'uploadpack.lazyFetchTrusted'Christian Couder, Oct 2, 2026
  49. 1/5 promisor-remote: factor out lazy_fetch_objects()Christian Couder, Oct 2, 2026
  50. 2/5 setup: extract path_allowlist_apply()Christian Couder, Oct 2, 2026
  51. 3/5 upload-pack: read uploadpack.lazyFetchTrustedChristian Couder, Oct 2, 2026
  52. 4/5 promisor-remote: prevent infinite recursion when lazy fetchingChristian Couder, Oct 2, 2026
  53. 5/5 builtin/upload-pack: don't disable lazy fetching on trusted repoChristian Couder, Oct 2, 2026
  54. Junio C HamanoOct 5, 2026
  55. Christian CouderOct 6, 2026
  56. 1/5 promisor-remote: factor out lazy_fetch_objects()Christian Couder, Aug 13, 2026
  57. Junio C HamanoAug 14, 2026
  58. Christian CouderSep 8, 2026
  59. 2/5 setup: extract path_allowlist_apply()Christian Couder, Aug 13, 2026
  60. Junio C HamanoAug 14, 2026
  61. Christian CouderSep 8, 2026
  62. Junio C HamanoSep 8, 2026
  63. 3/5 setup: add 'allow_dot' arg to path_allowlist_apply()Christian Couder, Aug 13, 2026
  64. Junio C HamanoAug 14, 2026
  65. Christian CouderSep 8, 2026
  66. 4/5 upload-pack: read uploadpack.lazyFetchTrustedChristian Couder, Aug 13, 2026
  67. Junio C HamanoAug 14, 2026
  68. 5/5 builtin/upload-pack: set GIT_NO_LAZY_FETCH to 0 on trusted repoChristian Couder, Aug 13, 2026
  69. Junio C HamanoAug 14, 2026
  70. Christian CouderSep 8, 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.