目前 libgit2 对 pull 操作的支持还不是太好,所以目前能找到的资料都指出 pull 操作就是先 fetch 然后再 merge 目标分支。比如要实现 git pull origin master ,就先 fetch 然后将 master 切换到 head 再将 origin master 合并到 head。
那么就先 fetch
1 2 3 4 5 6 7 8 |
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 证书的回调。
12345 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 参数里做一个设置:
1 fetch_opts.prune = GIT_FETCH_PRUNE;这样就强制使用 prune 参数了。另外几个可选枚举值可以去读注释。
接下来为了实现类似 pull 的效果,就是蛋疼的合并操作了,顺便可以复习一下之前的内容。
首先将本地 master 设为 head
1 2 3 |
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
1 2 |
git_reference* origin_master = nullptr; git_branch_lookup(&origin_master, rep, "origin/master", GIT_BRANCH_REMOTE); |
合并
1 2 3 4 5 |
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); |
解决冲突
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
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
1 2 3 4 5 6 7 8 9 10 11 |
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); |
清空状态
1 |
git_repository_state_cleanup(rep); |
打完收工。
示例代码:sample9