主要内容:
1. 使用 libgit2 的准备工作
2. 构建和运行
3. 初始化一个 git 仓库
示例代码:https://github.com/XiaochenFTX/libgit2_samples
进入正题
开始直接到 libgit2 的 readme 我们首先需要从那里获取一些有用的信息。
官网:https://libgit2.github.com/
API:http://libgit2.github.com/libgit2/
虽然官方文档写的挺水的,不过毕竟可以获得信息的途径就这么少,所以凑合着也还能用。
接下来提到了两个非常重要的函数, init 和 shutdown ,必须保证在任何操作之前调用初始化:
git_libgit2_init();
释放 init 申请的资源,使用:
git_libgit2_shutdown();
这样就可以开始第一个程序了
#include <git2.h>
int main()
{
git_libgit2_init();
git_libgit2_shutdown();
return 0;
}
ok 打完收工。
先把它跑一下看看,如果可以正常跑完,就说明所有的构建基本上都没啥问题了。
当然,这并没有什么卵用。接下来来搞个有意义的代码来结束这篇。
第一个例子就实现一下 git init
const char* path = "/Users/XiaochenFTX/Documents/data";
git_repository *rep = nullptr;
git_repository_init(&rep, path, 0);
打完收工。
当然,良好的编程习惯,在最后不要忘了释放资源。
git_repository_free(rep);
git_libgit2_shutdown();
接口的详细说明可以去查官方的 API 我会在必要的地方对一些我觉得比较重要的东西进行说明。
git_repository_init 的第一个参数是将初始化好的 git_repository 指针返回,这个库的接口基本上都是这个风格的,以后就不再提这个事了。
这个函数会返回一个错误码,返回值为 0 表示执行成功,小于 0 表示有错误。所以可以通过判断返回值得方式来确定函数调用是否成功。
想知道具体报错信息,可以调用 giterr_last() 这个函数返回一个 git_error 结构体指针。
/**
* Structure to store extra details of the last error that occurred.
*
* This is kept on a per-thread basis if GIT_THREADS was defined when the
* library was build, otherwise one is kept globally for the library
*/
typedef struct {
char *message;
int klass;
} git_error;
klass 对应 git_error_t 的枚举值,用于说明是哪部分出的问题。message 是一个人类可以看懂的的说明信息。开发中遇到问题可以把这个信息打印出来,对找问题有很大帮助。
最终完成 sample1 的代码就是这个样子
#include <git2.h>
#include <iostream>
int main()
{
git_libgit2_init();
const char *path = "/Users/XiaochenFTX/Documents/data";
git_repository *rep = nullptr;
// git init
int error = git_repository_init(&rep, path, 0);
if (error < 0) {
const git_error *e = giterr_last();
std::cout << "Error: " << error << " / " << e->klass << " : " << e->message << std::endl;
goto SHUTDOWN;
}
SHUTDOWN:
git_repository_free(rep);
git_libgit2_shutdown();
return 0;
}
最后解释一下为什么要用 goto
很多同学在学习 C 语言的时候,如果遇到不负责任的老师,都会强调不要用 goto 这个东西,更有甚者干脆讲都不讲。而具体原因不外乎:会让代码逻辑混乱,可读性差,不好调试
当然,造成这些后果,都是在“如果用不好”的前提下的。然而,我认为这个语言特性真的不是不值一提的垃圾,把 goto 用好在一定程度上是可以让代码更美观、更简洁、更易读。
随便举个例子,比如跳出多层循环,难道还要引入一个外层变量再逐层判断吗?在这种情况下那种方式可读性更高?
还有就是在错误发生的情况下,直接跳到函数结尾进行清理。如果不使用 goto ,我见过几种奇葩方案,最突出的应该是用 do{}while(0); 吧,这样强行为了不用 goto 而产生的奇葩行为,我只能呵呵了。
所以,我推荐在必要的时候使用 goto 来使代码更清晰、简洁。当然,也不是随便瞎用,任何工具都有其最佳适用范围,不能矫枉过正。
附一、代码中引用 libgit2:
在根目录的 CMakeLists.txt 中把相关工程文件夹都用 add_subdirectory 加进来,并且指定 libgit2 的 include 为引用目录
include_directories(extras/libgit2/include)
add_subdirectory(extras/libgit2)
add_subdirectory(sample1)
在需要链接这个库的工程中使用 target_link_libraries 链接上 libgit2
就像我们的 sample1 中这样
target_link_libraries(sample1 git2)
有些环境下会报找不到 -lssh2 的错误:
ld: library not found for -lssh2
只要在根目录的 CMakeLists.txt 中加上一句:
LINK_DIRECTORIES(${LIBSSH2_LIBRARY_DIRS})
这样简单的构建系统就搭建好了,可以开始写代码了
使用其他 IDE 的话,直接用 cmake 导出对应的工程,在自己创建的工程中引入就可以了。
不太推荐直接编译好库放到工程里使用,我自己试了之后发现它用到的几个第三方库还需要自己手动构建再引用。
附二、示例代码的使用:
1. 从 github 上拉代码
git clone git@github.com:XiaochenFTX/libgit2_samples.git
cd libgit2_samples
git submodule update –init –recursive
2. 使用 cmake 导出熟悉的 IDE 工程,或者直接构建运行
mkdir build
cd build/
Xcode:
cmake .. -G “Xcode”
Visual Studio:
cmake .. -G "Visual Studio"
直接构建:
cmake ..
make
我使用的 IDE 是 Clion ,使用 Clion 导入工程也可以直接使用