Skip to content

Commit ccf6d62

Browse files
committed
Merge branch 'jc/checkout-orphan-warning'
* jc/checkout-orphan-warning: commit: give final warning when reattaching HEAD to leave commits behind
2 parents 663ee1a + 8e2dc6a commit ccf6d62

File tree

1 file changed

+95
-6
lines changed

1 file changed

+95
-6
lines changed

builtin/checkout.c

Lines changed: 95 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,100 @@ static void update_refs_for_switch(struct checkout_opts *opts,
582582
report_tracking(new);
583583
}
584584

585+
struct rev_list_args {
586+
int argc;
587+
int alloc;
588+
const char **argv;
589+
};
590+
591+
static void add_one_rev_list_arg(struct rev_list_args *args, const char *s)
592+
{
593+
ALLOC_GROW(args->argv, args->argc + 1, args->alloc);
594+
args->argv[args->argc++] = s;
595+
}
596+
597+
static int add_one_ref_to_rev_list_arg(const char *refname,
598+
const unsigned char *sha1,
599+
int flags,
600+
void *cb_data)
601+
{
602+
add_one_rev_list_arg(cb_data, refname);
603+
return 0;
604+
}
605+
606+
607+
static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
608+
{
609+
struct pretty_print_context ctx = { 0 };
610+
611+
parse_commit(commit);
612+
strbuf_addstr(sb, " - ");
613+
pretty_print_commit(CMIT_FMT_ONELINE, commit, sb, &ctx);
614+
strbuf_addch(sb, '\n');
615+
}
616+
617+
#define ORPHAN_CUTOFF 4
618+
static void suggest_reattach(struct commit *commit, struct rev_info *revs)
619+
{
620+
struct commit *c, *last = NULL;
621+
struct strbuf sb = STRBUF_INIT;
622+
int lost = 0;
623+
while ((c = get_revision(revs)) != NULL) {
624+
if (lost < ORPHAN_CUTOFF)
625+
describe_one_orphan(&sb, c);
626+
last = c;
627+
lost++;
628+
}
629+
if (ORPHAN_CUTOFF < lost) {
630+
int more = lost - ORPHAN_CUTOFF;
631+
if (more == 1)
632+
describe_one_orphan(&sb, last);
633+
else
634+
strbuf_addf(&sb, " ... and %d more.\n", more);
635+
}
636+
637+
fprintf(stderr,
638+
"Warning: you are leaving %d commit%s behind, "
639+
"not connected to\n"
640+
"any of your branches:\n\n"
641+
"%s\n"
642+
"If you want to keep them by creating a new branch, "
643+
"this may be a good time\nto do so with:\n\n"
644+
" git branch new_branch_name %s\n\n",
645+
lost, ((1 < lost) ? "s" : ""),
646+
sb.buf,
647+
sha1_to_hex(commit->object.sha1));
648+
strbuf_release(&sb);
649+
}
650+
651+
/*
652+
* We are about to leave commit that was at the tip of a detached
653+
* HEAD. If it is not reachable from any ref, this is the last chance
654+
* for the user to do so without resorting to reflog.
655+
*/
656+
static void orphaned_commit_warning(struct commit *commit)
657+
{
658+
struct rev_list_args args = { 0, 0, NULL };
659+
struct rev_info revs;
660+
661+
add_one_rev_list_arg(&args, "(internal)");
662+
add_one_rev_list_arg(&args, sha1_to_hex(commit->object.sha1));
663+
add_one_rev_list_arg(&args, "--not");
664+
for_each_ref(add_one_ref_to_rev_list_arg, &args);
665+
add_one_rev_list_arg(&args, "--");
666+
add_one_rev_list_arg(&args, NULL);
667+
668+
init_revisions(&revs, NULL);
669+
if (setup_revisions(args.argc - 1, args.argv, &revs, NULL) != 1)
670+
die("internal error: only -- alone should have been left");
671+
if (prepare_revision_walk(&revs))
672+
die("internal error in revision walk");
673+
if (!(commit->object.flags & UNINTERESTING))
674+
suggest_reattach(commit, &revs);
675+
else
676+
describe_detached_head("Previous HEAD position was", commit);
677+
}
678+
585679
static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
586680
{
587681
int ret = 0;
@@ -609,13 +703,8 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
609703
if (ret)
610704
return ret;
611705

612-
/*
613-
* If we were on a detached HEAD, but have now moved to
614-
* a new commit, we want to mention the old commit once more
615-
* to remind the user that it might be lost.
616-
*/
617706
if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
618-
describe_detached_head("Previous HEAD position was", old.commit);
707+
orphaned_commit_warning(old.commit);
619708

620709
update_refs_for_switch(opts, &old, new);
621710

0 commit comments

Comments
 (0)