Skip to content

Commit 93b7de1

Browse files
peffdscho
authored andcommitted
clone: do not clean up directories we didn't create
Once upon a time, git-clone would refuse to write into a directory that it did not itself create. The cleanup routines for a failed clone could therefore just remove the git and worktree dirs completely. In 55892d2 (Allow cloning to an existing empty directory, 2009-01-11), we learned to write into an existing directory. Which means that doing: mkdir foo git clone will-fail foo ends up deleting foo. This isn't a huge catastrophe, since by definition foo must be empty. But it's somewhat confusing; we should leave the filesystem as we found it. Because we know that the only directory we'll write into is an empty one, we can handle this case by just passing the KEEP_TOPLEVEL flag to our recursive delete (if we could write into populated directories, we'd have to keep track of what we wrote and what we did not). Note that we need to handle the work-tree and git-dir separately, though, as only one might exist (and the new tests in t5600 cover all cases). Reported-by: Stephan Janssen <[email protected]>
1 parent f606900 commit 93b7de1

File tree

2 files changed

+67
-9
lines changed

2 files changed

+67
-9
lines changed

builtin/clone.c

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,9 @@ static void clone_local(const char *src_repo, const char *dest_repo)
473473
}
474474

475475
static const char *junk_work_tree;
476+
static int junk_work_tree_flags;
476477
static const char *junk_git_dir;
478+
static int junk_git_dir_flags;
477479
static enum {
478480
JUNK_LEAVE_NONE,
479481
JUNK_LEAVE_REPO,
@@ -502,12 +504,12 @@ static void remove_junk(void)
502504

503505
if (junk_git_dir) {
504506
strbuf_addstr(&sb, junk_git_dir);
505-
remove_dir_recursively(&sb, 0);
507+
remove_dir_recursively(&sb, junk_git_dir_flags);
506508
strbuf_reset(&sb);
507509
}
508510
if (junk_work_tree) {
509511
strbuf_addstr(&sb, junk_work_tree);
510-
remove_dir_recursively(&sb, 0);
512+
remove_dir_recursively(&sb, junk_work_tree_flags);
511513
}
512514
strbuf_release(&sb);
513515
}
@@ -974,14 +976,24 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
974976
if (safe_create_leading_directories_const(work_tree) < 0)
975977
die_errno(_("could not create leading directories of '%s'"),
976978
work_tree);
977-
if (!dest_exists && mkdir(work_tree, 0777))
979+
if (dest_exists)
980+
junk_work_tree_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
981+
else if (mkdir(work_tree, 0777))
978982
die_errno(_("could not create work tree dir '%s'"),
979983
work_tree);
980984
junk_work_tree = work_tree;
981985
set_git_work_tree(work_tree);
982986
}
983987

984-
junk_git_dir = real_git_dir ? real_git_dir : git_dir;
988+
if (real_git_dir) {
989+
if (dir_exists(real_git_dir))
990+
junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
991+
junk_git_dir = real_git_dir;
992+
} else {
993+
if (dest_exists)
994+
junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
995+
junk_git_dir = git_dir;
996+
}
985997
if (safe_create_leading_directories_const(git_dir) < 0)
986998
die(_("could not create leading directories of '%s'"), git_dir);
987999

t/t5600-clone-fail-cleanup.sh

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,21 @@ test_description='test git clone to cleanup after failure
77
88
This test covers the fact that if git clone fails, it should remove
99
the directory it created, to avoid the user having to manually
10-
remove the directory before attempting a clone again.'
10+
remove the directory before attempting a clone again.
11+
12+
Unless the directory already exists, in which case we clean up only what we
13+
wrote.
14+
'
1115

1216
. ./test-lib.sh
1317

18+
corrupt_repo () {
19+
test_when_finished "rmdir foo/.git/objects.bak" &&
20+
mkdir foo/.git/objects.bak/ &&
21+
test_when_finished "mv foo/.git/objects.bak/* foo/.git/objects/" &&
22+
mv foo/.git/objects/* foo/.git/objects.bak/
23+
}
24+
1425
test_expect_success 'clone of non-existent source should fail' '
1526
test_must_fail git clone foo bar
1627
'
@@ -42,13 +53,48 @@ test_expect_success 'successful clone must leave the directory' '
4253
'
4354

4455
test_expect_success 'failed clone --separate-git-dir should not leave any directories' '
45-
test_when_finished "rmdir foo/.git/objects.bak" &&
46-
mkdir foo/.git/objects.bak/ &&
47-
test_when_finished "mv foo/.git/objects.bak/* foo/.git/objects/" &&
48-
mv foo/.git/objects/* foo/.git/objects.bak/ &&
56+
corrupt_repo &&
4957
test_must_fail git clone --separate-git-dir gitdir foo worktree &&
5058
test_path_is_missing gitdir &&
5159
test_path_is_missing worktree
5260
'
5361

62+
test_expect_success 'failed clone into empty leaves directory (vanilla)' '
63+
mkdir -p empty &&
64+
corrupt_repo &&
65+
test_must_fail git clone foo empty &&
66+
test_dir_is_empty empty
67+
'
68+
69+
test_expect_success 'failed clone into empty leaves directory (bare)' '
70+
mkdir -p empty &&
71+
corrupt_repo &&
72+
test_must_fail git clone --bare foo empty &&
73+
test_dir_is_empty empty
74+
'
75+
76+
test_expect_success 'failed clone into empty leaves directory (separate)' '
77+
mkdir -p empty-git empty-wt &&
78+
corrupt_repo &&
79+
test_must_fail git clone --separate-git-dir empty-git foo empty-wt &&
80+
test_dir_is_empty empty-git &&
81+
test_dir_is_empty empty-wt
82+
'
83+
84+
test_expect_success 'failed clone into empty leaves directory (separate, git)' '
85+
mkdir -p empty-git &&
86+
corrupt_repo &&
87+
test_must_fail git clone --separate-git-dir empty-git foo no-wt &&
88+
test_dir_is_empty empty-git &&
89+
test_path_is_missing no-wt
90+
'
91+
92+
test_expect_success 'failed clone into empty leaves directory (separate, wt)' '
93+
mkdir -p empty-wt &&
94+
corrupt_repo &&
95+
test_must_fail git clone --separate-git-dir no-git foo empty-wt &&
96+
test_path_is_missing no-git &&
97+
test_dir_is_empty empty-wt
98+
'
99+
54100
test_done

0 commit comments

Comments
 (0)