Skip to content

Commit 7ad2458

Browse files
spearcegitster
authored andcommitted
Make "git-remote rm" delete refs acccording to fetch specs
A remote may be configured to fetch into tracking branches that don't match its name. A user may have created a remote by hand that will fetch to a different tracking branch namespace: [remote "alt"] url = git://repo.or.cz/alt-git.git fetch = refs/heads/*:refs/remotes/origin/* When deleting remote alt we should clean up the refs whose names start with "refs/remotes/origin/", even though the remote itself was named alt by the user. To avoid deleting refs used by another remote we only clear refs that are unique to this remote. This prevents `git prune rm alt` from removing the refs used by say origin if alt was just using a different URL for the same repository. Signed-off-by: Shawn O. Pearce <spearce@spearce.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent c175a7a commit 7ad2458

File tree

1 file changed

+54
-14
lines changed

1 file changed

+54
-14
lines changed

builtin-remote.c

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -267,27 +267,65 @@ static int get_ref_states(const struct ref *ref, struct ref_states *states)
267267
return 0;
268268
}
269269

270+
struct known_remote {
271+
struct known_remote *next;
272+
struct remote *remote;
273+
};
274+
275+
struct known_remotes {
276+
struct remote *to_delete;
277+
struct known_remote *list;
278+
};
279+
280+
static int add_known_remote(struct remote *remote, void *cb_data)
281+
{
282+
struct known_remotes *all = cb_data;
283+
struct known_remote *r;
284+
285+
if (!strcmp(all->to_delete->name, remote->name))
286+
return 0;
287+
288+
r = xmalloc(sizeof(*r));
289+
r->remote = remote;
290+
r->next = all->list;
291+
all->list = r;
292+
return 0;
293+
}
294+
270295
struct branches_for_remote {
271-
const char *prefix;
296+
struct remote *remote;
272297
struct path_list *branches;
298+
struct known_remotes *keep;
273299
};
274300

275301
static int add_branch_for_removal(const char *refname,
276302
const unsigned char *sha1, int flags, void *cb_data)
277303
{
278304
struct branches_for_remote *branches = cb_data;
305+
struct refspec refspec;
306+
struct path_list_item *item;
307+
struct known_remote *kr;
279308

280-
if (!prefixcmp(refname, branches->prefix)) {
281-
struct path_list_item *item;
309+
memset(&refspec, 0, sizeof(refspec));
310+
refspec.dst = (char *)refname;
311+
if (remote_find_tracking(branches->remote, &refspec))
312+
return 0;
313+
314+
/* don't delete a branch if another remote also uses it */
315+
for (kr = branches->keep->list; kr; kr = kr->next) {
316+
memset(&refspec, 0, sizeof(refspec));
317+
refspec.dst = (char *)refname;
318+
if (!remote_find_tracking(kr->remote, &refspec))
319+
return 0;
320+
}
282321

283-
/* make sure that symrefs are deleted */
284-
if (flags & REF_ISSYMREF)
285-
return unlink(git_path(refname));
322+
/* make sure that symrefs are deleted */
323+
if (flags & REF_ISSYMREF)
324+
return unlink(git_path(refname));
286325

287-
item = path_list_append(refname, branches->branches);
288-
item->util = xmalloc(20);
289-
hashcpy(item->util, sha1);
290-
}
326+
item = path_list_append(refname, branches->branches);
327+
item->util = xmalloc(20);
328+
hashcpy(item->util, sha1);
291329

292330
return 0;
293331
}
@@ -313,8 +351,9 @@ static int rm(int argc, const char **argv)
313351
};
314352
struct remote *remote;
315353
struct strbuf buf;
354+
struct known_remotes known_remotes = { NULL, NULL };
316355
struct path_list branches = { NULL, 0, 0, 1 };
317-
struct branches_for_remote cb_data = { NULL, &branches };
356+
struct branches_for_remote cb_data = { NULL, &branches, &known_remotes };
318357
int i;
319358

320359
if (argc != 2)
@@ -324,6 +363,9 @@ static int rm(int argc, const char **argv)
324363
if (!remote)
325364
die("No such remote: %s", argv[1]);
326365

366+
known_remotes.to_delete = remote;
367+
for_each_remote(add_known_remote, &known_remotes);
368+
327369
strbuf_init(&buf, 0);
328370
strbuf_addf(&buf, "remote.%s", remote->name);
329371
if (git_config_rename_section(buf.buf, NULL) < 1)
@@ -352,9 +394,7 @@ static int rm(int argc, const char **argv)
352394
* the branches one by one, since for_each_ref() relies on cached
353395
* refs, which are invalidated when deleting a branch.
354396
*/
355-
strbuf_reset(&buf);
356-
strbuf_addf(&buf, "refs/remotes/%s/", remote->name);
357-
cb_data.prefix = buf.buf;
397+
cb_data.remote = remote;
358398
i = for_each_ref(add_branch_for_removal, &cb_data);
359399
strbuf_release(&buf);
360400

0 commit comments

Comments
 (0)