Skip to content

Commit 7171d8c

Browse files
committed
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 <[email protected]>
1 parent a4d695d commit 7171d8c

File tree

1 file changed

+43
-5
lines changed

1 file changed

+43
-5
lines changed

upload-pack.c

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,16 @@ static int mark_our_ref(const char *refname, const unsigned char *sha1, int flag
734734
return 0;
735735
}
736736

737+
static void format_symref_info(struct strbuf *buf, struct string_list *symref)
738+
{
739+
struct string_list_item *item;
740+
741+
if (!symref->nr)
742+
return;
743+
for_each_string_list_item(item, symref)
744+
strbuf_addf(buf, " symref=%s:%s", item->string, (char *)item->util);
745+
}
746+
737747
static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
738748
{
739749
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
745755
if (mark_our_ref(refname, sha1, flag, NULL))
746756
return 0;
747757

748-
if (capabilities)
749-
packet_write(1, "%s %s%c%s%s%s agent=%s\n",
758+
if (capabilities) {
759+
struct strbuf symref_info = STRBUF_INIT;
760+
761+
format_symref_info(&symref_info, cb_data);
762+
packet_write(1, "%s %s%c%s%s%s%s agent=%s\n",
750763
sha1_to_hex(sha1), refname_nons,
751764
0, capabilities,
752765
allow_tip_sha1_in_want ? " allow-tip-sha1-in-want" : "",
753766
stateless_rpc ? " no-done" : "",
767+
symref_info.buf,
754768
git_user_agent_sanitized());
755-
else
769+
strbuf_release(&symref_info);
770+
} else {
756771
packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname_nons);
772+
}
757773
capabilities = NULL;
758774
if (!peel_ref(refname, peeled))
759775
packet_write(1, "%s %s^{}\n", sha1_to_hex(peeled), refname_nons);
760776
return 0;
761777
}
762778

779+
static int find_symref(const char *refname, const unsigned char *sha1, int flag,
780+
void *cb_data)
781+
{
782+
const char *symref_target;
783+
struct string_list_item *item;
784+
unsigned char unused[20];
785+
786+
if ((flag & REF_ISSYMREF) == 0)
787+
return 0;
788+
symref_target = resolve_ref_unsafe(refname, unused, 0, &flag);
789+
if (!symref_target || (flag & REF_ISSYMREF) == 0)
790+
die("'%s' is a symref but it is not?", refname);
791+
item = string_list_append(cb_data, refname);
792+
item->util = xstrdup(symref_target);
793+
return 0;
794+
}
795+
763796
static void upload_pack(void)
764797
{
798+
struct string_list symref = STRING_LIST_INIT_DUP;
799+
800+
head_ref_namespaced(find_symref, &symref);
801+
765802
if (advertise_refs || !stateless_rpc) {
766803
reset_timeout();
767-
head_ref_namespaced(send_ref, NULL);
768-
for_each_namespaced_ref(send_ref, NULL);
804+
head_ref_namespaced(send_ref, &symref);
805+
for_each_namespaced_ref(send_ref, &symref);
769806
packet_flush(1);
770807
} else {
771808
head_ref_namespaced(mark_our_ref, NULL);
772809
for_each_namespaced_ref(mark_our_ref, NULL);
773810
}
811+
string_list_clear(&symref, 1);
774812
if (advertise_refs)
775813
return;
776814

0 commit comments

Comments
 (0)