mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-02 14:37:30 +00:00
clone: fix cloning of empty repository
This commit is contained in:
parent
ebecf1e7d8
commit
bf0e62a2b8
147
src/clone.c
147
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);
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user