{"thread":{"id":"12772","subject":"[PATCH] \"git push\": tellme-more protocol extension","startedAt":"2008-03-20T07:58:24Z","lastAt":"2008-03-21T04:47:01Z","messageCount":5,"participants":["Junio C Hamano","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"72498","messageId":"7vd4ppyggv.fsf@gitster.siamese.dyndns.org","threadId":"12772","inReplyTo":null,"subject":"[PATCH] \"git push\": tellme-more protocol extension","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-03-20T07:58:24Z","receivedAt":"2008-03-20T07:58:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The workflow git supports has always been biased towards the kernel style\n\"each developer has his own publishing repository only himself pushes\ninto, and others pull from it\".  We have been fairly good at enhancing\nsupport for the workflow at the other end of the spectrum, a shared\nrepository everybody clones and updates from and everybody also pushes\ninto.  However, if you look under the hood and study the native protocol\nused for fetching and pushing, it becomes apparent that our origin is\nreally \"pushing into owned publishing point\" workflow.\n\nThe protocol between send-pack and receive-pack, which is used for \"git\npush\", is much simpler than the protocol used on the \"git fetch\" side.\n\nWhen the sender connects, the receiver first tells the sender what commits\nits refs are pointing at, and then the sender figures out what revision\nrange to build a pack out of, based on the \"here are my refs\" information\nit learned from the receiver and the set of refs it has.\n\nThis works well enough, if you assume that the sender always knows enough\nabout what the receiver has, and when the sender owns the receiving end\n(i.e. he is the only one who pushes into it), by definition, all the\ncommits at the receiving end will be what the sender has pushed there, and\nit already knows about them.\n\nAlso, because by default we do not accept a commit that does not\nfast-forward the old ref when you are updating an existing branch, this\nassumption often holds true even for shared repository workflow.  If your\npush is blocked because it does not fast-forward, the first thing you\nwould do is to fetch from there, which makes you learn everything the\nother end has.\n\nBut this breaks down in one case.  If you have a single branch shared\nrepository with two developers, and after one developer updates that\nbranch, if the second developer wants to push a new branch (fork) to the\nshared repository:\n\n (0) First, an illustration of the situation.  Developer one and two\n     cloned from the shared repository when its head was at commit A.\n     Developer one built some changes and pushed it back.  The shared\n     repository's \"master\" is now at commit B.\n\n     ---o----o----o----A----o----o----B\n                        \\\n                         o---o---o---C\n\n     In the meantime, developer two hacked on top of A and his tip is at\n     commit C.  He is not confident enough to push this back to \"master\"\n     (and if he did so, he won't succeed because it wouldn't fast-forward),\n     but wants to share it with co-workers by pushing it to a new \"xprm\"\n     branch at the shared repository.  He says:\n\n        $ git push origin master:refs/heads/xprm\n\n     Now what happens?\n\n (1) The recieving end (i.e. the shared repository) will say \"master is\n     now at commit B\".\n\n (2) The sending end, however, has not seen B.  All it knows is that there\n     is no \"xprm\" over there yet, and it wants to send C.\n\n (3) Based on the available information, it ends up sending the whole\n     history leading to C.  Oops.\n\nThis patch teaches the send-pack protocol a new extension, \"tellme-more\".\n\nWhen both ends support this extension, the receiving end first says \"I\nsupport tellme-more, and here are my refs\".  The sending side, when it\nsees commits that it does not know about (e.g. \"B\" in the above example),\ncan then ask \"I do not know about B, but it probably is related to what I\nalready have, so tell me more about it\".\n\nThe receiving end then responds with list of commits that are ancestors of\n\"B\", and in the above example, the sender will find out that the recieving\nend also has \"A\".  After this exchange, the sending end can build a pack\nthat contains objects between A..C, instead of having to send the full\nhistory leading to \"C\".\n\nWithout the protocol extension, the attached small test (t5407) ends up\ntransferring 39 objects, which exactly matches the above observation, and\nwith the patch it sends only 9.\n\nThere are a few obvious things that could be improved.\n\n- Currently, there is a backoff mechanism to optimize responses to\n  tellme-more requests by sending every N^2-th non-merge commit in a\n  single-strand-of-pearls ancestry chain so that a very stale sender can\n  find some common ancestor efficiently, but this backoff could be a bit\n  too aggressive.\n\n- There is no active defence against a malicious sender that asks\n  tellme-more over and over to force the receiver do extra work, except\n  the abovementioned backoff and the fact that the receiver stops working\n  once it reaches the root commit.  Push is allowed to only trusted\n  parties so hopefully this will not be such a big deal.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * Obviously post 1.5.5 material.\n\n Documentation/technical/pack-protocol.txt |   29 ++++++-\n builtin-send-pack.c                       |  108 ++++++++++++++++++++++-\n receive-pack.c                            |  141 ++++++++++++++++++++++++++++-\n t/t5407-send-pack-shared.sh               |   59 ++++++++++++\n 4 files changed, 332 insertions(+), 5 deletions(-)\n create mode 100755 t/t5407-send-pack-shared.sh\n\ndiff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt\nindex 9cd48b4..fc848b4 100644\n--- a/Documentation/technical/pack-protocol.txt\n+++ b/Documentation/technical/pack-protocol.txt\n@@ -29,7 +29,7 @@ upload-pack (S) | fetch/clone-pack (C) protocol:\n send-pack | receive-pack protocol.\n \n \t# Tell the pusher what commits we have and what their names are\n-\tC: SHA1 name\n+\tC: SHA1 name (capabilities)\n \tC: ...\n \tC: SHA1 name\n \tC: # flush -- it's your turn\n@@ -39,3 +39,30 @@ send-pack | receive-pack protocol.\n \tS: ...\n \tS: # flush -- done with the list\n \tS: XXXXXXX --- packfile contents.\n+\n+When tellme-more extension is used:\n+\n+\t# Tell the pusher what commits we have and what their names are\n+\tC: SHA1 name (capabilities)\n+\tC: ...\n+\tC: SHA1 name\n+\tC: # flush -- it's your turn\n+\t# Tell the puller what the pusher has\n+\tS: old-SHA1 new-SHA1 name\n+\tS: old-SHA1 new-SHA1 name\n+\tS: tellme-more old-SHA1\t-- I do not know about this one,\n+\tS: tellme-more old-SHA1 -- give me ancestors of them.\n+\tS: # flush -- done with the list\n+\t# Tell the pusher more about history\n+\tC: SHA1 old-SHA1 -- This is ancestor of that, you might know\n+\tC: SHA1 old-SHA1\n+\tC: ...\n+\tC: # flush -- have you heard ehough?\n+\tS: tellme-more old-SHA1\t-- I still want to know more about this one\n+\tS: # flush -- please give me more\n+\tC: SHA1 old-SHA1'\n+\tC: SHA1 old-SHA1'\n+\tC: ...\n+\tC: # flush -- have you heard ehough?\n+\tS: # flush -- yes, thanks, we've heard enough\n+\tS: XXXXXXX --- packfile contents.\ndiff --git a/builtin-send-pack.c b/builtin-send-pack.c\nindex 930e0fb..1a125cf 100644\n--- a/builtin-send-pack.c\n+++ b/builtin-send-pack.c\n@@ -15,6 +15,13 @@ static struct send_pack_args args = {\n \t/* .receivepack = */ \"git-receive-pack\",\n };\n \n+static struct additional_base {\n+\tunsigned char tip[20];\n+\tunsigned char last[20];\n+\tstruct commit *commit;\n+} *unknown;\n+static int unknown_nr = 0, unknown_alloc = 0;\n+\n /*\n  * Make a pack stream and spit it out into file descriptor fd\n  */\n@@ -71,6 +78,23 @@ static int pack_objects(int fd, struct ref *refs)\n \t\trefs = refs->next;\n \t}\n \n+\tif (unknown) {\n+\t\tint i;\n+\t\tchar buf[42];\n+\n+\t\tfor (i = 0; i < unknown_nr; i++) {\n+\t\t\tstruct commit *c = unknown[i].commit;\n+\t\t\tif (!c)\n+\t\t\t\tcontinue;\n+\t\t\tmemcpy(buf + 1, sha1_to_hex(c->object.sha1), 40);\n+\t\t\tbuf[0] = '^';\n+\t\t\tbuf[41] = '\\n';\n+\t\t\tif (!write_or_whine(po.in, buf, 42,\n+\t\t\t\t\t    \"send-pack: send additional base\"))\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\n \tclose(po.in);\n \tif (finish_command(&po))\n \t\treturn error(\"pack-objects died with strange error\");\n@@ -373,6 +397,81 @@ static int refs_pushed(struct ref *ref)\n \treturn 0;\n }\n \n+static void ask_for_more(int in, int out, struct ref *remote_refs)\n+{\n+\tstruct ref *refs;\n+\tint i;\n+\n+\tfor (refs = remote_refs; refs; refs = refs->next) {\n+\t\tstruct commit *commit;\n+\n+\t\tif (is_null_sha1(refs->old_sha1))\n+\t\t\tcontinue;\n+\t\tcommit = lookup_commit_reference_gently(refs->old_sha1, 1);\n+\t\tif (!commit) {\n+\t\t\tALLOC_GROW(unknown, unknown_nr+1, unknown_alloc);\n+\t\t\thashcpy(unknown[unknown_nr].last, refs->old_sha1);\n+\t\t\tunknown[unknown_nr].commit = NULL;\n+\t\t\tunknown_nr++;\n+\t\t}\n+\t}\n+\tif (!unknown_nr) {\n+\t\tpacket_flush(out);\n+\t\treturn;\n+\t}\n+\n+\tfor (i = 0; i < 5; i++) {\n+\t\tint j, sent = 0;\n+\t\tfor (j = 0; j < unknown_nr; j++) {\n+\t\t\tif (unknown[j].commit)\n+\t\t\t\tcontinue;\n+\t\t\thashcpy(unknown[j].tip, unknown[j].last);\n+\t\t\tpacket_write(out, \"tellme-more %s\\n\",\n+\t\t\t\t     sha1_to_hex(unknown[j].tip));\n+\t\t\tsent++;\n+\t\t}\n+\t\tpacket_flush(out);\n+\t\tif (!sent)\n+\t\t\tbreak;\n+\t\tsent = 0;\n+\t\twhile (1) {\n+\t\t\tchar line[1000];\n+\t\t\tunsigned char tip[20];\n+\t\t\tunsigned char last[20];\n+\t\t\tint len;\n+\t\t\tstruct commit *commit;\n+\n+\t\t\tlen = packet_read_line(in, line, sizeof(line));\n+\t\t\tif (!len)\n+\t\t\t\tbreak;\n+\t\t\tsent++;\n+\t\t\tif (line[len-1] == '\\n')\n+\t\t\t\tline[--len] = '\\0';\n+\t\t\tif (len < 81 ||\n+\t\t\t    line[40] != ' ' ||\n+\t\t\t    get_sha1_hex(line, last) ||\n+\t\t\t    get_sha1_hex(line + 41, tip))\n+\t\t\t\tdie(\"protocol error: got '%s'\", line);\n+\t\t\tfor (j = 0; j < unknown_nr; j++)\n+\t\t\t\tif (!hashcmp(unknown[j].tip, tip))\n+\t\t\t\t\tbreak;\n+\t\t\tif (unknown_nr <= j || unknown[j].commit)\n+\t\t\t\tcontinue;\n+\t\t\thashcpy(unknown[j].last, last);\n+\t\t\tcommit = lookup_commit_reference_gently(last, 1);\n+\t\t\tif (!commit)\n+\t\t\t\tcontinue;\n+\t\t\tunknown[j].commit = commit;\n+\t\t}\n+\n+\t\tif (!sent) {\n+\t\t\t/* they do not have any more to tell us */\n+\t\t\tpacket_flush(out);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+}\n+\n static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec)\n {\n \tstruct ref *ref;\n@@ -380,6 +479,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest\n \tint ask_for_status_report = 0;\n \tint allow_deleting_refs = 0;\n \tint expect_status_report = 0;\n+\tint can_ask_for_more = 0;\n \tint flags = MATCH_REFS_NONE;\n \tint ret;\n \n@@ -397,6 +497,8 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest\n \t\task_for_status_report = 1;\n \tif (server_supports(\"delete-refs\"))\n \t\tallow_deleting_refs = 1;\n+\tif (server_supports(\"tellme-more\"))\n+\t\tcan_ask_for_more = 1;\n \n \t/* match them up */\n \tif (!remote_tail)\n@@ -495,7 +597,11 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest\n \t\t\tREF_STATUS_OK;\n \t}\n \n-\tpacket_flush(out);\n+\tif (can_ask_for_more && new_refs)\n+\t\task_for_more(in, out, remote_refs);\n+\telse\n+\t\tpacket_flush(out);\n+\n \tif (new_refs && !args.dry_run) {\n \t\tif (pack_objects(out, remote_refs) < 0)\n \t\t\treturn -1;\ndiff --git a/receive-pack.c b/receive-pack.c\nindex f83ae87..0574e37 100644\n--- a/receive-pack.c\n+++ b/receive-pack.c\n@@ -15,7 +15,7 @@ static int transfer_unpack_limit = -1;\n static int unpack_limit = 100;\n static int report_status;\n \n-static char capabilities[] = \" report-status delete-refs \";\n+static char capabilities[] = \" report-status delete-refs tellme-more \";\n static int capabilities_sent;\n \n static int receive_pack_config(const char *var, const char *value)\n@@ -293,7 +293,23 @@ static void execute_commands(const char *unpacker_error)\n \t}\n }\n \n-static void read_head_info(void)\n+static int parse_tellme_more(char *line, int len, struct commit_list **list)\n+{\n+\tunsigned char sha1[20];\n+\tstruct commit *c;\n+\n+\tif (52 <= len &&\n+\t    !strncmp(line, \"tellme-more \", 12) &&\n+\t    !get_sha1_hex(line + 12, sha1)) {\n+\t\tc = lookup_commit_reference_gently(sha1, 1);\n+\t\tif (c)\n+\t\t\tcommit_list_insert(c, list);\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+static void read_head_info(struct commit_list **tellme_more)\n {\n \tstruct command **p = &commands;\n \tfor (;;) {\n@@ -308,6 +324,8 @@ static void read_head_info(void)\n \t\t\tbreak;\n \t\tif (line[len-1] == '\\n')\n \t\t\tline[--len] = 0;\n+\t\tif (parse_tellme_more(line, len, tellme_more))\n+\t\t\tcontinue;\n \t\tif (len < 83 ||\n \t\t    line[40] != ' ' ||\n \t\t    line[81] != ' ' ||\n@@ -333,6 +351,118 @@ static void read_head_info(void)\n \t}\n }\n \n+static void sender_wants_more(struct commit_list **tellme_more)\n+{\n+\tchar line[1024];\n+\tint len;\n+\n+\twhile (1) {\n+\t\tlen = packet_read_line(0, line, sizeof(line));\n+\t\tif (!len)\n+\t\t\treturn;\n+\t\tif (line[len-1] == '\\n')\n+\t\t\tline[--len] = '\\0';\n+\t\tif (parse_tellme_more(line, len, tellme_more))\n+\t\t\tcontinue;\n+\t\t/* ignore other responses for future extension */\n+\t}\n+}\n+\n+#define SHOWN_TO_SENDER (1<<20)\n+static struct commit_list **add_parents(struct commit *commit, struct commit_list **tail, int round)\n+{\n+\tstruct commit_list *parents = commit->parents;\n+\n+\t/*\n+\t * In later rounds, we will send one commit for every N\n+\t * commits in single parent chain to optimize for very stale\n+\t * pushers.\n+\t */\n+\tround *= round;\n+\twhile (round--) {\n+\t\tif (!parents || (parents && parents->next))\n+\t\t\tbreak; /* do not skip merges */\n+\t\tif (parse_commit(parents->item))\n+\t\t\treturn tail; /* oops */\n+\t\tif (!parents->item->parents)\n+\t\t\tbreak; /* do not skip root */\n+\t\tparents = parents->item->parents;\n+\t}\n+\n+\twhile (parents) {\n+\t\tcommit = parents->item;\n+\t\tparents = parents->next;\n+\t\tif (commit->object.flags & SHOWN_TO_SENDER)\n+\t\t\tcontinue;\n+\t\tif (parse_commit(commit))\n+\t\t\tcontinue;\n+\t\ttail = &commit_list_insert(commit, tail)->next;\n+\t}\n+\treturn tail;\n+}\n+\n+#define BATCH 20\n+static void send_ancestors(struct commit *commit, int round)\n+{\n+\tstruct commit_list *list = NULL;\n+\tstruct commit_list **tail = &list;\n+\tint count = 0;\n+\n+\ttail = &commit_list_insert(commit, tail)->next;\n+\twhile (list && count < BATCH) {\n+\t\tstruct commit_list *elem = list;\n+\t\tstruct commit *c = elem->item;\n+\n+\t\tlist = list->next;\n+\t\tfree(elem);\n+\t\tif (!list)\n+\t\t\ttail = &list;\n+\n+\t\tif (c->object.flags & SHOWN_TO_SENDER)\n+\t\t\tcontinue;\n+\t\tif (parse_commit(c))\n+\t\t\tcontinue;\n+\t\tc->object.flags |= SHOWN_TO_SENDER;\n+\t\tif (c != commit) {\n+\t\t\tpacket_write(1, \"%s %s\\n\",\n+\t\t\t\t     sha1_to_hex(c->object.sha1),\n+\t\t\t\t     sha1_to_hex(commit->object.sha1));\n+\t\t\tcount++;\n+\t\t}\n+\t\ttail = add_parents(c, tail, round);\n+\t}\n+\tclear_commit_marks(commit, SHOWN_TO_SENDER);\n+\tfree_commit_list(list);\n+}\n+\n+static void exchange_history(struct commit_list *list)\n+{\n+\tint round = 0;\n+\n+\twhile (list) {\n+\t\twhile (list) {\n+\t\t\tstruct commit_list *elem = list;\n+\t\t\tstruct commit *commit = elem->item;\n+\n+\t\t\tlist = list->next;\n+\t\t\tfree(elem);\n+\n+\t\t\t/*\n+\t\t\t * The sender does not know about commit and asks its\n+\t\t\t * ancestors to be sent.\n+\t\t\t */\n+\t\t\tif (parse_commit(commit))\n+\t\t\t\tcontinue;\n+\n+\t\t\tsend_ancestors(commit, round);\n+\t\t}\n+\t\tpacket_flush(1); /* have you heard enough? */\n+\n+\t\tsender_wants_more(&list);\n+\t\tround++;\n+\t}\n+}\n+\n static const char *parse_pack_header(struct pack_header *hdr)\n {\n \tswitch (read_pack_header(0, hdr)) {\n@@ -455,6 +585,7 @@ int main(int argc, char **argv)\n {\n \tint i;\n \tchar *dir = NULL;\n+\tstruct commit_list *tellme_more = NULL;\n \n \targv++;\n \tfor (i = 1; i < argc; i++) {\n@@ -491,7 +622,11 @@ int main(int argc, char **argv)\n \t/* EOF */\n \tpacket_flush(1);\n \n-\tread_head_info();\n+\tread_head_info(&tellme_more);\n+\n+\tif (tellme_more)\n+\t\texchange_history(tellme_more);\n+\n \tif (commands) {\n \t\tconst char *unpack_status = NULL;\n \ndiff --git a/t/t5407-send-pack-shared.sh b/t/t5407-send-pack-shared.sh\nnew file mode 100755\nindex 0000000..c31322c\n--- /dev/null\n+++ b/t/t5407-send-pack-shared.sh\n@@ -0,0 +1,59 @@\n+#!/bin/sh\n+\n+test_description='shared repository workflow, not sending too many'\n+\n+. ./test-lib.sh\n+\n+commit () {\n+\ttest_tick &&\n+\techo >file \"$1\" &&\n+\tgit add file &&\n+\tgit commit -m \"$1\"\n+}\n+\n+test_expect_success setup '\n+\n+\ttest_create_repo shared &&\n+\t(\n+\t\tcd shared &&\n+\t\tfor i in 1 2 3 4 5 6 7 8 9 10\n+\t\tdo\n+\t\t\tcommit $i || exit 1\n+\t\tdone\n+\t) &&\n+\n+\tgit clone shared one &&\n+\tgit clone shared two &&\n+\n+\t(\n+\t\tcd one &&\n+\t\tfor i in 11 12 13 14\n+\t\tdo\n+\t\t\tcommit $i || exit 1\n+\t\tdone\n+\t\tgit push origin master\n+\t) &&\n+\n+\t(\n+\t\tcd shared &&\n+\t\tgit fsck-objects --full &&\n+\t\tgit prune\n+\t)\n+'\n+\n+test_expect_success 'push from side' '\n+\n+\t(\n+\t\tcd two &&\n+\t\tfor i in 15 16 17\n+\t\tdo\n+\t\t\tcommit $i || exit 1\n+\t\tdone\n+\t\tgit push --verbose origin master:refs/heads/side 2>../log\n+\t) &&\n+\ttr \"\\015\" \"\\012\" < log >out &&\n+\tgrep \"^Total 9 \" out\n+\n+'\n+\n+test_done\n-- \n1.5.5.rc0.133.g7e207\n"},{"id":"72553","messageId":"20080321023303.GE1613@coredump.intra.peff.net","threadId":"12772","inReplyTo":"7vd4ppyggv.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] \"git push\": tellme-more protocol extension","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2008-03-21T02:33:03Z","receivedAt":"2008-03-21T02:33:03Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Mar 20, 2008 at 12:58:24AM -0700, Junio C Hamano wrote:\n\n> When both ends support this extension, the receiving end first says \"I\n> support tellme-more, and here are my refs\".  The sending side, when it\n> sees commits that it does not know about (e.g. \"B\" in the above example),\n> can then ask \"I do not know about B, but it probably is related to what I\n> already have, so tell me more about it\".\n> \n> The receiving end then responds with list of commits that are ancestors of\n> \"B\", and in the above example, the sender will find out that the recieving\n> end also has \"A\".  After this exchange, the sending end can build a pack\n> that contains objects between A..C, instead of having to send the full\n> history leading to \"C\".\n\nHrm. It seems like we are sending a lot more data than we need. The\nsender doesn't actually _care_ about A..B, which can be arbitrarily\nlong; he only cares about finding A.\n\nI wonder if there's a way to for the _sender_ to claim \"I have up to A\",\nand the receiver to send back an \"OK, just send A..C.\" If the receiver\nis currently at \"B\", then the sender can't figure it out from the refs\nthe receiver shows. But he might he might optimistically suggest it if\nhe has other refs pointing to \"A\" (like remotes/origin/master).\n\nIOW, something like:\n\n  [S connects to R]\n  R: master is at B\n  [S realizes he doesn't have B, so he shows his refs, without names]\n  S: I have optimistically interesting commits: A, C\n  R: I have A\n  S: OK, here's C ^A\n\nIn the case that the receiver doesn't have any of those refs, then we\nfall back to the old behavior. So basically rather than enumerating\nthe ancestors, we optimistically assume that things that are in refs are\nlikely to be of interest.\n\nTwo disadvantages I can think of:\n\n  1. S actually \"leaks\" some knowledge about which commits he has to R\n     (not refnames, but actual SHA1s). I don't know if we care about\n     that or not.\n\n  2. This can actually be _more_ network intensive than your proposal.\n     It trades off sending all of the ancestors A..B for sending the tip\n     of all of the refs. So if A..B is short, but you have a ton of\n     local refs, it's a lose. You could maybe be smarter about the\n     optimistic choice (e.g., \"I'm talking with origin, therefore\n     consider only refs/remotes/origin/*\"). That also alleviates the\n     \"leak\" problem since you are saying \"I have this commit that I got\n     from you before.\" But it doesn't kick in if you usually talk to one\n     remote, but now are talking to a different, related one.\n\n-Peff\n"},{"id":"72559","messageId":"7vy78cu2kt.fsf@gitster.siamese.dyndns.org","threadId":"12772","inReplyTo":"20080321023303.GE1613@coredump.intra.peff.net","subject":"Re: [PATCH] \"git push\": tellme-more protocol extension","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-03-21T04:24:18Z","receivedAt":"2008-03-21T04:24:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n>   2. This can actually be _more_ network intensive than your proposal.\n\nI do not think this matters much in practice.  If you have tons of refs on\n_your_ side to matter, some of them would be tags that are known to both\nends.  The only case it really is troublesome is when the sender does not\nknow _any_ of the refs the receiver presented to it.\n\nIn the illustration in the message you are responding to, if the commit A\nwas tagged at the shared repository when two developers cloned, it does\nnot matter if C is unknown to the second pusher.\n\nSo a potential improvement might be to ask for \"tellme-more\" only if we do\nnot know any of the refs the receiver showed us; my patch does it if there\nis at least one unknown instead.\n"},{"id":"72560","messageId":"20080321042821.GA5453@coredump.intra.peff.net","threadId":"12772","inReplyTo":"7vy78cu2kt.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] \"git push\": tellme-more protocol extension","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2008-03-21T04:28:21Z","receivedAt":"2008-03-21T04:28:21Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Mar 20, 2008 at 09:24:18PM -0700, Junio C Hamano wrote:\n\n> So a potential improvement might be to ask for \"tellme-more\" only if we do\n> not know any of the refs the receiver showed us; my patch does it if there\n> is at least one unknown instead.\n\nYou can still get cases where doing this is more expensive than your\npatch, because A..B is very small but shared_tag..A is very large. I'm\nnot sure which is more likely in practice.\n\n-Peff\n"},{"id":"72563","messageId":"7viqzgu1iy.fsf@gitster.siamese.dyndns.org","threadId":"12772","inReplyTo":"20080321042821.GA5453@coredump.intra.peff.net","subject":"Re: [PATCH] \"git push\": tellme-more protocol extension","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-03-21T04:47:01Z","receivedAt":"2008-03-21T04:47:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Thu, Mar 20, 2008 at 09:24:18PM -0700, Junio C Hamano wrote:\n>\n>> So a potential improvement might be to ask for \"tellme-more\" only if we do\n>> not know any of the refs the receiver showed us; my patch does it if there\n>> is at least one unknown instead.\n>\n> You can still get cases where doing this is more expensive than your\n> patch, because A..B is very small but shared_tag..A is very large. I'm\n> not sure which is more likely in practice.\n\nOh, I have a few fix-ups since I sent the patch, but I am not planning to\nexperiment with _your_ protocol myself.  Debugging one protocol extension\na week is painful enough ;-)\n\nAnd tellme-more does not send _every_ commit.  The sender asks only about\nthe refs it does not know about, and the receiver sends densely near the\ntip and then walks sparsely as it goes deeper, so the traffic wouldn't be\nso bad.  Latency might be a different story, though.\n"}]}