mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-02 21:34:15 +00:00
clone: perform a "local clone" when given a local path
When git is given such a path, it will perform a "local clone", bypassing the git-aware protocol and simply copying over all objects that exist in the source. Copy this behaviour when given a local path.
This commit is contained in:
parent
433ba614a2
commit
4386d80be1
@ -123,6 +123,31 @@ GIT_EXTERN(int) git_clone_into(
|
||||
const char *branch,
|
||||
const git_signature *signature);
|
||||
|
||||
/**
|
||||
* Perform a local clone into a repository
|
||||
*
|
||||
* A "local clone" bypasses any git-aware protocols and simply copies
|
||||
* over the object database from the source repository. It is often
|
||||
* faster than a git-aware clone, but no verification of the data is
|
||||
* performed, and can copy over too much data.
|
||||
*
|
||||
* @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
|
||||
* @param signature the identity used when updating the reflog
|
||||
* @return 0 on success, any non-zero return value from a callback
|
||||
* function, or a negative value to indicate an error (use
|
||||
* `giterr_last` for a detailed error message)
|
||||
*/
|
||||
GIT_EXTERN(int) git_clone_local_into(
|
||||
git_repository *repo,
|
||||
git_remote *remote,
|
||||
const git_checkout_options *co_opts,
|
||||
const char *branch,
|
||||
const git_signature *signature);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
114
src/clone.c
114
src/clone.c
@ -22,6 +22,7 @@
|
||||
#include "refs.h"
|
||||
#include "path.h"
|
||||
#include "repository.h"
|
||||
#include "odb.h"
|
||||
|
||||
static int create_branch(
|
||||
git_reference **branch,
|
||||
@ -280,6 +281,23 @@ static bool should_checkout(
|
||||
return !git_repository_head_unborn(repo);
|
||||
}
|
||||
|
||||
static int checkout_branch(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature, const char *reflog_message)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (branch)
|
||||
error = update_head_to_branch(repo, git_remote_name(remote), branch,
|
||||
signature, reflog_message);
|
||||
/* Point HEAD to the same ref as the remote's head */
|
||||
else
|
||||
error = update_head_to_remote(repo, remote, signature, reflog_message);
|
||||
|
||||
if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
|
||||
error = git_checkout_head(repo, co_opts);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_clone_into(git_repository *repo, git_remote *_remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature)
|
||||
{
|
||||
int error;
|
||||
@ -311,15 +329,7 @@ int git_clone_into(git_repository *repo, git_remote *_remote, const git_checkout
|
||||
if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0)
|
||||
goto cleanup;
|
||||
|
||||
if (branch)
|
||||
error = update_head_to_branch(repo, git_remote_name(remote), branch,
|
||||
signature, git_buf_cstr(&reflog_message));
|
||||
/* Point HEAD to the same ref as the remote's head */
|
||||
else
|
||||
error = update_head_to_remote(repo, remote, signature, git_buf_cstr(&reflog_message));
|
||||
|
||||
if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
|
||||
error = git_checkout_head(repo, co_opts);
|
||||
error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));
|
||||
|
||||
cleanup:
|
||||
git_remote_free(remote);
|
||||
@ -362,8 +372,15 @@ int git_clone(
|
||||
return error;
|
||||
|
||||
if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
|
||||
error = git_clone_into(
|
||||
repo, origin, &options.checkout_opts, options.checkout_branch, options.signature);
|
||||
if (git__prefixcmp(url, "file://")) {
|
||||
error = git_clone_local_into(
|
||||
repo, origin, &options.checkout_opts,
|
||||
options.checkout_branch, options.signature);
|
||||
} else {
|
||||
error = git_clone_into(
|
||||
repo, origin, &options.checkout_opts,
|
||||
options.checkout_branch, options.signature);
|
||||
}
|
||||
|
||||
git_remote_free(origin);
|
||||
}
|
||||
@ -390,3 +407,78 @@ int git_clone_init_options(git_clone_options *opts, unsigned int version)
|
||||
opts, version, git_clone_options, GIT_CLONE_OPTIONS_INIT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *repository_base(git_repository *repo)
|
||||
{
|
||||
if (git_repository_is_bare(repo))
|
||||
return git_repository_path(repo);
|
||||
|
||||
return git_repository_workdir(repo);
|
||||
}
|
||||
|
||||
int git_clone_local_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature)
|
||||
{
|
||||
int error, root;
|
||||
git_repository *src;
|
||||
git_buf src_odb = GIT_BUF_INIT, dst_odb = GIT_BUF_INIT, src_path = GIT_BUF_INIT;
|
||||
git_buf reflog_message = GIT_BUF_INIT;
|
||||
const char *url;
|
||||
|
||||
assert(repo && remote && co_opts);
|
||||
|
||||
if (!git_repository_is_empty(repo)) {
|
||||
giterr_set(GITERR_INVALID, "the repository is not empty");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Let's figure out what path we should use for the source
|
||||
* repo, if it's not rooted, the path should be relative to
|
||||
* the repository's worktree/gitdir.
|
||||
*/
|
||||
url = git_remote_url(remote);
|
||||
if (!git__prefixcmp(url, "file://"))
|
||||
root = strlen("file://");
|
||||
else
|
||||
root = git_path_root(url);
|
||||
|
||||
if (root >= 0)
|
||||
git_buf_puts(&src_path, url + root);
|
||||
else
|
||||
git_buf_joinpath(&src_path, repository_base(repo), url);
|
||||
|
||||
if (git_buf_oom(&src_path))
|
||||
return -1;
|
||||
|
||||
/* Copy .git/objects/ from the source to the target */
|
||||
if ((error = git_repository_open(&src, git_buf_cstr(&src_path))) < 0) {
|
||||
git_buf_free(&src_path);
|
||||
return error;
|
||||
}
|
||||
|
||||
git_buf_joinpath(&src_odb, git_repository_path(src), GIT_OBJECTS_DIR);
|
||||
git_buf_joinpath(&dst_odb, git_repository_path(repo), GIT_OBJECTS_DIR);
|
||||
if (git_buf_oom(&src_odb) || git_buf_oom(&dst_odb)) {
|
||||
error = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb),
|
||||
0, GIT_OBJECT_DIR_MODE)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
|
||||
|
||||
if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0)
|
||||
goto cleanup;
|
||||
|
||||
error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&reflog_message);
|
||||
git_buf_free(&src_path);
|
||||
git_buf_free(&src_odb);
|
||||
git_buf_free(&dst_odb);
|
||||
git_repository_free(src);
|
||||
return error;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user