mirror of
https://git.proxmox.com/git/libgit2
synced 2026-01-03 02:04:25 +00:00
clone: allow for linking in local clone
If requested, git_clone_local_into() will try to link the object files instead of copying them. This only works on non-Windows (since it doesn't have this) when both are on the same filesystem (which are unix semantics).
This commit is contained in:
parent
94f742bac6
commit
2614819cf3
@ -144,6 +144,9 @@ GIT_EXTERN(int) git_clone_into(
|
||||
* @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 link wether to use hardlinks instead of copying
|
||||
* objects. This is only possible if both repositories are on the same
|
||||
* filesystem.
|
||||
* @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
|
||||
@ -154,6 +157,7 @@ GIT_EXTERN(int) git_clone_local_into(
|
||||
git_remote *remote,
|
||||
const git_checkout_options *co_opts,
|
||||
const char *branch,
|
||||
int link,
|
||||
const git_signature *signature);
|
||||
|
||||
/** @} */
|
||||
|
||||
36
src/clone.c
36
src/clone.c
@ -405,9 +405,10 @@ int git_clone(
|
||||
|
||||
if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
|
||||
if (git_clone__should_clone_local(url, options.local)) {
|
||||
int link = options.local != GIT_CLONE_LOCAL_NO_LINKS;
|
||||
error = git_clone_local_into(
|
||||
repo, origin, &options.checkout_opts,
|
||||
options.checkout_branch, options.signature);
|
||||
options.checkout_branch, link, options.signature);
|
||||
} else {
|
||||
error = git_clone_into(
|
||||
repo, origin, &options.checkout_opts,
|
||||
@ -448,15 +449,36 @@ static const char *repository_base(git_repository *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)
|
||||
static bool can_link(const char *src, const char *dst, int link)
|
||||
{
|
||||
int error, root;
|
||||
#ifdef GIT_WIN32
|
||||
return false;
|
||||
#else
|
||||
|
||||
struct stat st_src, st_dst;
|
||||
|
||||
if (!link)
|
||||
return false;
|
||||
|
||||
if (p_stat(src, &st_src) < 0)
|
||||
return false;
|
||||
|
||||
if (p_stat(dst, &st_dst) < 0)
|
||||
return false;
|
||||
|
||||
return st_src.st_dev == st_dst.st_dev;
|
||||
#endif
|
||||
}
|
||||
|
||||
int git_clone_local_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, int link, const git_signature *signature)
|
||||
{
|
||||
int error, root, flags;
|
||||
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);
|
||||
assert(repo && remote);
|
||||
|
||||
if (!git_repository_is_empty(repo)) {
|
||||
giterr_set(GITERR_INVALID, "the repository is not empty");
|
||||
@ -495,8 +517,12 @@ int git_clone_local_into(git_repository *repo, git_remote *remote, const git_che
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
flags = 0;
|
||||
if (can_link(git_repository_path(src), git_repository_path(repo), link))
|
||||
flags |= GIT_CPDIR_LINK_FILES;
|
||||
|
||||
if ((error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb),
|
||||
0, GIT_OBJECT_DIR_MODE)) < 0)
|
||||
flags, GIT_OBJECT_DIR_MODE)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
#include "git2/clone.h"
|
||||
#include "clone.h"
|
||||
#include "buffer.h"
|
||||
#include "path.h"
|
||||
#include "posix.h"
|
||||
|
||||
void assert_clone(const char *path, git_clone_local_t opt, int val)
|
||||
{
|
||||
@ -29,3 +31,60 @@ void test_clone_local__should_clone_local(void)
|
||||
cl_assert_equal_i(true, git_clone__should_clone_local(path, GIT_CLONE_LOCAL_NO_LINKS));
|
||||
cl_assert_equal_i(false, git_clone__should_clone_local(path, GIT_CLONE_NO_LOCAL));
|
||||
}
|
||||
|
||||
void test_clone_local__hardlinks(void)
|
||||
{
|
||||
git_repository *repo;
|
||||
git_remote *remote;
|
||||
git_signature *sig;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
struct stat st;
|
||||
|
||||
cl_git_pass(git_repository_init(&repo, "./clone.git", true));
|
||||
cl_git_pass(git_remote_create(&remote, repo, "origin", cl_fixture("testrepo.git")));
|
||||
cl_git_pass(git_signature_now(&sig, "foo", "bar"));
|
||||
cl_git_pass(git_clone_local_into(repo, remote, NULL, NULL, true, sig));
|
||||
|
||||
git_remote_free(remote);
|
||||
git_repository_free(repo);
|
||||
|
||||
/*
|
||||
* We can't rely on the link option taking effect in the first
|
||||
* clone, since the temp dir and fixtures dir may reside on
|
||||
* different filesystems. We perform the second clone
|
||||
* side-by-side to make sure this is the case.
|
||||
*/
|
||||
|
||||
cl_git_pass(git_repository_init(&repo, "./clone2.git", true));
|
||||
cl_git_pass(git_buf_puts(&buf, cl_git_path_url("clone.git")));
|
||||
cl_git_pass(git_remote_create(&remote, repo, "origin", buf.ptr));
|
||||
cl_git_pass(git_clone_local_into(repo, remote, NULL, NULL, true, sig));
|
||||
|
||||
#ifndef GIT_WIN32
|
||||
git_buf_clear(&buf);
|
||||
cl_git_pass(git_buf_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125"));
|
||||
|
||||
cl_git_pass(p_stat(buf.ptr, &st));
|
||||
cl_assert(st.st_nlink > 1);
|
||||
#endif
|
||||
|
||||
git_remote_free(remote);
|
||||
git_repository_free(repo);
|
||||
git_buf_clear(&buf);
|
||||
|
||||
cl_git_pass(git_repository_init(&repo, "./clone3.git", true));
|
||||
cl_git_pass(git_buf_puts(&buf, cl_git_path_url("clone.git")));
|
||||
cl_git_pass(git_remote_create(&remote, repo, "origin", buf.ptr));
|
||||
cl_git_pass(git_clone_local_into(repo, remote, NULL, NULL, false, sig));
|
||||
|
||||
git_buf_clear(&buf);
|
||||
cl_git_pass(git_buf_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125"));
|
||||
|
||||
cl_git_pass(p_stat(buf.ptr, &st));
|
||||
cl_assert_equal_i(1, st.st_nlink);
|
||||
|
||||
git_buf_free(&buf);
|
||||
git_signature_free(sig);
|
||||
git_remote_free(remote);
|
||||
git_repository_free(repo);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user