mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-05 22:55:47 +00:00
clone: implement git_clone_into
This allows you to set up the repository and remote as you which to have them before performing the clone operation.
This commit is contained in:
parent
e3c131c544
commit
d19870d947
@ -99,6 +99,22 @@ GIT_EXTERN(int) git_clone(
|
|||||||
const char *local_path,
|
const char *local_path,
|
||||||
const git_clone_options *options);
|
const git_clone_options *options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clone into a repository
|
||||||
|
*
|
||||||
|
* After creating the repository and remote and configuring them for
|
||||||
|
* paths and callbacks respectively, you can call this function to
|
||||||
|
* perform the clone operation and optionally checkout files.
|
||||||
|
*
|
||||||
|
* @param repo the repository to use
|
||||||
|
* @param remote the remote repository to clone from
|
||||||
|
* @param co_opts options to use during checkout
|
||||||
|
* @param branch the branch to checkout after the clone, pass NULL for the remote's
|
||||||
|
* default branch
|
||||||
|
* @return 0 on success or an error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_clone_into(git_repository *repo, git_remote *remote, git_checkout_opts *co_opts, const char *branch);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
GIT_END_DECL
|
GIT_END_DECL
|
||||||
#endif
|
#endif
|
||||||
|
78
src/clone.c
78
src/clone.c
@ -270,23 +270,23 @@ cleanup:
|
|||||||
|
|
||||||
static int update_head_to_branch(
|
static int update_head_to_branch(
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const git_clone_options *options)
|
const char *remote_name,
|
||||||
|
const char *branch)
|
||||||
{
|
{
|
||||||
int retcode;
|
int retcode;
|
||||||
git_buf remote_branch_name = GIT_BUF_INIT;
|
git_buf remote_branch_name = GIT_BUF_INIT;
|
||||||
git_reference* remote_ref = NULL;
|
git_reference* remote_ref = NULL;
|
||||||
|
|
||||||
assert(options->checkout_branch);
|
assert(remote_name && branch);
|
||||||
|
|
||||||
if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s",
|
if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s",
|
||||||
options->remote_name, options->checkout_branch)) < 0 )
|
remote_name, branch)) < 0 )
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0)
|
if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref),
|
retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch);
|
||||||
options->checkout_branch);
|
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
git_reference_free(remote_ref);
|
git_reference_free(remote_ref);
|
||||||
@ -350,6 +350,23 @@ on_error:
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int do_fetch(git_remote *origin)
|
||||||
|
{
|
||||||
|
int retcode;
|
||||||
|
|
||||||
|
/* Connect and download everything */
|
||||||
|
if ((retcode = git_remote_connect(origin, GIT_DIRECTION_FETCH)) < 0)
|
||||||
|
return retcode;
|
||||||
|
|
||||||
|
if ((retcode = git_remote_download(origin)) < 0)
|
||||||
|
return retcode;
|
||||||
|
|
||||||
|
/* Create "origin/foo" branches for all remote branches */
|
||||||
|
if ((retcode = git_remote_update_tips(origin)) < 0)
|
||||||
|
return retcode;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int setup_remotes_and_fetch(
|
static int setup_remotes_and_fetch(
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
@ -374,20 +391,12 @@ static int setup_remotes_and_fetch(
|
|||||||
((retcode = git_remote_add_fetch(origin, "refs/tags/*:refs/tags/*")) < 0))
|
((retcode = git_remote_add_fetch(origin, "refs/tags/*:refs/tags/*")) < 0))
|
||||||
goto on_error;
|
goto on_error;
|
||||||
|
|
||||||
/* Connect and download everything */
|
if ((retcode = do_fetch(origin)) < 0)
|
||||||
if ((retcode = git_remote_connect(origin, GIT_DIRECTION_FETCH)) < 0)
|
|
||||||
goto on_error;
|
|
||||||
|
|
||||||
if ((retcode = git_remote_download(origin)) < 0)
|
|
||||||
goto on_error;
|
|
||||||
|
|
||||||
/* Create "origin/foo" branches for all remote branches */
|
|
||||||
if ((retcode = git_remote_update_tips(origin)) < 0)
|
|
||||||
goto on_error;
|
goto on_error;
|
||||||
|
|
||||||
/* Point HEAD to the requested branch */
|
/* Point HEAD to the requested branch */
|
||||||
if (options->checkout_branch)
|
if (options->checkout_branch)
|
||||||
retcode = update_head_to_branch(repo, options);
|
retcode = update_head_to_branch(repo, options->remote_name, options->checkout_branch);
|
||||||
/* Point HEAD to the same ref as the remote's head */
|
/* Point HEAD to the same ref as the remote's head */
|
||||||
else
|
else
|
||||||
retcode = update_head_to_remote(repo, origin);
|
retcode = update_head_to_remote(repo, origin);
|
||||||
@ -432,6 +441,45 @@ static void normalize_options(git_clone_options *dst, const git_clone_options *s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int git_clone_into(git_repository *repo, git_remote *remote, git_checkout_opts *co_opts, const char *branch)
|
||||||
|
{
|
||||||
|
int error = 0, old_fetchhead;
|
||||||
|
size_t nspecs;
|
||||||
|
|
||||||
|
assert(repo && remote);
|
||||||
|
|
||||||
|
if (!git_repository_is_empty(repo)) {
|
||||||
|
giterr_set(GITERR_INVALID, "the repository is not empty");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
old_fetchhead = git_remote_update_fetchhead(remote);
|
||||||
|
git_remote_set_update_fetchhead(remote, 0);
|
||||||
|
|
||||||
|
if ((error = do_fetch(remote)) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (branch)
|
||||||
|
error = update_head_to_branch(repo, git_remote_name(remote), branch);
|
||||||
|
/* Point HEAD to the same ref as the remote's head */
|
||||||
|
else
|
||||||
|
error = update_head_to_remote(repo, remote);
|
||||||
|
|
||||||
|
if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
|
||||||
|
error = git_checkout_head(repo, co_opts);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
git_remote_set_update_fetchhead(remote, old_fetchhead);
|
||||||
|
/* Remove the tags refspec */
|
||||||
|
nspecs = git_remote_refspec_count(remote);
|
||||||
|
git_remote_remove_refspec(remote, nspecs);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
int git_clone(
|
int git_clone(
|
||||||
git_repository **out,
|
git_repository **out,
|
||||||
const char *url,
|
const char *url,
|
||||||
|
@ -126,6 +126,45 @@ void test_online_clone__can_checkout_a_cloned_repo(void)
|
|||||||
git_buf_free(&path);
|
git_buf_free(&path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_online_clone__clone_into(void)
|
||||||
|
{
|
||||||
|
git_buf path = GIT_BUF_INIT;
|
||||||
|
git_remote *remote;
|
||||||
|
git_reference *head;
|
||||||
|
git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT;
|
||||||
|
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
|
||||||
|
|
||||||
|
bool checkout_progress_cb_was_called = false,
|
||||||
|
fetch_progress_cb_was_called = false;
|
||||||
|
|
||||||
|
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
|
||||||
|
checkout_opts.progress_cb = &checkout_progress;
|
||||||
|
checkout_opts.progress_payload = &checkout_progress_cb_was_called;
|
||||||
|
|
||||||
|
cl_git_pass(git_repository_init(&g_repo, "./foo", false));
|
||||||
|
cl_git_pass(git_remote_create(&remote, g_repo, "origin", LIVE_REPO_URL));
|
||||||
|
|
||||||
|
callbacks.transfer_progress = &fetch_progress;
|
||||||
|
callbacks.payload = &fetch_progress_cb_was_called;
|
||||||
|
git_remote_set_callbacks(remote, &callbacks);
|
||||||
|
|
||||||
|
cl_git_pass(git_clone_into(g_repo, remote, &checkout_opts, NULL));
|
||||||
|
|
||||||
|
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
|
||||||
|
cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&path)));
|
||||||
|
|
||||||
|
cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
|
||||||
|
cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
|
||||||
|
cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
|
||||||
|
|
||||||
|
cl_assert_equal_i(true, checkout_progress_cb_was_called);
|
||||||
|
cl_assert_equal_i(true, fetch_progress_cb_was_called);
|
||||||
|
|
||||||
|
git_remote_free(remote);
|
||||||
|
git_reference_free(head);
|
||||||
|
git_buf_free(&path);
|
||||||
|
}
|
||||||
|
|
||||||
static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *payload)
|
static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *payload)
|
||||||
{
|
{
|
||||||
int *callcount = (int*)payload;
|
int *callcount = (int*)payload;
|
||||||
|
Loading…
Reference in New Issue
Block a user