checkout: clean up half-prepared directories in --to mode

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed Nov 30, 2014 at 15:24 UTC 3b8925c78bdbea49c846e350e66c13923441fff8
2 files changed +54
builtin/checkout.c
+48
@@ -20,6 +20,7 @@
20 #include "resolve-undo.h"
21 #include "submodule.h"
22 #include "argv-array.h"
23 +#include "sigchain.h"
24
25 static const char * const checkout_usage[] = {
26 N_("git checkout [options] <branch>"),
@@ -823,6 +824,35 @@ static int switch_branches(const struct checkout_opts *opts,
824 return ret || writeout_error;
825 }
826
827 +static char *junk_work_tree;
828 +static char *junk_git_dir;
829 +static int is_junk;
830 +static pid_t junk_pid;
831 +
832 +static void remove_junk(void)
833 +{
834 + struct strbuf sb = STRBUF_INIT;
835 + if (!is_junk || getpid() != junk_pid)
836 + return;
837 + if (junk_git_dir) {
838 + strbuf_addstr(&sb, junk_git_dir);
839 + remove_dir_recursively(&sb, 0);
840 + strbuf_reset(&sb);
841 + }
842 + if (junk_work_tree) {
843 + strbuf_addstr(&sb, junk_work_tree);
844 + remove_dir_recursively(&sb, 0);
845 + }
846 + strbuf_release(&sb);
847 +}
848 +
849 +static void remove_junk_on_signal(int signo)
850 +{
851 + remove_junk();
852 + sigchain_pop(signo);
853 + raise(signo);
854 +}
855 +
856 static int prepare_linked_checkout(const struct checkout_opts *opts,
857 struct branch_info *new)
858 {
@@ -859,8 +889,15 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
889 strbuf_addf(&sb_repo, "%d", counter);
890 }
891 name = strrchr(sb_repo.buf, '/') + 1;
892 +
893 + junk_pid = getpid();
894 + atexit(remove_junk);
895 + sigchain_push_common(remove_junk_on_signal);
896 +
897 if (mkdir(sb_repo.buf, 0777))
898 die_errno(_("could not create directory of '%s'"), sb_repo.buf);
899 + junk_git_dir = xstrdup(sb_repo.buf);
900 + is_junk = 1;
901
902 /*
903 * lock the incomplete repo so prune won't delete it, unlock
@@ -873,6 +910,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
910 if (safe_create_leading_directories_const(sb_git.buf))
911 die_errno(_("could not create leading directories of '%s'"),
912 sb_git.buf);
913 + junk_work_tree = xstrdup(path);
914
915 strbuf_reset(&sb);
916 strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
@@ -902,9 +940,19 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
940 cp.git_cmd = 1;
941 cp.argv = opts->saved_argv;
942 ret = run_command(&cp);
943 + if (!ret) {
944 + is_junk = 0;
945 + free(junk_work_tree);
946 + free(junk_git_dir);
947 + junk_work_tree = NULL;
948 + junk_git_dir = NULL;
949 + }
950 strbuf_reset(&sb);
951 strbuf_addf(&sb, "%s/locked", sb_repo.buf);
952 unlink_or_warn(sb.buf);
953 + strbuf_release(&sb);
954 + strbuf_release(&sb_repo);
955 + strbuf_release(&sb_git);
956 return ret;
957 }
958
t/t2025-checkout-to.sh
+6
@@ -17,6 +17,12 @@ test_expect_success 'checkout --to an existing worktree' '
17 test_must_fail git checkout --detach --to existing master
18 '
19
20 +test_expect_success 'checkout --to refuses to checkout locked branch' '
21 + test_must_fail git checkout --to zere master &&
22 + ! test -d zere &&
23 + ! test -d .git/worktrees/zere
24 +'
25 +
26 test_expect_success 'checkout --to a new worktree' '
27 git rev-parse HEAD >expect &&
28 git checkout --detach --to here master &&