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