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>

Junio C Hamano committed Sep 17, 2013 at 16:17 UTC 7171d8c15f919c760d52f814a0aee1b1253385b1
1 file changed +43 -5
upload-pack.c
+43 -5
@@ -734,6 +734,16 @@ static int mark_our_ref(const char *refname, const unsigned char *sha1, int flag
734 return 0;
735 }
736
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 +
747 static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
748 {
749 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
755 if (mark_our_ref(refname, sha1, flag, NULL))
756 return 0;
757
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",
763 sha1_to_hex(sha1), refname_nons,
764 0, capabilities,
765 allow_tip_sha1_in_want ? " allow-tip-sha1-in-want" : "",
766 stateless_rpc ? " no-done" : "",
767 + symref_info.buf,
768 git_user_agent_sanitized());
755 - else
769 + strbuf_release(&symref_info);
770 + } else {
771 packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname_nons);
772 + }
773 capabilities = NULL;
774 if (!peel_ref(refname, peeled))
775 packet_write(1, "%s %s^{}\n", sha1_to_hex(peeled), refname_nons);
776 return 0;
777 }
778
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 +
796 static void upload_pack(void)
797 {
798 + struct string_list symref = STRING_LIST_INIT_DUP;
799 +
800 + head_ref_namespaced(find_symref, &symref);
801 +
802 if (advertise_refs || !stateless_rpc) {
803 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);
806 packet_flush(1);
807 } else {
808 head_ref_namespaced(mark_our_ref, NULL);
809 for_each_namespaced_ref(mark_our_ref, NULL);
810 }
811 + string_list_clear(&symref, 1);
812 if (advertise_refs)
813 return;
814