{"thread":{"id":"23527","subject":"[PATCH v2 1/2] receive-pack: switch global variable 'commands' to a parameter","startedAt":"2010-04-19T16:25:42Z","lastAt":"2012-12-12T19:38:44Z","messageCount":17,"participants":["Jay Soffian","Junio C Hamano","Ævar Arnfjörð Bjarmason","Shawn Pearce"],"isPatch":true,"patchVersion":2,"patchTotal":2},"messages":[{"id":"139911","messageId":"1271694343-31876-1-git-send-email-jaysoffian@gmail.com","threadId":"23527","inReplyTo":null,"subject":"[PATCH v2 1/2] receive-pack: switch global variable 'commands' to a parameter","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2010-04-19T16:25:42Z","receivedAt":"2010-04-19T16:25:42Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"Receive-pack is inconsistent in its usage of the 'commands'\nvariable; though it is setup as a global and accessed that way by\nexecute_commands(), report(), and run_receive_hook(), it is also\npassed as a parameter to delete_only() and run_update_post_hook().\n\nFor consistency, make it local to cmd_receive_pack and pass it as a\nparameter. As long as we're cleaning up, also make our use of the\nnames 'commands' and 'cmd' consistent.\n\nSigned-off-by: Jay Soffian <jaysoffian@gmail.com>\n---\n builtin/receive-pack.c |   60 +++++++++++++++++++++--------------------------\n 1 files changed, 27 insertions(+), 33 deletions(-)\n\nChanges from v1:\n\n- Minor reword of commit message\n- Removed two extraneous whitespace changes\n\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 0559fcc..fffb6ea 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -134,8 +134,6 @@ struct command {\n \tchar ref_name[FLEX_ARRAY]; /* more */\n };\n \n-static struct command *commands;\n-\n static const char pre_receive_hook[] = \"hooks/pre-receive\";\n static const char post_receive_hook[] = \"hooks/post-receive\";\n \n@@ -188,7 +186,7 @@ static int copy_to_sideband(int in, int out, void *arg)\n \treturn 0;\n }\n \n-static int run_receive_hook(const char *hook_name)\n+static int run_receive_hook(struct command *commands, const char *hook_name)\n {\n \tstatic char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];\n \tstruct command *cmd;\n@@ -447,15 +445,15 @@ static const char *update(struct command *cmd)\n \n static char update_post_hook[] = \"hooks/post-update\";\n \n-static void run_update_post_hook(struct command *cmd)\n+static void run_update_post_hook(struct command *commands)\n {\n-\tstruct command *cmd_p;\n+\tstruct command *cmd;\n \tint argc;\n \tconst char **argv;\n \tstruct child_process proc;\n \n-\tfor (argc = 0, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {\n-\t\tif (cmd_p->error_string)\n+\tfor (argc = 0, cmd = commands; cmd; cmd = cmd->next) {\n+\t\tif (cmd->error_string)\n \t\t\tcontinue;\n \t\targc++;\n \t}\n@@ -464,12 +462,12 @@ static void run_update_post_hook(struct command *cmd)\n \targv = xmalloc(sizeof(*argv) * (2 + argc));\n \targv[0] = update_post_hook;\n \n-\tfor (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {\n+\tfor (argc = 1, cmd = commands; cmd; cmd = cmd->next) {\n \t\tchar *p;\n-\t\tif (cmd_p->error_string)\n+\t\tif (cmd->error_string)\n \t\t\tcontinue;\n-\t\tp = xmalloc(strlen(cmd_p->ref_name) + 1);\n-\t\tstrcpy(p, cmd_p->ref_name);\n+\t\tp = xmalloc(strlen(cmd->ref_name) + 1);\n+\t\tstrcpy(p, cmd->ref_name);\n \t\targv[argc] = p;\n \t\targc++;\n \t}\n@@ -488,37 +486,32 @@ static void run_update_post_hook(struct command *cmd)\n \t}\n }\n \n-static void execute_commands(const char *unpacker_error)\n+static void execute_commands(struct command *commands, const char *unpacker_error)\n {\n-\tstruct command *cmd = commands;\n+\tstruct command *cmd;\n \tunsigned char sha1[20];\n \n \tif (unpacker_error) {\n-\t\twhile (cmd) {\n+\t\tfor (cmd = commands; cmd; cmd = cmd->next)\n \t\t\tcmd->error_string = \"n/a (unpacker error)\";\n-\t\t\tcmd = cmd->next;\n-\t\t}\n \t\treturn;\n \t}\n \n-\tif (run_receive_hook(pre_receive_hook)) {\n-\t\twhile (cmd) {\n+\tif (run_receive_hook(commands, pre_receive_hook)) {\n+\t\tfor (cmd = commands; cmd; cmd = cmd->next)\n \t\t\tcmd->error_string = \"pre-receive hook declined\";\n-\t\t\tcmd = cmd->next;\n-\t\t}\n \t\treturn;\n \t}\n \n \thead_name = resolve_ref(\"HEAD\", sha1, 0, NULL);\n \n-\twhile (cmd) {\n+\tfor (cmd = commands; cmd; cmd = cmd->next)\n \t\tcmd->error_string = update(cmd);\n-\t\tcmd = cmd->next;\n-\t}\n }\n \n-static void read_head_info(void)\n+static struct command *read_head_info(void)\n {\n+\tstruct command *commands = NULL;\n \tstruct command **p = &commands;\n \tfor (;;) {\n \t\tstatic char line[1000];\n@@ -557,6 +550,7 @@ static void read_head_info(void)\n \t\t*p = cmd;\n \t\tp = &cmd->next;\n \t}\n+\treturn commands;\n }\n \n static const char *parse_pack_header(struct pack_header *hdr)\n@@ -643,7 +637,7 @@ static const char *unpack(void)\n \t}\n }\n \n-static void report(const char *unpack_status)\n+static void report(struct command *commands, const char *unpack_status)\n {\n \tstruct command *cmd;\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -667,12 +661,12 @@ static void report(const char *unpack_status)\n \tstrbuf_release(&buf);\n }\n \n-static int delete_only(struct command *cmd)\n+static int delete_only(struct command *commands)\n {\n-\twhile (cmd) {\n+\tstruct command *cmd;\n+\tfor (cmd = commands; cmd; cmd = cmd->next) {\n \t\tif (!is_null_sha1(cmd->new_sha1))\n \t\t\treturn 0;\n-\t\tcmd = cmd->next;\n \t}\n \treturn 1;\n }\n@@ -722,6 +716,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)\n \tint stateless_rpc = 0;\n \tint i;\n \tchar *dir = NULL;\n+\tstruct command *commands;\n \n \targv++;\n \tfor (i = 1; i < argc; i++) {\n@@ -772,18 +767,17 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)\n \tif (advertise_refs)\n \t\treturn 0;\n \n-\tread_head_info();\n-\tif (commands) {\n+\tif ((commands = read_head_info()) != NULL) {\n \t\tconst char *unpack_status = NULL;\n \n \t\tif (!delete_only(commands))\n \t\t\tunpack_status = unpack();\n-\t\texecute_commands(unpack_status);\n+\t\texecute_commands(commands, unpack_status);\n \t\tif (pack_lockfile)\n \t\t\tunlink_or_warn(pack_lockfile);\n \t\tif (report_status)\n-\t\t\treport(unpack_status);\n-\t\trun_receive_hook(post_receive_hook);\n+\t\t\treport(commands, unpack_status);\n+\t\trun_receive_hook(commands, post_receive_hook);\n \t\trun_update_post_hook(commands);\n \t\tif (auto_gc) {\n \t\t\tconst char *argv_gc_auto[] = {\n-- \n1.7.0.3.436.g2b878\n"},{"id":"139912","messageId":"1271694343-31876-2-git-send-email-jaysoffian@gmail.com","threadId":"23527","inReplyTo":"1271694343-31876-1-git-send-email-jaysoffian@gmail.com","subject":"[PATCH v2 2/2] receive-pack: detect aliased updates which can occur with symrefs","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2010-04-19T16:25:43Z","receivedAt":"2010-04-19T16:25:43Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"When pushing to a remote repo the sending side filters out aliased\nupdates (e.g., foo:baz bar:baz). However, it is not possible for the\nsender to know if two refs are aliased on the receiving side via\nsymrefs. Here is one such scenario:\n\n  $ git init origin\n  $ (cd origin && touch file && git add file && git commit -a -m intial)\n  $ git clone --bare origin origin.git\n  $ rm -rf origin\n\n  $ git clone origin.git client\n\n  $ git clone --mirror client backup.git &&\n  $ (cd backup.git && git remote set-head origin --auto)\n\n  $ (cd client &&\n\tgit remote add --mirror backup ../backup.git &&\n\techo change1 > file && git commit -a -m change1 &&\n\tgit push origin &&\n\tgit push backup\n\t)\n\nThe push to backup fails with:\n\n  Counting objects: 5, done.\n  Writing objects: 100% (3/3), 244 bytes, done.\n  Total 3 (delta 0), reused 0 (delta 0)\n  Unpacking objects: 100% (3/3), done.\n  error: Ref refs/remotes/origin/master is at ef3... but expected 262...\n  remote: error: failed to lock refs/remotes/origin/master\n  To ../backup.git\n     262cd57..ef307ff  master -> master\n     262cd57..ef307ff  origin/HEAD -> origin/HEAD\n   ! [remote rejected] origin/master -> origin/master (failed to lock)\n  error: failed to push some refs to '../backup.git'\n\nThe reason is that refs/remotes/origin/HEAD is a symref to\nrefs/remotes/origin/master, but it is not possible for the sending side\nto unambiguously know this.\n\nThis commit fixes the issue by having receive-pack ignore any update to\na symref whose target is being identically updated. If a symref and its\ntarget are being updated inconsistently, then the update for both fails\nwith an error message (\"refusing inconsistent update...\") to help\ndiagnose the situation.\n\nSigned-off-by: Jay Soffian <jaysoffian@gmail.com>\n---\n builtin/receive-pack.c |   62 +++++++++++++++++++++++++++++++++++++++++++++++-\n t/t5516-fetch-push.sh  |   45 ++++++++++++++++++++++++++++++++++\n 2 files changed, 106 insertions(+), 1 deletions(-)\n\nChanges from v1 (incorporating Junio's feedback):\n- Reformatted commit message; minor rewording.\n- Detect situation where there is an inconsistent aliased update and\n  give a better diagnostic than \"failed to lock\"\n- Add additional test case for inconsistent update situation\n\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex fffb6ea..414446b 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -9,6 +9,7 @@\n #include \"object.h\"\n #include \"remote.h\"\n #include \"transport.h\"\n+#include \"string-list.h\"\n \n static const char receive_pack_usage[] = \"git receive-pack <git-dir>\";\n \n@@ -129,6 +130,7 @@ static void write_head_info(void)\n struct command {\n \tstruct command *next;\n \tconst char *error_string;\n+\tunsigned int skip_update;\n \tunsigned char old_sha1[20];\n \tunsigned char new_sha1[20];\n \tchar ref_name[FLEX_ARRAY]; /* more */\n@@ -486,6 +488,61 @@ static void run_update_post_hook(struct command *commands)\n \t}\n }\n \n+static void check_aliased_update(struct command *cmd, struct string_list *list)\n+{\n+\tstruct string_list_item *item;\n+\tstruct command *dst_cmd;\n+\tunsigned char sha1[20];\n+\tchar cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];\n+\tint flag;\n+\n+\tconst char *dst_name = resolve_ref(cmd->ref_name, sha1, 0, &flag);\n+\n+\tif (!(flag & REF_ISSYMREF))\n+\t\treturn;\n+\n+\tif ((item = string_list_lookup(dst_name, list)) == NULL)\n+\t\treturn;\n+\n+\tcmd->skip_update = 1;\n+\n+\tdst_cmd = (struct command *) item->util;\n+\n+\tif (!hashcmp(cmd->old_sha1, dst_cmd->old_sha1) &&\n+\t    !hashcmp(cmd->new_sha1, dst_cmd->new_sha1))\n+\t\treturn;\n+\n+\tstrcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV));\n+\tstrcat(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));\n+\tstrcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV));\n+\tstrcat(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));\n+\trp_error(\"refusing inconsistent update between symref '%s' (%s..%s) and\"\n+\t\t \" its target '%s' (%s..%s)\",\n+\t\t cmd->ref_name, cmd_oldh, cmd_newh,\n+\t\t dst_cmd->ref_name, dst_oldh, dst_newh);\n+\n+\tcmd->error_string = dst_cmd->error_string =\n+\t\t\"inconsistent aliased update\";\n+}\n+\n+static void check_aliased_updates(struct command *commands)\n+{\n+\tstruct command *cmd;\n+\tstruct string_list ref_list = { NULL, 0, 0, 0 };\n+\n+\tfor (cmd = commands; cmd; cmd = cmd->next) {\n+\t\tstruct string_list_item *item =\n+\t\t\tstring_list_append(cmd->ref_name, &ref_list);\n+\t\titem->util = (void *)cmd;\n+\t}\n+\tsort_string_list(&ref_list);\n+\n+\tfor (cmd = commands; cmd; cmd = cmd->next)\n+\t\tcheck_aliased_update(cmd, &ref_list);\n+\n+\tstring_list_clear(&ref_list, 0);\n+}\n+\n static void execute_commands(struct command *commands, const char *unpacker_error)\n {\n \tstruct command *cmd;\n@@ -503,9 +560,11 @@ static void execute_commands(struct command *commands, const char *unpacker_erro\n \t\treturn;\n \t}\n \n+\tcheck_aliased_updates(commands);\n+\n \thead_name = resolve_ref(\"HEAD\", sha1, 0, NULL);\n \n-\tfor (cmd = commands; cmd; cmd = cmd->next)\n+\tfor (cmd = commands; cmd && !cmd->skip_update; cmd = cmd->next)\n \t\tcmd->error_string = update(cmd);\n }\n \n@@ -545,6 +604,7 @@ static struct command *read_head_info(void)\n \t\thashcpy(cmd->old_sha1, old_sha1);\n \t\thashcpy(cmd->new_sha1, new_sha1);\n \t\tmemcpy(cmd->ref_name, line + 82, len - 81);\n+\t\tcmd->skip_update = 0;\n \t\tcmd->error_string = NULL;\n \t\tcmd->next = NULL;\n \t\t*p = cmd;\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 2de98e6..bc3f24f 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -660,6 +660,51 @@ test_expect_success 'push with branches containing #' '\n \tgit checkout master\n '\n \n+test_expect_success 'push into aliased refs (consistent)' '\n+\tmk_test heads/master &&\n+\tmk_child child1 &&\n+\tmk_child child2 &&\n+\t(cd child1 &&\n+\t\tgit branch foo &&\n+\t\tgit symbolic-ref refs/heads/bar refs/heads/foo\n+\t\tgit config receive.denyCurrentBranch false\n+\t) &&\n+\t(cd child2 &&\n+\t\t: >path2 &&\n+\t\tgit add path2 &&\n+\t\ttest_tick &&\n+\t\tgit commit -a -m child2 &&\n+\t\tgit branch foo &&\n+\t\tgit branch bar &&\n+\t\tgit push ../child1 foo bar\n+\t)\n+'\n+\n+test_expect_success 'push into aliased refs (inconsistent)' '\n+\tmk_test heads/master &&\n+\tmk_child child1 &&\n+\tmk_child child2 &&\n+\t(cd child1 &&\n+\t\tgit branch foo &&\n+\t\tgit symbolic-ref refs/heads/bar refs/heads/foo\n+\t\tgit config receive.denyCurrentBranch false\n+\t) &&\n+\t(cd child2 &&\n+\t\t: >path2 &&\n+\t\tgit add path2 &&\n+\t\ttest_tick &&\n+\t\tgit commit -a -m child2 &&\n+\t\tgit branch foo &&\n+\t\t: >path3 &&\n+\t\tgit add path3 &&\n+\t\ttest_tick &&\n+\t\tgit commit -a -m child2 &&\n+\t\tgit branch bar &&\n+\t\ttest_must_fail git push ../child1 foo bar 2>stderr &&\n+\t\tgrep \"refusing inconsistent update\" stderr\n+\t)\n+'\n+\n test_expect_success 'push --porcelain' '\n \tmk_empty &&\n \techo >.git/foo  \"To testrepo\" &&\n-- \n1.7.0.3.436.g2b878\n"},{"id":"139915","messageId":"v2r76718491004190931rb9813deex2797a440d09b50a5@mail.gmail.com","threadId":"23527","inReplyTo":"1271694343-31876-2-git-send-email-jaysoffian@gmail.com","subject":"Re: [PATCH v2 2/2] receive-pack: detect aliased updates which can occur with symrefs","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2010-04-19T16:31:44Z","receivedAt":"2010-04-19T16:31:44Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Mon, Apr 19, 2010 at 12:25 PM, Jay Soffian <jaysoffian@gmail.com> wrote:\n> When pushing to a remote repo the sending side filters out aliased\n> updates (e.g., foo:baz bar:baz). However, it is not possible for the\n> sender to know if two refs are aliased on the receiving side via\n> symrefs. Here is one such scenario:\n\nDon't receive this. There's a logic error in it. I'll send a follow up shortly.\n\nj.\n"},{"id":"139916","messageId":"1271695179-32131-1-git-send-email-jaysoffian@gmail.com","threadId":"23527","inReplyTo":"1271694343-31876-2-git-send-email-jaysoffian@gmail.com","subject":"[PATCH v2 2/2] receive-pack: detect aliased updates which can occur with symrefs","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2010-04-19T16:39:39Z","receivedAt":"2010-04-19T16:39:39Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"When pushing to a remote repo the sending side filters out aliased\nupdates (e.g., foo:baz bar:baz). However, it is not possible for the\nsender to know if two refs are aliased on the receiving side via\nsymrefs. Here is one such scenario:\n\n  $ git init origin\n  $ (cd origin && touch file && git add file && git commit -a -m intial)\n  $ git clone --bare origin origin.git\n  $ rm -rf origin\n\n  $ git clone origin.git client\n\n  $ git clone --mirror client backup.git &&\n  $ (cd backup.git && git remote set-head origin --auto)\n\n  $ (cd client &&\n\tgit remote add --mirror backup ../backup.git &&\n\techo change1 > file && git commit -a -m change1 &&\n\tgit push origin &&\n\tgit push backup\n\t)\n\nThe push to backup fails with:\n\n  Counting objects: 5, done.\n  Writing objects: 100% (3/3), 244 bytes, done.\n  Total 3 (delta 0), reused 0 (delta 0)\n  Unpacking objects: 100% (3/3), done.\n  error: Ref refs/remotes/origin/master is at ef3... but expected 262...\n  remote: error: failed to lock refs/remotes/origin/master\n  To ../backup.git\n     262cd57..ef307ff  master -> master\n     262cd57..ef307ff  origin/HEAD -> origin/HEAD\n   ! [remote rejected] origin/master -> origin/master (failed to lock)\n  error: failed to push some refs to '../backup.git'\n\nThe reason is that refs/remotes/origin/HEAD is a symref to\nrefs/remotes/origin/master, but it is not possible for the sending side\nto unambiguously know this.\n\nThis commit fixes the issue by having receive-pack ignore any update to\na symref whose target is being identically updated. If a symref and its\ntarget are being updated inconsistently, then the update for both fails\nwith an error message (\"refusing inconsistent update...\") to help\ndiagnose the situation.\n\nSigned-off-by: Jay Soffian <jaysoffian@gmail.com>\n---\n builtin/receive-pack.c |   64 +++++++++++++++++++++++++++++++++++++++++++++++-\n t/t5516-fetch-push.sh  |   45 +++++++++++++++++++++++++++++++++\n 2 files changed, 108 insertions(+), 1 deletions(-)\n\nChanges from v1 (incorporating Junio's feedback):\n- Reformatted commit message; minor rewording.\n- Detect situation where there is an inconsistent aliased update and\n  give a better diagnostic than \"failed to lock\"\n- Add additional test case for inconsistent update situation\n\n[\nThis one is correct, the interdiff from\n<1271694343-31876-2-git-send-email-jaysoffian@gmail.com> is\n\n  diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\n  index 414446b..7b269d2 100644\n  --- a/builtin/receive-pack.c\n  +++ b/builtin/receive-pack.c\n  @@ -512,6 +512,8 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)\n   \t    !hashcmp(cmd->new_sha1, dst_cmd->new_sha1))\n   \t\treturn;\n   \n  +\tdst_cmd->skip_update = 1;\n  +\n   \tstrcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV));\n   \tstrcat(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));\n   \tstrcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV));\n  )\n]\n  \ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex fffb6ea..7b269d2 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -9,6 +9,7 @@\n #include \"object.h\"\n #include \"remote.h\"\n #include \"transport.h\"\n+#include \"string-list.h\"\n \n static const char receive_pack_usage[] = \"git receive-pack <git-dir>\";\n \n@@ -129,6 +130,7 @@ static void write_head_info(void)\n struct command {\n \tstruct command *next;\n \tconst char *error_string;\n+\tunsigned int skip_update;\n \tunsigned char old_sha1[20];\n \tunsigned char new_sha1[20];\n \tchar ref_name[FLEX_ARRAY]; /* more */\n@@ -486,6 +488,63 @@ static void run_update_post_hook(struct command *commands)\n \t}\n }\n \n+static void check_aliased_update(struct command *cmd, struct string_list *list)\n+{\n+\tstruct string_list_item *item;\n+\tstruct command *dst_cmd;\n+\tunsigned char sha1[20];\n+\tchar cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];\n+\tint flag;\n+\n+\tconst char *dst_name = resolve_ref(cmd->ref_name, sha1, 0, &flag);\n+\n+\tif (!(flag & REF_ISSYMREF))\n+\t\treturn;\n+\n+\tif ((item = string_list_lookup(dst_name, list)) == NULL)\n+\t\treturn;\n+\n+\tcmd->skip_update = 1;\n+\n+\tdst_cmd = (struct command *) item->util;\n+\n+\tif (!hashcmp(cmd->old_sha1, dst_cmd->old_sha1) &&\n+\t    !hashcmp(cmd->new_sha1, dst_cmd->new_sha1))\n+\t\treturn;\n+\n+\tdst_cmd->skip_update = 1;\n+\n+\tstrcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV));\n+\tstrcat(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));\n+\tstrcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV));\n+\tstrcat(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));\n+\trp_error(\"refusing inconsistent update between symref '%s' (%s..%s) and\"\n+\t\t \" its target '%s' (%s..%s)\",\n+\t\t cmd->ref_name, cmd_oldh, cmd_newh,\n+\t\t dst_cmd->ref_name, dst_oldh, dst_newh);\n+\n+\tcmd->error_string = dst_cmd->error_string =\n+\t\t\"inconsistent aliased update\";\n+}\n+\n+static void check_aliased_updates(struct command *commands)\n+{\n+\tstruct command *cmd;\n+\tstruct string_list ref_list = { NULL, 0, 0, 0 };\n+\n+\tfor (cmd = commands; cmd; cmd = cmd->next) {\n+\t\tstruct string_list_item *item =\n+\t\t\tstring_list_append(cmd->ref_name, &ref_list);\n+\t\titem->util = (void *)cmd;\n+\t}\n+\tsort_string_list(&ref_list);\n+\n+\tfor (cmd = commands; cmd; cmd = cmd->next)\n+\t\tcheck_aliased_update(cmd, &ref_list);\n+\n+\tstring_list_clear(&ref_list, 0);\n+}\n+\n static void execute_commands(struct command *commands, const char *unpacker_error)\n {\n \tstruct command *cmd;\n@@ -503,9 +562,11 @@ static void execute_commands(struct command *commands, const char *unpacker_erro\n \t\treturn;\n \t}\n \n+\tcheck_aliased_updates(commands);\n+\n \thead_name = resolve_ref(\"HEAD\", sha1, 0, NULL);\n \n-\tfor (cmd = commands; cmd; cmd = cmd->next)\n+\tfor (cmd = commands; cmd && !cmd->skip_update; cmd = cmd->next)\n \t\tcmd->error_string = update(cmd);\n }\n \n@@ -545,6 +606,7 @@ static struct command *read_head_info(void)\n \t\thashcpy(cmd->old_sha1, old_sha1);\n \t\thashcpy(cmd->new_sha1, new_sha1);\n \t\tmemcpy(cmd->ref_name, line + 82, len - 81);\n+\t\tcmd->skip_update = 0;\n \t\tcmd->error_string = NULL;\n \t\tcmd->next = NULL;\n \t\t*p = cmd;\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 2de98e6..bc3f24f 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -660,6 +660,51 @@ test_expect_success 'push with branches containing #' '\n \tgit checkout master\n '\n \n+test_expect_success 'push into aliased refs (consistent)' '\n+\tmk_test heads/master &&\n+\tmk_child child1 &&\n+\tmk_child child2 &&\n+\t(cd child1 &&\n+\t\tgit branch foo &&\n+\t\tgit symbolic-ref refs/heads/bar refs/heads/foo\n+\t\tgit config receive.denyCurrentBranch false\n+\t) &&\n+\t(cd child2 &&\n+\t\t: >path2 &&\n+\t\tgit add path2 &&\n+\t\ttest_tick &&\n+\t\tgit commit -a -m child2 &&\n+\t\tgit branch foo &&\n+\t\tgit branch bar &&\n+\t\tgit push ../child1 foo bar\n+\t)\n+'\n+\n+test_expect_success 'push into aliased refs (inconsistent)' '\n+\tmk_test heads/master &&\n+\tmk_child child1 &&\n+\tmk_child child2 &&\n+\t(cd child1 &&\n+\t\tgit branch foo &&\n+\t\tgit symbolic-ref refs/heads/bar refs/heads/foo\n+\t\tgit config receive.denyCurrentBranch false\n+\t) &&\n+\t(cd child2 &&\n+\t\t: >path2 &&\n+\t\tgit add path2 &&\n+\t\ttest_tick &&\n+\t\tgit commit -a -m child2 &&\n+\t\tgit branch foo &&\n+\t\t: >path3 &&\n+\t\tgit add path3 &&\n+\t\ttest_tick &&\n+\t\tgit commit -a -m child2 &&\n+\t\tgit branch bar &&\n+\t\ttest_must_fail git push ../child1 foo bar 2>stderr &&\n+\t\tgrep \"refusing inconsistent update\" stderr\n+\t)\n+'\n+\n test_expect_success 'push --porcelain' '\n \tmk_empty &&\n \techo >.git/foo  \"To testrepo\" &&\n-- \n1.7.0.3.436.g2b878\n"},{"id":"139925","messageId":"7veiibqirf.fsf@alter.siamese.dyndns.org","threadId":"23527","inReplyTo":"1271695179-32131-1-git-send-email-jaysoffian@gmail.com","subject":"Re: [PATCH v2 2/2] receive-pack: detect aliased updates which can occur with symrefs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-04-19T20:39:32Z","receivedAt":"2010-04-19T20:39:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jay Soffian <jaysoffian@gmail.com> writes:\n\n> @@ -503,9 +562,11 @@ static void execute_commands(struct command *commands, const char *unpacker_erro\n>  \t\treturn;\n>  \t}\n>  \n> +\tcheck_aliased_updates(commands);\n> +\n>  \thead_name = resolve_ref(\"HEAD\", sha1, 0, NULL);\n>  \n> -\tfor (cmd = commands; cmd; cmd = cmd->next)\n> +\tfor (cmd = commands; cmd && !cmd->skip_update; cmd = cmd->next)\n>  \t\tcmd->error_string = update(cmd);\n>  }\n\nDo you really mean to have \"skip-update\" check as the loop termination\ncondition like this (i.e. \"upon seeing any skip-update, abandon the rest\nof the update queue\"), or is this a typo of \"skip this entry but keep\ngoing, processing the rest\" that should have been a separate \"if\" in the\nbody of the loop?\n\n> @@ -545,6 +606,7 @@ static struct command *read_head_info(void)\n>  \t\thashcpy(cmd->old_sha1, old_sha1);\n>  \t\thashcpy(cmd->new_sha1, new_sha1);\n>  \t\tmemcpy(cmd->ref_name, line + 82, len - 81);\n> +\t\tcmd->skip_update = 0;\n>  \t\tcmd->error_string = NULL;\n>  \t\tcmd->next = NULL;\n\nIt would make sense to do xcalloc(nmemb, size) of one member of that\nlength to allocate cmd at this point, instead of adding yet another\nassignment like this.\n\nIt also would help me a slight bit if you compared what has been queued\nwith what you sent to catch minor differences between my expectation from\nthis series and what you have (e.g. I'd like to keep this as a fix that is\nback-mergeable to 'maint' and also I have already done some style fixes to\nthe test).\n\nThanks.\n"},{"id":"139929","messageId":"z2s76718491004191357gf4783770i84308e930d9adb22@mail.gmail.com","threadId":"23527","inReplyTo":"7veiibqirf.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 2/2] receive-pack: detect aliased updates which can occur with symrefs","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2010-04-19T20:57:06Z","receivedAt":"2010-04-19T20:57:06Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Mon, Apr 19, 2010 at 4:39 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> -     for (cmd = commands; cmd; cmd = cmd->next)\n>> +     for (cmd = commands; cmd && !cmd->skip_update; cmd = cmd->next)\n>>               cmd->error_string = update(cmd);\n>>  }\n>\n> Do you really mean to have \"skip-update\" check as the loop termination\n> condition like this (i.e. \"upon seeing any skip-update, abandon the rest\n> of the update queue\"), or is this a typo of \"skip this entry but keep\n> going, processing the rest\" that should have been a separate \"if\" in the\n> body of the loop?\n\nOh wow, the perils of coding before coffee. I wish I could call it a\ntypo, it's a thinko which is much worse.\n\n>> @@ -545,6 +606,7 @@ static struct command *read_head_info(void)\n>>               hashcpy(cmd->old_sha1, old_sha1);\n>>               hashcpy(cmd->new_sha1, new_sha1);\n>>               memcpy(cmd->ref_name, line + 82, len - 81);\n>> +             cmd->skip_update = 0;\n>>               cmd->error_string = NULL;\n>>               cmd->next = NULL;\n>\n> It would make sense to do xcalloc(nmemb, size) of one member of that\n> length to allocate cmd at this point, instead of adding yet another\n> assignment like this.\n\nOkay.\n\n> It also would help me a slight bit if you compared what has been queued\n> with what you sent to catch minor differences between my expectation from\n> this series and what you have (e.g. I'd like to keep this as a fix that is\n> back-mergeable to 'maint' and also I have already done some style fixes to\n> the test).\n\nI didn't realize you'd queued it before I sent out v2. But I'm not\nsure what you're asking for beyond what I already said in the email:\n\n- Reformatted commit message; minor rewording.\n- Detect situation where there is an inconsistent aliased update and\n give a better diagnostic than \"failed to lock\"\n- Add additional test case for inconsistent update situation\n\nj.\n"},{"id":"139934","messageId":"1271714912-56659-1-git-send-email-jaysoffian@gmail.com","threadId":"23527","inReplyTo":"1271694343-31876-2-git-send-email-jaysoffian@gmail.com","subject":"[PATCH v3 0/3] js/maint-receive-pack-symref-alias","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2010-04-19T22:08:29Z","receivedAt":"2010-04-19T22:08:29Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"- Incorporated Junio's feedback from v1 and v2.\n- Rebuilt on-top of f78683f, which is where v1 was applied in pu.\n- Added an additional patch to the series to reformat the tests in\n  t5516-fetch-push.sh to use a consistent style, per Junio's tweaks to the\n  test I wrote for v1.\n\nJay Soffian (3):\n  receive-pack: switch global variable 'commands' to a parameter\n  t5516-fetch-push.sh: style cleanup\n  receive-pack: detect aliased updates which can occur with symrefs\n\n builtin-receive-pack.c |  128 ++++++++++++++++++++++++++++++++++--------------\n t/t5516-fetch-push.sh  |  118 +++++++++++++++++++++++++++++++++++---------\n 2 files changed, 186 insertions(+), 60 deletions(-)\n\ninterdiff from what's in pu:\n builtin-receive-pack.c |   76 +++++++++++++++++++++++++------------\n t/t5516-fetch-push.sh  |   98 ++++++++++++++++++++++++++++++++++++------------\n 2 files changed, 126 insertions(+), 48 deletions(-)\n\ndiff --git a/builtin-receive-pack.c b/builtin-receive-pack.c\nindex a2e3bc8..bb34757 100644\n--- a/builtin-receive-pack.c\n+++ b/builtin-receive-pack.c\n@@ -130,6 +130,7 @@ static void write_head_info(void)\n struct command {\n \tstruct command *next;\n \tconst char *error_string;\n+\tunsigned int skip_update;\n \tunsigned char old_sha1[20];\n \tunsigned char new_sha1[20];\n \tchar ref_name[FLEX_ARRAY]; /* more */\n@@ -487,30 +488,67 @@ static void run_update_post_hook(struct command *commands)\n \t}\n }\n \n-static int aliased_ref(struct command *cmd, struct string_list *list)\n+static void check_aliased_update(struct command *cmd, struct string_list *list)\n {\n \tstruct string_list_item *item;\n+\tstruct command *dst_cmd;\n \tunsigned char sha1[20];\n+\tchar cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];\n \tint flag;\n \n \tconst char *dst_name = resolve_ref(cmd->ref_name, sha1, 0, &flag);\n \n \tif (!(flag & REF_ISSYMREF))\n-\t\treturn 0;\n+\t\treturn;\n+\n+\tif ((item = string_list_lookup(dst_name, list)) == NULL)\n+\t\treturn;\n+\n+\tcmd->skip_update = 1;\n+\n+\tdst_cmd = (struct command *) item->util;\n+\n+\tif (!hashcmp(cmd->old_sha1, dst_cmd->old_sha1) &&\n+\t    !hashcmp(cmd->new_sha1, dst_cmd->new_sha1))\n+\t\treturn;\n+\n+\tdst_cmd->skip_update = 1;\n+\n+\tstrcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV));\n+\tstrcat(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));\n+\tstrcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV));\n+\tstrcat(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));\n+\trp_error(\"refusing inconsistent update between symref '%s' (%s..%s) and\"\n+\t\t \" its target '%s' (%s..%s)\",\n+\t\t cmd->ref_name, cmd_oldh, cmd_newh,\n+\t\t dst_cmd->ref_name, dst_oldh, dst_newh);\n \n-\tif ((item = string_list_lookup(dst_name, list)) != NULL) {\n-\t\tstruct command *other_cmd = (struct command *) item->util;\n-\t\treturn (!(hashcmp(cmd->old_sha1, other_cmd->old_sha1) &&\n-\t\t\thashcmp(cmd->new_sha1, other_cmd->new_sha1)));\n+\tcmd->error_string = dst_cmd->error_string =\n+\t\t\"inconsistent aliased update\";\n+}\n+\n+static void check_aliased_updates(struct command *commands)\n+{\n+\tstruct command *cmd;\n+\tstruct string_list ref_list = { NULL, 0, 0, 0 };\n+\n+\tfor (cmd = commands; cmd; cmd = cmd->next) {\n+\t\tstruct string_list_item *item =\n+\t\t\tstring_list_append(cmd->ref_name, &ref_list);\n+\t\titem->util = (void *)cmd;\n \t}\n-\treturn 0;\n+\tsort_string_list(&ref_list);\n+\n+\tfor (cmd = commands; cmd; cmd = cmd->next)\n+\t\tcheck_aliased_update(cmd, &ref_list);\n+\n+\tstring_list_clear(&ref_list, 0);\n }\n \n static void execute_commands(struct command *commands, const char *unpacker_error)\n {\n \tstruct command *cmd;\n \tunsigned char sha1[20];\n-\tstruct string_list ref_list = { NULL, 0, 0, 0 };\n \n \tif (unpacker_error) {\n \t\tfor (cmd = commands; cmd; cmd = cmd->next)\n@@ -524,27 +562,19 @@ static void execute_commands(struct command *commands, const char *unpacker_erro\n \t\treturn;\n \t}\n \n-\thead_name = resolve_ref(\"HEAD\", sha1, 0, NULL);\n+\tcheck_aliased_updates(commands);\n \n-\tfor (cmd = commands; cmd; cmd = cmd->next) {\n-\t\tstruct string_list_item *item =\n-\t\t\tstring_list_append(cmd->ref_name, &ref_list);\n-\t\titem->util = (void *)cmd;\n-\t}\n-\tsort_string_list(&ref_list);\n+\thead_name = resolve_ref(\"HEAD\", sha1, 0, NULL);\n \n-\tfor (cmd = commands; cmd; cmd = cmd->next) {\n-\t\tif (!aliased_ref(cmd, &ref_list))\n+\tfor (cmd = commands; cmd; cmd = cmd->next)\n+\t\tif (!cmd->skip_update)\n \t\t\tcmd->error_string = update(cmd);\n-\t}\n-\tstring_list_clear(&ref_list, 0);\n }\n \n static struct command *read_head_info(void)\n {\n \tstruct command *commands = NULL;\n \tstruct command **p = &commands;\n-\n \tfor (;;) {\n \t\tstatic char line[1000];\n \t\tunsigned char old_sha1[20], new_sha1[20];\n@@ -573,12 +603,10 @@ static struct command *read_head_info(void)\n \t\t\tif (strstr(refname + reflen + 1, \"side-band-64k\"))\n \t\t\t\tuse_sideband = LARGE_PACKET_MAX;\n \t\t}\n-\t\tcmd = xmalloc(sizeof(struct command) + len - 80);\n+\t\tcmd = xcalloc(1, sizeof(struct command) + len - 80);\n \t\thashcpy(cmd->old_sha1, old_sha1);\n \t\thashcpy(cmd->new_sha1, new_sha1);\n \t\tmemcpy(cmd->ref_name, line + 82, len - 81);\n-\t\tcmd->error_string = NULL;\n-\t\tcmd->next = NULL;\n \t\t*p = cmd;\n \t\tp = &cmd->next;\n \t}\n@@ -671,8 +699,8 @@ static const char *unpack(void)\n \n static void report(struct command *commands, const char *unpack_status)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n \tstruct command *cmd;\n+\tstruct strbuf buf = STRBUF_INIT;\n \n \tpacket_buf_write(&buf, \"unpack %s\\n\",\n \t\t\t unpack_status ? unpack_status : \"ok\");\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 350e734..f0813e0 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -64,13 +64,13 @@ check_push_result () {\n \n test_expect_success setup '\n \n-\t: >path1 &&\n+\t>path1 &&\n \tgit add path1 &&\n \ttest_tick &&\n \tgit commit -a -m repo &&\n \tthe_first_commit=$(git show-ref -s --verify refs/heads/master) &&\n \n-\t: >path2 &&\n+\t>path2 &&\n \tgit add path2 &&\n \ttest_tick &&\n \tgit commit -a -m second &&\n@@ -483,8 +483,10 @@ git config --remove-section remote.there\n test_expect_success 'push with dry-run' '\n \n \tmk_test heads/master &&\n-\t(cd testrepo &&\n-\t old_commit=$(git show-ref -s --verify refs/heads/master)) &&\n+\t(\n+\t\tcd testrepo &&\n+\t\told_commit=$(git show-ref -s --verify refs/heads/master)\n+\t) &&\n \tgit push --dry-run testrepo &&\n \tcheck_push_result $old_commit heads/master\n '\n@@ -493,10 +495,13 @@ test_expect_success 'push updates local refs' '\n \n \tmk_test heads/master &&\n \tmk_child child &&\n-\t(cd child &&\n+\t(\n+\t\tcd child &&\n \t\tgit pull .. master &&\n \t\tgit push &&\n-\ttest $(git rev-parse master) = $(git rev-parse remotes/origin/master))\n+\t\ttest $(git rev-parse master) = \\\n+\t\t\t$(git rev-parse remotes/origin/master)\n+\t)\n \n '\n \n@@ -506,10 +511,13 @@ test_expect_success 'push updates up-to-date local refs' '\n \tmk_child child1 &&\n \tmk_child child2 &&\n \t(cd child1 && git pull .. master && git push) &&\n-\t(cd child2 &&\n+\t(\n+\t\tcd child2 &&\n \t\tgit pull ../child1 master &&\n \t\tgit push &&\n-\ttest $(git rev-parse master) = $(git rev-parse remotes/origin/master))\n+\t\ttest $(git rev-parse master) = \\\n+\t\t\t$(git rev-parse remotes/origin/master)\n+\t)\n \n '\n \n@@ -517,9 +525,11 @@ test_expect_success 'push preserves up-to-date packed refs' '\n \n \tmk_test heads/master &&\n \tmk_child child &&\n-\t(cd child &&\n+\t(\n+\t\tcd child &&\n \t\tgit push &&\n-\t! test -f .git/refs/remotes/origin/master)\n+\t\t! test -f .git/refs/remotes/origin/master\n+\t)\n \n '\n \n@@ -530,11 +540,13 @@ test_expect_success 'push does not update local refs on failure' '\n \tmkdir testrepo/.git/hooks &&\n \techo exit 1 >testrepo/.git/hooks/pre-receive &&\n \tchmod +x testrepo/.git/hooks/pre-receive &&\n-\t(cd child &&\n+\t(\n+\t\tcd child &&\n \t\tgit pull .. master\n \t\ttest_must_fail git push &&\n \t\ttest $(git rev-parse master) != \\\n-\t\t\t$(git rev-parse remotes/origin/master))\n+\t\t\t$(git rev-parse remotes/origin/master)\n+\t)\n \n '\n \n@@ -575,34 +587,41 @@ test_expect_success 'push --delete refuses src:dest refspecs' '\n \n test_expect_success 'warn on push to HEAD of non-bare repository' '\n \tmk_test heads/master\n-\t(cd testrepo &&\n+\t(\n+\t\tcd testrepo &&\n \t\tgit checkout master &&\n-\t\tgit config receive.denyCurrentBranch warn) &&\n+\t\tgit config receive.denyCurrentBranch warn\n+\t) &&\n \tgit push testrepo master 2>stderr &&\n \tgrep \"warning: updating the current branch\" stderr\n '\n \n test_expect_success 'deny push to HEAD of non-bare repository' '\n \tmk_test heads/master\n-\t(cd testrepo &&\n+\t(\n+\t\tcd testrepo &&\n \t\tgit checkout master &&\n-\t\tgit config receive.denyCurrentBranch true) &&\n+\t\tgit config receive.denyCurrentBranch true\n+\t) &&\n \ttest_must_fail git push testrepo master\n '\n \n test_expect_success 'allow push to HEAD of bare repository (bare)' '\n \tmk_test heads/master\n-\t(cd testrepo &&\n+\t(\n+\t\tcd testrepo &&\n \t\tgit checkout master &&\n \t\tgit config receive.denyCurrentBranch true &&\n-\t\tgit config core.bare true) &&\n+\t\tgit config core.bare true\n+\t) &&\n \tgit push testrepo master 2>stderr &&\n \t! grep \"warning: updating the current branch\" stderr\n '\n \n test_expect_success 'allow push to HEAD of non-bare repository (config)' '\n \tmk_test heads/master\n-\t(cd testrepo &&\n+\t(\n+\t\tcd testrepo &&\n \t\tgit checkout master &&\n \t\tgit config receive.denyCurrentBranch false\n \t) &&\n@@ -615,7 +634,8 @@ test_expect_success 'fetch with branches' '\n \tgit branch second $the_first_commit &&\n \tgit checkout second &&\n \techo \"..\" > testrepo/.git/branches/branch1 &&\n-\t(cd testrepo &&\n+\t(\n+\t\tcd testrepo &&\n \t\tgit fetch branch1 &&\n \t\tr=$(git show-ref -s --verify refs/heads/branch1) &&\n \t\ttest \"z$r\" = \"z$the_commit\" &&\n@@ -627,7 +647,8 @@ test_expect_success 'fetch with branches' '\n test_expect_success 'fetch with branches containing #' '\n \tmk_empty &&\n \techo \"..#second\" > testrepo/.git/branches/branch2 &&\n-\t(cd testrepo &&\n+\t(\n+\t\tcd testrepo &&\n \t\tgit fetch branch2 &&\n \t\tr=$(git show-ref -s --verify refs/heads/branch2) &&\n \t\ttest \"z$r\" = \"z$the_first_commit\" &&\n@@ -641,7 +662,8 @@ test_expect_success 'push with branches' '\n \tgit checkout second &&\n \techo \"testrepo\" > .git/branches/branch1 &&\n \tgit push branch1 &&\n-\t(cd testrepo &&\n+\t(\n+\t\tcd testrepo &&\n \t\tr=$(git show-ref -s --verify refs/heads/master) &&\n \t\ttest \"z$r\" = \"z$the_first_commit\" &&\n \t\ttest 1 = $(git for-each-ref refs/heads | wc -l)\n@@ -652,7 +674,8 @@ test_expect_success 'push with branches containing #' '\n \tmk_empty &&\n \techo \"testrepo#branch3\" > .git/branches/branch2 &&\n \tgit push branch2 &&\n-\t(cd testrepo &&\n+\t(\n+\t\tcd testrepo &&\n \t\tr=$(git show-ref -s --verify refs/heads/branch3) &&\n \t\ttest \"z$r\" = \"z$the_first_commit\" &&\n \t\ttest 1 = $(git for-each-ref refs/heads | wc -l)\n@@ -660,7 +683,7 @@ test_expect_success 'push with branches containing #' '\n \tgit checkout master\n '\n \n-test_expect_success 'push into aliased refs' '\n+test_expect_success 'push into aliased refs (consistent)' '\n \tmk_test heads/master &&\n \tmk_child child1 &&\n \tmk_child child2 &&\n@@ -682,4 +705,31 @@ test_expect_success 'push into aliased refs' '\n \t)\n '\n \n+test_expect_success 'push into aliased refs (inconsistent)' '\n+\tmk_test heads/master &&\n+\tmk_child child1 &&\n+\tmk_child child2 &&\n+\t(\n+\t\tcd child1 &&\n+\t\tgit branch foo &&\n+\t\tgit symbolic-ref refs/heads/bar refs/heads/foo\n+\t\tgit config receive.denyCurrentBranch false\n+\t) &&\n+\t(\n+\t\tcd child2 &&\n+\t\t>path2 &&\n+\t\tgit add path2 &&\n+\t\ttest_tick &&\n+\t\tgit commit -a -m child2 &&\n+\t\tgit branch foo &&\n+\t\t>path3 &&\n+\t\tgit add path3 &&\n+\t\ttest_tick &&\n+\t\tgit commit -a -m child2 &&\n+\t\tgit branch bar &&\n+\t\ttest_must_fail git push ../child1 foo bar 2>stderr &&\n+\t\tgrep \"refusing inconsistent update\" stderr\n+\t)\n+'\n+\n test_done\n"},{"id":"139935","messageId":"1271714912-56659-2-git-send-email-jaysoffian@gmail.com","threadId":"23527","inReplyTo":"1271714912-56659-1-git-send-email-jaysoffian@gmail.com","subject":"[PATCH v3 1/3] receive-pack: switch global variable 'commands' to a parameter","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2010-04-19T22:08:30Z","receivedAt":"2010-04-19T22:08:30Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"Receive-pack is inconsistent in its usage of the 'commands'\nvariable; though it is setup as a global and accessed that way by\nexecute_commands(), report(), and run_receive_hook(), it is also\npassed as a parameter to delete_only() and run_update_post_hook().\n\nFor consistency, make it local to cmd_receive_pack and pass it as a\nparameter. As long as we're cleaning up, also make our use of the\nnames 'commands' and 'cmd' consistent.\n\nSigned-off-by: Jay Soffian <jaysoffian@gmail.com>\n---\n builtin-receive-pack.c |   60 +++++++++++++++++++++--------------------------\n 1 files changed, 27 insertions(+), 33 deletions(-)\n\ndiff --git a/builtin-receive-pack.c b/builtin-receive-pack.c\nindex 0559fcc..fffb6ea 100644\n--- a/builtin-receive-pack.c\n+++ b/builtin-receive-pack.c\n@@ -134,8 +134,6 @@ struct command {\n \tchar ref_name[FLEX_ARRAY]; /* more */\n };\n \n-static struct command *commands;\n-\n static const char pre_receive_hook[] = \"hooks/pre-receive\";\n static const char post_receive_hook[] = \"hooks/post-receive\";\n \n@@ -188,7 +186,7 @@ static int copy_to_sideband(int in, int out, void *arg)\n \treturn 0;\n }\n \n-static int run_receive_hook(const char *hook_name)\n+static int run_receive_hook(struct command *commands, const char *hook_name)\n {\n \tstatic char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];\n \tstruct command *cmd;\n@@ -447,15 +445,15 @@ static const char *update(struct command *cmd)\n \n static char update_post_hook[] = \"hooks/post-update\";\n \n-static void run_update_post_hook(struct command *cmd)\n+static void run_update_post_hook(struct command *commands)\n {\n-\tstruct command *cmd_p;\n+\tstruct command *cmd;\n \tint argc;\n \tconst char **argv;\n \tstruct child_process proc;\n \n-\tfor (argc = 0, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {\n-\t\tif (cmd_p->error_string)\n+\tfor (argc = 0, cmd = commands; cmd; cmd = cmd->next) {\n+\t\tif (cmd->error_string)\n \t\t\tcontinue;\n \t\targc++;\n \t}\n@@ -464,12 +462,12 @@ static void run_update_post_hook(struct command *cmd)\n \targv = xmalloc(sizeof(*argv) * (2 + argc));\n \targv[0] = update_post_hook;\n \n-\tfor (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {\n+\tfor (argc = 1, cmd = commands; cmd; cmd = cmd->next) {\n \t\tchar *p;\n-\t\tif (cmd_p->error_string)\n+\t\tif (cmd->error_string)\n \t\t\tcontinue;\n-\t\tp = xmalloc(strlen(cmd_p->ref_name) + 1);\n-\t\tstrcpy(p, cmd_p->ref_name);\n+\t\tp = xmalloc(strlen(cmd->ref_name) + 1);\n+\t\tstrcpy(p, cmd->ref_name);\n \t\targv[argc] = p;\n \t\targc++;\n \t}\n@@ -488,37 +486,32 @@ static void run_update_post_hook(struct command *cmd)\n \t}\n }\n \n-static void execute_commands(const char *unpacker_error)\n+static void execute_commands(struct command *commands, const char *unpacker_error)\n {\n-\tstruct command *cmd = commands;\n+\tstruct command *cmd;\n \tunsigned char sha1[20];\n \n \tif (unpacker_error) {\n-\t\twhile (cmd) {\n+\t\tfor (cmd = commands; cmd; cmd = cmd->next)\n \t\t\tcmd->error_string = \"n/a (unpacker error)\";\n-\t\t\tcmd = cmd->next;\n-\t\t}\n \t\treturn;\n \t}\n \n-\tif (run_receive_hook(pre_receive_hook)) {\n-\t\twhile (cmd) {\n+\tif (run_receive_hook(commands, pre_receive_hook)) {\n+\t\tfor (cmd = commands; cmd; cmd = cmd->next)\n \t\t\tcmd->error_string = \"pre-receive hook declined\";\n-\t\t\tcmd = cmd->next;\n-\t\t}\n \t\treturn;\n \t}\n \n \thead_name = resolve_ref(\"HEAD\", sha1, 0, NULL);\n \n-\twhile (cmd) {\n+\tfor (cmd = commands; cmd; cmd = cmd->next)\n \t\tcmd->error_string = update(cmd);\n-\t\tcmd = cmd->next;\n-\t}\n }\n \n-static void read_head_info(void)\n+static struct command *read_head_info(void)\n {\n+\tstruct command *commands = NULL;\n \tstruct command **p = &commands;\n \tfor (;;) {\n \t\tstatic char line[1000];\n@@ -557,6 +550,7 @@ static void read_head_info(void)\n \t\t*p = cmd;\n \t\tp = &cmd->next;\n \t}\n+\treturn commands;\n }\n \n static const char *parse_pack_header(struct pack_header *hdr)\n@@ -643,7 +637,7 @@ static const char *unpack(void)\n \t}\n }\n \n-static void report(const char *unpack_status)\n+static void report(struct command *commands, const char *unpack_status)\n {\n \tstruct command *cmd;\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -667,12 +661,12 @@ static void report(const char *unpack_status)\n \tstrbuf_release(&buf);\n }\n \n-static int delete_only(struct command *cmd)\n+static int delete_only(struct command *commands)\n {\n-\twhile (cmd) {\n+\tstruct command *cmd;\n+\tfor (cmd = commands; cmd; cmd = cmd->next) {\n \t\tif (!is_null_sha1(cmd->new_sha1))\n \t\t\treturn 0;\n-\t\tcmd = cmd->next;\n \t}\n \treturn 1;\n }\n@@ -722,6 +716,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)\n \tint stateless_rpc = 0;\n \tint i;\n \tchar *dir = NULL;\n+\tstruct command *commands;\n \n \targv++;\n \tfor (i = 1; i < argc; i++) {\n@@ -772,18 +767,17 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)\n \tif (advertise_refs)\n \t\treturn 0;\n \n-\tread_head_info();\n-\tif (commands) {\n+\tif ((commands = read_head_info()) != NULL) {\n \t\tconst char *unpack_status = NULL;\n \n \t\tif (!delete_only(commands))\n \t\t\tunpack_status = unpack();\n-\t\texecute_commands(unpack_status);\n+\t\texecute_commands(commands, unpack_status);\n \t\tif (pack_lockfile)\n \t\t\tunlink_or_warn(pack_lockfile);\n \t\tif (report_status)\n-\t\t\treport(unpack_status);\n-\t\trun_receive_hook(post_receive_hook);\n+\t\t\treport(commands, unpack_status);\n+\t\trun_receive_hook(commands, post_receive_hook);\n \t\trun_update_post_hook(commands);\n \t\tif (auto_gc) {\n \t\t\tconst char *argv_gc_auto[] = {\n-- \n1.7.0.3.436.g2b878\n"},{"id":"139936","messageId":"1271714912-56659-3-git-send-email-jaysoffian@gmail.com","threadId":"23527","inReplyTo":"1271714912-56659-1-git-send-email-jaysoffian@gmail.com","subject":"[PATCH v3 2/3] t5516-fetch-push.sh: style cleanup","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2010-04-19T22:08:31Z","receivedAt":"2010-04-19T22:08:31Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"Cleanup t5516-fetch-push.sh to use prevailing test script style\n\nSigned-off-by: Jay Soffian <jaysoffian@gmail.com>\n---\n t/t5516-fetch-push.sh |   69 ++++++++++++++++++++++++++++++++----------------\n 1 files changed, 46 insertions(+), 23 deletions(-)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 0f04b2e..3148789 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -64,13 +64,13 @@ check_push_result () {\n \n test_expect_success setup '\n \n-\t: >path1 &&\n+\t>path1 &&\n \tgit add path1 &&\n \ttest_tick &&\n \tgit commit -a -m repo &&\n \tthe_first_commit=$(git show-ref -s --verify refs/heads/master) &&\n \n-\t: >path2 &&\n+\t>path2 &&\n \tgit add path2 &&\n \ttest_tick &&\n \tgit commit -a -m second &&\n@@ -483,8 +483,10 @@ git config --remove-section remote.there\n test_expect_success 'push with dry-run' '\n \n \tmk_test heads/master &&\n-\t(cd testrepo &&\n-\t old_commit=$(git show-ref -s --verify refs/heads/master)) &&\n+\t(\n+\t\tcd testrepo &&\n+\t\told_commit=$(git show-ref -s --verify refs/heads/master)\n+\t) &&\n \tgit push --dry-run testrepo &&\n \tcheck_push_result $old_commit heads/master\n '\n@@ -493,10 +495,13 @@ test_expect_success 'push updates local refs' '\n \n \tmk_test heads/master &&\n \tmk_child child &&\n-\t(cd child &&\n+\t(\n+\t\tcd child &&\n \t\tgit pull .. master &&\n \t\tgit push &&\n-\ttest $(git rev-parse master) = $(git rev-parse remotes/origin/master))\n+\t\ttest $(git rev-parse master) = \\\n+\t\t\t$(git rev-parse remotes/origin/master)\n+\t)\n \n '\n \n@@ -506,10 +511,13 @@ test_expect_success 'push updates up-to-date local refs' '\n \tmk_child child1 &&\n \tmk_child child2 &&\n \t(cd child1 && git pull .. master && git push) &&\n-\t(cd child2 &&\n+\t(\n+\t\tcd child2 &&\n \t\tgit pull ../child1 master &&\n \t\tgit push &&\n-\ttest $(git rev-parse master) = $(git rev-parse remotes/origin/master))\n+\t\ttest $(git rev-parse master) = \\\n+\t\t\t$(git rev-parse remotes/origin/master)\n+\t)\n \n '\n \n@@ -517,9 +525,11 @@ test_expect_success 'push preserves up-to-date packed refs' '\n \n \tmk_test heads/master &&\n \tmk_child child &&\n-\t(cd child &&\n+\t(\n+\t\tcd child &&\n \t\tgit push &&\n-\t! test -f .git/refs/remotes/origin/master)\n+\t\t! test -f .git/refs/remotes/origin/master\n+\t)\n \n '\n \n@@ -530,11 +540,13 @@ test_expect_success 'push does not update local refs on failure' '\n \tmkdir testrepo/.git/hooks &&\n \techo exit 1 >testrepo/.git/hooks/pre-receive &&\n \tchmod +x testrepo/.git/hooks/pre-receive &&\n-\t(cd child &&\n+\t(\n+\t\tcd child &&\n \t\tgit pull .. master\n \t\ttest_must_fail git push &&\n \t\ttest $(git rev-parse master) != \\\n-\t\t\t$(git rev-parse remotes/origin/master))\n+\t\t\t$(git rev-parse remotes/origin/master)\n+\t)\n \n '\n \n@@ -575,34 +587,41 @@ test_expect_success 'push --delete refuses src:dest refspecs' '\n \n test_expect_success 'warn on push to HEAD of non-bare repository' '\n \tmk_test heads/master\n-\t(cd testrepo &&\n+\t(\n+\t\tcd testrepo &&\n \t\tgit checkout master &&\n-\t\tgit config receive.denyCurrentBranch warn) &&\n+\t\tgit config receive.denyCurrentBranch warn\n+\t) &&\n \tgit push testrepo master 2>stderr &&\n \tgrep \"warning: updating the current branch\" stderr\n '\n \n test_expect_success 'deny push to HEAD of non-bare repository' '\n \tmk_test heads/master\n-\t(cd testrepo &&\n+\t(\n+\t\tcd testrepo &&\n \t\tgit checkout master &&\n-\t\tgit config receive.denyCurrentBranch true) &&\n+\t\tgit config receive.denyCurrentBranch true\n+\t) &&\n \ttest_must_fail git push testrepo master\n '\n \n test_expect_success 'allow push to HEAD of bare repository (bare)' '\n \tmk_test heads/master\n-\t(cd testrepo &&\n+\t(\n+\t\tcd testrepo &&\n \t\tgit checkout master &&\n \t\tgit config receive.denyCurrentBranch true &&\n-\t\tgit config core.bare true) &&\n+\t\tgit config core.bare true\n+\t) &&\n \tgit push testrepo master 2>stderr &&\n \t! grep \"warning: updating the current branch\" stderr\n '\n \n test_expect_success 'allow push to HEAD of non-bare repository (config)' '\n \tmk_test heads/master\n-\t(cd testrepo &&\n+\t(\n+\t\tcd testrepo &&\n \t\tgit checkout master &&\n \t\tgit config receive.denyCurrentBranch false\n \t) &&\n@@ -615,7 +634,8 @@ test_expect_success 'fetch with branches' '\n \tgit branch second $the_first_commit &&\n \tgit checkout second &&\n \techo \"..\" > testrepo/.git/branches/branch1 &&\n-\t(cd testrepo &&\n+\t(\n+\t\tcd testrepo &&\n \t\tgit fetch branch1 &&\n \t\tr=$(git show-ref -s --verify refs/heads/branch1) &&\n \t\ttest \"z$r\" = \"z$the_commit\" &&\n@@ -627,7 +647,8 @@ test_expect_success 'fetch with branches' '\n test_expect_success 'fetch with branches containing #' '\n \tmk_empty &&\n \techo \"..#second\" > testrepo/.git/branches/branch2 &&\n-\t(cd testrepo &&\n+\t(\n+\t\tcd testrepo &&\n \t\tgit fetch branch2 &&\n \t\tr=$(git show-ref -s --verify refs/heads/branch2) &&\n \t\ttest \"z$r\" = \"z$the_first_commit\" &&\n@@ -641,7 +662,8 @@ test_expect_success 'push with branches' '\n \tgit checkout second &&\n \techo \"testrepo\" > .git/branches/branch1 &&\n \tgit push branch1 &&\n-\t(cd testrepo &&\n+\t(\n+\t\tcd testrepo &&\n \t\tr=$(git show-ref -s --verify refs/heads/master) &&\n \t\ttest \"z$r\" = \"z$the_first_commit\" &&\n \t\ttest 1 = $(git for-each-ref refs/heads | wc -l)\n@@ -652,7 +674,8 @@ test_expect_success 'push with branches containing #' '\n \tmk_empty &&\n \techo \"testrepo#branch3\" > .git/branches/branch2 &&\n \tgit push branch2 &&\n-\t(cd testrepo &&\n+\t(\n+\t\tcd testrepo &&\n \t\tr=$(git show-ref -s --verify refs/heads/branch3) &&\n \t\ttest \"z$r\" = \"z$the_first_commit\" &&\n \t\ttest 1 = $(git for-each-ref refs/heads | wc -l)\n-- \n1.7.0.3.436.g2b878\n"},{"id":"139937","messageId":"1271714912-56659-4-git-send-email-jaysoffian@gmail.com","threadId":"23527","inReplyTo":"1271714912-56659-1-git-send-email-jaysoffian@gmail.com","subject":"[PATCH v3 3/3] receive-pack: detect aliased updates which can occur with symrefs","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2010-04-19T22:08:32Z","receivedAt":"2010-04-19T22:08:32Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"When pushing to a remote repo the sending side filters out aliased\nupdates (e.g., foo:baz bar:baz). However, it is not possible for the\nsender to know if two refs are aliased on the receiving side via\nsymrefs. Here is one such scenario:\n\n  $ git init origin\n  $ (cd origin && touch file && git add file && git commit -a -m intial)\n  $ git clone --bare origin origin.git\n  $ rm -rf origin\n\n  $ git clone origin.git client\n\n  $ git clone --mirror client backup.git &&\n  $ (cd backup.git && git remote set-head origin --auto)\n\n  $ (cd client &&\n\tgit remote add --mirror backup ../backup.git &&\n\techo change1 > file && git commit -a -m change1 &&\n\tgit push origin &&\n\tgit push backup\n\t)\n\nThe push to backup fails with:\n\n  Counting objects: 5, done.\n  Writing objects: 100% (3/3), 244 bytes, done.\n  Total 3 (delta 0), reused 0 (delta 0)\n  Unpacking objects: 100% (3/3), done.\n  error: Ref refs/remotes/origin/master is at ef3... but expected 262...\n  remote: error: failed to lock refs/remotes/origin/master\n  To ../backup.git\n     262cd57..ef307ff  master -> master\n     262cd57..ef307ff  origin/HEAD -> origin/HEAD\n   ! [remote rejected] origin/master -> origin/master (failed to lock)\n  error: failed to push some refs to '../backup.git'\n\nThe reason is that refs/remotes/origin/HEAD is a symref to\nrefs/remotes/origin/master, but it is not possible for the sending side\nto unambiguously know this.\n\nThis commit fixes the issue by having receive-pack ignore any update to\na symref whose target is being identically updated. If a symref and its\ntarget are being updated inconsistently, then the update for both fails\nwith an error message (\"refusing inconsistent update...\") to help\ndiagnose the situation.\n\nSigned-off-by: Jay Soffian <jaysoffian@gmail.com>\n---\n builtin-receive-pack.c |   68 +++++++++++++++++++++++++++++++++++++++++++++---\n t/t5516-fetch-push.sh  |   49 ++++++++++++++++++++++++++++++++++\n 2 files changed, 113 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin-receive-pack.c b/builtin-receive-pack.c\nindex fffb6ea..bb34757 100644\n--- a/builtin-receive-pack.c\n+++ b/builtin-receive-pack.c\n@@ -9,6 +9,7 @@\n #include \"object.h\"\n #include \"remote.h\"\n #include \"transport.h\"\n+#include \"string-list.h\"\n \n static const char receive_pack_usage[] = \"git receive-pack <git-dir>\";\n \n@@ -129,6 +130,7 @@ static void write_head_info(void)\n struct command {\n \tstruct command *next;\n \tconst char *error_string;\n+\tunsigned int skip_update;\n \tunsigned char old_sha1[20];\n \tunsigned char new_sha1[20];\n \tchar ref_name[FLEX_ARRAY]; /* more */\n@@ -486,6 +488,63 @@ static void run_update_post_hook(struct command *commands)\n \t}\n }\n \n+static void check_aliased_update(struct command *cmd, struct string_list *list)\n+{\n+\tstruct string_list_item *item;\n+\tstruct command *dst_cmd;\n+\tunsigned char sha1[20];\n+\tchar cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];\n+\tint flag;\n+\n+\tconst char *dst_name = resolve_ref(cmd->ref_name, sha1, 0, &flag);\n+\n+\tif (!(flag & REF_ISSYMREF))\n+\t\treturn;\n+\n+\tif ((item = string_list_lookup(dst_name, list)) == NULL)\n+\t\treturn;\n+\n+\tcmd->skip_update = 1;\n+\n+\tdst_cmd = (struct command *) item->util;\n+\n+\tif (!hashcmp(cmd->old_sha1, dst_cmd->old_sha1) &&\n+\t    !hashcmp(cmd->new_sha1, dst_cmd->new_sha1))\n+\t\treturn;\n+\n+\tdst_cmd->skip_update = 1;\n+\n+\tstrcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV));\n+\tstrcat(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));\n+\tstrcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV));\n+\tstrcat(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));\n+\trp_error(\"refusing inconsistent update between symref '%s' (%s..%s) and\"\n+\t\t \" its target '%s' (%s..%s)\",\n+\t\t cmd->ref_name, cmd_oldh, cmd_newh,\n+\t\t dst_cmd->ref_name, dst_oldh, dst_newh);\n+\n+\tcmd->error_string = dst_cmd->error_string =\n+\t\t\"inconsistent aliased update\";\n+}\n+\n+static void check_aliased_updates(struct command *commands)\n+{\n+\tstruct command *cmd;\n+\tstruct string_list ref_list = { NULL, 0, 0, 0 };\n+\n+\tfor (cmd = commands; cmd; cmd = cmd->next) {\n+\t\tstruct string_list_item *item =\n+\t\t\tstring_list_append(cmd->ref_name, &ref_list);\n+\t\titem->util = (void *)cmd;\n+\t}\n+\tsort_string_list(&ref_list);\n+\n+\tfor (cmd = commands; cmd; cmd = cmd->next)\n+\t\tcheck_aliased_update(cmd, &ref_list);\n+\n+\tstring_list_clear(&ref_list, 0);\n+}\n+\n static void execute_commands(struct command *commands, const char *unpacker_error)\n {\n \tstruct command *cmd;\n@@ -503,10 +562,13 @@ static void execute_commands(struct command *commands, const char *unpacker_erro\n \t\treturn;\n \t}\n \n+\tcheck_aliased_updates(commands);\n+\n \thead_name = resolve_ref(\"HEAD\", sha1, 0, NULL);\n \n \tfor (cmd = commands; cmd; cmd = cmd->next)\n-\t\tcmd->error_string = update(cmd);\n+\t\tif (!cmd->skip_update)\n+\t\t\tcmd->error_string = update(cmd);\n }\n \n static struct command *read_head_info(void)\n@@ -541,12 +603,10 @@ static struct command *read_head_info(void)\n \t\t\tif (strstr(refname + reflen + 1, \"side-band-64k\"))\n \t\t\t\tuse_sideband = LARGE_PACKET_MAX;\n \t\t}\n-\t\tcmd = xmalloc(sizeof(struct command) + len - 80);\n+\t\tcmd = xcalloc(1, sizeof(struct command) + len - 80);\n \t\thashcpy(cmd->old_sha1, old_sha1);\n \t\thashcpy(cmd->new_sha1, new_sha1);\n \t\tmemcpy(cmd->ref_name, line + 82, len - 81);\n-\t\tcmd->error_string = NULL;\n-\t\tcmd->next = NULL;\n \t\t*p = cmd;\n \t\tp = &cmd->next;\n \t}\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 3148789..f0813e0 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -683,4 +683,53 @@ test_expect_success 'push with branches containing #' '\n \tgit checkout master\n '\n \n+test_expect_success 'push into aliased refs (consistent)' '\n+\tmk_test heads/master &&\n+\tmk_child child1 &&\n+\tmk_child child2 &&\n+\t(\n+\t\tcd child1 &&\n+\t\tgit branch foo &&\n+\t\tgit symbolic-ref refs/heads/bar refs/heads/foo\n+\t\tgit config receive.denyCurrentBranch false\n+\t) &&\n+\t(\n+\t\tcd child2 &&\n+\t\t>path2 &&\n+\t\tgit add path2 &&\n+\t\ttest_tick &&\n+\t\tgit commit -a -m child2 &&\n+\t\tgit branch foo &&\n+\t\tgit branch bar &&\n+\t\tgit push ../child1 foo bar\n+\t)\n+'\n+\n+test_expect_success 'push into aliased refs (inconsistent)' '\n+\tmk_test heads/master &&\n+\tmk_child child1 &&\n+\tmk_child child2 &&\n+\t(\n+\t\tcd child1 &&\n+\t\tgit branch foo &&\n+\t\tgit symbolic-ref refs/heads/bar refs/heads/foo\n+\t\tgit config receive.denyCurrentBranch false\n+\t) &&\n+\t(\n+\t\tcd child2 &&\n+\t\t>path2 &&\n+\t\tgit add path2 &&\n+\t\ttest_tick &&\n+\t\tgit commit -a -m child2 &&\n+\t\tgit branch foo &&\n+\t\t>path3 &&\n+\t\tgit add path3 &&\n+\t\ttest_tick &&\n+\t\tgit commit -a -m child2 &&\n+\t\tgit branch bar &&\n+\t\ttest_must_fail git push ../child1 foo bar 2>stderr &&\n+\t\tgrep \"refusing inconsistent update\" stderr\n+\t)\n+'\n+\n test_done\n-- \n1.7.0.3.436.g2b878\n"},{"id":"139938","messageId":"1271715558-56781-1-git-send-email-jaysoffian@gmail.com","threadId":"23527","inReplyTo":"1271694343-31876-2-git-send-email-jaysoffian@gmail.com","subject":"[PATCH v3 3/3] receive-pack: detect aliased updates which can occur with symrefs","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2010-04-19T22:19:18Z","receivedAt":"2010-04-19T22:19:18Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"When pushing to a remote repo the sending side filters out aliased\nupdates (e.g., foo:baz bar:baz). However, it is not possible for the\nsender to know if two refs are aliased on the receiving side via\nsymrefs. Here is one such scenario:\n\n  $ git init origin\n  $ (cd origin && touch file && git add file && git commit -a -m intial)\n  $ git clone --bare origin origin.git\n  $ rm -rf origin\n\n  $ git clone origin.git client\n\n  $ git clone --mirror client backup.git &&\n  $ (cd backup.git && git remote set-head origin --auto)\n\n  $ (cd client &&\n\tgit remote add --mirror backup ../backup.git &&\n\techo change1 > file && git commit -a -m change1 &&\n\tgit push origin &&\n\tgit push backup\n\t)\n\nThe push to backup fails with:\n\n  Counting objects: 5, done.\n  Writing objects: 100% (3/3), 244 bytes, done.\n  Total 3 (delta 0), reused 0 (delta 0)\n  Unpacking objects: 100% (3/3), done.\n  error: Ref refs/remotes/origin/master is at ef3... but expected 262...\n  remote: error: failed to lock refs/remotes/origin/master\n  To ../backup.git\n     262cd57..ef307ff  master -> master\n     262cd57..ef307ff  origin/HEAD -> origin/HEAD\n   ! [remote rejected] origin/master -> origin/master (failed to lock)\n  error: failed to push some refs to '../backup.git'\n\nThe reason is that refs/remotes/origin/HEAD is a symref to\nrefs/remotes/origin/master, but it is not possible for the sending side\nto unambiguously know this.\n\nThis commit fixes the issue by having receive-pack ignore any update to\na symref whose target is being identically updated. If a symref and its\ntarget are being updated inconsistently, then the update for both fails\nwith an error message (\"refusing inconsistent update...\") to help\ndiagnose the situation.\n\nSigned-off-by: Jay Soffian <jaysoffian@gmail.com>\n---\n builtin-receive-pack.c |   68 +++++++++++++++++++++++++++++++++++++++++++++---\n t/t5516-fetch-push.sh  |   49 ++++++++++++++++++++++++++++++++++\n 2 files changed, 113 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin-receive-pack.c b/builtin-receive-pack.c\nindex fffb6ea..bb34757 100644\n--- a/builtin-receive-pack.c\n+++ b/builtin-receive-pack.c\n@@ -9,6 +9,7 @@\n #include \"object.h\"\n #include \"remote.h\"\n #include \"transport.h\"\n+#include \"string-list.h\"\n \n static const char receive_pack_usage[] = \"git receive-pack <git-dir>\";\n \n@@ -129,6 +130,7 @@ static void write_head_info(void)\n struct command {\n \tstruct command *next;\n \tconst char *error_string;\n+\tunsigned int skip_update;\n \tunsigned char old_sha1[20];\n \tunsigned char new_sha1[20];\n \tchar ref_name[FLEX_ARRAY]; /* more */\n@@ -486,6 +488,63 @@ static void run_update_post_hook(struct command *commands)\n \t}\n }\n \n+static void check_aliased_update(struct command *cmd, struct string_list *list)\n+{\n+\tstruct string_list_item *item;\n+\tstruct command *dst_cmd;\n+\tunsigned char sha1[20];\n+\tchar cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];\n+\tint flag;\n+\n+\tconst char *dst_name = resolve_ref(cmd->ref_name, sha1, 0, &flag);\n+\n+\tif (!(flag & REF_ISSYMREF))\n+\t\treturn;\n+\n+\tif ((item = string_list_lookup(dst_name, list)) == NULL)\n+\t\treturn;\n+\n+\tcmd->skip_update = 1;\n+\n+\tdst_cmd = (struct command *) item->util;\n+\n+\tif (!hashcmp(cmd->old_sha1, dst_cmd->old_sha1) &&\n+\t    !hashcmp(cmd->new_sha1, dst_cmd->new_sha1))\n+\t\treturn;\n+\n+\tdst_cmd->skip_update = 1;\n+\n+\tstrcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV));\n+\tstrcat(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));\n+\tstrcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV));\n+\tstrcat(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));\n+\trp_error(\"refusing inconsistent update between symref '%s' (%s..%s) and\"\n+\t\t \" its target '%s' (%s..%s)\",\n+\t\t cmd->ref_name, cmd_oldh, cmd_newh,\n+\t\t dst_cmd->ref_name, dst_oldh, dst_newh);\n+\n+\tcmd->error_string = dst_cmd->error_string =\n+\t\t\"inconsistent aliased update\";\n+}\n+\n+static void check_aliased_updates(struct command *commands)\n+{\n+\tstruct command *cmd;\n+\tstruct string_list ref_list = { NULL, 0, 0, 0 };\n+\n+\tfor (cmd = commands; cmd; cmd = cmd->next) {\n+\t\tstruct string_list_item *item =\n+\t\t\tstring_list_append(cmd->ref_name, &ref_list);\n+\t\titem->util = (void *)cmd;\n+\t}\n+\tsort_string_list(&ref_list);\n+\n+\tfor (cmd = commands; cmd; cmd = cmd->next)\n+\t\tcheck_aliased_update(cmd, &ref_list);\n+\n+\tstring_list_clear(&ref_list, 0);\n+}\n+\n static void execute_commands(struct command *commands, const char *unpacker_error)\n {\n \tstruct command *cmd;\n@@ -503,10 +562,13 @@ static void execute_commands(struct command *commands, const char *unpacker_erro\n \t\treturn;\n \t}\n \n+\tcheck_aliased_updates(commands);\n+\n \thead_name = resolve_ref(\"HEAD\", sha1, 0, NULL);\n \n \tfor (cmd = commands; cmd; cmd = cmd->next)\n-\t\tcmd->error_string = update(cmd);\n+\t\tif (!cmd->skip_update)\n+\t\t\tcmd->error_string = update(cmd);\n }\n \n static struct command *read_head_info(void)\n@@ -541,12 +603,10 @@ static struct command *read_head_info(void)\n \t\t\tif (strstr(refname + reflen + 1, \"side-band-64k\"))\n \t\t\t\tuse_sideband = LARGE_PACKET_MAX;\n \t\t}\n-\t\tcmd = xmalloc(sizeof(struct command) + len - 80);\n+\t\tcmd = xcalloc(1, sizeof(struct command) + len - 80);\n \t\thashcpy(cmd->old_sha1, old_sha1);\n \t\thashcpy(cmd->new_sha1, new_sha1);\n \t\tmemcpy(cmd->ref_name, line + 82, len - 81);\n-\t\tcmd->error_string = NULL;\n-\t\tcmd->next = NULL;\n \t\t*p = cmd;\n \t\tp = &cmd->next;\n \t}\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 3148789..f0813e0 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -683,4 +683,53 @@ test_expect_success 'push with branches containing #' '\n \tgit checkout master\n '\n \n+test_expect_success 'push into aliased refs (consistent)' '\n+\tmk_test heads/master &&\n+\tmk_child child1 &&\n+\tmk_child child2 &&\n+\t(\n+\t\tcd child1 &&\n+\t\tgit branch foo &&\n+\t\tgit symbolic-ref refs/heads/bar refs/heads/foo\n+\t\tgit config receive.denyCurrentBranch false\n+\t) &&\n+\t(\n+\t\tcd child2 &&\n+\t\t>path2 &&\n+\t\tgit add path2 &&\n+\t\ttest_tick &&\n+\t\tgit commit -a -m child2 &&\n+\t\tgit branch foo &&\n+\t\tgit branch bar &&\n+\t\tgit push ../child1 foo bar\n+\t)\n+'\n+\n+test_expect_success 'push into aliased refs (inconsistent)' '\n+\tmk_test heads/master &&\n+\tmk_child child1 &&\n+\tmk_child child2 &&\n+\t(\n+\t\tcd child1 &&\n+\t\tgit branch foo &&\n+\t\tgit symbolic-ref refs/heads/bar refs/heads/foo\n+\t\tgit config receive.denyCurrentBranch false\n+\t) &&\n+\t(\n+\t\tcd child2 &&\n+\t\t>path2 &&\n+\t\tgit add path2 &&\n+\t\ttest_tick &&\n+\t\tgit commit -a -m child2 &&\n+\t\tgit branch foo &&\n+\t\t>path3 &&\n+\t\tgit add path3 &&\n+\t\ttest_tick &&\n+\t\tgit commit -a -m child2 &&\n+\t\tgit branch bar &&\n+\t\ttest_must_fail git push ../child1 foo bar 2>stderr &&\n+\t\tgrep \"refusing inconsistent update\" stderr\n+\t)\n+'\n+\n test_done\n-- \n1.7.0.3.436.g2b878\n"},{"id":"143461","messageId":"AANLkTindct9FNsDGeGwFcMxFqgyxa4FlvtE0Lbw95GQM@mail.gmail.com","threadId":"23527","inReplyTo":"1271715558-56781-1-git-send-email-jaysoffian@gmail.com","subject":"Re: [PATCH v3 3/3] receive-pack: detect aliased updates which can occur with symrefs","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2010-06-10T18:06:35Z","receivedAt":"2010-06-10T18:06:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"On Mon, Apr 19, 2010 at 22:19, Jay Soffian <jaysoffian@gmail.com> wrote:\n> When pushing to a remote repo the sending side filters out aliased\n> updates (e.g., foo:baz bar:baz). However, it is not possible for the\n> sender to know if two refs are aliased on the receiving side via\n> symrefs. Here is one such scenario:\n>\n>  $ git init origin\n>  $ (cd origin && touch file && git add file && git commit -a -m intial)\n>  $ git clone --bare origin origin.git\n>  $ rm -rf origin\n>\n>  $ git clone origin.git client\n>\n>  $ git clone --mirror client backup.git &&\n>  $ (cd backup.git && git remote set-head origin --auto)\n>\n>  $ (cd client &&\n>        git remote add --mirror backup ../backup.git &&\n>        echo change1 > file && git commit -a -m change1 &&\n>        git push origin &&\n>        git push backup\n>        )\n>\n> The push to backup fails with:\n>\n>  Counting objects: 5, done.\n>  Writing objects: 100% (3/3), 244 bytes, done.\n>  Total 3 (delta 0), reused 0 (delta 0)\n>  Unpacking objects: 100% (3/3), done.\n>  error: Ref refs/remotes/origin/master is at ef3... but expected 262...\n>  remote: error: failed to lock refs/remotes/origin/master\n>  To ../backup.git\n>     262cd57..ef307ff  master -> master\n>     262cd57..ef307ff  origin/HEAD -> origin/HEAD\n>   ! [remote rejected] origin/master -> origin/master (failed to lock)\n>  error: failed to push some refs to '../backup.git'\n>\n> The reason is that refs/remotes/origin/HEAD is a symref to\n> refs/remotes/origin/master, but it is not possible for the sending side\n> to unambiguously know this.\n>\n> This commit fixes the issue by having receive-pack ignore any update to\n> a symref whose target is being identically updated. If a symref and its\n> target are being updated inconsistently, then the update for both fails\n> with an error message (\"refusing inconsistent update...\") to help\n> diagnose the situation.\n>\n> Signed-off-by: Jay Soffian <jaysoffian@gmail.com>\n> ---\n>  builtin-receive-pack.c |   68 +++++++++++++++++++++++++++++++++++++++++++++---\n>  t/t5516-fetch-push.sh  |   49 ++++++++++++++++++++++++++++++++++\n>  2 files changed, 113 insertions(+), 4 deletions(-)\n>\n> diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c\n> index fffb6ea..bb34757 100644\n> --- a/builtin-receive-pack.c\n> +++ b/builtin-receive-pack.c\n> @@ -9,6 +9,7 @@\n>  #include \"object.h\"\n>  #include \"remote.h\"\n>  #include \"transport.h\"\n> +#include \"string-list.h\"\n>\n>  static const char receive_pack_usage[] = \"git receive-pack <git-dir>\";\n>\n> @@ -129,6 +130,7 @@ static void write_head_info(void)\n>  struct command {\n>        struct command *next;\n>        const char *error_string;\n> +       unsigned int skip_update;\n>        unsigned char old_sha1[20];\n>        unsigned char new_sha1[20];\n>        char ref_name[FLEX_ARRAY]; /* more */\n> @@ -486,6 +488,63 @@ static void run_update_post_hook(struct command *commands)\n>        }\n>  }\n>\n> +static void check_aliased_update(struct command *cmd, struct string_list *list)\n> +{\n> +       struct string_list_item *item;\n> +       struct command *dst_cmd;\n> +       unsigned char sha1[20];\n> +       char cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];\n> +       int flag;\n> +\n> +       const char *dst_name = resolve_ref(cmd->ref_name, sha1, 0, &flag);\n> +\n> +       if (!(flag & REF_ISSYMREF))\n> +               return;\n> +\n> +       if ((item = string_list_lookup(dst_name, list)) == NULL)\n> +               return;\n> +\n> +       cmd->skip_update = 1;\n> +\n> +       dst_cmd = (struct command *) item->util;\n> +\n> +       if (!hashcmp(cmd->old_sha1, dst_cmd->old_sha1) &&\n> +           !hashcmp(cmd->new_sha1, dst_cmd->new_sha1))\n> +               return;\n> +\n> +       dst_cmd->skip_update = 1;\n> +\n> +       strcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV));\n> +       strcat(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));\n> +       strcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV));\n> +       strcat(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));\n> +       rp_error(\"refusing inconsistent update between symref '%s' (%s..%s) and\"\n> +                \" its target '%s' (%s..%s)\",\n> +                cmd->ref_name, cmd_oldh, cmd_newh,\n> +                dst_cmd->ref_name, dst_oldh, dst_newh);\n> +\n> +       cmd->error_string = dst_cmd->error_string =\n> +               \"inconsistent aliased update\";\n> +}\n> +\n> +static void check_aliased_updates(struct command *commands)\n> +{\n> +       struct command *cmd;\n> +       struct string_list ref_list = { NULL, 0, 0, 0 };\n> +\n> +       for (cmd = commands; cmd; cmd = cmd->next) {\n> +               struct string_list_item *item =\n> +                       string_list_append(cmd->ref_name, &ref_list);\n> +               item->util = (void *)cmd;\n> +       }\n> +       sort_string_list(&ref_list);\n> +\n> +       for (cmd = commands; cmd; cmd = cmd->next)\n> +               check_aliased_update(cmd, &ref_list);\n> +\n> +       string_list_clear(&ref_list, 0);\n> +}\n> +\n>  static void execute_commands(struct command *commands, const char *unpacker_error)\n>  {\n>        struct command *cmd;\n> @@ -503,10 +562,13 @@ static void execute_commands(struct command *commands, const char *unpacker_erro\n>                return;\n>        }\n>\n> +       check_aliased_updates(commands);\n> +\n>        head_name = resolve_ref(\"HEAD\", sha1, 0, NULL);\n>\n>        for (cmd = commands; cmd; cmd = cmd->next)\n> -               cmd->error_string = update(cmd);\n> +               if (!cmd->skip_update)\n> +                       cmd->error_string = update(cmd);\n>  }\n>\n>  static struct command *read_head_info(void)\n> @@ -541,12 +603,10 @@ static struct command *read_head_info(void)\n>                        if (strstr(refname + reflen + 1, \"side-band-64k\"))\n>                                use_sideband = LARGE_PACKET_MAX;\n>                }\n> -               cmd = xmalloc(sizeof(struct command) + len - 80);\n> +               cmd = xcalloc(1, sizeof(struct command) + len - 80);\n>                hashcpy(cmd->old_sha1, old_sha1);\n>                hashcpy(cmd->new_sha1, new_sha1);\n>                memcpy(cmd->ref_name, line + 82, len - 81);\n> -               cmd->error_string = NULL;\n> -               cmd->next = NULL;\n>                *p = cmd;\n>                p = &cmd->next;\n>        }\n> diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\n> index 3148789..f0813e0 100755\n> --- a/t/t5516-fetch-push.sh\n> +++ b/t/t5516-fetch-push.sh\n> @@ -683,4 +683,53 @@ test_expect_success 'push with branches containing #' '\n>        git checkout master\n>  '\n>\n> +test_expect_success 'push into aliased refs (consistent)' '\n> +       mk_test heads/master &&\n> +       mk_child child1 &&\n> +       mk_child child2 &&\n> +       (\n> +               cd child1 &&\n> +               git branch foo &&\n> +               git symbolic-ref refs/heads/bar refs/heads/foo\n> +               git config receive.denyCurrentBranch false\n> +       ) &&\n> +       (\n> +               cd child2 &&\n> +               >path2 &&\n> +               git add path2 &&\n> +               test_tick &&\n> +               git commit -a -m child2 &&\n> +               git branch foo &&\n> +               git branch bar &&\n> +               git push ../child1 foo bar\n> +       )\n> +'\n> +\n> +test_expect_success 'push into aliased refs (inconsistent)' '\n> +       mk_test heads/master &&\n> +       mk_child child1 &&\n> +       mk_child child2 &&\n> +       (\n> +               cd child1 &&\n> +               git branch foo &&\n> +               git symbolic-ref refs/heads/bar refs/heads/foo\n> +               git config receive.denyCurrentBranch false\n> +       ) &&\n> +       (\n> +               cd child2 &&\n> +               >path2 &&\n> +               git add path2 &&\n> +               test_tick &&\n> +               git commit -a -m child2 &&\n> +               git branch foo &&\n> +               >path3 &&\n> +               git add path3 &&\n> +               test_tick &&\n> +               git commit -a -m child2 &&\n> +               git branch bar &&\n> +               test_must_fail git push ../child1 foo bar 2>stderr &&\n> +               grep \"refusing inconsistent update\" stderr\n> +       )\n> +'\n> +\n>  test_done\n> --\n> 1.7.0.3.436.g2b878\n\nI've been having troubles running t5516-fetch-push.sh which I traced\ndown to this patch, here's the gist of it:\n\n    $ make\n    rm -f -r test-results\n    make aggregate-results-and-cleanup\n    make[1]: Entering directory `/home/avar/g/git/t'\n    echo \"*** t5516-fetch-push.sh ***\"; GIT_CONFIG=.git/config\n'/bin/sh' t5516-fetch-push.sh\n    *** t5516-fetch-push.sh ***\n    *   ok 1: setup\n    *   ok 2: fetch without wildcard\n    *   ok 3: fetch with wildcard\n    *   ok 4: fetch with insteadOf\n    *   ok 5: fetch with pushInsteadOf (should not rewrite)\n    *   ok 6: push without wildcard\n    *   ok 7: push with wildcard\n    *   ok 8: push with insteadOf\n    *   ok 9: push with pushInsteadOf\n    *   ok 10: push with pushInsteadOf and explicit pushurl\n(pushInsteadOf should not rewrite)\n    *   ok 11: push with matching heads\n    *   ok 12: push with matching heads on the command line\n    *   ok 13: failed (non-fast-forward) push with matching heads\n    *   ok 14: push --force with matching heads\n    *   ok 15: push with matching heads and forced update\n    *   ok 16: push with no ambiguity (1)\n    *   ok 17: push with no ambiguity (2)\n    *   ok 18: push with colon-less refspec, no ambiguity\n    *   ok 19: push with weak ambiguity (1)\n    *   ok 20: push with weak ambiguity (2)\n    *   ok 21: push with ambiguity\n    *   ok 22: push with colon-less refspec (1)\n    *   ok 23: push with colon-less refspec (2)\n    *   ok 24: push with colon-less refspec (3)\n    *   ok 25: push with colon-less refspec (4)\n    *   ok 26: push head with non-existant, incomplete dest\n    *   ok 27: push tag with non-existant, incomplete dest\n    *   ok 28: push sha1 with non-existant, incomplete dest\n    *   ok 29: push ref expression with non-existant, incomplete dest\n    *   ok 30: push with HEAD\n    *   ok 31: push with HEAD nonexisting at remote\n    *   ok 32: push with +HEAD\n    *   ok 33: push HEAD with non-existant, incomplete dest\n    *   ok 34: push with config remote.*.push = HEAD\n    *   ok 35: push with config remote.*.pushurl\n    *   ok 36: push with dry-run\n    *   ok 37: push updates local refs\n    *   ok 38: push updates up-to-date local refs\n    *   ok 39: push preserves up-to-date packed refs\n    *   ok 40: push does not update local refs on failure\n    *   ok 41: allow deleting an invalid remote ref\n    *   ok 42: allow deleting a ref using --delete\n    *   ok 43: allow deleting a tag using --delete\n    *   ok 44: push --delete without args aborts\n    *   ok 45: push --delete refuses src:dest refspecs\n    *   ok 46: warn on push to HEAD of non-bare repository\n    *   ok 47: deny push to HEAD of non-bare repository\n    *   ok 48: allow push to HEAD of bare repository (bare)\n    *   ok 49: allow push to HEAD of non-bare repository (config)\n    *   ok 50: fetch with branches\n    *   ok 51: fetch with branches containing #\n    *   ok 52: push with branches\n    *   ok 53: push with branches containing #\n    *   ok 54: push into aliased refs (consistent)\n    *** buffer overflow detected ***: receive-pack terminated\n    ======= Backtrace: =========\n    /lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x50)[0x402a0350]\n    /lib/tls/i686/cmov/libc.so.6(+0xe128a)[0x4029f28a]\n    /lib/tls/i686/cmov/libc.so.6(+0xe05ba)[0x4029e5ba]\n    receive-pack[0x8094de8]\n    receive-pack[0x804b8ab]\n    receive-pack[0x804be73]\n    /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0x401d4bd6]\n    receive-pack[0x804b211]\n    ======= Memory map: ========\n    08048000-0813e000 r-xp 00000000 08:02 6792494\n/home/avar/g/git/git-receive-pack\n    0813e000-0813f000 r--p 000f6000 08:02 6792494\n/home/avar/g/git/git-receive-pack\n    0813f000-08143000 rw-p 000f7000 08:02 6792494\n/home/avar/g/git/git-receive-pack\n    08143000-08189000 rw-p 00000000 00:00 0\n    0996b000-0998c000 rw-p 00000000 00:00 0          [heap]\n    40000000-4001b000 r-xp 00000000 08:02 5496926    /lib/ld-2.11.1.so\n    4001b000-4001c000 r--p 0001a000 08:02 5496926    /lib/ld-2.11.1.so\n    4001c000-4001d000 rw-p 0001b000 08:02 5496926    /lib/ld-2.11.1.so\n    4001d000-4001e000 r-xp 00000000 00:00 0          [vdso]\n    4001e000-40020000 rw-p 00000000 00:00 0\n    4003d000-40050000 r-xp 00000000 08:02 5497495    /lib/libz.so.1.2.3.3\n    40050000-40051000 r--p 00012000 08:02 5497495    /lib/libz.so.1.2.3.3\n    40051000-40052000 rw-p 00013000 08:02 5497495    /lib/libz.so.1.2.3.3\n    40052000-4018a000 r-xp 00000000 08:02 4431990\n/lib/i686/cmov/libcrypto.so.0.9.8\n    4018a000-40192000 r--p 00137000 08:02 4431990\n/lib/i686/cmov/libcrypto.so.0.9.8\n    40192000-401a0000 rw-p 0013f000 08:02 4431990\n/lib/i686/cmov/libcrypto.so.0.9.8\n    401a0000-401a5000 rw-p 00000000 00:00 0\n    401a5000-401ba000 r-xp 00000000 08:02 7234200\n/lib/tls/i686/cmov/libpthread-2.11.1.so\n    401ba000-401bb000 r--p 00014000 08:02 7234200\n/lib/tls/i686/cmov/libpthread-2.11.1.so\n    401bb000-401bc000 rw-p 00015000 08:02 7234200\n/lib/tls/i686/cmov/libpthread-2.11.1.so\n    401bc000-401be000 rw-p 00000000 00:00 0\n    401be000-40311000 r-xp 00000000 08:02 7233919\n/lib/tls/i686/cmov/libc-2.11.1.so\n    40311000-40312000 ---p 00153000 08:02 7233919\n/lib/tls/i686/cmov/libc-2.11.1.so\n    40312000-40314000 r--p 00153000 08:02 7233919\n/lib/tls/i686/cmov/libc-2.11.1.so\n    40314000-40315000 rw-p 00155000 08:02 7233919\n/lib/tls/i686/cmov/libc-2.11.1.so\n    40315000-40318000 rw-p 00000000 00:00 0\n    40318000-4031a000 r-xp 00000000 08:02 7233925\n/lib/tls/i686/cmov/libdl-2.11.1.so\n    4031a000-4031b000 r--p 00001000 08:02 7233925\n/lib/tls/i686/cmov/libdl-2.11.1.so\n    4031b000-4031c000 rw-p 00002000 08:02 7233925\n/lib/tls/i686/cmov/libdl-2.11.1.so\n    4031c000-4031d000 rw-p 00000000 00:00 0\n    4031d000-4033a000 r-xp 00000000 08:02 7217157    /lib/libgcc_s.so.1\n    4033a000-4033b000 r--p 0001c000 08:02 7217157    /lib/libgcc_s.so.1\n    4033b000-4033c000 rw-p 0001d000 08:02 7217157    /lib/libgcc_s.so.1\n    bf9b1000-bf9c6000 rw-p 00000000 00:00 0          [stack]\n    * FAIL 55: push into aliased refs (inconsistent)\n    \t\n    \t\tmk_test heads/master &&\n    \t\tmk_child child1 &&\n    \t\tmk_child child2 &&\n    \t\t(\n    \t\t\tcd child1 &&\n    \t\t\tgit branch foo &&\n    \t\t\tgit symbolic-ref refs/heads/bar refs/heads/foo\n    \t\t\tgit config receive.denyCurrentBranch false\n    \t\t) &&\n    \t\t(\n    \t\t\tcd child2 &&\n    \t\t\t>path2 &&\n    \t\t\tgit add path2 &&\n    \t\t\ttest_tick &&\n    \t\t\tgit commit -a -m child2 &&\n    \t\t\tgit branch foo &&\n    \t\t\t>path3 &&\n    \t\t\tgit add path3 &&\n    \t\t\ttest_tick &&\n    \t\t\tgit commit -a -m child2 &&\n    \t\t\tgit branch bar &&\n    \t\t\ttest_must_fail git push ../child1 foo bar 2>stderr &&\n    \t\t\tgrep \"refusing inconsistent update\" stderr\n    \t\t)\n    \t\n    *   ok 56: push --porcelain\n    *   ok 57: push --porcelain bad url\n    *   ok 58: push --porcelain rejected\n    *   ok 59: push --porcelain --dry-run rejected\n    * failed 1 among 59 test(s)\n    make[1]: *** [t5516-fetch-push.sh] Error 1\n    make[1]: Leaving directory `/home/avar/g/git/t'\n    make: *** [all] Error 2\n\n\nAnd the bisect script/log from 1.7.0 to master:\n\n    #!/bin/sh\n    cd ~/g/git\n    git reset --hard >/dev/null\n    git clean -dxf >/dev/null\n\n    make >/dev/null 2>&1\n    cp -v /tmp/Makefile t/Makefile\n    make test\n    ret=$?\n\n    git reset --hard >/dev/null\n    git clean -dxf  >/dev/null\n\n    exit $ret\n\nlog:\n\n    $ git bisect log\n    git bisect start\n    # bad: [92a75a391e66cfc278cf59741e484efd80c02176] Merge branch 'maint'\n    git bisect bad 92a75a391e66cfc278cf59741e484efd80c02176\n    # good: [e923eaeb901ff056421b9007adcbbce271caa7b6] Git 1.7.0\n    git bisect good e923eaeb901ff056421b9007adcbbce271caa7b6\n    # good: [419fe5bc861517c789c8f028519e085fd8d1992f] fmt-merge-msg:\nbe quiet if nothing to merge\n    git bisect good 419fe5bc861517c789c8f028519e085fd8d1992f\n    # good: [b6b0afdc30e066788592ca07c9a6c6936c68cc11] test-lib: some\nshells do not let $? propagate into an eval\n    git bisect good b6b0afdc30e066788592ca07c9a6c6936c68cc11\n    # good: [cd4ce1e8a81ef5c24af7b914fb72212273e7d489] Merge branch\n'jc/status-show-ignored'\n    git bisect good cd4ce1e8a81ef5c24af7b914fb72212273e7d489\n    # bad: [71f1d729b39ce5c92df6d623151f88bbb5d4c774] Merge branch\n'jn/gitweb-our-squelch'\n    git bisect bad 71f1d729b39ce5c92df6d623151f88bbb5d4c774\n    # good: [465ef577b59986d70c51ea72dd3f87759b2bcd4f] Merge branch\n'jn/submodule-basic-test'\n    git bisect good 465ef577b59986d70c51ea72dd3f87759b2bcd4f\n    # bad: [9215f76fb6d938ae93889f46f27cff22723fe0e4] Merge branch\n'js/maint-receive-pack-symref-alias'\n    git bisect bad 9215f76fb6d938ae93889f46f27cff22723fe0e4\n    # good: [9b0aa728705439ca4b4e7ec845f79f8487059320] Extract\nverify_pack_index for reuse from verify_pack\n    git bisect good 9b0aa728705439ca4b4e7ec845f79f8487059320\n    # good: [90d05713575ea6ed21d05228bcda8461f7b28ccf]\nhttp.c::new_http_pack_request: do away with the temp variable filename\n    git bisect good 90d05713575ea6ed21d05228bcda8461f7b28ccf\n    # bad: [da3efdb17bef25dedc753131462ee784d822132e] receive-pack:\ndetect aliased updates which can occur with symrefs\n    git bisect bad da3efdb17bef25dedc753131462ee784d822132e\n    # good: [5e1c71fd1488a33680c313f287b88d9f7a7d3e45] receive-pack:\nswitch global variable 'commands' to a parameter\n    git bisect good 5e1c71fd1488a33680c313f287b88d9f7a7d3e45\n\nWhat the t/Makefile patch does is this:\n\n    diff --git a/t/Makefile b/t/Makefile\n    index 25c559b..da0d5bc 100644\n    --- a/t/Makefile\n    +++ b/t/Makefile\n    @@ -13,7 +13,7 @@ RM ?= rm -f\n     # Shell quote;\n     SHELL_PATH_SQ = $(subst ','\\'',$(SHELL_PATH))\n\n    -T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)\n    +T = $(wildcard t[5][5][1][6]-*.sh)\n     TSVN = $(wildcard t91[0-9][0-9]-*.sh)\n\nThe reason is that I can't reproduce this when running just the\nscript, it has to be through make test, which suggests some sort of\nevil heisenbug. Even running the exact same commands the Makefile\nwould run to execute the script doesn't work.\n\nI also ran the tests under valgrind, here's the full output:\nhttp://gist.github.com/433379\n"},{"id":"204686","messageId":"7v62488j8a.fsf_-_@alter.siamese.dyndns.org","threadId":"23527","inReplyTo":"1271714912-56659-1-git-send-email-jaysoffian@gmail.com","subject":"[RFC/PATCH] ignoring a fetch that overwrites local symref","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-12-11T19:46:45Z","receivedAt":"2012-12-11T19:46:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This is a companion to an ancient thread\n\n    http://thread.gmane.org/gmane.comp.version-control.git/145311/focus=145337\n\nin which an error was dealt with while pushing into a \"mirror\"\nrepository that has a symbolic reference refs/remotes/origin/HEAD\npointing at refs/remotes/origin/master with \"git push --mirror\".\nThe issue was that the receiving end was told to update origin/HEAD\nand origin/master separately; if origin/HEAD is updated, that would\nupdate origin/master at the same time, and then when attempting to\nupdate origin/master, it would notice that it no longer has the\nexpected old value and barf.  After the series, we started ignoring\nsuch pushes to HEAD on the receiving end.\n\nBut you can suffer from a similar issue transferring objects in the\nopposite direction.  If you run \"fetch --mirror\" in to such a\n\"mirror\" repository, the other side would advertise both 'master'\nand 'HEAD' under refs/remotes/origin/ hierarchy, and refs/*:refs/*\nwildcard would try to grab both of them.\n\nWork it around by noticing a wildcard match that attempts to update\na local symbolic ref and ignoring it.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * While I do not think it is sane to have symbolic refs in the\n   receiving ref hierarchy (e.g. refs/remotes/origin/) that has a\n   matching ref in the corresponding ref hierarchy in the sending\n   side (e.g. the same, or refs/heads/ if you are doing a mirror) in\n   the first place, we addressed the issue brought by such a setting\n   on the push side, so it is probably a good idea to do it on the\n   fetch side.\n\n   This is marked RFC as it cheats by ignoring symrefs that were not\n   explicitly asked, instead of doing the \"is the underlying thing\n   going to be updated with the same operation?\" logic the old patch\n   did in da3efdb (receive-pack: detect aliased updates which can\n   occur with symrefs, 2010-04-19). I think this simpler logic is\n   sufficient but there may be corner cases that merit the more\n   elaborate one, hence RFC.\n\n remote.c | 13 ++++++++++++-\n 1 file changed, 12 insertions(+), 1 deletion(-)\n\ndiff --git i/remote.c w/remote.c\nindex 6aa49c0..ca1f8f2 100644\n--- i/remote.c\n+++ w/remote.c\n@@ -1370,6 +1370,16 @@ int branch_merge_matches(struct branch *branch,\n \treturn refname_match(branch->merge[i]->src, refname, ref_fetch_rules);\n }\n \n+static int ignore_symref_update(const char *refname)\n+{\n+\tunsigned char sha1[20];\n+\tint flag;\n+\n+\tif (!resolve_ref_unsafe(refname, sha1, 0, &flag))\n+\t\treturn 0; /* non-existing refs are OK */\n+\treturn (flag & REF_ISSYMREF);\n+}\n+\n static struct ref *get_expanded_map(const struct ref *remote_refs,\n \t\t\t\t    const struct refspec *refspec)\n {\n@@ -1383,7 +1393,8 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,\n \t\tif (strchr(ref->name, '^'))\n \t\t\tcontinue; /* a dereference item */\n \t\tif (match_name_with_pattern(refspec->src, ref->name,\n-\t\t\t\t\t    refspec->dst, &expn_name)) {\n+\t\t\t\t\t    refspec->dst, &expn_name) &&\n+\t\t    !ignore_symref_update(expn_name)) {\n \t\t\tstruct ref *cpy = copy_ref(ref);\n \n \t\t\tcpy->peer_ref = alloc_ref(expn_name);\n"},{"id":"204699","messageId":"7vmwxk6x0a.fsf_-_@alter.siamese.dyndns.org","threadId":"23527","inReplyTo":"7v62488j8a.fsf_-_@alter.siamese.dyndns.org","subject":"[PATCH] fetch: ignore wildcarded refspecs that update local symbolic refs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-12-11T22:32:05Z","receivedAt":"2012-12-11T22:32:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"In a repository cloned from somewhere else, you typically have a\nsymbolic ref refs/remotes/origin/HEAD pointing at the 'master'\nremote-tracking ref that is next to it.  When fetching into such a\nrepository with \"git fetch --mirror\" from another repository that\nwas similarly cloned, the implied wildcard refspec refs/*:refs/*\nwill end up asking to update refs/remotes/origin/HEAD with the\nobject at refs/remotes/origin/HEAD at the remote side, while asking\nto update refs/remotes/origin/master the same way.  Depending on the\norder the two updates happen, the latter one would find that the\nvalue of the ref before it is updated has changed from what the code\nexpects.\n\nWhen the user asks to update the underlying ref via the symbolic ref\nexplicitly without using a wildcard refspec, e.g. \"git fetch $there\nrefs/heads/master:refs/remotes/origin/HEAD\", we should still let him\ndo so, but when expanding wildcard refs, it will result in a more\nintuitive outcome if we simply ignore local symbolic refs.\n\nAs the purpose of the symbolic ref refs/remotes/origin/HEAD is to\nfollow the ref it points at (e.g. refs/remotes/origin/master), its\nvalue would change when the underlying ref is updated.\n\nEarlier commit da3efdb (receive-pack: detect aliased updates which\ncan occur with symrefs, 2010-04-19) fixed a similar issue for \"git\npush\".\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * This time with minimal tests and an updated log message.\n\n remote.c                     | 13 ++++++++++++-\n t/t5535-fetch-push-symref.sh | 42 ++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 54 insertions(+), 1 deletion(-)\n create mode 100755 t/t5535-fetch-push-symref.sh\n\ndiff --git a/remote.c b/remote.c\nindex 04fd9ea..a72748c 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1370,6 +1370,16 @@ int branch_merge_matches(struct branch *branch,\n \treturn refname_match(branch->merge[i]->src, refname, ref_fetch_rules);\n }\n \n+static int ignore_symref_update(const char *refname)\n+{\n+\tunsigned char sha1[20];\n+\tint flag;\n+\n+\tif (!resolve_ref_unsafe(refname, sha1, 0, &flag))\n+\t\treturn 0; /* non-existing refs are OK */\n+\treturn (flag & REF_ISSYMREF);\n+}\n+\n static struct ref *get_expanded_map(const struct ref *remote_refs,\n \t\t\t\t    const struct refspec *refspec)\n {\n@@ -1383,7 +1393,8 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,\n \t\tif (strchr(ref->name, '^'))\n \t\t\tcontinue; /* a dereference item */\n \t\tif (match_name_with_pattern(refspec->src, ref->name,\n-\t\t\t\t\t    refspec->dst, &expn_name)) {\n+\t\t\t\t\t    refspec->dst, &expn_name) &&\n+\t\t    !ignore_symref_update(expn_name)) {\n \t\t\tstruct ref *cpy = copy_ref(ref);\n \n \t\t\tcpy->peer_ref = alloc_ref(expn_name);\ndiff --git a/t/t5535-fetch-push-symref.sh b/t/t5535-fetch-push-symref.sh\nnew file mode 100755\nindex 0000000..8ed58d2\n--- /dev/null\n+++ b/t/t5535-fetch-push-symref.sh\n@@ -0,0 +1,42 @@\n+#!/bin/sh\n+\n+test_description='avoiding conflicting update thru symref aliasing'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit one &&\n+\tgit clone . src &&\n+\tgit clone src dst1 &&\n+\tgit clone src dst2 &&\n+\ttest_commit two &&\n+\t( cd src && git pull )\n+'\n+\n+test_expect_success 'push' '\n+\t(\n+\t\tcd src &&\n+\t\tgit push ../dst1 \"refs/remotes/*:refs/remotes/*\"\n+\t) &&\n+\tgit ls-remote src \"refs/remotes/*\" >expect &&\n+\tgit ls-remote dst1 \"refs/remotes/*\" >actual &&\n+\ttest_cmp expect actual &&\n+\t( cd src && git symbolic-ref refs/remotes/origin/HEAD ) >expect &&\n+\t( cd dst1 && git symbolic-ref refs/remotes/origin/HEAD ) >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'fetch' '\n+\t(\n+\t\tcd dst2 &&\n+\t\tgit fetch ../src \"refs/remotes/*:refs/remotes/*\"\n+\t) &&\n+\tgit ls-remote src \"refs/remotes/*\" >expect &&\n+\tgit ls-remote dst2 \"refs/remotes/*\" >actual &&\n+\ttest_cmp expect actual &&\n+\t( cd src && git symbolic-ref refs/remotes/origin/HEAD ) >expect &&\n+\t( cd dst2 && git symbolic-ref refs/remotes/origin/HEAD ) >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n1.8.1.rc1.128.gd8d1528\n"},{"id":"204755","messageId":"CAG+J_Dw5jG4GoTuteD=w+CFG3ZbJsCB=coW9CAw=g7kz3cD2og@mail.gmail.com","threadId":"23527","inReplyTo":"7vmwxk6x0a.fsf_-_@alter.siamese.dyndns.org","subject":"Re: [PATCH] fetch: ignore wildcarded refspecs that update local symbolic refs","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2012-12-12T17:17:35Z","receivedAt":"2012-12-12T17:17:35Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Tue, Dec 11, 2012 at 5:32 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> In a repository cloned from somewhere else, you typically have a\n> [...]\n>  * This time with minimal tests and an updated log message.\n\nSorry I haven't been reading the list much lately, so I don't know the\ncontext which motivated this patch beyond the commit message. But,\nthis message is quite clear. So:\n\nAcked-by: Jay Soffian\n\n> diff --git a/remote.c b/remote.c\n> index 04fd9ea..a72748c 100644\n> --- a/remote.c\n> +++ b/remote.c\n> @@ -1370,6 +1370,16 @@ int branch_merge_matches(struct branch *branch,\n>         return refname_match(branch->merge[i]->src, refname, ref_fetch_rules);\n>  }\n>\n> +static int ignore_symref_update(const char *refname)\n\ns/ignore_symref_update/is_existing_symref/ ?\n\nj.\n"},{"id":"204772","messageId":"CAJo=hJu2X5u4oCGRVHK5a4sbf4X2meUbq_8kaGiQB1mdQspaXw@mail.gmail.com","threadId":"23527","inReplyTo":"7v62488j8a.fsf_-_@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH] ignoring a fetch that overwrites local symref","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2012-12-12T19:13:01Z","receivedAt":"2012-12-12T19:13:01Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"On Tue, Dec 11, 2012 at 11:46 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> This is a companion to an ancient thread\n>\n>     http://thread.gmane.org/gmane.comp.version-control.git/145311/focus=145337\n>\n> in which an error was dealt with while pushing into a \"mirror\"\n> repository that has a symbolic reference refs/remotes/origin/HEAD\n> pointing at refs/remotes/origin/master with \"git push --mirror\".\n> The issue was that the receiving end was told to update origin/HEAD\n> and origin/master separately; if origin/HEAD is updated, that would\n> update origin/master at the same time, and then when attempting to\n> update origin/master, it would notice that it no longer has the\n> expected old value and barf.  After the series, we started ignoring\n> such pushes to HEAD on the receiving end.\n>\n> But you can suffer from a similar issue transferring objects in the\n> opposite direction.  If you run \"fetch --mirror\" in to such a\n> \"mirror\" repository, the other side would advertise both 'master'\n> and 'HEAD' under refs/remotes/origin/ hierarchy, and refs/*:refs/*\n> wildcard would try to grab both of them.\n>\n> Work it around by noticing a wildcard match that attempts to update\n> a local symbolic ref and ignoring it.\n\nAt what point should we just support symrefs on the protocol? :-(\n"},{"id":"204776","messageId":"7vip873vsr.fsf@alter.siamese.dyndns.org","threadId":"23527","inReplyTo":"CAJo=hJu2X5u4oCGRVHK5a4sbf4X2meUbq_8kaGiQB1mdQspaXw@mail.gmail.com","subject":"Re: [RFC/PATCH] ignoring a fetch that overwrites local symref","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-12-12T19:38:44Z","receivedAt":"2012-12-12T19:38:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Shawn Pearce <spearce@spearce.org> writes:\n\n>> Work it around by noticing a wildcard match that attempts to update\n>> a local symbolic ref and ignoring it.\n>\n> At what point should we just support symrefs on the protocol? :-(\n\nI think it is entirely an orthogonal matter.  When we learn that the\nother side now has this ref as a symref pointing to this other ref,\nan update of the local ref on the RHS of the refspec that has such a\nsymref on its LHS will not be using the current codepath to call\nupdate_ref() to write the object name thru an existing symref.\n"}]}