mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-02 22:49:04 +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 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
|
||||
#endif
|
||||
|
78
src/clone.c
78
src/clone.c
@ -270,23 +270,23 @@ cleanup:
|
||||
|
||||
static int update_head_to_branch(
|
||||
git_repository *repo,
|
||||
const git_clone_options *options)
|
||||
const char *remote_name,
|
||||
const char *branch)
|
||||
{
|
||||
int retcode;
|
||||
git_buf remote_branch_name = GIT_BUF_INIT;
|
||||
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",
|
||||
options->remote_name, options->checkout_branch)) < 0 )
|
||||
remote_name, branch)) < 0 )
|
||||
goto cleanup;
|
||||
|
||||
if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0)
|
||||
goto cleanup;
|
||||
|
||||
retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref),
|
||||
options->checkout_branch);
|
||||
retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch);
|
||||
|
||||
cleanup:
|
||||
git_reference_free(remote_ref);
|
||||
@ -350,6 +350,23 @@ on_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(
|
||||
git_repository *repo,
|
||||
@ -374,20 +391,12 @@ static int setup_remotes_and_fetch(
|
||||
((retcode = git_remote_add_fetch(origin, "refs/tags/*:refs/tags/*")) < 0))
|
||||
goto on_error;
|
||||
|
||||
/* Connect and download everything */
|
||||
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)
|
||||
if ((retcode = do_fetch(origin)) < 0)
|
||||
goto on_error;
|
||||
|
||||
/* Point HEAD to the requested 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 */
|
||||
else
|
||||
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(
|
||||
git_repository **out,
|
||||
const char *url,
|
||||
|
@ -126,6 +126,45 @@ void test_online_clone__can_checkout_a_cloned_repo(void)
|
||||
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)
|
||||
{
|
||||
int *callcount = (int*)payload;
|
||||
|
Loading…
Reference in New Issue
Block a user