mirror of https://github.com/KLayout/klayout.git
Refined solution for git clone: now accepts tags too.
This commit is contained in:
parent
4b6cac9527
commit
2e16a1e3e4
|
|
@ -25,6 +25,7 @@
|
|||
#include "tlFileUtils.h"
|
||||
#include "tlProgress.h"
|
||||
#include "tlStaticObjects.h"
|
||||
#include "tlLog.h"
|
||||
|
||||
#include <git2.h>
|
||||
#include <cstdio>
|
||||
|
|
@ -124,6 +125,101 @@ checkout_progress(const char * /*path*/, size_t cur, size_t tot, void *payload)
|
|||
progress->set (count + 5000u);
|
||||
}
|
||||
|
||||
static void check (int error)
|
||||
{
|
||||
if (error != 0) {
|
||||
#if LIBGIT2_VER_MAJOR > 0 || (LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR >= 28)
|
||||
const git_error *err = git_error_last ();
|
||||
#else
|
||||
const git_error *err = giterr_last ();
|
||||
#endif
|
||||
throw tl::Exception (tl::to_string (tr ("Error cloning Git repo: %s")), (const char *) err->message);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
ref_matches (const char *name, const std::string &ref)
|
||||
{
|
||||
if (!name) {
|
||||
return false;
|
||||
} else if (name == ref) {
|
||||
return true;
|
||||
} else if (name == "refs/heads/" + ref) {
|
||||
return true;
|
||||
} else if (name == "refs/tags/" + ref) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
checkout_branch (git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch)
|
||||
{
|
||||
git_buf remote_branch = GIT_BUF_INIT_CONST (NULL, 0);
|
||||
|
||||
try {
|
||||
|
||||
git_oid oid;
|
||||
|
||||
// if no branch is given, use the default branch
|
||||
if (! branch) {
|
||||
check (git_remote_default_branch (&remote_branch, remote));
|
||||
branch = remote_branch.ptr;
|
||||
if (tl::verbosity () >= 10) {
|
||||
tl::info << tr ("Git checkout: Using default branch for repository ") << git_remote_url (remote) << ": " << branch;
|
||||
}
|
||||
} else {
|
||||
if (tl::verbosity () >= 10) {
|
||||
tl::info << tr ("Git checkout: Checking out branch for repository ") << git_remote_url (remote) << ": " << branch;
|
||||
}
|
||||
}
|
||||
|
||||
// resolve the branch by using ls-remote:
|
||||
|
||||
size_t n = 0;
|
||||
const git_remote_head **ls = NULL;
|
||||
check (git_remote_ls (&ls, &n, remote));
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::info << "Git checkout: ls-remote on " << git_remote_url (remote) << ":";
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
const git_remote_head *rh = ls[i];
|
||||
if (tl::verbosity () >= 20) {
|
||||
char oid_fmt [80];
|
||||
git_oid_tostr (oid_fmt, sizeof (oid_fmt), &rh->oid);
|
||||
tl::info << " " << rh->name << ": " << (const char *) oid_fmt;
|
||||
}
|
||||
if (ref_matches (rh->name, branch)) {
|
||||
oid = rh->oid;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (! found) {
|
||||
throw tl::Exception (tl::to_string (tr ("Git checkout - Unable to resolve reference name: ")) + branch);
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 10) {
|
||||
char oid_fmt [80];
|
||||
git_oid_tostr (oid_fmt, sizeof (oid_fmt), &oid);
|
||||
tl::info << tr ("Git checkout: resolving ") << branch << tr (" to ") << (const char *) oid_fmt;
|
||||
}
|
||||
|
||||
check (git_repository_set_head_detached (repo, &oid));
|
||||
check (git_checkout_head (repo, co_opts));
|
||||
|
||||
} catch (...) {
|
||||
git_buf_dispose (&remote_branch);
|
||||
throw;
|
||||
}
|
||||
|
||||
git_buf_dispose (&remote_branch);
|
||||
}
|
||||
|
||||
void
|
||||
GitObject::read (const std::string &org_url, const std::string &org_filter, const std::string &branch, double timeout, tl::InputHttpStreamCallback *callback)
|
||||
|
|
@ -151,6 +247,8 @@ GitObject::read (const std::string &org_url, const std::string &org_filter, cons
|
|||
// @@@ use callback, timeout?
|
||||
tl::RelativeProgress progress (tl::to_string (tr ("Download progress")), 10000, 1 /*yield always*/);
|
||||
|
||||
// build checkout options
|
||||
|
||||
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
|
||||
|
||||
const char *paths_cstr[1];
|
||||
|
|
@ -160,49 +258,68 @@ GitObject::read (const std::string &org_url, const std::string &org_filter, cons
|
|||
checkout_opts.paths.strings = (char **) &paths_cstr;
|
||||
}
|
||||
|
||||
/*
|
||||
checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE |
|
||||
GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
|
||||
@@@*/
|
||||
checkout_opts.progress_cb = &checkout_progress;
|
||||
checkout_opts.progress_payload = (void *) &progress;
|
||||
|
||||
git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
|
||||
// build fetch options
|
||||
|
||||
clone_opts.checkout_opts = checkout_opts;
|
||||
git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
|
||||
|
||||
// NOTE: really has to be a branch! Tags won't work.
|
||||
if (! branch.empty ()) {
|
||||
clone_opts.checkout_branch = branch.c_str ();
|
||||
}
|
||||
fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
|
||||
|
||||
#if LIBGIT2_VER_MAJOR > 1 || (LIBGIT2_VER_MAJOR == 1 && LIBGIT2_VER_MINOR >= 7)
|
||||
clone_opts.fetch_opts.depth = 1; // shallow (single commit)
|
||||
fetch_opts.depth = 1; // shallow (single commit)
|
||||
#endif
|
||||
clone_opts.fetch_opts.callbacks.transfer_progress = &fetch_progress;
|
||||
clone_opts.fetch_opts.callbacks.payload = (void *) &progress;
|
||||
fetch_opts.callbacks.transfer_progress = &fetch_progress;
|
||||
fetch_opts.callbacks.payload = (void *) &progress;
|
||||
|
||||
// build refspecs in case they are needed
|
||||
|
||||
char *refs[] = { (char *) branch.c_str () };
|
||||
git_strarray refspecs;
|
||||
refspecs.count = 1;
|
||||
refspecs.strings = refs;
|
||||
git_strarray *refspecs_p = branch.empty () ? NULL : &refspecs;
|
||||
|
||||
// Make repository
|
||||
|
||||
// Do the clone
|
||||
git_repository *cloned_repo = NULL;
|
||||
int error = git_clone (&cloned_repo, url.c_str (), m_local_path.c_str (), &clone_opts);
|
||||
if (error != 0) {
|
||||
#if LIBGIT2_VER_MAJOR > 0 || (LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR >= 28)
|
||||
const git_error *err = git_error_last ();
|
||||
#else
|
||||
const git_error *err = giterr_last ();
|
||||
#endif
|
||||
throw tl::Exception (tl::to_string (tr ("Error cloning Git repo: %s")), (const char *) err->message);
|
||||
git_remote *remote = NULL;
|
||||
|
||||
try {
|
||||
|
||||
check (git_repository_init (&cloned_repo, m_local_path.c_str (), 0));
|
||||
|
||||
check (git_remote_create (&remote, cloned_repo, "download", url.c_str ()));
|
||||
|
||||
// actually fetch
|
||||
if (tl::verbosity () >= 10) {
|
||||
tl::info << tr ("Fetching Git repo from ") << git_remote_url (remote) << " ...";
|
||||
}
|
||||
check (git_remote_fetch (remote, refspecs_p, &fetch_opts, NULL));
|
||||
|
||||
// checkout
|
||||
checkout_branch (cloned_repo, remote, &checkout_opts, branch.empty () ? 0 : branch.c_str ());
|
||||
|
||||
// free the repo and remote
|
||||
git_repository_free (cloned_repo);
|
||||
git_remote_free (remote);
|
||||
|
||||
// get rid of ".git" - we do not need it anymore
|
||||
|
||||
tl::rm_dir_recursive (tl::combine_path (m_local_path, ".git"));
|
||||
|
||||
} catch (...) {
|
||||
// free the repo in the error case
|
||||
if (cloned_repo != NULL) {
|
||||
git_repository_free (cloned_repo);
|
||||
}
|
||||
if (remote != NULL) {
|
||||
git_remote_free (remote);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
if (! cloned_repo) {
|
||||
throw tl::Exception (tl::to_string (tr ("Error cloning Git repo - no data available")));
|
||||
}
|
||||
|
||||
git_repository_free (cloned_repo);
|
||||
|
||||
// remove the worktree as we don't need it
|
||||
tl::rm_dir_recursive (tl::combine_path (m_local_path, ".git"));
|
||||
|
||||
// pull subfolder files to target path level
|
||||
if (! subdir.empty ()) {
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,73 @@ TEST(6_branch)
|
|||
EXPECT_EQ (found, true);
|
||||
}
|
||||
|
||||
TEST(7_invalid_branch)
|
||||
TEST(7_tag)
|
||||
{
|
||||
std::string path = tl::TestBase::tmp_file ("repo");
|
||||
tl::GitObject repo (path);
|
||||
repo.read (test_url + "/src", std::string ("grain.xml"), std::string ("1.2"));
|
||||
|
||||
EXPECT_EQ (tl::file_exists (tl::combine_path (path, ".git")), false);
|
||||
EXPECT_EQ (tl::file_exists (tl::combine_path (path, "grain.xml")), true);
|
||||
EXPECT_EQ (tl::file_exists (tl::combine_path (path, "macros")), false);
|
||||
|
||||
tl::InputStream file (tl::combine_path (path, "grain.xml"));
|
||||
tl::TextInputStream grain (file);
|
||||
bool found = false;
|
||||
while (! grain.at_end () && ! found) {
|
||||
std::string line = grain.get_line ();
|
||||
if (line.find ("<version>1.2</version>") != std::string::npos) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ (found, true);
|
||||
}
|
||||
|
||||
TEST(8_refspec)
|
||||
{
|
||||
std::string path = tl::TestBase::tmp_file ("repo");
|
||||
tl::GitObject repo (path);
|
||||
repo.read (test_url + "/src", std::string ("grain.xml"), std::string ("refs/tags/1.5"));
|
||||
|
||||
EXPECT_EQ (tl::file_exists (tl::combine_path (path, ".git")), false);
|
||||
EXPECT_EQ (tl::file_exists (tl::combine_path (path, "grain.xml")), true);
|
||||
EXPECT_EQ (tl::file_exists (tl::combine_path (path, "macros")), false);
|
||||
|
||||
tl::InputStream file (tl::combine_path (path, "grain.xml"));
|
||||
tl::TextInputStream grain (file);
|
||||
bool found = false;
|
||||
while (! grain.at_end () && ! found) {
|
||||
std::string line = grain.get_line ();
|
||||
if (line.find ("<version>1.5</version>") != std::string::npos) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ (found, true);
|
||||
}
|
||||
|
||||
TEST(9_HEAD)
|
||||
{
|
||||
std::string path = tl::TestBase::tmp_file ("repo");
|
||||
tl::GitObject repo (path);
|
||||
repo.read (test_url + "/src", std::string ("grain.xml"), std::string ("HEAD"));
|
||||
|
||||
EXPECT_EQ (tl::file_exists (tl::combine_path (path, ".git")), false);
|
||||
EXPECT_EQ (tl::file_exists (tl::combine_path (path, "grain.xml")), true);
|
||||
EXPECT_EQ (tl::file_exists (tl::combine_path (path, "macros")), false);
|
||||
|
||||
tl::InputStream file (tl::combine_path (path, "grain.xml"));
|
||||
tl::TextInputStream grain (file);
|
||||
bool found = false;
|
||||
while (! grain.at_end () && ! found) {
|
||||
std::string line = grain.get_line ();
|
||||
if (line.find ("<version>1.7</version>") != std::string::npos) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ (found, true);
|
||||
}
|
||||
|
||||
TEST(10_invalid_branch)
|
||||
{
|
||||
std::string path = tl::TestBase::tmp_file ("repo");
|
||||
tl::GitObject repo (path);
|
||||
|
|
@ -129,7 +195,7 @@ TEST(7_invalid_branch)
|
|||
repo.read (test_url, std::string (), std::string ("brxxx"));
|
||||
EXPECT_EQ (true, false);
|
||||
} catch (tl::Exception &ex) {
|
||||
EXPECT_EQ (ex.msg (), "Error cloning Git repo: reference 'refs/remotes/origin/brxxx' not found");
|
||||
EXPECT_EQ (ex.msg (), "Git checkout - Unable to resolve reference name: brxxx");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue