目前 libgit2 对 pull 操作的支持还不是太好,所以目前能找到的资料都指出 pull 操作就是先 fetch 然后再 merge 目标分支。比如要实现 git pull origin master ,就先 fetch 然后将 master 切换到 head 再将 origin master 合并到 head。
那么就先 fetch
git_remote *remote = nullptr;
git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
// get a remote
git_remote_lookup(&remote, rep, "origin");
// git fetch
fetch_opts.callbacks.credentials = cred_acquire_cb;
git_remote_fetch(remote, nullptr, &fetch_opts, nullptr);
git_remote_fetch 这个接口的后三个参数都是可以传 null 的,但是如果报了下面这个错:
authentication required but no callback set
说明远端地址需要验证身份,所以我们要设置 ssh 证书的回调。
int cred_acquire_cb(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types,
void *payload)
{
git_cred_ssh_key_new(cred, username_from_url, nullptr, "/Users/xiaochen/.ssh/id_rsa", nullptr);
return 0;
}
这样就完成了 fetch 操作。so easy
prune 也是 fetch 操作的一个重要参数。表示:清理掉远端已经删除的分支对应的本地分支,有点绕,不过应该还好理解吧。具体信息可以查一下下面这个命令。
git fetch -p (或 git fetch –prune)
在代码里实现只需要在 option 参数里做一个设置:
fetch_opts.prune = GIT_FETCH_PRUNE;
这样就强制使用 prune 参数了。另外几个可选枚举值可以去读注释。
接下来为了实现类似 pull 的效果,就是蛋疼的合并操作了,顺便可以复习一下之前的内容。
首先将本地 master 设为 head
git_reference *local_master = nullptr;
git_branch_lookup(&local_master, rep, "master", GIT_BRANCH_LOCAL);
git_repository_set_head(rep, git_reference_name(local_master));
然后去拿 origin master 的 commit
git_reference *origin_master = nullptr;
git_branch_lookup(&origin_master, rep, "origin/master", GIT_BRANCH_REMOTE);
合并
git_merge_options merge_opt = GIT_MERGE_OPTIONS_INIT;
git_checkout_options checkout_opt = GIT_CHECKOUT_OPTIONS_INIT;
const git_annotated_commit *their_head[10];
git_annotated_commit_from_ref((git_annotated_commit **) &their_head[0], rep, origin_master);
git_merge(rep, their_head, 1, &merge_opt, &checkout_opt);
解决冲突
git_index *index = nullptr;
git_index_conflict_iterator *conflict_iterator = nullptr;
git_repository_index(&index, rep);
if (git_index_has_conflicts(index))
{
const git_index_entry *ancestor_out = nullptr;
const git_index_entry *our_out = nullptr;
const git_index_entry *their_out = nullptr;
git_index_conflict_iterator_new(&conflict_iterator, index);
while (git_index_conflict_next(&ancestor_out, &our_out, &their_out, conflict_iterator) != GIT_ITEROVER)
{
if (ancestor_out) std::cout << "ancestor: " << ancestor_out->path << std::endl;
if (our_out) std::cout << "our: " << our_out->path << std::endl;
if (their_out) std::cout << "their: " << their_out->path << std::endl;
}
// git checkout --theirs <file>
git_checkout_options opt = GIT_CHECKOUT_OPTIONS_INIT;
opt.checkout_strategy |= GIT_CHECKOUT_USE_THEIRS;
git_checkout_index(rep, index, &opt);
git_index_conflict_iterator_free(conflict_iterator);
}
add 和 commit
git_commit_lookup(&their_commit, rep, git_reference_target(origin_master));
git_commit_lookup(&our_commit, rep, git_reference_target(local_master));
git_index_update_all(index, nullptr, nullptr, nullptr);
git_index_write(index);
git_index_write_tree(&new_tree_id, index);
git_tree_lookup(&new_tree, rep, &new_tree_id);
git_signature_now(&me, "XiaochenFTX", "xiaochenftx@gmail.com");
git_commit_create_v(&commit_id, rep, git_reference_name(local_master), me, me, "UTF-8", "pull commit", new_tree, 2,
our_commit, their_commit);
清空状态
git_repository_state_cleanup(rep);
打完收工。
示例代码:sample9