diff options
-rw-r--r-- | src/clone.c | 147 | ||||
-rw-r--r-- | tests-clar/clone/clone.c | 21 |
2 files changed, 126 insertions, 42 deletions
diff --git a/src/clone.c b/src/clone.c index 312a38e1f..a5f4867db 100644 --- a/src/clone.c +++ b/src/clone.c @@ -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); } diff --git a/tests-clar/clone/clone.c b/tests-clar/clone/clone.c index 661094310..42ddb8ae6 100644 --- a/tests-clar/clone/clone.c +++ b/tests-clar/clone/clone.c @@ -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 +} |