diff options
-rw-r--r-- | builtin-remote.c | 92 | ||||
-rwxr-xr-x | t/t5505-remote.sh | 18 |
2 files changed, 74 insertions, 36 deletions
diff --git a/builtin-remote.c b/builtin-remote.c index 99a34dfe8..c49f00f58 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -206,7 +206,6 @@ static void read_branches(void) struct ref_states { struct remote *remote; - struct strbuf remote_prefix; struct path_list new, stale, tracked; }; @@ -262,35 +261,71 @@ static int get_ref_states(const struct ref *ref, struct ref_states *states) } free_refs(fetch_map); - strbuf_addf(&states->remote_prefix, - "refs/remotes/%s/", states->remote->name); for_each_ref(handle_one_branch, states); sort_path_list(&states->stale); return 0; } +struct known_remote { + struct known_remote *next; + struct remote *remote; +}; + +struct known_remotes { + struct remote *to_delete; + struct known_remote *list; +}; + +static int add_known_remote(struct remote *remote, void *cb_data) +{ + struct known_remotes *all = cb_data; + struct known_remote *r; + + if (!strcmp(all->to_delete->name, remote->name)) + return 0; + + r = xmalloc(sizeof(*r)); + r->remote = remote; + r->next = all->list; + all->list = r; + return 0; +} + struct branches_for_remote { - const char *prefix; + struct remote *remote; struct path_list *branches; + struct known_remotes *keep; }; static int add_branch_for_removal(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { struct branches_for_remote *branches = cb_data; + struct refspec refspec; + struct path_list_item *item; + struct known_remote *kr; - if (!prefixcmp(refname, branches->prefix)) { - struct path_list_item *item; + memset(&refspec, 0, sizeof(refspec)); + refspec.dst = (char *)refname; + if (remote_find_tracking(branches->remote, &refspec)) + return 0; + + /* don't delete a branch if another remote also uses it */ + for (kr = branches->keep->list; kr; kr = kr->next) { + memset(&refspec, 0, sizeof(refspec)); + refspec.dst = (char *)refname; + if (!remote_find_tracking(kr->remote, &refspec)) + return 0; + } - /* make sure that symrefs are deleted */ - if (flags & REF_ISSYMREF) - return unlink(git_path(refname)); + /* make sure that symrefs are deleted */ + if (flags & REF_ISSYMREF) + return unlink(git_path(refname)); - item = path_list_append(refname, branches->branches); - item->util = xmalloc(20); - hashcpy(item->util, sha1); - } + item = path_list_append(refname, branches->branches); + item->util = xmalloc(20); + hashcpy(item->util, sha1); return 0; } @@ -316,8 +351,9 @@ static int rm(int argc, const char **argv) }; struct remote *remote; struct strbuf buf; + struct known_remotes known_remotes = { NULL, NULL }; struct path_list branches = { NULL, 0, 0, 1 }; - struct branches_for_remote cb_data = { NULL, &branches }; + struct branches_for_remote cb_data = { NULL, &branches, &known_remotes }; int i; if (argc != 2) @@ -327,6 +363,9 @@ static int rm(int argc, const char **argv) if (!remote) die("No such remote: %s", argv[1]); + known_remotes.to_delete = remote; + for_each_remote(add_known_remote, &known_remotes); + strbuf_init(&buf, 0); strbuf_addf(&buf, "remote.%s", remote->name); if (git_config_rename_section(buf.buf, NULL) < 1) @@ -355,9 +394,7 @@ static int rm(int argc, const char **argv) * the branches one by one, since for_each_ref() relies on cached * refs, which are invalidated when deleting a branch. */ - strbuf_reset(&buf); - strbuf_addf(&buf, "refs/remotes/%s/", remote->name); - cb_data.prefix = buf.buf; + cb_data.remote = remote; i = for_each_ref(add_branch_for_removal, &cb_data); strbuf_release(&buf); @@ -422,27 +459,10 @@ static int show_or_prune(int argc, const char **argv, int prune) states.remote->name); if (prune) { - struct strbuf buf; - int prefix_len; - - strbuf_init(&buf, 0); - if (states.remote->fetch_refspec_nr == 1 && - states.remote->fetch->pattern && - !strcmp(states.remote->fetch->src, - states.remote->fetch->dst)) - /* handle --mirror remote */ - strbuf_addstr(&buf, "refs/heads/"); - else - strbuf_addf(&buf, "refs/remotes/%s/", *argv); - prefix_len = buf.len; - for (i = 0; i < states.stale.nr; i++) { - strbuf_setlen(&buf, prefix_len); - strbuf_addstr(&buf, states.stale.items[i].path); - result |= delete_ref(buf.buf, NULL); + const char *refname = states.stale.items[i].util; + result |= delete_ref(refname, NULL); } - - strbuf_release(&buf); goto cleanup_states; } diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index a37b6f521..0d7ed1f99 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -164,6 +164,24 @@ test_expect_success 'add --mirror && prune' ' git rev-parse --verify refs/heads/side) ' +test_expect_success 'add alt && prune' ' + (mkdir alttst && + cd alttst && + git init && + git remote add -f origin ../one && + git config remote.alt.url ../one && + git config remote.alt.fetch "+refs/heads/*:refs/remotes/origin/*") && + (cd one && + git branch -m side side2) && + (cd alttst && + git rev-parse --verify refs/remotes/origin/side && + ! git rev-parse --verify refs/remotes/origin/side2 && + git fetch alt && + git remote prune alt && + ! git rev-parse --verify refs/remotes/origin/side && + git rev-parse --verify refs/remotes/origin/side2) +' + cat > one/expect << EOF apis/master apis/side |