diff options
author | Carlos Martín Nieto <cmn@dwim.me> | 2013-12-21 17:18:21 +0000 |
---|---|---|
committer | Carlos Martín Nieto <cmn@dwim.me> | 2014-05-28 15:40:20 +0200 |
commit | 4386d80be108102548d4ff52c875aedfa94e7412 (patch) | |
tree | 776b4eeb7aeffd59c87606031b2a2e78661cea13 /src/clone.c | |
parent | 433ba614a2ef948008510a1b1189702d515d2fc4 (diff) | |
download | libgit2-4386d80be108102548d4ff52c875aedfa94e7412.tar.gz |
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.
Diffstat (limited to 'src/clone.c')
-rw-r--r-- | src/clone.c | 114 |
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; +} |