Skip to content

Commit 55892d2

Browse files
aspotashevgitster
authored andcommitted
Allow cloning to an existing empty directory
The die() message updated accordingly. The previous behaviour was to only allow cloning when the destination directory doesn't exist. [jc: added trivial tests] Signed-off-by: Alexander Potashev <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 8ca12c0 commit 55892d2

File tree

4 files changed

+46
-3
lines changed

4 files changed

+46
-3
lines changed

builtin-clone.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
357357
struct stat buf;
358358
const char *repo_name, *repo, *work_tree, *git_dir;
359359
char *path, *dir;
360+
int dest_exists;
360361
const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
361362
struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
362363
struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
@@ -406,8 +407,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
406407
dir = guess_dir_name(repo_name, is_bundle, option_bare);
407408
strip_trailing_slashes(dir);
408409

409-
if (!stat(dir, &buf))
410-
die("destination directory '%s' already exists.", dir);
410+
dest_exists = !stat(dir, &buf);
411+
if (dest_exists && !is_empty_dir(dir))
412+
die("destination path '%s' already exists and is not "
413+
"an empty directory.", dir);
411414

412415
strbuf_addf(&reflog_msg, "clone: from %s", repo);
413416

@@ -431,7 +434,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
431434
if (safe_create_leading_directories_const(work_tree) < 0)
432435
die("could not create leading directories of '%s': %s",
433436
work_tree, strerror(errno));
434-
if (mkdir(work_tree, 0755))
437+
if (!dest_exists && mkdir(work_tree, 0755))
435438
die("could not create work tree dir '%s': %s.",
436439
work_tree, strerror(errno));
437440
set_git_work_tree(work_tree);

dir.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,25 @@ int is_inside_dir(const char *dir)
777777
return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL;
778778
}
779779

780+
int is_empty_dir(const char *path)
781+
{
782+
DIR *dir = opendir(path);
783+
struct dirent *e;
784+
int ret = 1;
785+
786+
if (!dir)
787+
return 0;
788+
789+
while ((e = readdir(dir)) != NULL)
790+
if (!is_dot_or_dotdot(e->d_name)) {
791+
ret = 0;
792+
break;
793+
}
794+
795+
closedir(dir);
796+
return ret;
797+
}
798+
780799
int remove_dir_recursively(struct strbuf *path, int only_empty)
781800
{
782801
DIR *dir = opendir(path->buf);

dir.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ static inline int is_dot_or_dotdot(const char *name)
8484
(name[1] == '.' && name[2] == '\0')));
8585
}
8686

87+
extern int is_empty_dir(const char *dir);
88+
8789
extern void setup_standard_excludes(struct dir_struct *dir);
8890
extern int remove_dir_recursively(struct strbuf *path, int only_empty);
8991

t/t5601-clone.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,23 @@ test_expect_success 'clone to destination with extra trailing /' '
125125
126126
'
127127

128+
test_expect_success 'clone to an existing empty directory' '
129+
mkdir target-3 &&
130+
git clone src target-3 &&
131+
T=$( cd target-3 && git rev-parse HEAD ) &&
132+
S=$( cd src && git rev-parse HEAD ) &&
133+
test "$T" = "$S"
134+
'
135+
136+
test_expect_success 'clone to an existing non-empty directory' '
137+
mkdir target-4 &&
138+
>target-4/Fakefile &&
139+
test_must_fail git clone src target-4
140+
'
141+
142+
test_expect_success 'clone to an existing path' '
143+
>target-5 &&
144+
test_must_fail git clone src target-5
145+
'
146+
128147
test_done

0 commit comments

Comments
 (0)