upload-pack: send symbolic ref information as capability

One long-standing flaw in the pack transfer protocol was that there
was no way to tell the other end which branch "HEAD" points at.
With a capability "symref=HEAD:refs/heads/master", let the sender to
tell the receiver what symbolic ref points at what ref.

This capability can be repeated more than once to represent symbolic
refs other than HEAD, such as "refs/remotes/origin/HEAD").

Add an infrastructure to collect symbolic refs, format them as extra
capabilities and put it on the wire.  For now, just send information
on the "HEAD" and nothing else.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Junio C Hamano 2013-09-17 16:17:33 -07:00
parent a4d695de0d
commit 7171d8c15f
1 changed files with 43 additions and 5 deletions

View File

@ -734,6 +734,16 @@ static int mark_our_ref(const char *refname, const unsigned char *sha1, int flag
return 0;
}
static void format_symref_info(struct strbuf *buf, struct string_list *symref)
{
struct string_list_item *item;
if (!symref->nr)
return;
for_each_string_list_item(item, symref)
strbuf_addf(buf, " symref=%s:%s", item->string, (char *)item->util);
}
static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
static const char *capabilities = "multi_ack thin-pack side-band"
@ -745,32 +755,60 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
if (mark_our_ref(refname, sha1, flag, NULL))
return 0;
if (capabilities)
packet_write(1, "%s %s%c%s%s%s agent=%s\n",
if (capabilities) {
struct strbuf symref_info = STRBUF_INIT;
format_symref_info(&symref_info, cb_data);
packet_write(1, "%s %s%c%s%s%s%s agent=%s\n",
sha1_to_hex(sha1), refname_nons,
0, capabilities,
allow_tip_sha1_in_want ? " allow-tip-sha1-in-want" : "",
stateless_rpc ? " no-done" : "",
symref_info.buf,
git_user_agent_sanitized());
else
strbuf_release(&symref_info);
} else {
packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname_nons);
}
capabilities = NULL;
if (!peel_ref(refname, peeled))
packet_write(1, "%s %s^{}\n", sha1_to_hex(peeled), refname_nons);
return 0;
}
static int find_symref(const char *refname, const unsigned char *sha1, int flag,
void *cb_data)
{
const char *symref_target;
struct string_list_item *item;
unsigned char unused[20];
if ((flag & REF_ISSYMREF) == 0)
return 0;
symref_target = resolve_ref_unsafe(refname, unused, 0, &flag);
if (!symref_target || (flag & REF_ISSYMREF) == 0)
die("'%s' is a symref but it is not?", refname);
item = string_list_append(cb_data, refname);
item->util = xstrdup(symref_target);
return 0;
}
static void upload_pack(void)
{
struct string_list symref = STRING_LIST_INIT_DUP;
head_ref_namespaced(find_symref, &symref);
if (advertise_refs || !stateless_rpc) {
reset_timeout();
head_ref_namespaced(send_ref, NULL);
for_each_namespaced_ref(send_ref, NULL);
head_ref_namespaced(send_ref, &symref);
for_each_namespaced_ref(send_ref, &symref);
packet_flush(1);
} else {
head_ref_namespaced(mark_our_ref, NULL);
for_each_namespaced_ref(mark_our_ref, NULL);
}
string_list_clear(&symref, 1);
if (advertise_refs)
return;