Subject: Re: Cloning empty repository uses locally configured default branch name

From: Junio C Hamano <[email-masked]>
Date: Mon, 07 Dec 2020 18:16:07 -0800

Jonathan Tan <[email-masked]> writes:

> Has anyone looked into solutions for this? Both protocol v0 and v2 do
> not send symref information about unborn branches (v0 because, as
> protocol-capabilities.txt says, "servers SHOULD include this capability
> for the HEAD symref if it is one of the refs being sent"; v2 because
> a symref is included only if it refers to one of the refs being sent).
> In protocol v2, this could be done by adding a capability to ls-remote
> (maybe, "unborn"), and in protocol v0, this could be done either by
> updating the existing "symref" capability to be written even when the
> target branch is unborn (which is potentially backwards incompatible) or
> introducing a new capability which is like "symref".

Thanks for looking into this (I think this came up again today
during my reviews of some topic).

It would be a backward incompatible change to add to v0, but at this
point shouldn't we be leaving v0 as-is and move everybody to v2?

If it is a simple and safe enough change, though, saying "why not"
is very tempting, though.

> A small issue is that upload-pack protocol v0 doesn't even write the
> blank ref line ("000...000 capabilities^{}") if HEAD points to an unborn
> branch, but that can be fixed as in the patch below.

I think the codepaths we have today in process_capabilities() and
process_dummy_ref() (both in connect.c) would do the right thing
when it sees a blank ref line even when nothing gets transported,
but I smell that the rewrite of this state machine is fairly recent
(say in the past few years) and I do not offhand know if clients
before the rewrite of the state machine (say in v2.18.0) would be OK
with the change.  It should be easy to check, though.

> Signed-off-by: Jonathan Tan <[email-masked]>
> ---
>  upload-pack.c | 40 +++++++++++++++++++++++++++-------------
>  1 file changed, 27 insertions(+), 13 deletions(-)
>
> diff --git a/upload-pack.c b/upload-pack.c
> index 1006bebd50..d2359a8560 100644
> --- a/upload-pack.c
> +++ b/upload-pack.c
> @@ -1179,18 +1179,15 @@ static void format_symref_info(struct strbuf *buf, struct string_list *symref)
>  		strbuf_addf(buf, " symref=%s:%s", item->string, (char *)item->util);
>  }
>  
> -static int send_ref(const char *refname, const struct object_id *oid,
> -		    int flag, void *cb_data)
> +static const char *capabilities = "multi_ack thin-pack side-band"
> +	" side-band-64k ofs-delta shallow deepen-since deepen-not"
> +	" deepen-relative no-progress include-tag multi_ack_detailed";
> +
> +static void write_ref_lines(const char *refname_nons,
> +			    const struct object_id *oid,
> +			    const struct object_id *peeled,
> +			    struct upload_pack_data *data)
>  {
> -	static const char *capabilities = "multi_ack thin-pack side-band"
> -		" side-band-64k ofs-delta shallow deepen-since deepen-not"
> -		" deepen-relative no-progress include-tag multi_ack_detailed";
> -	const char *refname_nons = strip_namespace(refname);
> -	struct object_id peeled;
> -	struct upload_pack_data *data = cb_data;
> -
> -	if (mark_our_ref(refname_nons, refname, oid))
> -		return 0;
>  
>  	if (capabilities) {
>  		struct strbuf symref_info = STRBUF_INIT;
> @@ -1213,8 +1210,23 @@ static int send_ref(const char *refname, const struct object_id *oid,
>  		packet_write_fmt(1, "%s %s\n", oid_to_hex(oid), refname_nons);
>  	}
>  	capabilities = NULL;
> -	if (!peel_ref(refname, &peeled))
> -		packet_write_fmt(1, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
> +	if (peeled)
> +		packet_write_fmt(1, "%s %s^{}\n", oid_to_hex(peeled), refname_nons);
> +}
> +
> +static int send_ref(const char *refname, const struct object_id *oid,
> +		    int flag, void *cb_data)
> +{
> +	const char *refname_nons = strip_namespace(refname);
> +	struct object_id peeled;
> +	struct upload_pack_data *data = cb_data;
> +
> +	if (mark_our_ref(refname_nons, refname, oid))
> +		return 0;
> +	write_ref_lines(refname_nons,
> +			oid,
> +			peel_ref(refname, &peeled) ? NULL : &peeled,
> +			data);
>  	return 0;
>  }
>  
> @@ -1332,6 +1344,8 @@ void upload_pack(struct upload_pack_options *options)
>  		reset_timeout(data.timeout);
>  		head_ref_namespaced(send_ref, &data);
>  		for_each_namespaced_ref(send_ref, &data);
> +		if (capabilities)
> +			write_ref_lines("capabilities^{}", &null_oid, NULL, &data);
>  		advertise_shallow_grafts(1);
>  		packet_flush(1);
>  	} else {