diff mbox series

git: extend --no-lazy-fetch to work across subprocesses

Message ID xmqqr0hcglpk.fsf_-_@gitster.g (mailing list archive)
State New
Headers show
Series git: extend --no-lazy-fetch to work across subprocesses | expand

Commit Message

Junio C Hamano Feb. 16, 2024, 9:09 p.m. UTC
Modeling after how the `--no-replace-objects` option is made usable
across subprocess spawning (e.g., cURL based remote helpers are
spawned as a separate process while running "git fetch"), allow the
`--no-lazy-fetch` option to be passed across process boundary.

Do not model how the value of GIT_NO_REPLACE_OBJECTS environment
variable is ignored, though.  Just use the usual git_env_bool() to
allow "export GIT_NO_LAZY_FETCH=0" and "unset GIT_NO_LAZY_FETCH"
to be equivalents.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---

 * And this comes on top of the original one plus the documentation
   update for GIT_NO_REPLACE_OBJECTS.

 Documentation/git.txt    | 2 ++
 environment.c            | 4 ++++
 environment.h            | 1 +
 git.c                    | 3 +++
 t/t0410-partial-clone.sh | 9 +++++++++
 5 files changed, 19 insertions(+)

Comments

Linus Arver Feb. 16, 2024, 10:30 p.m. UTC | #1
FWIW, I see some typos. Otherwise this patch along with your "git:
document GIT_NO_REPLACE_OBJECTS environment variable" one both LGTM for
wording/readability. I must defer to Peff and others for "is this patch
actually doing the right thing?" ;).

Junio C Hamano <gitster@pobox.com> writes:

> Modeling after how the `--no-replace-objects` option is made usable
> across subprocess spawning (e.g., cURL based remote helpers are
> spawned as a separate process while running "git fetch"), allow the
> `--no-lazy-fetch` option to be passed across process boundary.

s/boundary/boundaries

> Do not model how the value of GIT_NO_REPLACE_OBJECTS environment
> variable is ignored, though.  Just use the usual git_env_bool() to
> allow "export GIT_NO_LAZY_FETCH=0" and "unset GIT_NO_LAZY_FETCH"
> to be equivalents.

s/equivalents/equivalent
Junio C Hamano Feb. 16, 2024, 11:01 p.m. UTC | #2
Linus Arver <linusa@google.com> writes:

> FWIW, I see some typos. Otherwise this patch along with your "git:
> document GIT_NO_REPLACE_OBJECTS environment variable" one both LGTM for
> wording/readability. I must defer to Peff and others for "is this patch
> actually doing the right thing?" ;).
>
> Junio C Hamano <gitster@pobox.com> writes:
>
>> Modeling after how the `--no-replace-objects` option is made usable
>> across subprocess spawning (e.g., cURL based remote helpers are
>> spawned as a separate process while running "git fetch"), allow the
>> `--no-lazy-fetch` option to be passed across process boundary.
>
> s/boundary/boundaries

Right; thanks.

>
>> Do not model how the value of GIT_NO_REPLACE_OBJECTS environment
>> variable is ignored, though.  Just use the usual git_env_bool() to
>> allow "export GIT_NO_LAZY_FETCH=0" and "unset GIT_NO_LAZY_FETCH"
>> to be equivalents.
>
> s/equivalents/equivalent

I meant to say that these two are "equivalents" (noun, plural).

I can rephrase to

	Just use git_env_bool() to make "export GIT_NO_LAZY_FETCH=0"
	an equivalent to "unset GIT_NO_LAZY_FETCH".

though, of course.
Linus Arver Feb. 16, 2024, 11:12 p.m. UTC | #3
Junio C Hamano <gitster@pobox.com> writes:

>>> Do not model how the value of GIT_NO_REPLACE_OBJECTS environment
>>> variable is ignored, though.  Just use the usual git_env_bool() to
>>> allow "export GIT_NO_LAZY_FETCH=0" and "unset GIT_NO_LAZY_FETCH"
>>> to be equivalents.
>>
>> s/equivalents/equivalent
>
> I meant to say that these two are "equivalents" (noun, plural).

Ah, I see.

> I can rephrase to
>
> 	Just use git_env_bool() to make "export GIT_NO_LAZY_FETCH=0"
> 	an equivalent to "unset GIT_NO_LAZY_FETCH".
>
> though, of course.

SGTM either way. Thanks.
Jeff King Feb. 17, 2024, 5:40 a.m. UTC | #4
On Fri, Feb 16, 2024 at 01:09:43PM -0800, Junio C Hamano wrote:

> Modeling after how the `--no-replace-objects` option is made usable
> across subprocess spawning (e.g., cURL based remote helpers are
> spawned as a separate process while running "git fetch"), allow the
> `--no-lazy-fetch` option to be passed across process boundary.
> 
> Do not model how the value of GIT_NO_REPLACE_OBJECTS environment
> variable is ignored, though.  Just use the usual git_env_bool() to
> allow "export GIT_NO_LAZY_FETCH=0" and "unset GIT_NO_LAZY_FETCH"
> to be equivalents.

Nice. I wondered if we would need to hide fetch_if_missing behind a
function that lazy-evaluates the environment variable, but it looks like
setup_git_env() is central enough to cover us here.

> diff --git a/Documentation/git.txt b/Documentation/git.txt
> index 2f1cb3ef4e..be2829003d 100644
> --- a/Documentation/git.txt
> +++ b/Documentation/git.txt
> @@ -183,6 +183,8 @@ If you just want to run git as if it was started in `<path>` then use
>  	Do not fetch missing objects from the promisor remote on
>  	demand.  Useful together with `git cat-file -e <object>` to
>  	see if the object is locally available.
> +	This is equivalent to setting the `GIT_NO_LAZY_FETCH`
> +	environment variable to `1`.

As with the other patch, I'd suggest adding an entry to the list of
environment variables later in the manpage.

> --- a/environment.h
> +++ b/environment.h
> @@ -35,6 +35,7 @@ const char *getenv_safe(struct strvec *argv, const char *name);
>  #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
>  #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
>  #define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
> +#define NO_LAZY_FETCH_ENVIRONMENT "GIT_NO_LAZY_FETCH"
>  #define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE"
>  #define GITATTRIBUTES_FILE ".gitattributes"
>  #define INFOATTRIBUTES_FILE "info/attributes"

A small nit, but maybe worth keeping the two replace-related variables
next to each other, rather than sticking the new one in the middle?

> diff --git a/git.c b/git.c
> index 28e8bf7497..d11d4dc77b 100644
> --- a/git.c
> +++ b/git.c
> @@ -189,6 +189,9 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
>  				*envchanged = 1;
>  		} else if (!strcmp(cmd, "--no-lazy-fetch")) {
>  			fetch_if_missing = 0;
> +			setenv(NO_LAZY_FETCH_ENVIRONMENT, "1", 1);
> +			if (envchanged)
> +				*envchanged = 1;
>  		} else if (!strcmp(cmd, "--no-replace-objects")) {
>  			disable_replace_refs();
>  			setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);

I _suspect_ this makes the fetch_if_missing call redundant, since we
should be parsing these before doing any repo setup that would trigger
the code that reads the environment variable.

This should probably also be xsetenv(), though as you can see in the
context we are not very consistent about using it. :) But certainly if
we failed to set it I would prefer to see an error rather than
accidentally lazy-fetching.

> diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
> index 5b7bee888d..59629cea1f 100755
> --- a/t/t0410-partial-clone.sh
> +++ b/t/t0410-partial-clone.sh
> @@ -665,6 +665,15 @@ test_expect_success 'lazy-fetch when accessing object not in the_repository' '
>  	git -C partial.git rev-list --objects --missing=print HEAD >out &&
>  	grep "[?]$FILE_HASH" out &&
>  
> +	# The no-lazy-fetch mechanism prevents Git from fetching
> +	test_must_fail env GIT_NO_LAZY_FETCH=1 \
> +		git -C partial.git cat-file -e "$FILE_HASH" &&
> +	test_must_fail git --no-lazy-fetch -C partial.git cat-file -e "$FILE_HASH" &&
> +
> +	# Sanity check that the file is still missing
> +	git -C partial.git rev-list --objects --missing=print HEAD >out &&
> +	grep "[?]$FILE_HASH" out &&

OK, we exercise it by setting the variable directly. A more interesting
one might be:

  git -c alias.foo='!git cat-file' --no-lazy-fetch ...

which should fail without the patch.

I also wondered why we were testing the direct-builtin invocation here,
since we are building on top of the original --no-lazy-fetch patch, but
it looks like it is because that patch didn't add any tests at all). I
think they would make more sense as a single patch, but I guess the
original is already in 'next' (though I suppose it is about to be
rewound). I am OK with it either way.

-Peff
Junio C Hamano Feb. 27, 2024, 6:04 a.m. UTC | #5
Jeff King <peff@peff.net> writes:

>> diff --git a/Documentation/git.txt b/Documentation/git.txt
>> index 2f1cb3ef4e..be2829003d 100644
>> --- a/Documentation/git.txt
>> +++ b/Documentation/git.txt
>> @@ -183,6 +183,8 @@ If you just want to run git as if it was started in `<path>` then use
>>  	Do not fetch missing objects from the promisor remote on
>>  	demand.  Useful together with `git cat-file -e <object>` to
>>  	see if the object is locally available.
>> +	This is equivalent to setting the `GIT_NO_LAZY_FETCH`
>> +	environment variable to `1`.
>
> As with the other patch, I'd suggest adding an entry to the list of
> environment variables later in the manpage.

Thanks, done.

We'll have to make a separate patch to add GIT_NO_REPLACE_OBJECTS to
the list, by the way (#leftoverbits), which I used as a model to
find what needs to be updated.

>> --- a/environment.h
>> +++ b/environment.h
>> @@ -35,6 +35,7 @@ const char *getenv_safe(struct strvec *argv, const char *name);
>>  #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
>>  #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
>>  #define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
>> +#define NO_LAZY_FETCH_ENVIRONMENT "GIT_NO_LAZY_FETCH"
>>  #define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE"
>>  #define GITATTRIBUTES_FILE ".gitattributes"
>>  #define INFOATTRIBUTES_FILE "info/attributes"
>
> A small nit, but maybe worth keeping the two replace-related variables
> next to each other, rather than sticking the new one in the middle?

OK.

>> diff --git a/git.c b/git.c
>> index 28e8bf7497..d11d4dc77b 100644
>> --- a/git.c
>> +++ b/git.c
>> @@ -189,6 +189,9 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
>>  				*envchanged = 1;
>>  		} else if (!strcmp(cmd, "--no-lazy-fetch")) {
>>  			fetch_if_missing = 0;
>> +			setenv(NO_LAZY_FETCH_ENVIRONMENT, "1", 1);
>> +			if (envchanged)
>> +				*envchanged = 1;
>>  		} else if (!strcmp(cmd, "--no-replace-objects")) {
>>  			disable_replace_refs();
>>  			setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
>
> I _suspect_ this makes the fetch_if_missing call redundant, since we
> should be parsing these before doing any repo setup that would trigger
> the code that reads the environment variable.

True.  Again, we'd need to clean-up the NO_REPLACE_OBJECTS codepath
as well---the disable_replace_refs() call should also be redundant,
which I mimicked to add the no-lazy-fetch codepath (#leftoverbits).

> This should probably also be xsetenv(), though as you can see in the
> context we are not very consistent about using it. :) But certainly if
> we failed to set it I would prefer to see an error rather than
> accidentally lazy-fetching.

I'd probably leave it outside the scope of this series; as nobody
uses xsetenv() in git.c currently, it would be a clean-up topic on
its own (#leftoverbits).

>> diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
>> index 5b7bee888d..59629cea1f 100755
>> --- a/t/t0410-partial-clone.sh
>> +++ b/t/t0410-partial-clone.sh
>> @@ -665,6 +665,15 @@ test_expect_success 'lazy-fetch when accessing object not in the_repository' '
>>  	git -C partial.git rev-list --objects --missing=print HEAD >out &&
>>  	grep "[?]$FILE_HASH" out &&
>>  
>> +	# The no-lazy-fetch mechanism prevents Git from fetching
>> +	test_must_fail env GIT_NO_LAZY_FETCH=1 \
>> +		git -C partial.git cat-file -e "$FILE_HASH" &&
>> +	test_must_fail git --no-lazy-fetch -C partial.git cat-file -e "$FILE_HASH" &&
>> +
>> +	# Sanity check that the file is still missing
>> +	git -C partial.git rev-list --objects --missing=print HEAD >out &&
>> +	grep "[?]$FILE_HASH" out &&
>
> OK, we exercise it by setting the variable directly. A more interesting
> one might be:
>
>   git -c alias.foo='!git cat-file' --no-lazy-fetch ...
>
> which should fail without the patch.

True, again.  I'll test all three variants (i.e. environment,
command line option to "git", forced subprocess turning command line
option to "git" into environment).

Thanks.



----- >8 --------- >8 --------- >8 --------- >8 -----
Subject: [PATCH v2 3/3] git: extend --no-lazy-fetch to work across subprocesses

Modeling after how the `--no-replace-objects` option is made usable
across subprocess spawning (e.g., cURL based remote helpers are
spawned as a separate process while running "git fetch"), allow the
`--no-lazy-fetch` option to be passed across process boundaries.

Do not model how the value of GIT_NO_REPLACE_OBJECTS environment
variable is ignored, though.  Just use the usual git_env_bool() to
allow "export GIT_NO_LAZY_FETCH=0" and "unset GIT_NO_LAZY_FETCH"
to be equivalents.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
Range-diff:
1:  00e202b679 ! 1:  e0764f2e21 git: extend --no-lazy-fetch to work across subprocesses
    @@ Documentation/git.txt: If you just want to run git as if it was started in `<pat
      
      --literal-pathspecs::
      	Treat pathspecs literally (i.e. no globbing, no pathspec magic).
    +@@ Documentation/git.txt: for full details.
    + 	Setting this Boolean environment variable to true will cause Git to treat all
    + 	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.
    ++
    + `GIT_REFLOG_ACTION`::
    + 	When a ref is updated, reflog entries are created to keep
    + 	track of the reason why the ref was updated (which is
     
      ## environment.c ##
     @@ environment.c: const char * const local_repo_env[] = {
    @@ environment.c: void setup_git_env(const char *git_dir)
     
      ## environment.h ##
     @@ environment.h: const char *getenv_safe(struct strvec *argv, const char *name);
    - #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
      #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
      #define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
    -+#define NO_LAZY_FETCH_ENVIRONMENT "GIT_NO_LAZY_FETCH"
      #define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE"
    ++#define NO_LAZY_FETCH_ENVIRONMENT "GIT_NO_LAZY_FETCH"
      #define GITATTRIBUTES_FILE ".gitattributes"
      #define INFOATTRIBUTES_FILE "info/attributes"
    + #define ATTRIBUTE_MACRO_PREFIX "[attr]"
     
      ## git.c ##
     @@ git.c: static int handle_options(const char ***argv, int *argc, int *envchanged)
    @@ t/t0410-partial-clone.sh: test_expect_success 'lazy-fetch when accessing object
     +	# The no-lazy-fetch mechanism prevents Git from fetching
     +	test_must_fail env GIT_NO_LAZY_FETCH=1 \
     +		git -C partial.git cat-file -e "$FILE_HASH" &&
    ++
    ++	# The same with command line option to "git"
     +	test_must_fail git --no-lazy-fetch -C partial.git cat-file -e "$FILE_HASH" &&
     +
    ++	# The same, forcing a subprocess via an alias
    ++	test_must_fail git --no-lazy-fetch -C partial.git \
    ++		-c alias.foo="!git cat-file" foo -e "$FILE_HASH" &&
    ++
     +	# Sanity check that the file is still missing
     +	git -C partial.git rev-list --objects --missing=print HEAD >out &&
     +	grep "[?]$FILE_HASH" out &&

 Documentation/git.txt    |  7 +++++++
 environment.c            |  4 ++++
 environment.h            |  1 +
 git.c                    |  3 +++
 t/t0410-partial-clone.sh | 15 +++++++++++++++
 5 files changed, 30 insertions(+)

diff --git a/Documentation/git.txt b/Documentation/git.txt
index 2f1cb3ef4e..6fbaa9dd2b 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -183,6 +183,8 @@ If you just want to run git as if it was started in `<path>` then use
 	Do not fetch missing objects from the promisor remote on
 	demand.  Useful together with `git cat-file -e <object>` to
 	see if the object is locally available.
+	This is equivalent to setting the `GIT_NO_LAZY_FETCH`
+	environment variable to `1`.
 
 --literal-pathspecs::
 	Treat pathspecs literally (i.e. no globbing, no pathspec magic).
@@ -896,6 +898,11 @@ for full details.
 	Setting this Boolean environment variable to true will cause Git to treat all
 	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.
+
 `GIT_REFLOG_ACTION`::
 	When a ref is updated, reflog entries are created to keep
 	track of the reason why the ref was updated (which is
diff --git a/environment.c b/environment.c
index 9e37bf58c0..afad78a3f8 100644
--- a/environment.c
+++ b/environment.c
@@ -136,6 +136,7 @@ const char * const local_repo_env[] = {
 	GRAFT_ENVIRONMENT,
 	INDEX_ENVIRONMENT,
 	NO_REPLACE_OBJECTS_ENVIRONMENT,
+	NO_LAZY_FETCH_ENVIRONMENT,
 	GIT_REPLACE_REF_BASE_ENVIRONMENT,
 	GIT_PREFIX_ENVIRONMENT,
 	GIT_SHALLOW_FILE_ENVIRONMENT,
@@ -207,6 +208,9 @@ void setup_git_env(const char *git_dir)
 	shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
 	if (shallow_file)
 		set_alternate_shallow_file(the_repository, shallow_file, 0);
+
+	if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))
+		fetch_if_missing = 0;
 }
 
 int is_bare_repository(void)
diff --git a/environment.h b/environment.h
index e5351c9dd9..5cec19cecc 100644
--- a/environment.h
+++ b/environment.h
@@ -36,6 +36,7 @@ const char *getenv_safe(struct strvec *argv, const char *name);
 #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
 #define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
 #define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE"
+#define NO_LAZY_FETCH_ENVIRONMENT "GIT_NO_LAZY_FETCH"
 #define GITATTRIBUTES_FILE ".gitattributes"
 #define INFOATTRIBUTES_FILE "info/attributes"
 #define ATTRIBUTE_MACRO_PREFIX "[attr]"
diff --git a/git.c b/git.c
index 28e8bf7497..d11d4dc77b 100644
--- a/git.c
+++ b/git.c
@@ -189,6 +189,9 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
 				*envchanged = 1;
 		} else if (!strcmp(cmd, "--no-lazy-fetch")) {
 			fetch_if_missing = 0;
+			setenv(NO_LAZY_FETCH_ENVIRONMENT, "1", 1);
+			if (envchanged)
+				*envchanged = 1;
 		} else if (!strcmp(cmd, "--no-replace-objects")) {
 			disable_replace_refs();
 			setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
index 5b7bee888d..c282851af7 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -665,6 +665,21 @@ test_expect_success 'lazy-fetch when accessing object not in the_repository' '
 	git -C partial.git rev-list --objects --missing=print HEAD >out &&
 	grep "[?]$FILE_HASH" out &&
 
+	# The no-lazy-fetch mechanism prevents Git from fetching
+	test_must_fail env GIT_NO_LAZY_FETCH=1 \
+		git -C partial.git cat-file -e "$FILE_HASH" &&
+
+	# The same with command line option to "git"
+	test_must_fail git --no-lazy-fetch -C partial.git cat-file -e "$FILE_HASH" &&
+
+	# The same, forcing a subprocess via an alias
+	test_must_fail git --no-lazy-fetch -C partial.git \
+		-c alias.foo="!git cat-file" foo -e "$FILE_HASH" &&
+
+	# Sanity check that the file is still missing
+	git -C partial.git rev-list --objects --missing=print HEAD >out &&
+	grep "[?]$FILE_HASH" out &&
+
 	git -C full cat-file -s "$FILE_HASH" >expect &&
 	test-tool partial-clone object-info partial.git "$FILE_HASH" >actual &&
 	test_cmp expect actual &&
Jeff King Feb. 27, 2024, 7:49 a.m. UTC | #6
On Mon, Feb 26, 2024 at 10:04:54PM -0800, Junio C Hamano wrote:

> ----- >8 --------- >8 --------- >8 --------- >8 -----
> Subject: [PATCH v2 3/3] git: extend --no-lazy-fetch to work across subprocesses
> [...]

This looks pretty reasonable to me, and the lines you drew for
#leftoverbits all seemed liked good spots.

The only thing I noticed in the patch was this (which I think is not
even new in this round):

> diff --git a/environment.c b/environment.c
> index 9e37bf58c0..afad78a3f8 100644
> --- a/environment.c
> +++ b/environment.c
> @@ -136,6 +136,7 @@ const char * const local_repo_env[] = {
>  	GRAFT_ENVIRONMENT,
>  	INDEX_ENVIRONMENT,
>  	NO_REPLACE_OBJECTS_ENVIRONMENT,
> +	NO_LAZY_FETCH_ENVIRONMENT,
>  	GIT_REPLACE_REF_BASE_ENVIRONMENT,
>  	GIT_PREFIX_ENVIRONMENT,
>  	GIT_SHALLOW_FILE_ENVIRONMENT,

This will clear the environment variable when we move between
repositories. I can see an argument for it, and certainly that's how
GIT_NO_REPLACE_OBJECTS works.

But I can also see an argument that this is not a property of the
_repository_, but of the request. For example, if I run "git
--no-lazy-fetch log" and we cross into a submodule to do a diff, should
that submodule also avoid lazy-fetching? I'd think so, but I think your
patch would restore the defaults when we "enter" the submodule repo.

There's some prior art there, I think, in how GIT_CEILING_DIRECTORIES
works, or even something like "git --literal-pathspecs", neither of
which appear in local_repo_env.

-Peff
Junio C Hamano Feb. 27, 2024, 4:48 p.m. UTC | #7
Jeff King <peff@peff.net> writes:

> There's some prior art there, I think, in how GIT_CEILING_DIRECTORIES
> works, or even something like "git --literal-pathspecs", neither of
> which appear in local_repo_env.

Unlike GIT_CEILING_DIRECTORIES that is more or less permanent and a
part of configuring an everyday environment for real work, I view
this --no-lazy-fetch thing as solely a debugging aid.  A repository
with promisor wouldn't be able to "fill the gap" by forbidding
on-demand fetching of promised objects while running say "git fetch"
from elsewhere and it just will die with "some objects we are
supposed to have are missing from this repository".

So I do not have a strong opinion either way, if it is more
convenient to propagate the request out to other repositories when
we run processes in two or more repositories (e.g. "git clone
--local"), or if it is more convenient to make sure that the request
is limited to the target repository.  Here is a version without the
local_repo_env[] change.

----- >8 --------- >8 --------- >8 --------- >8 --------- >8 -----
Subject: [PATCH v3 3/3] git: extend --no-lazy-fetch to work across subprocesses

Modeling after how the `--no-replace-objects` option is made usable
across subprocess spawning (e.g., cURL based remote helpers are
spawned as a separate process while running "git fetch"), allow the
`--no-lazy-fetch` option to be passed across process boundaries.

Do not model how the value of GIT_NO_REPLACE_OBJECTS environment
variable is ignored, though.  Just use the usual git_env_bool() to
allow "export GIT_NO_LAZY_FETCH=0" and "unset GIT_NO_LAZY_FETCH"
to be equivalents.

Also do not model how the request is not propagated to subprocesses
we spawn (e.g. "git clone --local" that spawns a new process to work
in the origin repository, while the original one working in the
newly created one) by the "--no-replace-objects" option, as this "do
not lazily fetch from the promisor" is more about a per-request
debugging aid, not "this repository's promisor should not be relied
upon" property specific to a repository.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
Range-diff:
1:  5fa7ddbf68 ! 1:  ea40bb9a1d git: extend --no-lazy-fetch to work across subprocesses
    @@ Commit message
         allow "export GIT_NO_LAZY_FETCH=0" and "unset GIT_NO_LAZY_FETCH"
         to be equivalents.
     
    +    Also do not model how the request is not propagated to subprocesses
    +    we spawn (e.g. "git clone --local" that spawns a new process to work
    +    in the origin repository, while the original one working in the
    +    newly created one) by the "--no-replace-objects" option, as this "do
    +    not lazily fetch from the promisor" is more about a per-request
    +    debugging aid, not "this repository's promisor should not be relied
    +    upon" property specific to a repository.
    +
         Signed-off-by: Junio C Hamano <gitster@pobox.com>
     
      ## Documentation/git.txt ##
    @@ Documentation/git.txt: for full details.
      	track of the reason why the ref was updated (which is
     
      ## environment.c ##
    -@@ environment.c: const char * const local_repo_env[] = {
    - 	GRAFT_ENVIRONMENT,
    - 	INDEX_ENVIRONMENT,
    - 	NO_REPLACE_OBJECTS_ENVIRONMENT,
    -+	NO_LAZY_FETCH_ENVIRONMENT,
    - 	GIT_REPLACE_REF_BASE_ENVIRONMENT,
    - 	GIT_PREFIX_ENVIRONMENT,
    - 	GIT_SHALLOW_FILE_ENVIRONMENT,
     @@ environment.c: void setup_git_env(const char *git_dir)
      	shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
      	if (shallow_file)

 Documentation/git.txt    |  7 +++++++
 environment.c            |  3 +++
 environment.h            |  1 +
 git.c                    |  3 +++
 t/t0410-partial-clone.sh | 15 +++++++++++++++
 5 files changed, 29 insertions(+)

diff --git a/Documentation/git.txt b/Documentation/git.txt
index b1f754e84b..a517d94a7a 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -183,6 +183,8 @@ If you just want to run git as if it was started in `<path>` then use
 	Do not fetch missing objects from the promisor remote on
 	demand.  Useful together with `git cat-file -e <object>` to
 	see if the object is locally available.
+	This is equivalent to setting the `GIT_NO_LAZY_FETCH`
+	environment variable to `1`.
 
 --literal-pathspecs::
 	Treat pathspecs literally (i.e. no globbing, no pathspec magic).
@@ -900,6 +902,11 @@ for full details.
 	Setting this Boolean environment variable to true will cause Git to treat all
 	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.
+
 `GIT_REFLOG_ACTION`::
 	When a ref is updated, reflog entries are created to keep
 	track of the reason why the ref was updated (which is
diff --git a/environment.c b/environment.c
index 9e37bf58c0..0ae5bbd4a1 100644
--- a/environment.c
+++ b/environment.c
@@ -207,6 +207,9 @@ void setup_git_env(const char *git_dir)
 	shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
 	if (shallow_file)
 		set_alternate_shallow_file(the_repository, shallow_file, 0);
+
+	if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))
+		fetch_if_missing = 0;
 }
 
 int is_bare_repository(void)
diff --git a/environment.h b/environment.h
index e5351c9dd9..5cec19cecc 100644
--- a/environment.h
+++ b/environment.h
@@ -36,6 +36,7 @@ const char *getenv_safe(struct strvec *argv, const char *name);
 #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
 #define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
 #define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE"
+#define NO_LAZY_FETCH_ENVIRONMENT "GIT_NO_LAZY_FETCH"
 #define GITATTRIBUTES_FILE ".gitattributes"
 #define INFOATTRIBUTES_FILE "info/attributes"
 #define ATTRIBUTE_MACRO_PREFIX "[attr]"
diff --git a/git.c b/git.c
index 28e8bf7497..d11d4dc77b 100644
--- a/git.c
+++ b/git.c
@@ -189,6 +189,9 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
 				*envchanged = 1;
 		} else if (!strcmp(cmd, "--no-lazy-fetch")) {
 			fetch_if_missing = 0;
+			setenv(NO_LAZY_FETCH_ENVIRONMENT, "1", 1);
+			if (envchanged)
+				*envchanged = 1;
 		} else if (!strcmp(cmd, "--no-replace-objects")) {
 			disable_replace_refs();
 			setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
index 5b7bee888d..c282851af7 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -665,6 +665,21 @@ test_expect_success 'lazy-fetch when accessing object not in the_repository' '
 	git -C partial.git rev-list --objects --missing=print HEAD >out &&
 	grep "[?]$FILE_HASH" out &&
 
+	# The no-lazy-fetch mechanism prevents Git from fetching
+	test_must_fail env GIT_NO_LAZY_FETCH=1 \
+		git -C partial.git cat-file -e "$FILE_HASH" &&
+
+	# The same with command line option to "git"
+	test_must_fail git --no-lazy-fetch -C partial.git cat-file -e "$FILE_HASH" &&
+
+	# The same, forcing a subprocess via an alias
+	test_must_fail git --no-lazy-fetch -C partial.git \
+		-c alias.foo="!git cat-file" foo -e "$FILE_HASH" &&
+
+	# Sanity check that the file is still missing
+	git -C partial.git rev-list --objects --missing=print HEAD >out &&
+	grep "[?]$FILE_HASH" out &&
+
 	git -C full cat-file -s "$FILE_HASH" >expect &&
 	test-tool partial-clone object-info partial.git "$FILE_HASH" >actual &&
 	test_cmp expect actual &&
Jeff King March 7, 2024, 9:56 a.m. UTC | #8
On Tue, Feb 27, 2024 at 08:48:29AM -0800, Junio C Hamano wrote:

> Jeff King <peff@peff.net> writes:
> 
> > There's some prior art there, I think, in how GIT_CEILING_DIRECTORIES
> > works, or even something like "git --literal-pathspecs", neither of
> > which appear in local_repo_env.
> 
> Unlike GIT_CEILING_DIRECTORIES that is more or less permanent and a
> part of configuring an everyday environment for real work, I view
> this --no-lazy-fetch thing as solely a debugging aid.  A repository
> with promisor wouldn't be able to "fill the gap" by forbidding
> on-demand fetching of promised objects while running say "git fetch"
> from elsewhere and it just will die with "some objects we are
> supposed to have are missing from this repository".
> 
> So I do not have a strong opinion either way, if it is more
> convenient to propagate the request out to other repositories when
> we run processes in two or more repositories (e.g. "git clone
> --local"), or if it is more convenient to make sure that the request
> is limited to the target repository.  Here is a version without the
> local_repo_env[] change.

Yeah, GIT_CEILING_DIRECTORIES is maybe a bad example. But I do think
LITERAL_PATHSPECS is a better one, and the submodule-fetch example I
gave would be genuinely surprising if it behaved differently than the
superproject, I'd think.

I do agree this is probably going to mostly be a debugging aid, so it
might not matter much. But once in the wild these things tend to take on
a life of their own. ;)

> ----- >8 --------- >8 --------- >8 --------- >8 --------- >8 -----
> Subject: [PATCH v3 3/3] git: extend --no-lazy-fetch to work across subprocesses

So anyway, this version seems good to me.

-Peff
Junio C Hamano March 7, 2024, 8:33 p.m. UTC | #9
Jeff King <peff@peff.net> writes:

>> So I do not have a strong opinion either way, if it is more
>> convenient to propagate the request out to other repositories when
>> we run processes in two or more repositories (e.g. "git clone
>> --local"), or if it is more convenient to make sure that the request
>> is limited to the target repository.  Here is a version without the
>> local_repo_env[] change.
>
> Yeah, GIT_CEILING_DIRECTORIES is maybe a bad example. But I do think
> LITERAL_PATHSPECS is a better one, and the submodule-fetch example I
> gave would be genuinely surprising if it behaved differently than the
> superproject, I'd think.
>
> I do agree this is probably going to mostly be a debugging aid, so it
> might not matter much. But once in the wild these things tend to take on
> a life of their own. ;)
>
>> ----- >8 --------- >8 --------- >8 --------- >8 --------- >8 -----
>> Subject: [PATCH v3 3/3] git: extend --no-lazy-fetch to work across subprocesses
>
> So anyway, this version seems good to me.

Thanks.
diff mbox series

Patch

diff --git a/Documentation/git.txt b/Documentation/git.txt
index 2f1cb3ef4e..be2829003d 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -183,6 +183,8 @@  If you just want to run git as if it was started in `<path>` then use
 	Do not fetch missing objects from the promisor remote on
 	demand.  Useful together with `git cat-file -e <object>` to
 	see if the object is locally available.
+	This is equivalent to setting the `GIT_NO_LAZY_FETCH`
+	environment variable to `1`.
 
 --literal-pathspecs::
 	Treat pathspecs literally (i.e. no globbing, no pathspec magic).
diff --git a/environment.c b/environment.c
index 9e37bf58c0..afad78a3f8 100644
--- a/environment.c
+++ b/environment.c
@@ -136,6 +136,7 @@  const char * const local_repo_env[] = {
 	GRAFT_ENVIRONMENT,
 	INDEX_ENVIRONMENT,
 	NO_REPLACE_OBJECTS_ENVIRONMENT,
+	NO_LAZY_FETCH_ENVIRONMENT,
 	GIT_REPLACE_REF_BASE_ENVIRONMENT,
 	GIT_PREFIX_ENVIRONMENT,
 	GIT_SHALLOW_FILE_ENVIRONMENT,
@@ -207,6 +208,9 @@  void setup_git_env(const char *git_dir)
 	shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
 	if (shallow_file)
 		set_alternate_shallow_file(the_repository, shallow_file, 0);
+
+	if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))
+		fetch_if_missing = 0;
 }
 
 int is_bare_repository(void)
diff --git a/environment.h b/environment.h
index e5351c9dd9..74b3124f55 100644
--- a/environment.h
+++ b/environment.h
@@ -35,6 +35,7 @@  const char *getenv_safe(struct strvec *argv, const char *name);
 #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
 #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
 #define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
+#define NO_LAZY_FETCH_ENVIRONMENT "GIT_NO_LAZY_FETCH"
 #define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE"
 #define GITATTRIBUTES_FILE ".gitattributes"
 #define INFOATTRIBUTES_FILE "info/attributes"
diff --git a/git.c b/git.c
index 28e8bf7497..d11d4dc77b 100644
--- a/git.c
+++ b/git.c
@@ -189,6 +189,9 @@  static int handle_options(const char ***argv, int *argc, int *envchanged)
 				*envchanged = 1;
 		} else if (!strcmp(cmd, "--no-lazy-fetch")) {
 			fetch_if_missing = 0;
+			setenv(NO_LAZY_FETCH_ENVIRONMENT, "1", 1);
+			if (envchanged)
+				*envchanged = 1;
 		} else if (!strcmp(cmd, "--no-replace-objects")) {
 			disable_replace_refs();
 			setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
index 5b7bee888d..59629cea1f 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -665,6 +665,15 @@  test_expect_success 'lazy-fetch when accessing object not in the_repository' '
 	git -C partial.git rev-list --objects --missing=print HEAD >out &&
 	grep "[?]$FILE_HASH" out &&
 
+	# The no-lazy-fetch mechanism prevents Git from fetching
+	test_must_fail env GIT_NO_LAZY_FETCH=1 \
+		git -C partial.git cat-file -e "$FILE_HASH" &&
+	test_must_fail git --no-lazy-fetch -C partial.git cat-file -e "$FILE_HASH" &&
+
+	# Sanity check that the file is still missing
+	git -C partial.git rev-list --objects --missing=print HEAD >out &&
+	grep "[?]$FILE_HASH" out &&
+
 	git -C full cat-file -s "$FILE_HASH" >expect &&
 	test-tool partial-clone object-info partial.git "$FILE_HASH" >actual &&
 	test_cmp expect actual &&