From f377e7a37c1b28359a228cf5bb43161a8a22b385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 27 Feb 2014 10:00:09 +0100 Subject: [PATCH 1/2] fetch: add a failing test for prunning with overlapping refspecs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a remote has multiple fetch refspecs and these overlap in the target namespace, fetch may prune a remote-tracking branch which still exists in the remote. The test uses a popular form of this, by putting pull requests as stored in a popular hosting platform alongside "real" remote-tracking branches. The fetch command makes a decision of whether to prune based on the first matching refspec, which in this case is insufficient, as it covers the pull request names. This pair of refspecs does work as expected if the more "specific" refspec is the first in the list. Signed-off-by: Carlos Martín Nieto Signed-off-by: Junio C Hamano --- t/t5510-fetch.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 07986d94bd..473e855c91 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -113,6 +113,26 @@ test_expect_success 'fetch --prune with a namespace keeps other namespaces' ' git rev-parse origin/master ' +test_expect_failure 'fetch --prune handles overlapping refspecs' ' + cd "$D" && + git update-ref refs/pull/42/head master && + git clone . prune-overlapping && + cd prune-overlapping && + git config --add remote.origin.fetch refs/pull/*/head:refs/remotes/origin/pr/* && + + git fetch --prune origin && + git rev-parse origin/master && + git rev-parse origin/pr/42 && + + git config --unset-all remote.origin.fetch + git config remote.origin.fetch refs/pull/*/head:refs/remotes/origin/pr/* && + git config --add remote.origin.fetch refs/heads/*:refs/remotes/origin/* && + + git fetch --prune origin && + git rev-parse origin/master && + git rev-parse origin/pr/42 +' + test_expect_success 'fetch --prune --tags does not delete the remote-tracking branches' ' cd "$D" && git clone . prune-tags && From e6f637122ecd60c59df27b8188b6ddac39ac0063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 27 Feb 2014 10:00:10 +0100 Subject: [PATCH 2/2] fetch: handle overlaping refspecs on --prune MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to consider that a remote-tracking branch may match more than one rhs of a fetch refspec. In such a case, it is not enough to stop at the first match but look at all of the matches in order to determine whether a head is stale. To this goal, introduce a variant of query_refspecs which returns all of the matching refspecs and loop over those answers to check for staleness. Signed-off-by: Carlos Martín Nieto Signed-off-by: Junio C Hamano --- remote.c | 50 ++++++++++++++++++++++++++++++++++++++++++------ t/t5510-fetch.sh | 2 +- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/remote.c b/remote.c index 9f1a8aa2c4..fde7b52f93 100644 --- a/remote.c +++ b/remote.c @@ -821,6 +821,32 @@ static int match_name_with_pattern(const char *key, const char *name, return ret; } +static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct refspec *query, struct string_list *results) +{ + int i; + int find_src = !query->src; + + if (find_src && !query->dst) + error("query_refspecs_multiple: need either src or dst"); + + for (i = 0; i < ref_count; i++) { + struct refspec *refspec = &refs[i]; + const char *key = find_src ? refspec->dst : refspec->src; + const char *value = find_src ? refspec->src : refspec->dst; + const char *needle = find_src ? query->dst : query->src; + char **result = find_src ? &query->src : &query->dst; + + if (!refspec->dst) + continue; + if (refspec->pattern) { + if (match_name_with_pattern(key, needle, value, result)) + string_list_append_nodup(results, *result); + } else if (!strcmp(needle, key)) { + string_list_append(results, value); + } + } +} + static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query) { int i; @@ -1954,25 +1980,37 @@ static int get_stale_heads_cb(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { struct stale_heads_info *info = cb_data; + struct string_list matches = STRING_LIST_INIT_DUP; struct refspec query; + int i, stale = 1; memset(&query, 0, sizeof(struct refspec)); query.dst = (char *)refname; - if (query_refspecs(info->refs, info->ref_count, &query)) - return 0; /* No matches */ + query_refspecs_multiple(info->refs, info->ref_count, &query, &matches); + if (matches.nr == 0) + goto clean_exit; /* No matches */ /* * If we did find a suitable refspec and it's not a symref and * it's not in the list of refs that currently exist in that - * remote we consider it to be stale. + * remote, we consider it to be stale. In order to deal with + * overlapping refspecs, we need to go over all of the + * matching refs. */ - if (!((flags & REF_ISSYMREF) || - string_list_has_string(info->ref_names, query.src))) { + if (flags & REF_ISSYMREF) + goto clean_exit; + + for (i = 0; stale && i < matches.nr; i++) + if (string_list_has_string(info->ref_names, matches.items[i].string)) + stale = 0; + + if (stale) { struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail); hashcpy(ref->new_sha1, sha1); } - free(query.src); +clean_exit: + string_list_clear(&matches, 0); return 0; } diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 473e855c91..06161280b5 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -113,7 +113,7 @@ test_expect_success 'fetch --prune with a namespace keeps other namespaces' ' git rev-parse origin/master ' -test_expect_failure 'fetch --prune handles overlapping refspecs' ' +test_expect_success 'fetch --prune handles overlapping refspecs' ' cd "$D" && git update-ref refs/pull/42/head master && git clone . prune-overlapping &&