Subject: Re: [RFC PATCH v2 1/2] fetch: set-head with --set-head option

From: Junio C Hamano <[email-masked]>
Date: Tue, 10 Sep 2024 15:19:54 -0700

Bence Ferdinandy <[email-masked]> writes:

> When cloning a repository refs/remotes/origin/HEAD is set automatically.
> In contrast, when using init, remote add and fetch to set a remote, one
> needs to call remote set-head --auto to achieve the same result.

In other words, "clone" is roughly equivalent to "init && remote add
&& fetch && remote set-head".

I do not see a problem with it.  You'd do such a repository creation
task only once per repository anyway.

> Add a --set-head option to git fetch to automatically set heads on
> remotes.

This practically would help only those who want to blindly follow
what the remote considers their primary branch.  If the remote
flip-flops that every day betweein 'master' and 'main', you'd want
to either "fetch && remote set-head" or "fetch --set-head", but it
would be weird to flip-flop the HEAD every day to begin with.

And the feature does not help those who want to inspect and then
switch.  When they say "--set-head", they will unconditionally
switch whatever the remote uses and there is no opportunity for them
to check.

Ideally, because every "git fetch" you do will automatically learn
what their HEAD of the day points at, even without "--set-head", it
may be nice to let the user know when their HEAD changed, so that
the user can inspect the situation and decide.

If "fetch $repo", when it notices that refs/remotes/$repo/HEAD is
missing, always unconditionally stores where their HEAD points at
in refs/remotes/$repo/HEAD, and did nothing else, wouldn't that be
sufficient?

The users have "remote set-head" to do this when needed.  What is
the true motivation that "fetch" (which presumably happens a lot
more often) needs to be involved in this process?  The *only* upside
I can see with "fetch --set-head" to blindly follow every switch of
HEAD on the remote end is that you can keep up with the remote that
flips its HEAD very often, but is that really a realistic need?  If
we reject such a use case as invalid, I suspect that the end-user
experience would be simplified quite a lot.  Imagine that we teach
"git fetch $repo" to notice refs/remotes/$repo/HEAD is missing, and
create it from the remote HEAD information automatically.  And we do
NOTHING ELSE.  What would the end-user experience look like?

 * Usually, you start with "git clone" and 'origin' will know which
   branch 'origin/HEAD' points at.

 * You may run "git remote add -f $repo $URL" to add one.  Because
   this runs "git fetch $repo", the previous addition to the "git
   fetch" will make sure refs/remotes/$repo/HEAD would be there.

 * You may run "git remote add $repo $URL" to add one, and then
   separately "git fetch $repo" yourself.  The end result would be
   the same; refs/remotes/$repo/HEAD will be there.

Having said all that, the implementation here ...

> +static int run_set_head(const char *name)
> +{
> +	struct child_process cmd = CHILD_PROCESS_INIT;
> +	strvec_push(&cmd.args, "remote");
> +	strvec_push(&cmd.args, "set-head");
> +	strvec_push(&cmd.args, "--auto");
> +	strvec_push(&cmd.args, name);
> +	cmd.git_cmd = 1;
> +	return run_command(&cmd);
> +}

... does look quite straight-forward.

> +static int fetch_multiple(struct string_list *list, int max_children, int set_head,
> +			const struct fetch_config *config)
>  {
>  	int i, result = 0;
>  	struct strvec argv = STRVEC_INIT;
> @@ -2014,6 +2025,8 @@ static int fetch_multiple(struct string_list *list, int max_children,
>  				error(_("could not fetch %s"), name);
>  				result = 1;
>  			}
> +			if (set_head && run_set_head(name))
> +				result = 1;

This looked a bit questionable---I expected that we'd run the
subfetches with "--set-head" option, but this makes us do the
set-head step ourselves after the subfetches are done.  What are
pros-and-cons considered to reach the decision to do it this way?

Thanks.