{"thread":{"id":"35243","subject":"[PATCH v2 00/23] Change semantics of \"fetch --tags\"","startedAt":"2013-10-30T05:32:49Z","lastAt":"2013-10-30T17:41:38Z","messageCount":28,"participants":["Michael Haggerty","Ramkumar Ramachandra"],"isPatch":true,"patchVersion":2,"patchTotal":23},"messages":[{"id":"229754","messageId":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":null,"subject":"[PATCH v2 00/23] Change semantics of \"fetch --tags\"","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:32:49Z","receivedAt":"2013-10-30T05:32:49Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"This is v2 of my proposed fix for the \"local tag killer\" problem that\nI reported recently [1].  Thanks a lot to Junio for his feedback on\nv1.\n\nChanges since v1:\n\n* Rebase to current upstream master (0d6cf2471f); no conflicts were\n  encountered.\n\n* Incorporate feedback from Junio:\n\n  * Improve the documentation as suggested\n\n  * Fix a few typos\n\n* Document get_expanded_map() and fix a minor memory leak that I found\n  there.\n\n* get_ref_map(): Do not look for references that can be updated\n  opportunistically among the entries added by \"--tags\".  Preserve the\n  order of output list when changing the function to handle the new\n  \"--tags\" semantics.  (I know more about how the output is used and\n  am less worried now that the changes will have bad interactions with\n  the rest of the system.)\n\n* Improve the description of tag-following in the \"git fetch\" manpage.\n\n* Moved the changes to ref_remove_duplicates() later in the series, as\n  they were not really integral to the rest of the patch series.\n  Make the following changes to that function:\n\n  * Add tests of how it handles duplicates.\n\n  * Simplify the loop in a different way than v1, to make it more\n    modular.\n\n  * Extract a function to handle duplicates.\n\n  * Improve the error messages emitted if ref_remove_duplicates()\n    finds conflicting duplicates, and mark the messages for\n    translation.\n\n  * Do not die() if a user-specified refspec conflicts with\n    an opportunistic update.\n\n[1] http://article.gmane.org/gmane.comp.version-control.git/234723\n\nMichael Haggerty (23):\n  t5510: use the correct tag name in test\n  t5510: prepare test refs more straightforwardly\n  t5510: check that \"git fetch --prune --tags\" does not prune branches\n  api-remote.txt: correct section \"struct refspec\"\n  get_ref_map(): rename local variables\n  builtin/fetch.c: reorder function definitions\n  get_expanded_map(): add docstring\n  get_expanded_map(): avoid memory leak\n  fetch: only opportunistically update references based on command line\n  fetch --tags: fetch tags *in addition to* other stuff\n  fetch --prune: prune only based on explicit refspecs\n  query_refspecs(): move some constants out of the loop\n  builtin/remote.c: reorder function definitions\n  builtin/remote.c:update(): use struct argv_array\n  fetch, remote: properly convey --no-prune options to subprocesses\n  fetch-options.txt: simplify ifdef/ifndef/endif usage\n  git-fetch.txt: improve description of tag auto-following\n  ref_remove_duplicates(): avoid redundant bisection\n  t5536: new test of refspec conflicts when fetching\n  ref_remove_duplicates(): simplify loop logic\n  ref_remote_duplicates(): extract a function handle_duplicate()\n  handle_duplicate(): mark error message for translation\n  fetch: improve the error messages emitted for conflicting refspecs\n\n Documentation/config.txt                 |   4 +-\n Documentation/fetch-options.txt          |  26 +--\n Documentation/git-fetch.txt              |  14 +-\n Documentation/technical/api-remote.txt   |  20 +--\n builtin/fetch.c                          | 298 ++++++++++++++++---------------\n builtin/remote.c                         | 196 ++++++++++----------\n git-pull.sh                              |   2 +-\n remote.c                                 |  94 +++++++---\n remote.h                                 |   8 +-\n t/t5510-fetch.sh                         |  36 +++-\n t/t5515/fetch.br-unconfig_--tags_.._.git |   1 +\n t/t5515/fetch.master_--tags_.._.git      |   1 +\n t/t5525-fetch-tagopt.sh                  |  23 ++-\n t/t5536-fetch-conflicts.sh               | 100 +++++++++++\n 14 files changed, 503 insertions(+), 320 deletions(-)\n create mode 100755 t/t5536-fetch-conflicts.sh\n\n-- \n1.8.4.1\n"},{"id":"229760","messageId":"1383111192-23780-2-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 01/23] t5510: use the correct tag name in test","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:32:50Z","receivedAt":"2013-10-30T05:32:50Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"Fix an apparent copy-paste error: A few lines earlier, a tag\n\"refs/tags/sometag\" is created.  Check for the (non-)existence of that\ntag, not \"somebranch\", which is otherwise never mentioned in the\nscript.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n t/t5510-fetch.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 1f0f8e6..c5e5dfc 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -121,7 +121,7 @@ test_expect_success 'fetch --prune --tags does not delete the remote-tracking br\n \n \tgit fetch --prune --tags origin &&\n \tgit rev-parse origin/master &&\n-\ttest_must_fail git rev-parse somebranch\n+\ttest_must_fail git rev-parse sometag\n '\n \n test_expect_success 'fetch --prune --tags with branch does not delete other remote-tracking branches' '\n-- \n1.8.4.1\n"},{"id":"229761","messageId":"1383111192-23780-3-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 02/23] t5510: prepare test refs more straightforwardly","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:32:51Z","receivedAt":"2013-10-30T05:32:51Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"\"git fetch\" was being used with contrived refspecs to create tags and\nremote-tracking branches in test repositories in preparation for the\nactual tests.  This is obscure and also makes one wonder whether this\nis indeed just preparation or whether some side-effect of \"git fetch\"\nis being tested.\n\nSo use the more straightforward commands \"git tag\" / \"git update-ref\"\nwhen preparing branches in test repositories.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n t/t5510-fetch.sh | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex c5e5dfc..08d8dbb 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -88,7 +88,7 @@ test_expect_success 'fetch --prune on its own works as expected' '\n \tcd \"$D\" &&\n \tgit clone . prune &&\n \tcd prune &&\n-\tgit fetch origin refs/heads/master:refs/remotes/origin/extrabranch &&\n+\tgit update-ref refs/remotes/origin/extrabranch master &&\n \n \tgit fetch --prune origin &&\n \ttest_must_fail git rev-parse origin/extrabranch\n@@ -98,7 +98,7 @@ test_expect_success 'fetch --prune with a branch name keeps branches' '\n \tcd \"$D\" &&\n \tgit clone . prune-branch &&\n \tcd prune-branch &&\n-\tgit fetch origin refs/heads/master:refs/remotes/origin/extrabranch &&\n+\tgit update-ref refs/remotes/origin/extrabranch master &&\n \n \tgit fetch --prune origin master &&\n \tgit rev-parse origin/extrabranch\n@@ -117,7 +117,7 @@ test_expect_success 'fetch --prune --tags does not delete the remote-tracking br\n \tcd \"$D\" &&\n \tgit clone . prune-tags &&\n \tcd prune-tags &&\n-\tgit fetch origin refs/heads/master:refs/tags/sometag &&\n+\tgit tag sometag master &&\n \n \tgit fetch --prune --tags origin &&\n \tgit rev-parse origin/master &&\n@@ -128,7 +128,7 @@ test_expect_success 'fetch --prune --tags with branch does not delete other remo\n \tcd \"$D\" &&\n \tgit clone . prune-tags-branch &&\n \tcd prune-tags-branch &&\n-\tgit fetch origin refs/heads/master:refs/remotes/origin/extrabranch &&\n+\tgit update-ref refs/remotes/origin/extrabranch master &&\n \n \tgit fetch --prune --tags origin master &&\n \tgit rev-parse origin/extrabranch\n-- \n1.8.4.1\n"},{"id":"229762","messageId":"1383111192-23780-4-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 03/23] t5510: check that \"git fetch --prune --tags\" does not prune branches","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:32:52Z","receivedAt":"2013-10-30T05:32:52Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"\"git fetch --prune --tags\" is currently interpreted as follows:\n\n* \"--tags\" is equivalent to specifying a refspec\n  \"refs/tags/*:refs/tags/*\", and supersedes any default refspecs\n  configured via remote.$REMOTE.fetch.\n\n* \"--prune\" only operates on the refspecs being fetched.\n\nTherefore, \"git fetch --prune --tags\" prunes tags in refs/tags/* but\ndoes not fetch or prune other references.  The fact that this command\ndoes not prune references outside of refs/tags/* was previously\nuntested.  So add a test that verifies the status quo.\n\nHowever, the status quo is surprising, so it will be changed later in\nthis patch series.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n t/t5510-fetch.sh | 4 ++++\n 1 file changed, 4 insertions(+)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 08d8dbb..8328be1 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -118,9 +118,13 @@ test_expect_success 'fetch --prune --tags does not delete the remote-tracking br\n \tgit clone . prune-tags &&\n \tcd prune-tags &&\n \tgit tag sometag master &&\n+\t# Create what looks like a remote-tracking branch from an earlier\n+\t# fetch that has since been deleted from the remote:\n+\tgit update-ref refs/remotes/origin/fake-remote master &&\n \n \tgit fetch --prune --tags origin &&\n \tgit rev-parse origin/master &&\n+\tgit rev-parse origin/fake-remote &&\n \ttest_must_fail git rev-parse sometag\n '\n \n-- \n1.8.4.1\n"},{"id":"229755","messageId":"1383111192-23780-5-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 04/23] api-remote.txt: correct section \"struct refspec\"","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:32:53Z","receivedAt":"2013-10-30T05:32:53Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"* Replace reference to function parse_ref_spec() with references to\n  functions parse_fetch_refspec() and parse_push_refspec().\n\n* Correct description of src and dst: they *do* include the '*'\n  characters.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n Documentation/technical/api-remote.txt | 20 ++++++++++----------\n 1 file changed, 10 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/technical/api-remote.txt b/Documentation/technical/api-remote.txt\nindex 4be8776..5d245aa 100644\n--- a/Documentation/technical/api-remote.txt\n+++ b/Documentation/technical/api-remote.txt\n@@ -58,16 +58,16 @@ default remote, given the current branch and configuration.\n struct refspec\n --------------\n \n-A struct refspec holds the parsed interpretation of a refspec. If it\n-will force updates (starts with a '+'), force is true. If it is a\n-pattern (sides end with '*') pattern is true. src and dest are the two\n-sides (if a pattern, only the part outside of the wildcards); if there\n-is only one side, it is src, and dst is NULL; if sides exist but are\n-empty (i.e., the refspec either starts or ends with ':'), the\n-corresponding side is \"\".\n-\n-This parsing can be done to an array of strings to give an array of\n-struct refpsecs with parse_ref_spec().\n+A struct refspec holds the parsed interpretation of a refspec.  If it\n+will force updates (starts with a '+'), force is true.  If it is a\n+pattern (sides end with '*') pattern is true.  src and dest are the\n+two sides (including '*' characters if present); if there is only one\n+side, it is src, and dst is NULL; if sides exist but are empty (i.e.,\n+the refspec either starts or ends with ':'), the corresponding side is\n+\"\".\n+\n+An array of strings can be parsed into an array of struct refspecs\n+using parse_fetch_refspec() or parse_push_refspec().\n \n remote_find_tracking(), given a remote and a struct refspec with\n either src or dst filled out, will fill out the other such that the\n-- \n1.8.4.1\n"},{"id":"229756","messageId":"1383111192-23780-6-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 05/23] get_ref_map(): rename local variables","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:32:54Z","receivedAt":"2013-10-30T05:32:54Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"Rename \"refs\" -> \"refspecs\" and \"ref_count\" -> \"refspec_count\" to\nreduce confusion, because they describe an array of \"struct refspec\",\nas opposed to the \"struct ref\" objects that are also used in this\nfunction.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n builtin/fetch.c | 12 ++++++------\n 1 file changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex bd7a101..2248abf 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -165,8 +165,8 @@ static void find_non_local_tags(struct transport *transport,\n \t\t\tstruct ref ***tail);\n \n static struct ref *get_ref_map(struct transport *transport,\n-\t\t\t       struct refspec *refs, int ref_count, int tags,\n-\t\t\t       int *autotags)\n+\t\t\t       struct refspec *refspecs, int refspec_count,\n+\t\t\t       int tags, int *autotags)\n {\n \tint i;\n \tstruct ref *rm;\n@@ -175,12 +175,12 @@ static struct ref *get_ref_map(struct transport *transport,\n \n \tconst struct ref *remote_refs = transport_get_remote_refs(transport);\n \n-\tif (ref_count || tags == TAGS_SET) {\n+\tif (refspec_count || tags == TAGS_SET) {\n \t\tstruct ref **old_tail;\n \n-\t\tfor (i = 0; i < ref_count; i++) {\n-\t\t\tget_fetch_map(remote_refs, &refs[i], &tail, 0);\n-\t\t\tif (refs[i].dst && refs[i].dst[0])\n+\t\tfor (i = 0; i < refspec_count; i++) {\n+\t\t\tget_fetch_map(remote_refs, &refspecs[i], &tail, 0);\n+\t\t\tif (refspecs[i].dst && refspecs[i].dst[0])\n \t\t\t\t*autotags = 1;\n \t\t}\n \t\t/* Merge everything on the command line, but not --tags */\n-- \n1.8.4.1\n"},{"id":"229763","messageId":"1383111192-23780-7-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 06/23] builtin/fetch.c: reorder function definitions","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:32:55Z","receivedAt":"2013-10-30T05:32:55Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"Reorder function definitions to avoid the need for a forward\ndeclaration of function find_non_local_tags().\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n builtin/fetch.c | 198 +++++++++++++++++++++++++++-----------------------------\n 1 file changed, 97 insertions(+), 101 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 2248abf..61e8117 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -160,9 +160,105 @@ static void add_merge_config(struct ref **head,\n \t}\n }\n \n+static int add_existing(const char *refname, const unsigned char *sha1,\n+\t\t\tint flag, void *cbdata)\n+{\n+\tstruct string_list *list = (struct string_list *)cbdata;\n+\tstruct string_list_item *item = string_list_insert(list, refname);\n+\titem->util = xmalloc(20);\n+\thashcpy(item->util, sha1);\n+\treturn 0;\n+}\n+\n+static int will_fetch(struct ref **head, const unsigned char *sha1)\n+{\n+\tstruct ref *rm = *head;\n+\twhile (rm) {\n+\t\tif (!hashcmp(rm->old_sha1, sha1))\n+\t\t\treturn 1;\n+\t\trm = rm->next;\n+\t}\n+\treturn 0;\n+}\n+\n static void find_non_local_tags(struct transport *transport,\n \t\t\tstruct ref **head,\n-\t\t\tstruct ref ***tail);\n+\t\t\tstruct ref ***tail)\n+{\n+\tstruct string_list existing_refs = STRING_LIST_INIT_DUP;\n+\tstruct string_list remote_refs = STRING_LIST_INIT_NODUP;\n+\tconst struct ref *ref;\n+\tstruct string_list_item *item = NULL;\n+\n+\tfor_each_ref(add_existing, &existing_refs);\n+\tfor (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {\n+\t\tif (prefixcmp(ref->name, \"refs/tags/\"))\n+\t\t\tcontinue;\n+\n+\t\t/*\n+\t\t * The peeled ref always follows the matching base\n+\t\t * ref, so if we see a peeled ref that we don't want\n+\t\t * to fetch then we can mark the ref entry in the list\n+\t\t * as one to ignore by setting util to NULL.\n+\t\t */\n+\t\tif (!suffixcmp(ref->name, \"^{}\")) {\n+\t\t\tif (item && !has_sha1_file(ref->old_sha1) &&\n+\t\t\t    !will_fetch(head, ref->old_sha1) &&\n+\t\t\t    !has_sha1_file(item->util) &&\n+\t\t\t    !will_fetch(head, item->util))\n+\t\t\t\titem->util = NULL;\n+\t\t\titem = NULL;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/*\n+\t\t * If item is non-NULL here, then we previously saw a\n+\t\t * ref not followed by a peeled reference, so we need\n+\t\t * to check if it is a lightweight tag that we want to\n+\t\t * fetch.\n+\t\t */\n+\t\tif (item && !has_sha1_file(item->util) &&\n+\t\t    !will_fetch(head, item->util))\n+\t\t\titem->util = NULL;\n+\n+\t\titem = NULL;\n+\n+\t\t/* skip duplicates and refs that we already have */\n+\t\tif (string_list_has_string(&remote_refs, ref->name) ||\n+\t\t    string_list_has_string(&existing_refs, ref->name))\n+\t\t\tcontinue;\n+\n+\t\titem = string_list_insert(&remote_refs, ref->name);\n+\t\titem->util = (void *)ref->old_sha1;\n+\t}\n+\tstring_list_clear(&existing_refs, 1);\n+\n+\t/*\n+\t * We may have a final lightweight tag that needs to be\n+\t * checked to see if it needs fetching.\n+\t */\n+\tif (item && !has_sha1_file(item->util) &&\n+\t    !will_fetch(head, item->util))\n+\t\titem->util = NULL;\n+\n+\t/*\n+\t * For all the tags in the remote_refs string list,\n+\t * add them to the list of refs to be fetched\n+\t */\n+\tfor_each_string_list_item(item, &remote_refs) {\n+\t\t/* Unless we have already decided to ignore this item... */\n+\t\tif (item->util)\n+\t\t{\n+\t\t\tstruct ref *rm = alloc_ref(item->string);\n+\t\t\trm->peer_ref = alloc_ref(item->string);\n+\t\t\thashcpy(rm->old_sha1, item->util);\n+\t\t\t**tail = rm;\n+\t\t\t*tail = &rm->next;\n+\t\t}\n+\t}\n+\n+\tstring_list_clear(&remote_refs, 0);\n+}\n \n static struct ref *get_ref_map(struct transport *transport,\n \t\t\t       struct refspec *refspecs, int refspec_count,\n@@ -612,106 +708,6 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)\n \treturn result;\n }\n \n-static int add_existing(const char *refname, const unsigned char *sha1,\n-\t\t\tint flag, void *cbdata)\n-{\n-\tstruct string_list *list = (struct string_list *)cbdata;\n-\tstruct string_list_item *item = string_list_insert(list, refname);\n-\titem->util = xmalloc(20);\n-\thashcpy(item->util, sha1);\n-\treturn 0;\n-}\n-\n-static int will_fetch(struct ref **head, const unsigned char *sha1)\n-{\n-\tstruct ref *rm = *head;\n-\twhile (rm) {\n-\t\tif (!hashcmp(rm->old_sha1, sha1))\n-\t\t\treturn 1;\n-\t\trm = rm->next;\n-\t}\n-\treturn 0;\n-}\n-\n-static void find_non_local_tags(struct transport *transport,\n-\t\t\tstruct ref **head,\n-\t\t\tstruct ref ***tail)\n-{\n-\tstruct string_list existing_refs = STRING_LIST_INIT_DUP;\n-\tstruct string_list remote_refs = STRING_LIST_INIT_NODUP;\n-\tconst struct ref *ref;\n-\tstruct string_list_item *item = NULL;\n-\n-\tfor_each_ref(add_existing, &existing_refs);\n-\tfor (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {\n-\t\tif (prefixcmp(ref->name, \"refs/tags/\"))\n-\t\t\tcontinue;\n-\n-\t\t/*\n-\t\t * The peeled ref always follows the matching base\n-\t\t * ref, so if we see a peeled ref that we don't want\n-\t\t * to fetch then we can mark the ref entry in the list\n-\t\t * as one to ignore by setting util to NULL.\n-\t\t */\n-\t\tif (!suffixcmp(ref->name, \"^{}\")) {\n-\t\t\tif (item && !has_sha1_file(ref->old_sha1) &&\n-\t\t\t    !will_fetch(head, ref->old_sha1) &&\n-\t\t\t    !has_sha1_file(item->util) &&\n-\t\t\t    !will_fetch(head, item->util))\n-\t\t\t\titem->util = NULL;\n-\t\t\titem = NULL;\n-\t\t\tcontinue;\n-\t\t}\n-\n-\t\t/*\n-\t\t * If item is non-NULL here, then we previously saw a\n-\t\t * ref not followed by a peeled reference, so we need\n-\t\t * to check if it is a lightweight tag that we want to\n-\t\t * fetch.\n-\t\t */\n-\t\tif (item && !has_sha1_file(item->util) &&\n-\t\t    !will_fetch(head, item->util))\n-\t\t\titem->util = NULL;\n-\n-\t\titem = NULL;\n-\n-\t\t/* skip duplicates and refs that we already have */\n-\t\tif (string_list_has_string(&remote_refs, ref->name) ||\n-\t\t    string_list_has_string(&existing_refs, ref->name))\n-\t\t\tcontinue;\n-\n-\t\titem = string_list_insert(&remote_refs, ref->name);\n-\t\titem->util = (void *)ref->old_sha1;\n-\t}\n-\tstring_list_clear(&existing_refs, 1);\n-\n-\t/*\n-\t * We may have a final lightweight tag that needs to be\n-\t * checked to see if it needs fetching.\n-\t */\n-\tif (item && !has_sha1_file(item->util) &&\n-\t    !will_fetch(head, item->util))\n-\t\titem->util = NULL;\n-\n-\t/*\n-\t * For all the tags in the remote_refs string list,\n-\t * add them to the list of refs to be fetched\n-\t */\n-\tfor_each_string_list_item(item, &remote_refs) {\n-\t\t/* Unless we have already decided to ignore this item... */\n-\t\tif (item->util)\n-\t\t{\n-\t\t\tstruct ref *rm = alloc_ref(item->string);\n-\t\t\trm->peer_ref = alloc_ref(item->string);\n-\t\t\thashcpy(rm->old_sha1, item->util);\n-\t\t\t**tail = rm;\n-\t\t\t*tail = &rm->next;\n-\t\t}\n-\t}\n-\n-\tstring_list_clear(&remote_refs, 0);\n-}\n-\n static void check_not_current_branch(struct ref *ref_map)\n {\n \tstruct branch *current_branch = branch_get(NULL);\n-- \n1.8.4.1\n"},{"id":"229758","messageId":"1383111192-23780-8-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 07/23] get_expanded_map(): add docstring","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:32:56Z","receivedAt":"2013-10-30T05:32:56Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n remote.c | 7 +++++++\n 1 file changed, 7 insertions(+)\n\ndiff --git a/remote.c b/remote.c\nindex 9f1a8aa..0d082cd 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1553,6 +1553,13 @@ static int ignore_symref_update(const char *refname)\n \treturn (flag & REF_ISSYMREF);\n }\n \n+/*\n+ * Create and return a list of (struct ref) consisting of copies of\n+ * each remote_ref that matches refspec.  refspec must be a pattern.\n+ * Fill in the copies' peer_ref to describe the local tracking refs to\n+ * which they map.  Omit any references that would map to an existing\n+ * local symbolic ref.\n+ */\n static struct ref *get_expanded_map(const struct ref *remote_refs,\n \t\t\t\t    const struct refspec *refspec)\n {\n-- \n1.8.4.1\n"},{"id":"229757","messageId":"1383111192-23780-9-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 08/23] get_expanded_map(): avoid memory leak","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:32:57Z","receivedAt":"2013-10-30T05:32:57Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"The old code could leak *expn_name if match_name_with_pattern()\nsucceeded but ignore_symref_update() returned true.  So make sure that\n*expn_name is freed in any case.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n remote.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 0d082cd..e42daa8 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1567,9 +1567,9 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,\n \tstruct ref *ret = NULL;\n \tstruct ref **tail = &ret;\n \n-\tchar *expn_name;\n-\n \tfor (ref = remote_refs; ref; ref = ref->next) {\n+\t\tchar *expn_name = NULL;\n+\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@@ -1578,12 +1578,12 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,\n \t\t\tstruct ref *cpy = copy_ref(ref);\n \n \t\t\tcpy->peer_ref = alloc_ref(expn_name);\n-\t\t\tfree(expn_name);\n \t\t\tif (refspec->force)\n \t\t\t\tcpy->peer_ref->force = 1;\n \t\t\t*tail = cpy;\n \t\t\ttail = &cpy->next;\n \t\t}\n+\t\tfree(expn_name);\n \t}\n \n \treturn ret;\n-- \n1.8.4.1\n"},{"id":"229765","messageId":"1383111192-23780-10-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 09/23] fetch: only opportunistically update references based on command line","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:32:58Z","receivedAt":"2013-10-30T05:32:58Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"The old code processed (tags == TAGS_SET) before adding the entries\nused to opportunistically update references mentioned on the command\nline.  The result was that all tags were also considered candidates\nfor opportunistic updating.\n\nThis is harmless for two reasons: (a) because it would only add\nentries if there is a configured refspec that covers tags *and* both\n--tags and another refspec appear on the command-line; (b) because any\nextra entries would be deleted later by the call to\nref_remove_duplicates() anyway.\n\nBut, to avoid extra work and extra memory usage, and to make the\nimplementation better match the intention, change the algorithm\nslightly: compute the opportunistic refspecs based only on the\ncommand-line arguments, storing the results into a separate temporary\nlist.  Then add the tags (which have to come earlier in the list so\nthat they are not de-duped in favor of an opportunistic entry).  Then\nconcatenate the temporary list onto the main list.\n\nThis change will also make later changes easier.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n builtin/fetch.c | 44 ++++++++++++++++++++++++++++++++------------\n 1 file changed, 32 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 61e8117..0849f09 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -272,32 +272,50 @@ static struct ref *get_ref_map(struct transport *transport,\n \tconst struct ref *remote_refs = transport_get_remote_refs(transport);\n \n \tif (refspec_count || tags == TAGS_SET) {\n-\t\tstruct ref **old_tail;\n+\t\t/* opportunistically-updated references: */\n+\t\tstruct ref *orefs = NULL, **oref_tail = &orefs;\n \n \t\tfor (i = 0; i < refspec_count; i++) {\n \t\t\tget_fetch_map(remote_refs, &refspecs[i], &tail, 0);\n \t\t\tif (refspecs[i].dst && refspecs[i].dst[0])\n \t\t\t\t*autotags = 1;\n \t\t}\n-\t\t/* Merge everything on the command line, but not --tags */\n+\t\t/* Merge everything on the command line (but not --tags) */\n \t\tfor (rm = ref_map; rm; rm = rm->next)\n \t\t\trm->fetch_head_status = FETCH_HEAD_MERGE;\n-\t\tif (tags == TAGS_SET)\n-\t\t\tget_fetch_map(remote_refs, tag_refspec, &tail, 0);\n \n \t\t/*\n-\t\t * For any refs that we happen to be fetching via command-line\n-\t\t * arguments, take the opportunity to update their configured\n-\t\t * counterparts. However, we do not want to mention these\n-\t\t * entries in FETCH_HEAD at all, as they would simply be\n-\t\t * duplicates of existing entries.\n+\t\t * For any refs that we happen to be fetching via\n+\t\t * command-line arguments, the destination ref might\n+\t\t * have been missing or have been different than the\n+\t\t * remote-tracking ref that would be derived from the\n+\t\t * configured refspec.  In these cases, we want to\n+\t\t * take the opportunity to update their configured\n+\t\t * remote-tracking reference.  However, we do not want\n+\t\t * to mention these entries in FETCH_HEAD at all, as\n+\t\t * they would simply be duplicates of existing\n+\t\t * entries, so we set them FETCH_HEAD_IGNORE below.\n+\t\t *\n+\t\t * We compute these entries now, based only on the\n+\t\t * refspecs specified on the command line.  But we add\n+\t\t * them to the list following the refspecs resulting\n+\t\t * from the tags option so that one of the latter,\n+\t\t * which has FETCH_HEAD_NOT_FOR_MERGE, is not removed\n+\t\t * by ref_remove_duplicates() in favor of one of these\n+\t\t * opportunistic entries with FETCH_HEAD_IGNORE.\n \t\t */\n-\t\told_tail = tail;\n \t\tfor (i = 0; i < transport->remote->fetch_refspec_nr; i++)\n \t\t\tget_fetch_map(ref_map, &transport->remote->fetch[i],\n-\t\t\t\t      &tail, 1);\n-\t\tfor (rm = *old_tail; rm; rm = rm->next)\n+\t\t\t\t      &oref_tail, 1);\n+\n+\t\tif (tags == TAGS_SET)\n+\t\t\tget_fetch_map(remote_refs, tag_refspec, &tail, 0);\n+\n+\t\t*tail = orefs;\n+\t\tfor (rm = orefs; rm; rm = rm->next) {\n \t\t\trm->fetch_head_status = FETCH_HEAD_IGNORE;\n+\t\t\ttail = &rm->next;\n+\t\t}\n \t} else {\n \t\t/* Use the defaults */\n \t\tstruct remote *remote = transport->remote;\n@@ -334,8 +352,10 @@ static struct ref *get_ref_map(struct transport *transport,\n \t\t\ttail = &ref_map->next;\n \t\t}\n \t}\n+\n \tif (tags == TAGS_DEFAULT && *autotags)\n \t\tfind_non_local_tags(transport, &ref_map, &tail);\n+\n \tref_remove_duplicates(ref_map);\n \n \treturn ref_map;\n-- \n1.8.4.1\n"},{"id":"229764","messageId":"1383111192-23780-11-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 10/23] fetch --tags: fetch tags *in addition to* other stuff","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:32:59Z","receivedAt":"2013-10-30T05:32:59Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"Previously, fetch's \"--tags\" option was considered equivalent to\nspecifying the refspec \"refs/tags/*:refs/tags/*\" on the command line;\nin particular, it caused the remote.<name>.refspec configuration to be\nignored.\n\nBut it is not very useful to fetch tags without also fetching other\nreferences, whereas it *is* quite useful to be able to fetch tags *in\naddition to* other references.  So change the semantics of this option\nto do the latter.\n\nIf a user wants to fetch *only* tags, then it is still possible to\nspecifying an explicit refspec:\n\n    git fetch <remote> 'refs/tags/*:refs/tags/*'\n\nPlease note that the documentation prior to 1.8.0.3 was ambiguous\nabout this aspect of \"fetch --tags\" behavior.  Commit\n\n    f0cb2f137c 2012-12-14 fetch --tags: clarify documentation\n\nmade the documentation match the old behavior.  This commit changes\nthe documentation to match the new behavior.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n Documentation/fetch-options.txt          |  8 ++---\n builtin/fetch.c                          | 59 +++++++++++++++++++-------------\n git-pull.sh                              |  2 +-\n t/t5510-fetch.sh                         | 22 ++++++++++--\n t/t5515/fetch.br-unconfig_--tags_.._.git |  1 +\n t/t5515/fetch.master_--tags_.._.git      |  1 +\n t/t5525-fetch-tagopt.sh                  | 23 ++++++++++---\n 7 files changed, 78 insertions(+), 38 deletions(-)\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex ba1fe49..921f6be 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -61,11 +61,9 @@ endif::git-pull[]\n ifndef::git-pull[]\n -t::\n --tags::\n-\tThis is a short-hand for giving `refs/tags/*:refs/tags/*`\n-\trefspec from the command line, to ask all tags to be fetched\n-\tand stored locally.  Because this acts as an explicit\n-\trefspec, the default refspecs (configured with the\n-\tremote.$name.fetch variable) are overridden and not used.\n+\tRequest that all tags be fetched from the remote in addition\n+\tto whatever else is being fetched.  Its effect is similar to\n+\tthat of the refspec `refs/tags/*:refs/tags/*`.\n \n --recurse-submodules[=yes|on-demand|no]::\n \tThis option controls if and under what conditions new commits of\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 0849f09..887fa3e 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -269,12 +269,12 @@ static struct ref *get_ref_map(struct transport *transport,\n \tstruct ref *ref_map = NULL;\n \tstruct ref **tail = &ref_map;\n \n-\tconst struct ref *remote_refs = transport_get_remote_refs(transport);\n+\t/* opportunistically-updated references: */\n+\tstruct ref *orefs = NULL, **oref_tail = &orefs;\n \n-\tif (refspec_count || tags == TAGS_SET) {\n-\t\t/* opportunistically-updated references: */\n-\t\tstruct ref *orefs = NULL, **oref_tail = &orefs;\n+\tconst struct ref *remote_refs = transport_get_remote_refs(transport);\n \n+\tif (refspec_count) {\n \t\tfor (i = 0; i < refspec_count; i++) {\n \t\t\tget_fetch_map(remote_refs, &refspecs[i], &tail, 0);\n \t\t\tif (refspecs[i].dst && refspecs[i].dst[0])\n@@ -310,12 +310,6 @@ static struct ref *get_ref_map(struct transport *transport,\n \n \t\tif (tags == TAGS_SET)\n \t\t\tget_fetch_map(remote_refs, tag_refspec, &tail, 0);\n-\n-\t\t*tail = orefs;\n-\t\tfor (rm = orefs; rm; rm = rm->next) {\n-\t\t\trm->fetch_head_status = FETCH_HEAD_IGNORE;\n-\t\t\ttail = &rm->next;\n-\t\t}\n \t} else {\n \t\t/* Use the defaults */\n \t\tstruct remote *remote = transport->remote;\n@@ -353,9 +347,19 @@ static struct ref *get_ref_map(struct transport *transport,\n \t\t}\n \t}\n \n-\tif (tags == TAGS_DEFAULT && *autotags)\n+\tif (tags == TAGS_SET)\n+\t\t/* also fetch all tags */\n+\t\tget_fetch_map(remote_refs, tag_refspec, &tail, 0);\n+\telse if (tags == TAGS_DEFAULT && *autotags)\n \t\tfind_non_local_tags(transport, &ref_map, &tail);\n \n+\t/* Now append any refs to be updated opportunistically: */\n+\t*tail = orefs;\n+\tfor (rm = orefs; rm; rm = rm->next) {\n+\t\trm->fetch_head_status = FETCH_HEAD_IGNORE;\n+\t\ttail = &rm->next;\n+\t}\n+\n \tref_remove_duplicates(ref_map);\n \n \treturn ref_map;\n@@ -846,31 +850,38 @@ static int do_fetch(struct transport *transport,\n \t\tgoto cleanup;\n \t}\n \tif (prune) {\n-\t\t/*\n-\t\t * If --tags was specified, pretend that the user gave us\n-\t\t * the canonical tags refspec\n-\t\t */\n+\t\tstruct refspec *prune_refspecs;\n+\t\tint prune_refspec_count;\n+\n+\t\tif (ref_count) {\n+\t\t\tprune_refspecs = refs;\n+\t\t\tprune_refspec_count = ref_count;\n+\t\t} else {\n+\t\t\tprune_refspecs = transport->remote->fetch;\n+\t\t\tprune_refspec_count = transport->remote->fetch_refspec_nr;\n+\t\t}\n+\n \t\tif (tags == TAGS_SET) {\n+\t\t\t/*\n+\t\t\t * --tags was specified.  Pretend that the user also\n+\t\t\t * gave us the canonical tags refspec\n+\t\t\t */\n \t\t\tconst char *tags_str = \"refs/tags/*:refs/tags/*\";\n \t\t\tstruct refspec *tags_refspec, *refspec;\n \n \t\t\t/* Copy the refspec and add the tags to it */\n-\t\t\trefspec = xcalloc(ref_count + 1, sizeof(struct refspec));\n+\t\t\trefspec = xcalloc(prune_refspec_count + 1, sizeof(*refspec));\n \t\t\ttags_refspec = parse_fetch_refspec(1, &tags_str);\n-\t\t\tmemcpy(refspec, refs, ref_count * sizeof(struct refspec));\n-\t\t\tmemcpy(&refspec[ref_count], tags_refspec, sizeof(struct refspec));\n-\t\t\tref_count++;\n+\t\t\tmemcpy(refspec, prune_refspecs, prune_refspec_count * sizeof(*refspec));\n+\t\t\tmemcpy(&refspec[prune_refspec_count], tags_refspec, sizeof(*refspec));\n \n-\t\t\tprune_refs(refspec, ref_count, ref_map);\n+\t\t\tprune_refs(refspec, prune_refspec_count + 1, ref_map);\n \n-\t\t\tref_count--;\n \t\t\t/* The rest of the strings belong to fetch_one */\n \t\t\tfree_refspec(1, tags_refspec);\n \t\t\tfree(refspec);\n-\t\t} else if (ref_count) {\n-\t\t\tprune_refs(refs, ref_count, ref_map);\n \t\t} else {\n-\t\t\tprune_refs(transport->remote->fetch, transport->remote->fetch_refspec_nr, ref_map);\n+\t\t\tprune_refs(prune_refspecs, prune_refspec_count, ref_map);\n \t\t}\n \t}\n \tfree_refs(ref_map);\ndiff --git a/git-pull.sh b/git-pull.sh\nindex b946fd9..97716b8 100755\n--- a/git-pull.sh\n+++ b/git-pull.sh\n@@ -172,7 +172,7 @@ error_on_no_merge_candidates () {\n \tdo\n \t\tcase \"$opt\" in\n \t\t-t|--t|--ta|--tag|--tags)\n-\t\t\techo \"Fetching tags only, you probably meant:\"\n+\t\t\techo \"It doesn't make sense to pull all tags; you probably meant:\"\n \t\t\techo \"  git fetch --tags\"\n \t\t\texit 1\n \t\tesac\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 8328be1..02e5901 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -113,7 +113,7 @@ test_expect_success 'fetch --prune with a namespace keeps other namespaces' '\n \tgit rev-parse origin/master\n '\n \n-test_expect_success 'fetch --prune --tags does not delete the remote-tracking branches' '\n+test_expect_success 'fetch --prune --tags prunes tags and branches' '\n \tcd \"$D\" &&\n \tgit clone . prune-tags &&\n \tcd prune-tags &&\n@@ -124,7 +124,7 @@ test_expect_success 'fetch --prune --tags does not delete the remote-tracking br\n \n \tgit fetch --prune --tags origin &&\n \tgit rev-parse origin/master &&\n-\tgit rev-parse origin/fake-remote &&\n+\ttest_must_fail git rev-parse origin/fake-remote &&\n \ttest_must_fail git rev-parse sometag\n '\n \n@@ -132,10 +132,26 @@ test_expect_success 'fetch --prune --tags with branch does not delete other remo\n \tcd \"$D\" &&\n \tgit clone . prune-tags-branch &&\n \tcd prune-tags-branch &&\n+\tgit tag sometag master &&\n \tgit update-ref refs/remotes/origin/extrabranch master &&\n \n \tgit fetch --prune --tags origin master &&\n-\tgit rev-parse origin/extrabranch\n+\tgit rev-parse origin/extrabranch &&\n+\ttest_must_fail git rev-parse sometag\n+'\n+\n+test_expect_success 'fetch --prune --tags with refspec prunes based on refspec' '\n+\tcd \"$D\" &&\n+\tgit clone . prune-tags-refspec &&\n+\tcd prune-tags-refspec &&\n+\tgit tag sometag master &&\n+\tgit update-ref refs/remotes/origin/foo/otherbranch master &&\n+\tgit update-ref refs/remotes/origin/extrabranch master &&\n+\n+\tgit fetch --prune --tags origin refs/heads/foo/*:refs/remotes/origin/foo/* &&\n+\ttest_must_fail git rev-parse refs/remotes/origin/foo/otherbranch &&\n+\tgit rev-parse origin/extrabranch &&\n+\ttest_must_fail git rev-parse sometag\n '\n \n test_expect_success 'fetch tags when there is no tags' '\ndiff --git a/t/t5515/fetch.br-unconfig_--tags_.._.git b/t/t5515/fetch.br-unconfig_--tags_.._.git\nindex 1669cc4..0f70f66 100644\n--- a/t/t5515/fetch.br-unconfig_--tags_.._.git\n+++ b/t/t5515/fetch.br-unconfig_--tags_.._.git\n@@ -1,4 +1,5 @@\n # br-unconfig --tags ../.git\n+0567da4d5edd2ff4bb292a465ba9e64dcad9536b\t\t../\n 6c9dec2b923228c9ff994c6cfe4ae16c12408dc5\tnot-for-merge\ttag 'tag-master' of ../\n 8e32a6d901327a23ef831511badce7bf3bf46689\tnot-for-merge\ttag 'tag-one' of ../\n 22feea448b023a2d864ef94b013735af34d238ba\tnot-for-merge\ttag 'tag-one-tree' of ../\ndiff --git a/t/t5515/fetch.master_--tags_.._.git b/t/t5515/fetch.master_--tags_.._.git\nindex 8a74935..ab473a6 100644\n--- a/t/t5515/fetch.master_--tags_.._.git\n+++ b/t/t5515/fetch.master_--tags_.._.git\n@@ -1,4 +1,5 @@\n # master --tags ../.git\n+0567da4d5edd2ff4bb292a465ba9e64dcad9536b\t\t../\n 6c9dec2b923228c9ff994c6cfe4ae16c12408dc5\tnot-for-merge\ttag 'tag-master' of ../\n 8e32a6d901327a23ef831511badce7bf3bf46689\tnot-for-merge\ttag 'tag-one' of ../\n 22feea448b023a2d864ef94b013735af34d238ba\tnot-for-merge\ttag 'tag-one-tree' of ../\ndiff --git a/t/t5525-fetch-tagopt.sh b/t/t5525-fetch-tagopt.sh\nindex 4fbf7a1..45815f7 100755\n--- a/t/t5525-fetch-tagopt.sh\n+++ b/t/t5525-fetch-tagopt.sh\n@@ -8,7 +8,8 @@ setup_clone () {\n \tgit clone --mirror . $1 &&\n \tgit remote add remote_$1 $1 &&\n \t(cd $1 &&\n-\tgit tag tag_$1)\n+\tgit tag tag_$1 &&\n+\tgit branch branch_$1)\n }\n \n test_expect_success setup '\n@@ -21,21 +22,33 @@ test_expect_success setup '\n \n test_expect_success \"fetch with tagopt=--no-tags does not get tag\" '\n \tgit fetch remote_one &&\n-\ttest_must_fail git show-ref tag_one\n+\ttest_must_fail git show-ref tag_one &&\n+\tgit show-ref remote_one/branch_one\n \t'\n \n test_expect_success \"fetch --tags with tagopt=--no-tags gets tag\" '\n+\t(\n+\t\tcd one &&\n+\t\tgit branch second_branch_one\n+\t) &&\n \tgit fetch --tags remote_one &&\n-\tgit show-ref tag_one\n+\tgit show-ref tag_one &&\n+\tgit show-ref remote_one/second_branch_one\n \t'\n \n test_expect_success \"fetch --no-tags with tagopt=--tags does not get tag\" '\n \tgit fetch --no-tags remote_two &&\n-\ttest_must_fail git show-ref tag_two\n+\ttest_must_fail git show-ref tag_two &&\n+\tgit show-ref remote_two/branch_two\n \t'\n \n test_expect_success \"fetch with tagopt=--tags gets tag\" '\n+\t(\n+\t\tcd two &&\n+\t\tgit branch second_branch_two\n+\t) &&\n \tgit fetch remote_two &&\n-\tgit show-ref tag_two\n+\tgit show-ref tag_two &&\n+\tgit show-ref remote_two/second_branch_two\n \t'\n test_done\n-- \n1.8.4.1\n"},{"id":"229770","messageId":"1383111192-23780-12-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 11/23] fetch --prune: prune only based on explicit refspecs","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:33:00Z","receivedAt":"2013-10-30T05:33:00Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"The old behavior of \"fetch --prune\" was to prune whatever was being\nfetched.  In particular, \"fetch --prune --tags\" caused tags not only\nto be fetched, but also to be pruned.  This is inappropriate because\nthere is only one tags namespace that is shared among the local\nrepository and all remotes.  Therefore, if the user defines a local\ntag and then runs \"git fetch --prune --tags\", then the local tag is\ndeleted.  Moreover, \"--prune\" and \"--tags\" can also be configured via\nfetch.prune / remote.<name>.prune and remote.<name>.tagopt, making it\neven less obvious that an invocation of \"git fetch\" could result in\ntag lossage.\n\nSince the command \"git remote update\" invokes \"git fetch\", it had the\nsame problem.\n\nThe command \"git remote prune\", on the other hand, disregarded the\nsetting of remote.<name>.tagopt, and so its behavior was inconsistent\nwith that of the other commands.\n\nSo the old behavior made it too easy to lose tags.  To fix this\nproblem, change \"fetch --prune\" to prune references based only on\nrefspecs specified explicitly by the user, either on the command line\nor via remote.<name>.fetch.  Thus, tags are no longer made subject to\npruning by the --tags option or the remote.<name>.tagopt setting.\n\nHowever, tags *are* still subject to pruning if they are fetched as\npart of a refspec, and that is good.  For example:\n\n* On the command line,\n\n      git fetch --prune 'refs/tags/*:refs/tags/*'\n\n  causes tags, and only tags, to be fetched and pruned, and is\n  therefore a simple way for the user to get the equivalent of the old\n  behavior of \"--prune --tag\".\n\n* For a remote that was configured with the \"--mirror\" option, the\n  configuration is set to include\n\n      [remote \"name\"]\n              fetch = +refs/*:refs/*\n\n  , which causes tags to be subject to pruning along with all other\n  references.  This is the behavior that will typically be desired for\n  a mirror.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n Documentation/config.txt        |  4 ++--\n Documentation/fetch-options.txt | 19 ++++++++++++++-----\n builtin/fetch.c                 | 39 +++++++++------------------------------\n t/t5510-fetch.sh                | 10 +++++-----\n 4 files changed, 30 insertions(+), 42 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex ab26963..a405806 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2087,8 +2087,8 @@ remote.<name>.vcs::\n \n remote.<name>.prune::\n \tWhen set to true, fetching from this remote by default will also\n-\tremove any remote-tracking branches which no longer exist on the\n-\tremote (as if the `--prune` option was give on the command line).\n+\tremove any remote-tracking references that no longer exist on the\n+\tremote (as if the `--prune` option was given on the command line).\n \tOverrides `fetch.prune` settings, if any.\n \n remotes.<group>::\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex 921f6be..12b1d92 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -41,8 +41,14 @@ ifndef::git-pull[]\n \n -p::\n --prune::\n-\tAfter fetching, remove any remote-tracking branches which\n-\tno longer exist\ton the remote.\n+\tAfter fetching, remove any remote-tracking references that no\n+\tlonger exist on the remote.  Tags are not subject to pruning\n+\tif they are fetched only because of the default tag\n+\tauto-following or due to a --tags option.  However, if tags\n+\tare fetched due to an explicit refspec (either on the command\n+\tline or in the remote configuration, for example if the remote\n+\twas cloned with the --mirror option), then they are also\n+\tsubject to pruning.\n endif::git-pull[]\n \n ifdef::git-pull[]\n@@ -61,9 +67,12 @@ endif::git-pull[]\n ifndef::git-pull[]\n -t::\n --tags::\n-\tRequest that all tags be fetched from the remote in addition\n-\tto whatever else is being fetched.  Its effect is similar to\n-\tthat of the refspec `refs/tags/*:refs/tags/*`.\n+\tFetch all tags from the remote (i.e., fetch remote tags\n+\t`refs/tags/*` into local tags with the same name), in addition\n+\tto whatever else would otherwise be fetched.  Using this\n+\toption alone does not subject tags to pruning, even if --prune\n+\tis used (though tags may be pruned anyway if they are also the\n+\tdestination of an explicit refspec; see '--prune').\n \n --recurse-submodules[=yes|on-demand|no]::\n \tThis option controls if and under what conditions new commits of\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 887fa3e..1514b90 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -850,38 +850,17 @@ static int do_fetch(struct transport *transport,\n \t\tgoto cleanup;\n \t}\n \tif (prune) {\n-\t\tstruct refspec *prune_refspecs;\n-\t\tint prune_refspec_count;\n-\n+\t\t/*\n+\t\t * We only prune based on refspecs specified\n+\t\t * explicitly (via command line or configuration); we\n+\t\t * don't care whether --tags was specified.\n+\t\t */\n \t\tif (ref_count) {\n-\t\t\tprune_refspecs = refs;\n-\t\t\tprune_refspec_count = ref_count;\n-\t\t} else {\n-\t\t\tprune_refspecs = transport->remote->fetch;\n-\t\t\tprune_refspec_count = transport->remote->fetch_refspec_nr;\n-\t\t}\n-\n-\t\tif (tags == TAGS_SET) {\n-\t\t\t/*\n-\t\t\t * --tags was specified.  Pretend that the user also\n-\t\t\t * gave us the canonical tags refspec\n-\t\t\t */\n-\t\t\tconst char *tags_str = \"refs/tags/*:refs/tags/*\";\n-\t\t\tstruct refspec *tags_refspec, *refspec;\n-\n-\t\t\t/* Copy the refspec and add the tags to it */\n-\t\t\trefspec = xcalloc(prune_refspec_count + 1, sizeof(*refspec));\n-\t\t\ttags_refspec = parse_fetch_refspec(1, &tags_str);\n-\t\t\tmemcpy(refspec, prune_refspecs, prune_refspec_count * sizeof(*refspec));\n-\t\t\tmemcpy(&refspec[prune_refspec_count], tags_refspec, sizeof(*refspec));\n-\n-\t\t\tprune_refs(refspec, prune_refspec_count + 1, ref_map);\n-\n-\t\t\t/* The rest of the strings belong to fetch_one */\n-\t\t\tfree_refspec(1, tags_refspec);\n-\t\t\tfree(refspec);\n+\t\t\tprune_refs(refs, ref_count, ref_map);\n \t\t} else {\n-\t\t\tprune_refs(prune_refspecs, prune_refspec_count, ref_map);\n+\t\t\tprune_refs(transport->remote->fetch,\n+\t\t\t\t   transport->remote->fetch_refspec_nr,\n+\t\t\t\t   ref_map);\n \t\t}\n \t}\n \tfree_refs(ref_map);\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 02e5901..5d4581d 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -113,7 +113,7 @@ test_expect_success 'fetch --prune with a namespace keeps other namespaces' '\n \tgit rev-parse origin/master\n '\n \n-test_expect_success 'fetch --prune --tags prunes tags and branches' '\n+test_expect_success 'fetch --prune --tags prunes branches but not tags' '\n \tcd \"$D\" &&\n \tgit clone . prune-tags &&\n \tcd prune-tags &&\n@@ -125,10 +125,10 @@ test_expect_success 'fetch --prune --tags prunes tags and branches' '\n \tgit fetch --prune --tags origin &&\n \tgit rev-parse origin/master &&\n \ttest_must_fail git rev-parse origin/fake-remote &&\n-\ttest_must_fail git rev-parse sometag\n+\tgit rev-parse sometag\n '\n \n-test_expect_success 'fetch --prune --tags with branch does not delete other remote-tracking branches' '\n+test_expect_success 'fetch --prune --tags with branch does not prune other things' '\n \tcd \"$D\" &&\n \tgit clone . prune-tags-branch &&\n \tcd prune-tags-branch &&\n@@ -137,7 +137,7 @@ test_expect_success 'fetch --prune --tags with branch does not delete other remo\n \n \tgit fetch --prune --tags origin master &&\n \tgit rev-parse origin/extrabranch &&\n-\ttest_must_fail git rev-parse sometag\n+\tgit rev-parse sometag\n '\n \n test_expect_success 'fetch --prune --tags with refspec prunes based on refspec' '\n@@ -151,7 +151,7 @@ test_expect_success 'fetch --prune --tags with refspec prunes based on refspec'\n \tgit fetch --prune --tags origin refs/heads/foo/*:refs/remotes/origin/foo/* &&\n \ttest_must_fail git rev-parse refs/remotes/origin/foo/otherbranch &&\n \tgit rev-parse origin/extrabranch &&\n-\ttest_must_fail git rev-parse sometag\n+\tgit rev-parse sometag\n '\n \n test_expect_success 'fetch tags when there is no tags' '\n-- \n1.8.4.1\n"},{"id":"229776","messageId":"1383111192-23780-13-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 12/23] query_refspecs(): move some constants out of the loop","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:33:01Z","receivedAt":"2013-10-30T05:33:01Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n remote.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex e42daa8..a5e6c7f 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -825,6 +825,8 @@ static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *q\n {\n \tint i;\n \tint find_src = !query->src;\n+\tconst char *needle = find_src ? query->dst : query->src;\n+\tchar **result = find_src ? &query->src : &query->dst;\n \n \tif (find_src && !query->dst)\n \t\treturn error(\"query_refspecs: need either src or dst\");\n@@ -833,8 +835,6 @@ static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *q\n \t\tstruct refspec *refspec = &refs[i];\n \t\tconst char *key = find_src ? refspec->dst : refspec->src;\n \t\tconst char *value = find_src ? refspec->src : refspec->dst;\n-\t\tconst char *needle = find_src ? query->dst : query->src;\n-\t\tchar **result = find_src ? &query->src : &query->dst;\n \n \t\tif (!refspec->dst)\n \t\t\tcontinue;\n-- \n1.8.4.1\n"},{"id":"229769","messageId":"1383111192-23780-14-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 13/23] builtin/remote.c: reorder function definitions","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:33:02Z","receivedAt":"2013-10-30T05:33:02Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"Reorder function definitions to remove the need for forward\ndeclarations.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n builtin/remote.c | 159 +++++++++++++++++++++++++++----------------------------\n 1 file changed, 78 insertions(+), 81 deletions(-)\n\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 4e14891..ecedd96 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -77,9 +77,6 @@ static const char * const builtin_remote_seturl_usage[] = {\n \n static int verbose;\n \n-static int show_all(void);\n-static int prune_remote(const char *remote, int dry_run);\n-\n static inline int postfixcmp(const char *string, const char *postfix)\n {\n \tint len1 = strlen(string), len2 = strlen(postfix);\n@@ -1084,6 +1081,64 @@ static int show_push_info_item(struct string_list_item *item, void *cb_data)\n \treturn 0;\n }\n \n+static int get_one_entry(struct remote *remote, void *priv)\n+{\n+\tstruct string_list *list = priv;\n+\tstruct strbuf url_buf = STRBUF_INIT;\n+\tconst char **url;\n+\tint i, url_nr;\n+\n+\tif (remote->url_nr > 0) {\n+\t\tstrbuf_addf(&url_buf, \"%s (fetch)\", remote->url[0]);\n+\t\tstring_list_append(list, remote->name)->util =\n+\t\t\t\tstrbuf_detach(&url_buf, NULL);\n+\t} else\n+\t\tstring_list_append(list, remote->name)->util = NULL;\n+\tif (remote->pushurl_nr) {\n+\t\turl = remote->pushurl;\n+\t\turl_nr = remote->pushurl_nr;\n+\t} else {\n+\t\turl = remote->url;\n+\t\turl_nr = remote->url_nr;\n+\t}\n+\tfor (i = 0; i < url_nr; i++)\n+\t{\n+\t\tstrbuf_addf(&url_buf, \"%s (push)\", url[i]);\n+\t\tstring_list_append(list, remote->name)->util =\n+\t\t\t\tstrbuf_detach(&url_buf, NULL);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int show_all(void)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_NODUP;\n+\tint result;\n+\n+\tlist.strdup_strings = 1;\n+\tresult = for_each_remote(get_one_entry, &list);\n+\n+\tif (!result) {\n+\t\tint i;\n+\n+\t\tsort_string_list(&list);\n+\t\tfor (i = 0; i < list.nr; i++) {\n+\t\t\tstruct string_list_item *item = list.items + i;\n+\t\t\tif (verbose)\n+\t\t\t\tprintf(\"%s\\t%s\\n\", item->string,\n+\t\t\t\t\titem->util ? (const char *)item->util : \"\");\n+\t\t\telse {\n+\t\t\t\tif (i && !strcmp((item - 1)->string, item->string))\n+\t\t\t\t\tcontinue;\n+\t\t\t\tprintf(\"%s\\n\", item->string);\n+\t\t\t}\n+\t\t}\n+\t}\n+\tstring_list_clear(&list, 1);\n+\treturn result;\n+}\n+\n static int show(int argc, const char **argv)\n {\n \tint no_query = 0, result = 0, query_flag = 0;\n@@ -1246,26 +1301,6 @@ static int set_head(int argc, const char **argv)\n \treturn result;\n }\n \n-static int prune(int argc, const char **argv)\n-{\n-\tint dry_run = 0, result = 0;\n-\tstruct option options[] = {\n-\t\tOPT__DRY_RUN(&dry_run, N_(\"dry run\")),\n-\t\tOPT_END()\n-\t};\n-\n-\targc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage,\n-\t\t\t     0);\n-\n-\tif (argc < 1)\n-\t\tusage_with_options(builtin_remote_prune_usage, options);\n-\n-\tfor (; argc; argc--, argv++)\n-\t\tresult |= prune_remote(*argv, dry_run);\n-\n-\treturn result;\n-}\n-\n static int prune_remote(const char *remote, int dry_run)\n {\n \tint result = 0, i;\n@@ -1304,6 +1339,26 @@ static int prune_remote(const char *remote, int dry_run)\n \treturn result;\n }\n \n+static int prune(int argc, const char **argv)\n+{\n+\tint dry_run = 0, result = 0;\n+\tstruct option options[] = {\n+\t\tOPT__DRY_RUN(&dry_run, N_(\"dry run\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage,\n+\t\t\t     0);\n+\n+\tif (argc < 1)\n+\t\tusage_with_options(builtin_remote_prune_usage, options);\n+\n+\tfor (; argc; argc--, argv++)\n+\t\tresult |= prune_remote(*argv, dry_run);\n+\n+\treturn result;\n+}\n+\n static int get_remote_default(const char *key, const char *value, void *priv)\n {\n \tif (strcmp(key, \"remotes.default\") == 0) {\n@@ -1505,64 +1560,6 @@ static int set_url(int argc, const char **argv)\n \treturn 0;\n }\n \n-static int get_one_entry(struct remote *remote, void *priv)\n-{\n-\tstruct string_list *list = priv;\n-\tstruct strbuf url_buf = STRBUF_INIT;\n-\tconst char **url;\n-\tint i, url_nr;\n-\n-\tif (remote->url_nr > 0) {\n-\t\tstrbuf_addf(&url_buf, \"%s (fetch)\", remote->url[0]);\n-\t\tstring_list_append(list, remote->name)->util =\n-\t\t\t\tstrbuf_detach(&url_buf, NULL);\n-\t} else\n-\t\tstring_list_append(list, remote->name)->util = NULL;\n-\tif (remote->pushurl_nr) {\n-\t\turl = remote->pushurl;\n-\t\turl_nr = remote->pushurl_nr;\n-\t} else {\n-\t\turl = remote->url;\n-\t\turl_nr = remote->url_nr;\n-\t}\n-\tfor (i = 0; i < url_nr; i++)\n-\t{\n-\t\tstrbuf_addf(&url_buf, \"%s (push)\", url[i]);\n-\t\tstring_list_append(list, remote->name)->util =\n-\t\t\t\tstrbuf_detach(&url_buf, NULL);\n-\t}\n-\n-\treturn 0;\n-}\n-\n-static int show_all(void)\n-{\n-\tstruct string_list list = STRING_LIST_INIT_NODUP;\n-\tint result;\n-\n-\tlist.strdup_strings = 1;\n-\tresult = for_each_remote(get_one_entry, &list);\n-\n-\tif (!result) {\n-\t\tint i;\n-\n-\t\tsort_string_list(&list);\n-\t\tfor (i = 0; i < list.nr; i++) {\n-\t\t\tstruct string_list_item *item = list.items + i;\n-\t\t\tif (verbose)\n-\t\t\t\tprintf(\"%s\\t%s\\n\", item->string,\n-\t\t\t\t\titem->util ? (const char *)item->util : \"\");\n-\t\t\telse {\n-\t\t\t\tif (i && !strcmp((item - 1)->string, item->string))\n-\t\t\t\t\tcontinue;\n-\t\t\t\tprintf(\"%s\\n\", item->string);\n-\t\t\t}\n-\t\t}\n-\t}\n-\tstring_list_clear(&list, 1);\n-\treturn result;\n-}\n-\n int cmd_remote(int argc, const char **argv, const char *prefix)\n {\n \tstruct option options[] = {\n-- \n1.8.4.1\n"},{"id":"229773","messageId":"1383111192-23780-15-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 14/23] builtin/remote.c:update(): use struct argv_array","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:33:03Z","receivedAt":"2013-10-30T05:33:03Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"Use struct argv_array for calling the \"git fetch\" subprocesses.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n builtin/remote.c | 33 +++++++++++++++++----------------\n 1 file changed, 17 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex ecedd96..bffe2f9 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -6,6 +6,7 @@\n #include \"strbuf.h\"\n #include \"run-command.h\"\n #include \"refs.h\"\n+#include \"argv-array.h\"\n \n static const char * const builtin_remote_usage[] = {\n \tN_(\"git remote [-v | --verbose]\"),\n@@ -1376,36 +1377,36 @@ static int update(int argc, const char **argv)\n \t\t\t N_(\"prune remotes after fetching\")),\n \t\tOPT_END()\n \t};\n-\tconst char **fetch_argv;\n-\tint fetch_argc = 0;\n+\tstruct argv_array fetch_argv = ARGV_ARRAY_INIT;\n \tint default_defined = 0;\n-\n-\tfetch_argv = xmalloc(sizeof(char *) * (argc+5));\n+\tint retval;\n \n \targc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage,\n \t\t\t     PARSE_OPT_KEEP_ARGV0);\n \n-\tfetch_argv[fetch_argc++] = \"fetch\";\n+\targv_array_push(&fetch_argv, \"fetch\");\n \n \tif (prune)\n-\t\tfetch_argv[fetch_argc++] = \"--prune\";\n+\t\targv_array_push(&fetch_argv, \"--prune\");\n \tif (verbose)\n-\t\tfetch_argv[fetch_argc++] = \"-v\";\n-\tfetch_argv[fetch_argc++] = \"--multiple\";\n+\t\targv_array_push(&fetch_argv, \"-v\");\n+\targv_array_push(&fetch_argv, \"--multiple\");\n \tif (argc < 2)\n-\t\tfetch_argv[fetch_argc++] = \"default\";\n+\t\targv_array_push(&fetch_argv, \"default\");\n \tfor (i = 1; i < argc; i++)\n-\t\tfetch_argv[fetch_argc++] = argv[i];\n+\t\targv_array_push(&fetch_argv, argv[i]);\n \n-\tif (strcmp(fetch_argv[fetch_argc-1], \"default\") == 0) {\n+\tif (strcmp(fetch_argv.argv[fetch_argv.argc-1], \"default\") == 0) {\n \t\tgit_config(get_remote_default, &default_defined);\n-\t\tif (!default_defined)\n-\t\t\tfetch_argv[fetch_argc-1] = \"--all\";\n+\t\tif (!default_defined) {\n+\t\t\targv_array_pop(&fetch_argv);\n+\t\t\targv_array_push(&fetch_argv, \"--all\");\n+\t\t}\n \t}\n \n-\tfetch_argv[fetch_argc] = NULL;\n-\n-\treturn run_command_v_opt(fetch_argv, RUN_GIT_CMD);\n+\tretval = run_command_v_opt(fetch_argv.argv, RUN_GIT_CMD);\n+\targv_array_clear(&fetch_argv);\n+\treturn retval;\n }\n \n static int remove_all_fetch_refspecs(const char *remote, const char *key)\n-- \n1.8.4.1\n"},{"id":"229759","messageId":"1383111192-23780-16-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 15/23] fetch, remote: properly convey --no-prune options to subprocesses","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:33:04Z","receivedAt":"2013-10-30T05:33:04Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"If --no-prune is passed to one of the following commands:\n\n    git fetch --all\n    git fetch --multiple\n    git fetch --recurse-submodules\n    git remote update\n\nthen it must also be passed to the \"fetch\" subprocesses that those\ncommands use to do their work.  Otherwise there might be a fetch.prune\nor remote.<name>.prune configuration setting that causes pruning to\noccur, contrary to the user's express wish.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n builtin/fetch.c  | 4 ++--\n builtin/remote.c | 6 +++---\n 2 files changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 1514b90..5ddb9af 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -936,8 +936,8 @@ static void add_options_to_argv(struct argv_array *argv)\n {\n \tif (dry_run)\n \t\targv_array_push(argv, \"--dry-run\");\n-\tif (prune > 0)\n-\t\targv_array_push(argv, \"--prune\");\n+\tif (prune != -1)\n+\t\targv_array_push(argv, prune ? \"--prune\" : \"--no-prune\");\n \tif (update_head_ok)\n \t\targv_array_push(argv, \"--update-head-ok\");\n \tif (force)\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex bffe2f9..f532f35 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -1371,7 +1371,7 @@ static int get_remote_default(const char *key, const char *value, void *priv)\n \n static int update(int argc, const char **argv)\n {\n-\tint i, prune = 0;\n+\tint i, prune = -1;\n \tstruct option options[] = {\n \t\tOPT_BOOL('p', \"prune\", &prune,\n \t\t\t N_(\"prune remotes after fetching\")),\n@@ -1386,8 +1386,8 @@ static int update(int argc, const char **argv)\n \n \targv_array_push(&fetch_argv, \"fetch\");\n \n-\tif (prune)\n-\t\targv_array_push(&fetch_argv, \"--prune\");\n+\tif (prune != -1)\n+\t\targv_array_push(&fetch_argv, prune ? \"--prune\" : \"--no-prune\");\n \tif (verbose)\n \t\targv_array_push(&fetch_argv, \"-v\");\n \targv_array_push(&fetch_argv, \"--multiple\");\n-- \n1.8.4.1\n"},{"id":"229768","messageId":"1383111192-23780-17-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 16/23] fetch-options.txt: simplify ifdef/ifndef/endif usage","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:33:05Z","receivedAt":"2013-10-30T05:33:05Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n Documentation/fetch-options.txt | 5 +----\n 1 file changed, 1 insertion(+), 4 deletions(-)\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex 12b1d92..f0ef7d0 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -51,13 +51,10 @@ ifndef::git-pull[]\n \tsubject to pruning.\n endif::git-pull[]\n \n-ifdef::git-pull[]\n---no-tags::\n-endif::git-pull[]\n ifndef::git-pull[]\n -n::\n---no-tags::\n endif::git-pull[]\n+--no-tags::\n \tBy default, tags that point at objects that are downloaded\n \tfrom the remote repository are fetched and stored locally.\n \tThis option disables this automatic tag following. The default\n-- \n1.8.4.1\n"},{"id":"229766","messageId":"1383111192-23780-18-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 17/23] git-fetch.txt: improve description of tag auto-following","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:33:06Z","receivedAt":"2013-10-30T05:33:06Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"Make it clearer that tags are fetched independent of which branches\nwere fetched from the remote in any particular fetch.  (Tags are even\nfetched if they point at objects that are in the current repository\nbut not reachable, which is probably a bug.)\n\nPut less emphasis on the mechanism and more on the effect of tag\nauto-following.  Also mention the options and configuration settings\nthat can change the tag-fetching behavior.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n\nThe \"probable bug\" mentioned above has been reported to the mailing\nlist [1] but is not addressed in the current patch series.\n\n[1] http://article.gmane.org/gmane.comp.version-control.git/236829\n\n Documentation/git-fetch.txt | 14 +++++++-------\n 1 file changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex e08a028..1065713 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -24,13 +24,13 @@ The ref names and their object names of fetched refs are stored\n in `.git/FETCH_HEAD`.  This information is left for a later merge\n operation done by 'git merge'.\n \n-When <refspec> stores the fetched result in remote-tracking branches,\n-the tags that point at these branches are automatically\n-followed.  This is done by first fetching from the remote using\n-the given <refspec>s, and if the repository has objects that are\n-pointed by remote tags that it does not yet have, then fetch\n-those missing tags.  If the other end has tags that point at\n-branches you are not interested in, you will not get them.\n+By default, tags are auto-followed.  This means that when fetching\n+from a remote, any tags on the remote that point to objects that exist\n+in the local repository are fetched.  The effect is to fetch tags that\n+point at branches that you are interested in.  This default behavior\n+can be changed by using the --tags or --no-tags options, by\n+configuring remote.<name>.tagopt, or by using a refspec that fetches\n+tags explicitly.\n \n 'git fetch' can fetch from either a single named repository,\n or from several repositories at once if <group> is given and\n-- \n1.8.4.1\n"},{"id":"229772","messageId":"1383111192-23780-19-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 18/23] ref_remove_duplicates(): avoid redundant bisection","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:33:07Z","receivedAt":"2013-10-30T05:33:07Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"The old code called string_list_lookup(), and if that failed called\nstring_list_insert(), thus doing the bisection search through the\nstring list twice in the latter code path.\n\nInstead, just call string_list_insert() right away.  If an entry for\nthat peer reference name already existed, then its util pointer is\nalways non-NULL.\n\nOf course this doesn't change the fact that the repeated\nstring_list_insert() calls make the function scale like O(N^2) if the\ninput reference list is not already approximately sorted.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n remote.c | 12 ++++++------\n 1 file changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex a5e6c7f..15f3dc5 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -750,13 +750,15 @@ void ref_remove_duplicates(struct ref *ref_map)\n \tstruct string_list refs = STRING_LIST_INIT_NODUP;\n \tstruct string_list_item *item = NULL;\n \tstruct ref *prev = NULL, *next = NULL;\n+\n \tfor (; ref_map; prev = ref_map, ref_map = next) {\n \t\tnext = ref_map->next;\n \t\tif (!ref_map->peer_ref)\n \t\t\tcontinue;\n \n-\t\titem = string_list_lookup(&refs, ref_map->peer_ref->name);\n-\t\tif (item) {\n+\t\titem = string_list_insert(&refs, ref_map->peer_ref->name);\n+\t\tif (item->util) {\n+\t\t\t/* Entry already existed */\n \t\t\tif (strcmp(((struct ref *)item->util)->name,\n \t\t\t\t   ref_map->name))\n \t\t\t\tdie(\"%s tracks both %s and %s\",\n@@ -767,11 +769,9 @@ void ref_remove_duplicates(struct ref *ref_map)\n \t\t\tfree(ref_map->peer_ref);\n \t\t\tfree(ref_map);\n \t\t\tref_map = prev; /* skip this; we freed it */\n-\t\t\tcontinue;\n+\t\t} else {\n+\t\t\titem->util = ref_map;\n \t\t}\n-\n-\t\titem = string_list_insert(&refs, ref_map->peer_ref->name);\n-\t\titem->util = ref_map;\n \t}\n \tstring_list_clear(&refs, 0);\n }\n-- \n1.8.4.1\n"},{"id":"229775","messageId":"1383111192-23780-20-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 19/23] t5536: new test of refspec conflicts when fetching","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:33:08Z","receivedAt":"2013-10-30T05:33:08Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"Add some tests that \"git fetch\" handles refspec conflicts (i.e., when\nthe same local reference should be updated from two different remote\nreferences) correctly.\n\nThere is a small bug when updating references opportunistically,\nnamely that an explicit user wish like\n\n    git fetch origin \\\n        refs/heads/branch1:refs/remotes/origin/branch2 \\\n        refs/heads/branch2:refs/remotes/origin/branch1\n\nshould override a configured refspec like\n\n    +refs/heads/*:refs/remotes/origin/*\n\nThe current code incorrectly treats this as a fatal error.\n\nIn a few commits we will improve the error messages for refspec\nconflicts in general and also turn this buggy fatal error into a\nwarning.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n t/t5536-fetch-conflicts.sh | 96 ++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 96 insertions(+)\n create mode 100755 t/t5536-fetch-conflicts.sh\n\ndiff --git a/t/t5536-fetch-conflicts.sh b/t/t5536-fetch-conflicts.sh\nnew file mode 100755\nindex 0000000..679c53e\n--- /dev/null\n+++ b/t/t5536-fetch-conflicts.sh\n@@ -0,0 +1,96 @@\n+#!/bin/sh\n+\n+test_description='fetch handles conflicting refspecs correctly'\n+\n+. ./test-lib.sh\n+\n+D=$(pwd)\n+\n+setup_repository () {\n+\tgit init \"$1\" && (\n+\t\tcd \"$1\" &&\n+\t\tgit config remote.origin.url \"$D\" &&\n+\t\tshift &&\n+\t\tfor refspec in \"$@\"\n+\t\tdo\n+\t\t\tgit config --add remote.origin.fetch \"$refspec\"\n+\t\tdone\n+\t)\n+}\n+\n+verify_stderr () {\n+\tcat >expected &&\n+\t# We're not interested in the error\n+\t# \"fatal: The remote end hung up unexpectedly\":\n+\tgrep -v \"hung up\" <error >actual &&\n+\ttest_cmp expected actual\n+}\n+\n+test_expect_success 'setup' '\n+\tgit commit --allow-empty -m \"Initial\" &&\n+\tgit branch branch1 &&\n+\tgit tag tag1 &&\n+\tgit commit --allow-empty -m \"First\" &&\n+\tgit branch branch2 &&\n+\tgit tag tag2\n+'\n+\n+test_expect_success 'fetch with no conflict' '\n+\tsetup_repository ok \"+refs/heads/*:refs/remotes/origin/*\" && (\n+\t\tcd ok &&\n+\t\tgit fetch origin\n+\t)\n+'\n+\n+test_expect_success 'fetch conflict: config vs. config' '\n+\tsetup_repository ccc \\\n+\t\t\"+refs/heads/branch1:refs/remotes/origin/branch1\" \\\n+\t\t\"+refs/heads/branch2:refs/remotes/origin/branch1\" && (\n+\t\tcd ccc &&\n+\t\ttest_must_fail git fetch origin 2>error &&\n+\t\tverify_stderr <<-\\EOF\n+\t\tfatal: refs/remotes/origin/branch1 tracks both refs/heads/branch1 and refs/heads/branch2\n+\t\tEOF\n+\t)\n+'\n+\n+test_expect_success 'fetch duplicate: config vs. config' '\n+\tsetup_repository dcc \\\n+\t\t\"+refs/heads/*:refs/remotes/origin/*\" \\\n+\t\t\"+refs/heads/branch1:refs/remotes/origin/branch1\" && (\n+\t\tcd dcc &&\n+\t\tgit fetch origin\n+\t)\n+'\n+\n+test_expect_success 'fetch conflict: arg overrides config' '\n+\tsetup_repository aoc \\\n+\t\t\"+refs/heads/*:refs/remotes/origin/*\" && (\n+\t\tcd aoc &&\n+\t\tgit fetch origin refs/heads/branch2:refs/remotes/origin/branch1\n+\t)\n+'\n+\n+test_expect_success 'fetch conflict: arg vs. arg' '\n+\tsetup_repository caa && (\n+\t\tcd caa &&\n+\t\ttest_must_fail git fetch origin \\\n+\t\t\trefs/heads/*:refs/remotes/origin/* \\\n+\t\t\trefs/heads/branch2:refs/remotes/origin/branch1 2>error &&\n+\t\tverify_stderr <<-\\EOF\n+\t\tfatal: refs/remotes/origin/branch1 tracks both refs/heads/branch1 and refs/heads/branch2\n+\t\tEOF\n+\t)\n+'\n+\n+test_expect_failure 'fetch conflict: criss-cross args' '\n+\tsetup_repository xaa \\\n+\t\t\"+refs/heads/*:refs/remotes/origin/*\" && (\n+\t\tcd xaa &&\n+\t\tgit fetch origin \\\n+\t\t\trefs/heads/branch1:refs/remotes/origin/branch2 \\\n+\t\t\trefs/heads/branch2:refs/remotes/origin/branch1\n+\t)\n+'\n+\n+test_done\n-- \n1.8.4.1\n"},{"id":"229767","messageId":"1383111192-23780-21-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 20/23] ref_remove_duplicates(): simplify loop logic","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:33:09Z","receivedAt":"2013-10-30T05:33:09Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"Change the loop body into the more straightforward\n\n* remove item from the front of the old list\n* if necessary, add it to the tail of the new list\n\nand return a pointer to the new list (even though it is currently\nalways the same as the input argument, because the first element in\nthe list is currently never deleted).\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n builtin/fetch.c |  4 +---\n remote.c        | 52 +++++++++++++++++++++++++++++++---------------------\n remote.h        |  8 ++++++--\n 3 files changed, 38 insertions(+), 26 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 5ddb9af..3d978eb 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -360,9 +360,7 @@ static struct ref *get_ref_map(struct transport *transport,\n \t\ttail = &rm->next;\n \t}\n \n-\tref_remove_duplicates(ref_map);\n-\n-\treturn ref_map;\n+\treturn ref_remove_duplicates(ref_map);\n }\n \n #define STORE_REF_ERROR_OTHER 1\ndiff --git a/remote.c b/remote.c\nindex 15f3dc5..c90a2bf 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -745,35 +745,45 @@ int for_each_remote(each_remote_fn fn, void *priv)\n \treturn result;\n }\n \n-void ref_remove_duplicates(struct ref *ref_map)\n+struct ref *ref_remove_duplicates(struct ref *ref_map)\n {\n \tstruct string_list refs = STRING_LIST_INIT_NODUP;\n-\tstruct string_list_item *item = NULL;\n-\tstruct ref *prev = NULL, *next = NULL;\n+\tstruct ref *retval = NULL;\n+\tstruct ref **p = &retval;\n \n-\tfor (; ref_map; prev = ref_map, ref_map = next) {\n-\t\tnext = ref_map->next;\n-\t\tif (!ref_map->peer_ref)\n-\t\t\tcontinue;\n+\twhile (ref_map) {\n+\t\tstruct ref *ref = ref_map;\n+\n+\t\tref_map = ref_map->next;\n+\t\tref->next = NULL;\n \n-\t\titem = string_list_insert(&refs, ref_map->peer_ref->name);\n-\t\tif (item->util) {\n-\t\t\t/* Entry already existed */\n-\t\t\tif (strcmp(((struct ref *)item->util)->name,\n-\t\t\t\t   ref_map->name))\n-\t\t\t\tdie(\"%s tracks both %s and %s\",\n-\t\t\t\t    ref_map->peer_ref->name,\n-\t\t\t\t    ((struct ref *)item->util)->name,\n-\t\t\t\t    ref_map->name);\n-\t\t\tprev->next = ref_map->next;\n-\t\t\tfree(ref_map->peer_ref);\n-\t\t\tfree(ref_map);\n-\t\t\tref_map = prev; /* skip this; we freed it */\n+\t\tif (!ref->peer_ref) {\n+\t\t\t*p = ref;\n+\t\t\tp = &ref->next;\n \t\t} else {\n-\t\t\titem->util = ref_map;\n+\t\t\tstruct string_list_item *item =\n+\t\t\t\tstring_list_insert(&refs, ref->peer_ref->name);\n+\n+\t\t\tif (item->util) {\n+\t\t\t\t/* Entry already existed */\n+\t\t\t\tif (strcmp(((struct ref *)item->util)->name,\n+\t\t\t\t\t   ref->name))\n+\t\t\t\t\tdie(\"%s tracks both %s and %s\",\n+\t\t\t\t\t    ref->peer_ref->name,\n+\t\t\t\t\t    ((struct ref *)item->util)->name,\n+\t\t\t\t\t    ref->name);\n+\t\t\t\tfree(ref->peer_ref);\n+\t\t\t\tfree(ref);\n+\t\t\t} else {\n+\t\t\t\t*p = ref;\n+\t\t\t\tp = &ref->next;\n+\t\t\t\titem->util = ref;\n+\t\t\t}\n \t\t}\n \t}\n+\n \tstring_list_clear(&refs, 0);\n+\treturn retval;\n }\n \n int remote_has_url(struct remote *remote, const char *url)\ndiff --git a/remote.h b/remote.h\nindex 131130a..c07eb99 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -149,9 +149,13 @@ int resolve_remote_symref(struct ref *ref, struct ref *list);\n int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1);\n \n /*\n- * Removes and frees any duplicate refs in the map.\n+ * Remove and free all but the first of any entries in the input list\n+ * that map the same remote reference to the same local reference.  If\n+ * there are two entries that map different remote references to the\n+ * same local reference, emit an error message and die.  Return a\n+ * pointer to the head of the resulting list.\n  */\n-void ref_remove_duplicates(struct ref *ref_map);\n+struct ref *ref_remove_duplicates(struct ref *ref_map);\n \n int valid_fetch_refspec(const char *refspec);\n struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);\n-- \n1.8.4.1\n"},{"id":"229777","messageId":"1383111192-23780-22-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 21/23] ref_remote_duplicates(): extract a function handle_duplicate()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:33:10Z","receivedAt":"2013-10-30T05:33:10Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"It will become more complex in a moment.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n remote.c | 18 ++++++++++--------\n 1 file changed, 10 insertions(+), 8 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex c90a2bf..2deb2db 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -745,6 +745,15 @@ int for_each_remote(each_remote_fn fn, void *priv)\n \treturn result;\n }\n \n+static void handle_duplicate(struct ref *ref1, struct ref *ref2)\n+{\n+\tif (strcmp(ref1->name, ref2->name))\n+\t\tdie(\"%s tracks both %s and %s\",\n+\t\t    ref2->peer_ref->name, ref1->name, ref2->name);\n+\tfree(ref2->peer_ref);\n+\tfree(ref2);\n+}\n+\n struct ref *ref_remove_duplicates(struct ref *ref_map)\n {\n \tstruct string_list refs = STRING_LIST_INIT_NODUP;\n@@ -766,14 +775,7 @@ struct ref *ref_remove_duplicates(struct ref *ref_map)\n \n \t\t\tif (item->util) {\n \t\t\t\t/* Entry already existed */\n-\t\t\t\tif (strcmp(((struct ref *)item->util)->name,\n-\t\t\t\t\t   ref->name))\n-\t\t\t\t\tdie(\"%s tracks both %s and %s\",\n-\t\t\t\t\t    ref->peer_ref->name,\n-\t\t\t\t\t    ((struct ref *)item->util)->name,\n-\t\t\t\t\t    ref->name);\n-\t\t\t\tfree(ref->peer_ref);\n-\t\t\t\tfree(ref);\n+\t\t\t\thandle_duplicate((struct ref *)item->util, ref);\n \t\t\t} else {\n \t\t\t\t*p = ref;\n \t\t\t\tp = &ref->next;\n-- \n1.8.4.1\n"},{"id":"229771","messageId":"1383111192-23780-23-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 22/23] handle_duplicate(): mark error message for translation","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:33:11Z","receivedAt":"2013-10-30T05:33:11Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n remote.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/remote.c b/remote.c\nindex 2deb2db..06a0eda 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -748,7 +748,7 @@ int for_each_remote(each_remote_fn fn, void *priv)\n static void handle_duplicate(struct ref *ref1, struct ref *ref2)\n {\n \tif (strcmp(ref1->name, ref2->name))\n-\t\tdie(\"%s tracks both %s and %s\",\n+\t\tdie(_(\"%s tracks both %s and %s\"),\n \t\t    ref2->peer_ref->name, ref1->name, ref2->name);\n \tfree(ref2->peer_ref);\n \tfree(ref2);\n-- \n1.8.4.1\n"},{"id":"229774","messageId":"1383111192-23780-24-git-send-email-mhagger@alum.mit.edu","threadId":"35243","inReplyTo":"1383111192-23780-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 23/23] fetch: improve the error messages emitted for conflicting refspecs","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T05:33:12Z","receivedAt":"2013-10-30T05:33:12Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"If we find two refspecs that want to update the same local reference,\nemit an error message that is more informative based on whether one of\nthe conflicting refspecs is an opportunistic update during a fetch\nwith explicit command-line refspecs.  And especially, do not die if an\nopportunistic reference update conflicts with an express wish of the\nuser; rather, just emit a warning and skip the opportunistic reference\nupdate.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n remote.c                   | 25 ++++++++++++++++++++++---\n t/t5536-fetch-conflicts.sh | 14 +++++++++-----\n 2 files changed, 31 insertions(+), 8 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 06a0eda..15e6e5e 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -747,9 +747,28 @@ int for_each_remote(each_remote_fn fn, void *priv)\n \n static void handle_duplicate(struct ref *ref1, struct ref *ref2)\n {\n-\tif (strcmp(ref1->name, ref2->name))\n-\t\tdie(_(\"%s tracks both %s and %s\"),\n-\t\t    ref2->peer_ref->name, ref1->name, ref2->name);\n+\tif (strcmp(ref1->name, ref2->name)) {\n+\t\tif (ref1->fetch_head_status != FETCH_HEAD_IGNORE &&\n+\t\t    ref2->fetch_head_status != FETCH_HEAD_IGNORE) {\n+\t\t\tdie(_(\"Cannot fetch both %s and %s to %s\"),\n+\t\t\t    ref1->name, ref2->name, ref2->peer_ref->name);\n+\t\t} else if (ref1->fetch_head_status != FETCH_HEAD_IGNORE &&\n+\t\t\t   ref2->fetch_head_status == FETCH_HEAD_IGNORE) {\n+\t\t\twarning(_(\"%s usually tracks %s, not %s\"),\n+\t\t\t\tref2->peer_ref->name, ref2->name, ref1->name);\n+\t\t} else if (ref1->fetch_head_status == FETCH_HEAD_IGNORE &&\n+\t\t\t   ref2->fetch_head_status == FETCH_HEAD_IGNORE) {\n+\t\t\tdie(_(\"%s tracks both %s and %s\"),\n+\t\t\t    ref2->peer_ref->name, ref1->name, ref2->name);\n+\t\t} else {\n+\t\t\t/*\n+\t\t\t * This last possibility doesn't occur because\n+\t\t\t * FETCH_HEAD_IGNORE entries always appear at\n+\t\t\t * the end of the list.\n+\t\t\t */\n+\t\t\tdie(_(\"Internal error\"));\n+\t\t}\n+\t}\n \tfree(ref2->peer_ref);\n \tfree(ref2);\n }\ndiff --git a/t/t5536-fetch-conflicts.sh b/t/t5536-fetch-conflicts.sh\nindex 679c53e..6c5d3a4 100755\n--- a/t/t5536-fetch-conflicts.sh\n+++ b/t/t5536-fetch-conflicts.sh\n@@ -22,7 +22,7 @@ verify_stderr () {\n \tcat >expected &&\n \t# We're not interested in the error\n \t# \"fatal: The remote end hung up unexpectedly\":\n-\tgrep -v \"hung up\" <error >actual &&\n+\tgrep -E '^(fatal|warning):' <error | grep -v 'hung up' >actual | sort &&\n \ttest_cmp expected actual\n }\n \n@@ -49,7 +49,7 @@ test_expect_success 'fetch conflict: config vs. config' '\n \t\tcd ccc &&\n \t\ttest_must_fail git fetch origin 2>error &&\n \t\tverify_stderr <<-\\EOF\n-\t\tfatal: refs/remotes/origin/branch1 tracks both refs/heads/branch1 and refs/heads/branch2\n+\t\tfatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1\n \t\tEOF\n \t)\n '\n@@ -78,18 +78,22 @@ test_expect_success 'fetch conflict: arg vs. arg' '\n \t\t\trefs/heads/*:refs/remotes/origin/* \\\n \t\t\trefs/heads/branch2:refs/remotes/origin/branch1 2>error &&\n \t\tverify_stderr <<-\\EOF\n-\t\tfatal: refs/remotes/origin/branch1 tracks both refs/heads/branch1 and refs/heads/branch2\n+\t\tfatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1\n \t\tEOF\n \t)\n '\n \n-test_expect_failure 'fetch conflict: criss-cross args' '\n+test_expect_success 'fetch conflict: criss-cross args' '\n \tsetup_repository xaa \\\n \t\t\"+refs/heads/*:refs/remotes/origin/*\" && (\n \t\tcd xaa &&\n \t\tgit fetch origin \\\n \t\t\trefs/heads/branch1:refs/remotes/origin/branch2 \\\n-\t\t\trefs/heads/branch2:refs/remotes/origin/branch1\n+\t\t\trefs/heads/branch2:refs/remotes/origin/branch1 2>error &&\n+\t\tverify_stderr <<-\\EOF\n+\t\twarning: refs/remotes/origin/branch1 usually tracks refs/heads/branch1, not refs/heads/branch2\n+\t\twarning: refs/remotes/origin/branch2 usually tracks refs/heads/branch2, not refs/heads/branch1\n+\t\tEOF\n \t)\n '\n \n-- \n1.8.4.1\n"},{"id":"229807","messageId":"CALkWK0=tcEbdvFaaV1YFBTXWAHaHYcJ1YNLpj0Sj5sGK_inapQ@mail.gmail.com","threadId":"35243","inReplyTo":"1383111192-23780-3-git-send-email-mhagger@alum.mit.edu","subject":"Re: [PATCH v2 02/23] t5510: prepare test refs more straightforwardly","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-10-30T10:57:28Z","receivedAt":"2013-10-30T10:57:28Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Michael Haggerty wrote:\n> \"git fetch\" was being used with contrived refspecs to create tags and\n> remote-tracking branches in test repositories in preparation for the\n> actual tests.  This is obscure and also makes one wonder whether this\n> is indeed just preparation or whether some side-effect of \"git fetch\"\n> is being tested.\n\nAs the test titles indicate, we are exercising the 'fetch --prune'\nfunctionality. However, I don't see the 'git fetch <remote>\n<src>:<dst>' form exercised anywhere else in the file.\n"},{"id":"229809","messageId":"CALkWK0m-qNcCe_Zmn6PeN91JEM-7Ki0F0b0Fp+1bhQTWdmR7EA@mail.gmail.com","threadId":"35243","inReplyTo":"1383111192-23780-13-git-send-email-mhagger@alum.mit.edu","subject":"Re: [PATCH v2 12/23] query_refspecs(): move some constants out of the loop","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-10-30T11:05:03Z","receivedAt":"2013-10-30T11:05:03Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Michael Haggerty wrote:\n> diff --git a/remote.c b/remote.c\n> index e42daa8..a5e6c7f 100644\n> --- a/remote.c\n> +++ b/remote.c\n> @@ -825,6 +825,8 @@ static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *q\n>  {\n>         int i;\n>         int find_src = !query->src;\n> +       const char *needle = find_src ? query->dst : query->src;\n> +       char **result = find_src ? &query->src : &query->dst;\n>\n>         if (find_src && !query->dst)\n>                 return error(\"query_refspecs: need either src or dst\");\n> @@ -833,8 +835,6 @@ static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *q\n>                 struct refspec *refspec = &refs[i];\n>                 const char *key = find_src ? refspec->dst : refspec->src;\n>                 const char *value = find_src ? refspec->src : refspec->dst;\n> -               const char *needle = find_src ? query->dst : query->src;\n> -               char **result = find_src ? &query->src : &query->dst;\n>\n>                 if (!refspec->dst)\n>                         continue;\n\nWhy? Is it used in some later patch?\n"},{"id":"229837","messageId":"52714268.5010608@alum.mit.edu","threadId":"35243","inReplyTo":"CALkWK0m-qNcCe_Zmn6PeN91JEM-7Ki0F0b0Fp+1bhQTWdmR7EA@mail.gmail.com","subject":"Re: [PATCH v2 12/23] query_refspecs(): move some constants out of the loop","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T17:31:20Z","receivedAt":"2013-10-30T17:31:20Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 10/30/2013 12:05 PM, Ramkumar Ramachandra wrote:\n> Michael Haggerty wrote:\n>> diff --git a/remote.c b/remote.c\n>> index e42daa8..a5e6c7f 100644\n>> --- a/remote.c\n>> +++ b/remote.c\n>> @@ -825,6 +825,8 @@ static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *q\n>>  {\n>>         int i;\n>>         int find_src = !query->src;\n>> +       const char *needle = find_src ? query->dst : query->src;\n>> +       char **result = find_src ? &query->src : &query->dst;\n>>\n>>         if (find_src && !query->dst)\n>>                 return error(\"query_refspecs: need either src or dst\");\n>> @@ -833,8 +835,6 @@ static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *q\n>>                 struct refspec *refspec = &refs[i];\n>>                 const char *key = find_src ? refspec->dst : refspec->src;\n>>                 const char *value = find_src ? refspec->src : refspec->dst;\n>> -               const char *needle = find_src ? query->dst : query->src;\n>> -               char **result = find_src ? &query->src : &query->dst;\n>>\n>>                 if (!refspec->dst)\n>>                         continue;\n> \n> Why? Is it used in some later patch?\n\nIt's just improving the code hygiene in a function that I was reading to\nunderstand the other code affected by the patches.  It's unrelated to\nthe other patches.\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"229842","messageId":"527144D2.1090702@alum.mit.edu","threadId":"35243","inReplyTo":"CALkWK0=tcEbdvFaaV1YFBTXWAHaHYcJ1YNLpj0Sj5sGK_inapQ@mail.gmail.com","subject":"Re: [PATCH v2 02/23] t5510: prepare test refs more straightforwardly","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-10-30T17:41:38Z","receivedAt":"2013-10-30T17:41:38Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 10/30/2013 11:57 AM, Ramkumar Ramachandra wrote:\n> Michael Haggerty wrote:\n>> \"git fetch\" was being used with contrived refspecs to create tags and\n>> remote-tracking branches in test repositories in preparation for the\n>> actual tests.  This is obscure and also makes one wonder whether this\n>> is indeed just preparation or whether some side-effect of \"git fetch\"\n>> is being tested.\n> \n> As the test titles indicate, we are exercising the 'fetch --prune'\n> functionality. However, I don't see the 'git fetch <remote>\n> <src>:<dst>' form exercised anywhere else in the file.\n\nI see a couple of examples:\n\n$ grep 'git fetch.*:' t/t5510*.sh\n\tgit fetch --prune origin refs/heads/a/*:refs/remotes/origin/a/* &&\n\tgit fetch --prune --tags origin\nrefs/heads/foo/*:refs/remotes/origin/foo/* &&\n\tgit fetch .. :track &&\n\ttest_must_fail git fetch .. anno:five\n\tgit fetch .. six:six\n\ttest_must_fail git fetch \"$D/bundle1\" master:master\n\tgit fetch ../bundle2 master:master &&\n\t git fetch \"rsync:$(pwd)/../.git\" master:refs/heads/master &&\n\ttest_must_fail git fetch . side:master\n\tgit fetch --update-head-ok . side:master\n\nThe tests in question didn't really check the results of the fetch\nanyway.  I don't think they were intended to test fetch but only to set\nup the initial conditions for the real test.\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"}]}