使用 git init、fetch 和 checkout 克隆一个 git 存储库
clone a git repository using git init, fetch, and checkout
简介
我正在使用用 C 编写的流行 libgit2
进行实验。
我正在尝试做一个 clone
但使用一种不常见的方式。按顺序,git 命令:
git init
git remote add origin https://repository.git
git fetch origin
git checkout master
通过使用 git bash
和以下命令,我可以获得一个包含所有历史记录的现有存储库。
问题
现在,让我们看看我当前的 C++ 实现。以下代码试图复制之前编写的 git 命令的行为。
#define url "https://repository.git"
#define path "./"
#define user "user"
#define pass "pass"
/** credential callback **/
int credentials(git_cred **cred, const char *, const char *, unsigned int, void *) {
return git_cred_userpass_plaintext_new(cred, user, pass);
}
class Git {
public:
Git() {
git_libgit2_init();
}
~Git() {
git_repository_free(repository);
git_libgit2_shutdown();
}
void update() {
init();
fetch();
checkout();
}
private:
void init() {
assertSuccess(git_repository_init(&repository, path, GIT_CVAR_FALSE));
git_remote *remote = nullptr;
git_remote_callbacks options = GIT_REMOTE_CALLBACKS_INIT;
assertSuccess(git_remote_create(&remote, repository, "origin", url));
options.credentials = credentials;
git_remote_connect(remote, GIT_DIRECTION_FETCH, &options, nullptr, nullptr);
}
void fetch() {
git_remote* remote = nullptr;
assertSuccess(git_remote_lookup(&remote, repository, "origin"));
git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
options.callbacks.credentials = credentials;
assertSuccess(git_remote_fetch(remote, nullptr, &options, nullptr));
}
void checkout() {
git_checkout_options options = GIT_CHECKOUT_OPTIONS_INIT;
options.checkout_strategy = GIT_CHECKOUT_FORCE;
assertSuccess(git_checkout_head(repository, &options));
assertSuccess(git_checkout_index(repository, nullptr, &options));
assertSuccess(git_repository_set_head(repository, "refs/heads/master"));
git_object *treeish = nullptr;
assertSuccess(git_revparse_single(&treeish, repository, "master"));
assertSuccess(git_checkout_tree(repository, treeish, &options));
}
void assertSuccess(int error) {
if (!error) return;
const git_error *e = giterr_last();
std::cout << "code: " << e->klass << " error: " << e->message << std::endl;
exit(1);
}
private:
git_repository *repository = nullptr;
};
int main() {
Git git;
git.update();
return 0;
}
显然,这是行不通的。 运行 这个程序(调用 Git().update()
),我在结帐步骤中遇到以下错误:
code: 4 error: reference 'refs/heads/master' not found
已创建 git 存储库,我可以看到已通过 git bash
成功设置的远程源。我可以从 git bash
做一个手册 git checkout master
所以我猜我目前对 checkout
的实施是失败的。
有人可以强调我这个错误吗?我找不到足够的资源,也找不到对互联网上所有找到的示例的支持。
编辑
由于测试我的代码可能会有所帮助,让我将我的 CMakeLists.txt
用于编译 libgit2
。 (源码https://github.com/libgit2/libgit2)
cmake_minimum_required(VERSION 3.13)
project(test)
include_directories(libgit/include)
LINK_DIRECTORIES(${LIBSSH2_LIBRARY_DIRS})
add_subdirectory(libgit)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_BUILD_TYPE Release)
add_executable(test src/Git.h)
target_link_libraries(test git2)
缺少的 link 是,由于您是从头开始构建存储库,因此您的存储库仍未诞生(即,它的 HEAD
指向 non-existent refs/heads/master
参考)。最重要的是,checkout
在 libgit2-land 中唯一关心的是从 ODB 中取出文件,它不会写入或更新引用。
因此,您错过了 git checkout
使用(很有可能)git update-ref
使 master
指向 origin/master
的 OID 的步骤,您可以通过 git_reference_create and friends.
做到这一点
类似于以下内容 (brain-compiled):
static int setup_tracking_branch(char *branch_name, git_reference *upstream)
{
git_reference *tracking;
git_oid up_oid = git_reference_target_peel(upstream);
char *ui_name;
#if 0
/* should be constructed from `upstream`. IIRC there are some
* git_reference accessors that can help
* (eg. `refs/remotes/origin/heads/master` is `origin/master`).
*/
#else
ui_name = "origin/master";
#endif
if (git_reference_create_matching(&tracking,
git_reference_owner(upstream),
branch_name, up_oid, 0, NULL, "branch: created from %s", ui_name) < 0 ||
git_branch_set_upstream(tracking, git_reference_name(upstream)) < 0) {
printf("failed to create remote-tracking branch\n");
return -1;
}
cleanup:
git_reference_free(tracking);
return 0;
}
这采用了您希望新分支的名称 (-b
),以及要跟踪的远程分支 (-t
),尽管这显然不是一个完整的重新实现,甚至不是正确的, 所以 YMMV.
简介
我正在使用用 C 编写的流行 libgit2
进行实验。
我正在尝试做一个 clone
但使用一种不常见的方式。按顺序,git 命令:
git init
git remote add origin https://repository.git
git fetch origin
git checkout master
通过使用 git bash
和以下命令,我可以获得一个包含所有历史记录的现有存储库。
问题
现在,让我们看看我当前的 C++ 实现。以下代码试图复制之前编写的 git 命令的行为。
#define url "https://repository.git"
#define path "./"
#define user "user"
#define pass "pass"
/** credential callback **/
int credentials(git_cred **cred, const char *, const char *, unsigned int, void *) {
return git_cred_userpass_plaintext_new(cred, user, pass);
}
class Git {
public:
Git() {
git_libgit2_init();
}
~Git() {
git_repository_free(repository);
git_libgit2_shutdown();
}
void update() {
init();
fetch();
checkout();
}
private:
void init() {
assertSuccess(git_repository_init(&repository, path, GIT_CVAR_FALSE));
git_remote *remote = nullptr;
git_remote_callbacks options = GIT_REMOTE_CALLBACKS_INIT;
assertSuccess(git_remote_create(&remote, repository, "origin", url));
options.credentials = credentials;
git_remote_connect(remote, GIT_DIRECTION_FETCH, &options, nullptr, nullptr);
}
void fetch() {
git_remote* remote = nullptr;
assertSuccess(git_remote_lookup(&remote, repository, "origin"));
git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
options.callbacks.credentials = credentials;
assertSuccess(git_remote_fetch(remote, nullptr, &options, nullptr));
}
void checkout() {
git_checkout_options options = GIT_CHECKOUT_OPTIONS_INIT;
options.checkout_strategy = GIT_CHECKOUT_FORCE;
assertSuccess(git_checkout_head(repository, &options));
assertSuccess(git_checkout_index(repository, nullptr, &options));
assertSuccess(git_repository_set_head(repository, "refs/heads/master"));
git_object *treeish = nullptr;
assertSuccess(git_revparse_single(&treeish, repository, "master"));
assertSuccess(git_checkout_tree(repository, treeish, &options));
}
void assertSuccess(int error) {
if (!error) return;
const git_error *e = giterr_last();
std::cout << "code: " << e->klass << " error: " << e->message << std::endl;
exit(1);
}
private:
git_repository *repository = nullptr;
};
int main() {
Git git;
git.update();
return 0;
}
显然,这是行不通的。 运行 这个程序(调用 Git().update()
),我在结帐步骤中遇到以下错误:
code: 4 error: reference 'refs/heads/master' not found
已创建 git 存储库,我可以看到已通过 git bash
成功设置的远程源。我可以从 git bash
做一个手册 git checkout master
所以我猜我目前对 checkout
的实施是失败的。
有人可以强调我这个错误吗?我找不到足够的资源,也找不到对互联网上所有找到的示例的支持。
编辑
由于测试我的代码可能会有所帮助,让我将我的 CMakeLists.txt
用于编译 libgit2
。 (源码https://github.com/libgit2/libgit2)
cmake_minimum_required(VERSION 3.13)
project(test)
include_directories(libgit/include)
LINK_DIRECTORIES(${LIBSSH2_LIBRARY_DIRS})
add_subdirectory(libgit)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_BUILD_TYPE Release)
add_executable(test src/Git.h)
target_link_libraries(test git2)
缺少的 link 是,由于您是从头开始构建存储库,因此您的存储库仍未诞生(即,它的 HEAD
指向 non-existent refs/heads/master
参考)。最重要的是,checkout
在 libgit2-land 中唯一关心的是从 ODB 中取出文件,它不会写入或更新引用。
因此,您错过了 git checkout
使用(很有可能)git update-ref
使 master
指向 origin/master
的 OID 的步骤,您可以通过 git_reference_create and friends.
类似于以下内容 (brain-compiled):
static int setup_tracking_branch(char *branch_name, git_reference *upstream)
{
git_reference *tracking;
git_oid up_oid = git_reference_target_peel(upstream);
char *ui_name;
#if 0
/* should be constructed from `upstream`. IIRC there are some
* git_reference accessors that can help
* (eg. `refs/remotes/origin/heads/master` is `origin/master`).
*/
#else
ui_name = "origin/master";
#endif
if (git_reference_create_matching(&tracking,
git_reference_owner(upstream),
branch_name, up_oid, 0, NULL, "branch: created from %s", ui_name) < 0 ||
git_branch_set_upstream(tracking, git_reference_name(upstream)) < 0) {
printf("failed to create remote-tracking branch\n");
return -1;
}
cleanup:
git_reference_free(tracking);
return 0;
}
这采用了您希望新分支的名称 (-b
),以及要跟踪的远程分支 (-t
),尽管这显然不是一个完整的重新实现,甚至不是正确的, 所以 YMMV.