branch: skip commit checks when deleting symref branches

Before a branch is deleted, we check that it points to a valid commit. With -d we also check that the commit is a merged; this check is not done with -D. The reason for that is that commits pointed to by branches should never go missing; if they do then something broke and it's better to stop instead of adding to the mess. And a non-merged commit may contain changes that are worth preserving, so we require the stronger option -D instead of -d to get rid of them. If a branch consists of a symref, these concerns don't apply. Deleting such a branch can't make a commit become unreferenced, so we don't need to check if it is merged, or even if it is actually a valid commit. Skip them in that case. This allows us to delete dangling symref branches. Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx> Signed-off-by: Junio C Hamano <gitster@pobox.com>

René Scharfe committed Oct 18, 2012 at 14:07 UTC 0fe700e311f2d7e55eb23fe941fab9155d7f91df
2 files changed +17 -2
builtin/branch.c
+8 -2
@@ -214,6 +214,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
214 die(_("Couldn't look up commit object for HEAD"));
215 }
216 for (i = 0; i < argc; i++, strbuf_release(&bname)) {
217 + const char *target;
218 + int flags = 0;
219 +
220 strbuf_branchname(&bname, argv[i]);
221 if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) {
222 error(_("Cannot delete the branch '%s' "
@@ -225,7 +228,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
228 free(name);
229
230 name = mkpathdup(fmt, bname.buf);
228 - if (read_ref(name, sha1)) {
231 + target = resolve_ref_unsafe(name, sha1, 0, &flags);
232 + if (!target ||
233 + (!(flags & REF_ISSYMREF) && is_null_sha1(sha1))) {
234 error(remote_branch
235 ? _("remote branch '%s' not found.")
236 : _("branch '%s' not found."), bname.buf);
@@ -233,7 +238,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
238 continue;
239 }
240
236 - if (check_branch_commit(bname.buf, name, sha1, head_rev, kinds,
241 + if (!(flags & REF_ISSYMREF) &&
242 + check_branch_commit(bname.buf, name, sha1, head_rev, kinds,
243 force)) {
244 ret = 1;
245 continue;
t/t3200-branch.sh
+9
@@ -273,6 +273,15 @@ test_expect_success 'deleting a symref' '
273 test_i18ncmp expect actual
274 '
275
276 +test_expect_success 'deleting a dangling symref' '
277 + git symbolic-ref refs/heads/dangling-symref nowhere &&
278 + test_path_is_file .git/refs/heads/dangling-symref &&
279 + echo "Deleted branch dangling-symref (was 0000000)." >expect &&
280 + git branch -d dangling-symref >actual &&
281 + test_path_is_missing .git/refs/heads/dangling-symref &&
282 + test_i18ncmp expect actual
283 +'
284 +
285 test_expect_success 'renaming a symref is not allowed' \
286 '
287 git symbolic-ref refs/heads/master2 refs/heads/master &&