summaryrefslogtreecommitdiff
path: root/src/clone.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/clone.c')
-rw-r--r--src/clone.c114
1 files changed, 103 insertions, 11 deletions
diff --git a/src/clone.c b/src/clone.c
index 8381ec63c..b66ba6b4c 100644
--- a/src/clone.c
+++ b/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;
+}