diff options
author | Junio C Hamano <gitster@pobox.com> | 2013-09-17 16:17:33 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2013-09-17 21:50:26 -0700 |
commit | 7171d8c15f919c760d52f814a0aee1b1253385b1 (patch) | |
tree | 55e34319af1d9155119cbf6de0ccf2852f08ffb0 | |
parent | a4d695de0dbaf41505cb6db44c6bb8290980398e (diff) | |
download | git-7171d8c15f919c760d52f814a0aee1b1253385b1.tar.gz |
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>
-rw-r--r-- | upload-pack.c | 48 |
1 files changed, 43 insertions, 5 deletions
diff --git a/upload-pack.c b/upload-pack.c index a6e107f0b1..979fc8eae3 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -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; |