diff options
-rw-r--r-- | builtin/checkout.c | 48 | ||||
-rwxr-xr-x | t/t2025-checkout-to.sh | 6 |
2 files changed, 54 insertions, 0 deletions
diff --git a/builtin/checkout.c b/builtin/checkout.c index 01a28b486..5dfdbda1a 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -20,6 +20,7 @@ #include "resolve-undo.h" #include "submodule.h" #include "argv-array.h" +#include "sigchain.h" static const char * const checkout_usage[] = { N_("git checkout [options] <branch>"), @@ -823,6 +824,35 @@ static int switch_branches(const struct checkout_opts *opts, return ret || writeout_error; } +static char *junk_work_tree; +static char *junk_git_dir; +static int is_junk; +static pid_t junk_pid; + +static void remove_junk(void) +{ + struct strbuf sb = STRBUF_INIT; + if (!is_junk || getpid() != junk_pid) + return; + if (junk_git_dir) { + strbuf_addstr(&sb, junk_git_dir); + remove_dir_recursively(&sb, 0); + strbuf_reset(&sb); + } + if (junk_work_tree) { + strbuf_addstr(&sb, junk_work_tree); + remove_dir_recursively(&sb, 0); + } + strbuf_release(&sb); +} + +static void remove_junk_on_signal(int signo) +{ + remove_junk(); + sigchain_pop(signo); + raise(signo); +} + static int prepare_linked_checkout(const struct checkout_opts *opts, struct branch_info *new) { @@ -859,8 +889,15 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, strbuf_addf(&sb_repo, "%d", counter); } name = strrchr(sb_repo.buf, '/') + 1; + + junk_pid = getpid(); + atexit(remove_junk); + sigchain_push_common(remove_junk_on_signal); + if (mkdir(sb_repo.buf, 0777)) die_errno(_("could not create directory of '%s'"), sb_repo.buf); + junk_git_dir = xstrdup(sb_repo.buf); + is_junk = 1; /* * 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, if (safe_create_leading_directories_const(sb_git.buf)) die_errno(_("could not create leading directories of '%s'"), sb_git.buf); + junk_work_tree = xstrdup(path); strbuf_reset(&sb); strbuf_addf(&sb, "%s/gitdir", sb_repo.buf); @@ -902,9 +940,19 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, cp.git_cmd = 1; cp.argv = opts->saved_argv; ret = run_command(&cp); + if (!ret) { + is_junk = 0; + free(junk_work_tree); + free(junk_git_dir); + junk_work_tree = NULL; + junk_git_dir = NULL; + } strbuf_reset(&sb); strbuf_addf(&sb, "%s/locked", sb_repo.buf); unlink_or_warn(sb.buf); + strbuf_release(&sb); + strbuf_release(&sb_repo); + strbuf_release(&sb_git); return ret; } diff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh index edd34049c..e2db07859 100755 --- a/t/t2025-checkout-to.sh +++ b/t/t2025-checkout-to.sh @@ -17,6 +17,12 @@ test_expect_success 'checkout --to an existing worktree' ' test_must_fail git checkout --detach --to existing master ' +test_expect_success 'checkout --to refuses to checkout locked branch' ' + test_must_fail git checkout --to zere master && + ! test -d zere && + ! test -d .git/worktrees/zere +' + test_expect_success 'checkout --to a new worktree' ' git rev-parse HEAD >expect && git checkout --detach --to here master && |