首先去看 API 中关于 commit 的部分都有哪个函数比较像,但是意外的发现了 API 中并没有官方 101-samples 中的 git_commit_create 函数,搞得我好意外。不过据我所知,实现 commit 操作肯定是要使用这个 git_commit_create 函数了。那咱们先去头文件看看它需要什么参数:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
/** * Create new commit in the repository from a list of `git_object` pointers * * The message will **not** be cleaned up automatically. You can do that * with the `git_message_prettify()` function. * * @param id Pointer in which to store the OID of the newly created commit * * @param repo Repository where to store the commit * * @param update_ref If not NULL, name of the reference that * will be updated to point to this commit. If the reference * is not direct, it will be resolved to a direct reference. * Use "HEAD" to update the HEAD of the current branch and * make it point to this commit. If the reference doesn't * exist yet, it will be created. If it does exist, the first * parent must be the tip of this branch. * * @param author Signature with author and author time of commit * * @param committer Signature with committer and * commit time of commit * * @param message_encoding The encoding for the message in the * commit, represented with a standard encoding name. * E.g. "UTF-8". If NULL, no encoding header is written and * UTF-8 is assumed. * * @param message Full message for this commit * * @param tree An instance of a `git_tree` object that will * be used as the tree for the commit. This tree object must * also be owned by the given `repo`. * * @param parent_count Number of parents for this commit * * @param parents Array of `parent_count` pointers to `git_commit` * objects that will be used as the parents for this commit. This * array may be NULL if `parent_count` is 0 (root commit). All the * given commits must be owned by the `repo`. * * @return 0 or an error code * The created commit will be written to the Object Database and * the given reference will be updated to point to it */ GIT_EXTERN(int) git_commit_create( git_oid *id, git_repository *repo, const char *update_ref, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_tree *tree, size_t parent_count, const git_commit *parents[]); |
·id 返回一个 OID 的指针,这个 OID 表示的就是新创建的这个 commit
·repo 仓库指针
·update_ref 需要提交的目标引用,使用当前分支的引用就用 “HEAD” 就可以了
·author 作者 git_signature 指针。git_signature 记录作者的名字、email、和一个时间
·committer 提交人也是 git_signature 指针,跟上边可以是同一个
·message_encoding 这次提交说明信息的字符集
·message 这次提交的完整说明
·tree 我们需要传入一个 git_tree 实例的指针,这个 tree 对象必须属于这个仓库。这个 ·tree 将是我们下边要解决的第一个问题
·parent_count 这个 commit 的父级的数量,如果是 merge 后的 commit 会有两个甚至以上,咱们暂时先不考虑分支的事情,所以只玩儿1个的
·parents 是一个 git_commit 指针的数组,表示上一级的提交,如果是第一次 commit 数组应该是空的,同时 parent_count 应该是0。如果不是,咱们就得先把爸爸找出来,这将是下边要解决的第二个问题
1. tree:
这个 tree 实际上就是把 index 的 entry 写到一个 tree 中,最终把这个 tree 提交。从 API 文档可以找到 git_index_write_tree 这个函数。它的作用就是写一个 tree,所以就可以写出下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
git_oid new_tree_id; git_tree* new_tree = nullptr; // write index to tree error = git_index_write_tree(&new_tree_id, index); if (error < 0) { const git_error *e = giterr_last(); std::cout << "Error: " << error << " / " << e->klass << " : " << e->message << std::endl; goto SHUTDOWN; } else { git_index_free(index); } git_tree_lookup(&new_tree, rep, &new_tree_id); |
2. parent:
因为我们还先不考虑 merge 的情况,所以父级 commit 只有一个,这个父级实际上就是 head 引用,所以我们第一步先拿出 head。
1 2 |
git_reference* ref_head = nullptr; git_repository_head(&ref_head, rep); |
这里我们要考虑一个情况:在一个全新的仓库的情况下,是没有之前的 commit 的,因此,取出来的 ref_head 会是空的。所以要做一个判断:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
size_t parent_count = 0; git_commit* parent_commit = nullptr; const git_commit *parents[] = {nullptr}; // has parent if (error == GIT_OK) { // get parent commit git_commit_lookup(&parent_commit, rep, git_reference_target(ref_head)); parents[0] = parent_commit; parent_count = 1; git_reference_free(ref_head); } else if (error != GIT_EUNBORNBRANCH) { const git_error *e = giterr_last(); std::cout << "Error: " << error << " / " << e->klass << " : " << e->message << std::endl; goto SHUTDOWN; } |
这样我们就拿到了我们需要的 tree 和 parent commit ,下面就是签名,然后提交了:
1 2 |
git_signature* me = nullptr; git_signature_now(&me, "XiaochenFTX", "xiaochenftx@gmail.com”); |
1 2 3 4 5 6 7 8 9 10 |
git_oid new_commit; error = git_commit_create(&new_commit, rep, "HEAD", me, me, "UTF-8", "commit message", new_tree, parent_count, parents); |
这样 commit 操作就完成了。
示例代码是 sample3
同样放在 github 的 libgit2_samples 中