Skip to content

Commit f8948e2

Browse files
committed
remote prune: warn dangling symrefs
If you prune from the remote "frotz" that deleted the ref your tracking branch remotes/frotz/HEAD points at, the symbolic ref will become dangling. We used to detect this as an error condition and issued a message every time refs are enumerated. This stops the error message, but moves the warning to "remote prune". Signed-off-by: Junio C Hamano <[email protected]>
1 parent 8ea7ad6 commit f8948e2

File tree

4 files changed

+86
-18
lines changed

4 files changed

+86
-18
lines changed

builtin-remote.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,12 +756,17 @@ static int prune(int argc, const char **argv)
756756
OPT_END()
757757
};
758758
struct ref_states states;
759+
const char *dangling_msg;
759760

760761
argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
761762

762763
if (argc < 1)
763764
usage_with_options(builtin_remote_usage, options);
764765

766+
dangling_msg = (dry_run
767+
? " %s will become dangling!\n"
768+
: " %s has become dangling!\n");
769+
765770
memset(&states, 0, sizeof(states));
766771
for (; argc; argc--, argv++) {
767772
int i;
@@ -784,6 +789,7 @@ static int prune(int argc, const char **argv)
784789

785790
printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
786791
abbrev_ref(refname, "refs/remotes/"));
792+
warn_dangling_symref(dangling_msg, refname);
787793
}
788794

789795
/* NEEDSWORK: free remote */

refs.c

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -275,10 +275,8 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
275275
list = get_ref_dir(ref, list);
276276
continue;
277277
}
278-
if (!resolve_ref(ref, sha1, 1, &flag)) {
279-
error("%s points nowhere!", ref);
280-
continue;
281-
}
278+
if (!resolve_ref(ref, sha1, 1, &flag))
279+
hashclr(sha1);
282280
list = add_ref(ref, sha1, flag, list, NULL);
283281
}
284282
free(ref);
@@ -287,6 +285,35 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
287285
return sort_ref_list(list);
288286
}
289287

288+
struct warn_if_dangling_data {
289+
const char *refname;
290+
const char *msg_fmt;
291+
};
292+
293+
static int warn_if_dangling_symref(const char *refname, const unsigned char *sha1,
294+
int flags, void *cb_data)
295+
{
296+
struct warn_if_dangling_data *d = cb_data;
297+
const char *resolves_to;
298+
unsigned char junk[20];
299+
300+
if (!(flags & REF_ISSYMREF))
301+
return 0;
302+
303+
resolves_to = resolve_ref(refname, junk, 0, NULL);
304+
if (!resolves_to || strcmp(resolves_to, d->refname))
305+
return 0;
306+
307+
printf(d->msg_fmt, refname);
308+
return 0;
309+
}
310+
311+
void warn_dangling_symref(const char *msg_fmt, const char *refname)
312+
{
313+
struct warn_if_dangling_data data = { refname, msg_fmt };
314+
for_each_rawref(warn_if_dangling_symref, &data);
315+
}
316+
290317
static struct ref_list *get_loose_refs(void)
291318
{
292319
if (!cached_refs.did_loose) {
@@ -498,16 +525,19 @@ int read_ref(const char *ref, unsigned char *sha1)
498525
return -1;
499526
}
500527

528+
#define DO_FOR_EACH_INCLUDE_BROKEN 01
501529
static int do_one_ref(const char *base, each_ref_fn fn, int trim,
502-
void *cb_data, struct ref_list *entry)
530+
int flags, void *cb_data, struct ref_list *entry)
503531
{
504532
if (strncmp(base, entry->name, trim))
505533
return 0;
506-
if (is_null_sha1(entry->sha1))
507-
return 0;
508-
if (!has_sha1_file(entry->sha1)) {
509-
error("%s does not point to a valid object!", entry->name);
510-
return 0;
534+
if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
535+
if (is_null_sha1(entry->sha1))
536+
return 0;
537+
if (!has_sha1_file(entry->sha1)) {
538+
error("%s does not point to a valid object!", entry->name);
539+
return 0;
540+
}
511541
}
512542
current_ref = entry;
513543
return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);
@@ -561,7 +591,7 @@ int peel_ref(const char *ref, unsigned char *sha1)
561591
}
562592

563593
static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
564-
void *cb_data)
594+
int flags, void *cb_data)
565595
{
566596
int retval = 0;
567597
struct ref_list *packed = get_packed_refs();
@@ -570,7 +600,7 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
570600
struct ref_list *extra;
571601

572602
for (extra = extra_refs; extra; extra = extra->next)
573-
retval = do_one_ref(base, fn, trim, cb_data, extra);
603+
retval = do_one_ref(base, fn, trim, flags, cb_data, extra);
574604

575605
while (packed && loose) {
576606
struct ref_list *entry;
@@ -586,13 +616,13 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
586616
entry = packed;
587617
packed = packed->next;
588618
}
589-
retval = do_one_ref(base, fn, trim, cb_data, entry);
619+
retval = do_one_ref(base, fn, trim, flags, cb_data, entry);
590620
if (retval)
591621
goto end_each;
592622
}
593623

594624
for (packed = packed ? packed : loose; packed; packed = packed->next) {
595-
retval = do_one_ref(base, fn, trim, cb_data, packed);
625+
retval = do_one_ref(base, fn, trim, flags, cb_data, packed);
596626
if (retval)
597627
goto end_each;
598628
}
@@ -614,22 +644,28 @@ int head_ref(each_ref_fn fn, void *cb_data)
614644

615645
int for_each_ref(each_ref_fn fn, void *cb_data)
616646
{
617-
return do_for_each_ref("refs/", fn, 0, cb_data);
647+
return do_for_each_ref("refs/", fn, 0, 0, cb_data);
618648
}
619649

620650
int for_each_tag_ref(each_ref_fn fn, void *cb_data)
621651
{
622-
return do_for_each_ref("refs/tags/", fn, 10, cb_data);
652+
return do_for_each_ref("refs/tags/", fn, 10, 0, cb_data);
623653
}
624654

625655
int for_each_branch_ref(each_ref_fn fn, void *cb_data)
626656
{
627-
return do_for_each_ref("refs/heads/", fn, 11, cb_data);
657+
return do_for_each_ref("refs/heads/", fn, 11, 0, cb_data);
628658
}
629659

630660
int for_each_remote_ref(each_ref_fn fn, void *cb_data)
631661
{
632-
return do_for_each_ref("refs/remotes/", fn, 13, cb_data);
662+
return do_for_each_ref("refs/remotes/", fn, 13, 0, cb_data);
663+
}
664+
665+
int for_each_rawref(each_ref_fn fn, void *cb_data)
666+
{
667+
return do_for_each_ref("refs/", fn, 0,
668+
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
633669
}
634670

635671
/*

refs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ extern int for_each_tag_ref(each_ref_fn, void *);
2424
extern int for_each_branch_ref(each_ref_fn, void *);
2525
extern int for_each_remote_ref(each_ref_fn, void *);
2626

27+
/* can be used to learn about broken ref and symref */
28+
extern int for_each_rawref(each_ref_fn, void *);
29+
30+
extern void warn_dangling_symref(const char *msg_fmt, const char *refname);
31+
2732
/*
2833
* Extra refs will be listed by for_each_ref() before any actual refs
2934
* for the duration of this process or until clear_extra_refs() is

t/t5505-remote.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,4 +402,25 @@ test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
402402
test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
403403
'
404404

405+
test_expect_success 'remote prune to cause a dangling symref' '
406+
git clone one seven &&
407+
(
408+
cd one &&
409+
git checkout side2 &&
410+
git branch -D master
411+
) &&
412+
(
413+
cd seven &&
414+
git remote prune origin
415+
) 2>err &&
416+
grep "has become dangling" err &&
417+
418+
: And the dangling symref will not cause other annoying errors
419+
(
420+
cd seven &&
421+
git branch -a
422+
) 2>err &&
423+
! grep "points nowhere" err
424+
'
425+
405426
test_done

0 commit comments

Comments
 (0)