Clone: allow empty dirs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
diff --git a/src/clone.c b/src/clone.c
index 2691786..7790049 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -6,6 +6,7 @@
*/
#include <assert.h>
+#include <dirent.h>
#include "git2/clone.h"
#include "git2/remote.h"
@@ -85,7 +86,7 @@ static int update_head_to_new_branch(git_repository *repo, git_oid *target, cons
if (!create_tracking_branch(repo, target, name)) {
git_reference *head;
- if (!git_repository_head(&head, repo)) {
+ if (!git_reference_lookup(&head, repo, GIT_HEAD_FILE)) {
git_buf target = GIT_BUF_INIT;
if (!git_buf_printf(&target, "refs/heads/%s", name) &&
!git_reference_set_target(head, git_buf_cstr(&target))) {
@@ -101,7 +102,7 @@ static int update_head_to_new_branch(git_repository *repo, git_oid *target, cons
static int update_head_to_remote(git_repository *repo, git_remote *remote)
{
- int retcode = 0;
+ int retcode = GIT_ERROR;
git_remote_head *remote_head;
git_oid oid;
struct HeadInfo head_info;
@@ -115,16 +116,15 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote)
/* Check to see if "master" matches the remote head */
if (!git_reference_name_to_oid(&oid, repo, "refs/remotes/origin/master") &&
!git_oid_cmp(&remote_head->oid, &oid)) {
- update_head_to_new_branch(repo, &oid, "master");
+ retcode = update_head_to_new_branch(repo, &oid, "master");
}
/* Not master. Check all the other refs. */
else if (!git_reference_foreach(repo, GIT_REF_LISTALL,
reference_matches_remote_head,
&head_info) &&
- git_buf_len(&head_info.branchname) > 0 &&
- update_head_to_new_branch(repo, &head_info.remote_head_oid,
- git_buf_cstr(&head_info.branchname))) {
- retcode = 0;
+ git_buf_len(&head_info.branchname) > 0) {
+ retcode = update_head_to_new_branch(repo, &head_info.remote_head_oid,
+ git_buf_cstr(&head_info.branchname));
}
git_buf_free(&head_info.branchname);
@@ -173,6 +173,50 @@ static int setup_remotes_and_fetch(git_repository *repo,
return retcode;
}
+
+static bool is_dot_or_dotdot(const char *name)
+{
+ return (name[0] == '.' &&
+ (name[1] == '\0' ||
+ (name[1] == '.' && name[2] == '\0')));
+}
+
+/* TODO: p_opendir, p_closedir */
+static bool path_is_okay(const char *path)
+{
+ DIR *dir;
+ struct dirent *e;
+ bool retval = true;
+
+ /* The path must either not exist, or be an empty directory */
+ if (!git_path_exists(path)) return true;
+
+ if (!git_path_isdir(path)) {
+ giterr_set(GITERR_INVALID,
+ "'%s' exists and is not an empty directory", path);
+ return false;
+ }
+
+ dir = opendir(path);
+ if (!dir) {
+ giterr_set(GITERR_OS, "Couldn't open '%s'", path);
+ return false;
+ }
+
+ while ((e = readdir(dir)) != NULL) {
+ if (!is_dot_or_dotdot(e->d_name)) {
+ giterr_set(GITERR_INVALID,
+ "'%s' exists and is not an empty directory", path);
+ retval = false;
+ break;
+ }
+ }
+
+ closedir(dir);
+ return retval;
+}
+
+
static int clone_internal(git_repository **out,
const char *origin_url,
const char *path,
@@ -182,8 +226,7 @@ static int clone_internal(git_repository **out,
int retcode = GIT_ERROR;
git_repository *repo = NULL;
- if (git_path_exists(path)) {
- giterr_set(GITERR_INVALID, "Path '%s' already exists.", path);
+ if (!path_is_okay(path)) {
return GIT_ERROR;
}
diff --git a/tests-clar/clone/clone.c b/tests-clar/clone/clone.c
index 1e6b4d9..fe4eb8b 100644
--- a/tests-clar/clone/clone.c
+++ b/tests-clar/clone/clone.c
@@ -117,7 +117,28 @@ void test_clone_clone__network_bare(void)
void test_clone_clone__already_exists(void)
{
+#if 0
+ int bar;
+
+ /* Should pass with existing-but-empty dir */
+ p_mkdir("./foo", GIT_DIR_MODE);
+ cl_git_pass(git_clone(&g_repo,
+ "http://github.com/libgit2/libgit2.git",
+ "./foo", NULL));
+ git_repository_free(g_repo); g_repo = NULL;
+ git_futils_rmdir_r("./foo", GIT_DIRREMOVAL_FILES_AND_DIRS);
+#endif
+
+ /* Should fail with a file */
+ cl_git_mkfile("./foo", "Bar!");
+ cl_git_fail(git_clone(&g_repo,
+ "http://github.com/libgit2/libgit2.git",
+ "./foo", NULL));
+ git_futils_rmdir_r("./foo", GIT_DIRREMOVAL_FILES_AND_DIRS);
+
+ /* Should fail with existing-and-nonempty dir */
p_mkdir("./foo", GIT_DIR_MODE);
+ cl_git_mkfile("./foo/bar", "Baz!");
cl_git_fail(git_clone(&g_repo,
"https://github.com/libgit2/libgit2.git",
"./foo", NULL));