]> git.proxmox.com Git - libgit2.git/commitdiff
clone: fix cloning of empty repository
authornulltoken <emeric.fermas@gmail.com>
Sun, 7 Oct 2012 10:50:18 +0000 (12:50 +0200)
committernulltoken <emeric.fermas@gmail.com>
Sun, 7 Oct 2012 22:44:21 +0000 (00:44 +0200)
src/clone.c
tests-clar/clone/clone.c

index 312a38e1f2aa73afce1e5e812be5e77bb797701e..a5f4867db95b34bf7d3900b967d761c40ebb85e9 100644 (file)
@@ -22,7 +22,7 @@
 #include "refs.h"
 #include "path.h"
 
-static int create_tracking_branch(
+static int create_branch(
        git_reference **branch,
        git_repository *repo,
        const git_oid *target,
@@ -30,43 +30,74 @@ static int create_tracking_branch(
 {
        git_object *head_obj = NULL;
        git_reference *branch_ref;
-       int retcode = GIT_ERROR;
+       int error;
 
        /* Find the target commit */
-       if (git_object_lookup(&head_obj, repo, target, GIT_OBJ_ANY) < 0)
-               return GIT_ERROR;
+       if ((error = git_object_lookup(&head_obj, repo, target, GIT_OBJ_ANY)) < 0)
+               return error;
 
        /* Create the new branch */
-       if (!git_branch_create(&branch_ref, repo, name, head_obj, 0)) {
-               git_config *cfg;
-
-               /* Set up tracking */
-               if (!git_repository_config(&cfg, repo)) {
-                       git_buf remote = GIT_BUF_INIT;
-                       git_buf merge = GIT_BUF_INIT;
-                       git_buf merge_target = GIT_BUF_INIT;
-                       if (!git_buf_printf(&remote, "branch.%s.remote", name) &&
-                                !git_buf_printf(&merge, "branch.%s.merge", name) &&
-                                !git_buf_printf(&merge_target, GIT_REFS_HEADS_DIR "%s", name) &&
-                                !git_config_set_string(cfg, git_buf_cstr(&remote), GIT_REMOTE_ORIGIN) &&
-                                !git_config_set_string(cfg, git_buf_cstr(&merge), git_buf_cstr(&merge_target))) {
-                               retcode = 0;
-                       }
-                       git_buf_free(&remote);
-                       git_buf_free(&merge);
-                       git_buf_free(&merge_target);
-                       git_config_free(cfg);
-               }
-       }
+       error = git_branch_create(&branch_ref, repo, name, head_obj, 0);
 
        git_object_free(head_obj);
 
-       if (!retcode)
+       if (!error)
                *branch = branch_ref;
        else
                git_reference_free(branch_ref);
 
-       return retcode;
+       return error;
+}
+
+static int setup_tracking_config(
+       git_repository *repo,
+       const char *branch_name,
+       const char *remote_name,
+       const char *merge_target)
+{
+       git_config *cfg;
+       git_buf remote_key = GIT_BUF_INIT, merge_key = GIT_BUF_INIT;
+       int error = -1;
+
+       if (git_repository_config__weakptr(&cfg, repo) < 0)
+               return -1;
+
+       if (git_buf_printf(&remote_key, "branch.%s.remote", branch_name) < 0)
+               goto cleanup;
+
+       if (git_buf_printf(&merge_key, "branch.%s.merge", branch_name) < 0)
+               goto cleanup;
+
+       if (git_config_set_string(cfg, git_buf_cstr(&remote_key), remote_name) < 0)
+               goto cleanup;
+
+       if (git_config_set_string(cfg, git_buf_cstr(&merge_key), merge_target) < 0)
+               goto cleanup;
+
+       error = 0;
+
+cleanup:
+       git_buf_free(&remote_key);
+       git_buf_free(&merge_key);
+       return error;
+}
+
+static int create_tracking_branch(
+       git_reference **branch,
+       git_repository *repo,
+       const git_oid *target,
+       const char *branch_name)
+{
+       int error;
+
+       if ((error = create_branch(branch, repo, target, branch_name)) < 0)
+               return error;
+
+       return setup_tracking_config(
+               repo,
+               branch_name,
+               GIT_REMOTE_ORIGIN,
+               git_reference_name(*branch));
 }
 
 struct head_info {
@@ -117,13 +148,20 @@ static int reference_matches_remote_head(
        return 0;
 }
 
-static int update_head_to_new_branch(git_repository *repo, const git_oid *target, const char *name)
+static int update_head_to_new_branch(
+       git_repository *repo,
+       const git_oid *target,
+       const char *name)
 {
        git_reference *tracking_branch;
        int error;
 
-       if (create_tracking_branch(&tracking_branch, repo, target, name) < 0)
-               return -1;
+       if ((error = create_tracking_branch(
+               &tracking_branch,
+               repo,
+               target,
+               name)) < 0)
+                       return error;
 
        error = git_repository_set_head(repo, git_reference_name(tracking_branch));
 
@@ -139,6 +177,15 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote)
        struct head_info head_info;
        git_buf remote_master_name = GIT_BUF_INIT;
 
+       /* Did we just clone an empty repository? */
+       if (remote->refs.length == 0) {
+               return setup_tracking_config(
+                       repo,
+                       "master",
+                       GIT_REMOTE_ORIGIN,
+                       GIT_REFS_HEADS_MASTER_FILE);
+       }
+
        /* Get the remote's HEAD. This is always the first ref in remote->refs. */
        remote_head = remote->refs.contents[0];
        git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid);
@@ -244,11 +291,14 @@ static bool path_is_okay(const char *path)
 }
 
 
-static int clone_internal(git_repository **out,
-                                                                 const char *origin_url,
-                                                                 const char *path,
-                                                                 git_indexer_stats *fetch_stats,
-                                                                 int is_bare)
+static int clone_internal(
+       git_repository **out,
+       const char *origin_url,
+       const char *path,
+       git_indexer_stats *fetch_stats,
+       git_indexer_stats *checkout_stats,
+       git_checkout_opts *checkout_opts,
+       int is_bare)
 {
        int retcode = GIT_ERROR;
        git_repository *repo = NULL;
@@ -271,6 +321,9 @@ static int clone_internal(git_repository **out,
                }
        }
 
+       if (!retcode && !is_bare && !git_repository_head_orphan(repo))
+               retcode = git_checkout_head(*out, checkout_opts, checkout_stats);
+
        return retcode;
 }
 
@@ -280,7 +333,15 @@ int git_clone_bare(git_repository **out,
                                                 git_indexer_stats *fetch_stats)
 {
        assert(out && origin_url && dest_path);
-       return clone_internal(out, origin_url, dest_path, fetch_stats, 1);
+
+       return clone_internal(
+               out,
+               origin_url,
+               dest_path,
+               fetch_stats,
+               NULL,
+               NULL,
+               1);
 }
 
 
@@ -291,12 +352,14 @@ int git_clone(git_repository **out,
                                  git_indexer_stats *checkout_stats,
                                  git_checkout_opts *checkout_opts)
 {
-       int retcode = GIT_ERROR;
-
        assert(out && origin_url && workdir_path);
 
-       if (!(retcode = clone_internal(out, origin_url, workdir_path, fetch_stats, 0)))
-               retcode = git_checkout_head(*out, checkout_opts, checkout_stats);
-
-       return retcode;
+       return clone_internal(
+               out,
+               origin_url,
+               workdir_path,
+               fetch_stats,
+               checkout_stats,
+               checkout_opts,
+               0);
 }
index 661094310a731ef6d45fe1cee9d4a5df2308d7d5..42ddb8ae679a2726ff94a500285437326072c4b2 100644 (file)
@@ -6,6 +6,7 @@
 #define DO_LOCAL_TEST 0
 #define DO_LIVE_NETWORK_TESTS 0
 #define LIVE_REPO_URL "git://github.com/nulltoken/TestGitRepository"
+#define LIVE_EMPTYREPO_URL "git://github.com/nulltoken/TestEmptyRepository"
 
 
 static git_repository *g_repo;
@@ -152,3 +153,23 @@ void test_clone_clone__fail_with_already_existing_but_non_empty_directory(void)
        cl_git_mkfile("./foo/bar", "Baz!");
        cl_git_fail(git_clone(&g_repo, LIVE_REPO_URL, "./foo", NULL, NULL, NULL));
 }
+
+void test_clone_clone__empty_repository(void)
+{
+#if DO_LIVE_NETWORK_TESTS
+       git_reference *head;
+
+       cl_set_cleanup(&cleanup_repository, "./empty");
+
+       cl_git_pass(git_clone(&g_repo, LIVE_EMPTYREPO_URL, "./empty", NULL, NULL, NULL));
+
+       cl_assert_equal_i(true, git_repository_is_empty(g_repo));
+       cl_assert_equal_i(true, git_repository_head_orphan(g_repo));
+
+       cl_git_pass(git_reference_lookup(&head, g_repo, GIT_HEAD_FILE));
+       cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
+       cl_assert_equal_s("refs/heads/master", git_reference_target(head));
+
+       git_reference_free(head);
+#endif
+}