使用libgit2列出分支中的所有提交记录

8
我该如何使用libgit2遍历一个分支的所有提交?
我已经有了下面这段代码,但它无法编译。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <git2.h>

int main(int argc, char *argv[]){
    git_repository *repo;
    git_repository_open(&repo, ".");

    git_odb *obj_db;
    obj_db = git_repository_database(repo);

    git_object commit;
    git_revparse_single(&commit, repo, "HEAD");


    git_repository_free(repo);
    return 0;
}

GCC 报告:

log.c: In function ‘main’:
log.c:11:9: warning: assignment makes pointer from integer without a cast [enabled by default]
log.c:13:13: error: storage size of ‘commit’ isn’t known

我使用-lgit2参数编译了代码。有没有快速的方法可以从根提交开始浏览所有提交记录? 更新 新代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <git2.h>

int main(int argc, char *argv[]){
    git_repository *repo;
    git_repository_open(&repo, ".");

    git_odb *obj_db;
    obj_db = git_repository_database(repo);

    git_object *commit;
    git_revparse_single(&commit, repo, "HEAD");


    git_repository_free(repo);
    return 0;
}

我收到了以下错误信息:
log.c:11: undefined reference to `git_repository_database'
log.c:14: undefined reference to `git_revparse_single'

2
确保你正在使用最新的库并查看该版本的文档。 git_repository_database 不存在。可能你想要 git_repository_odb,但是你不应该需要它来获取日志。git_revparse_single 是在版本0.18中引入的,这表明你的系统上安装了一个古老的库。 - Carlos Martín Nieto
被接受的答案似乎并不限制于单个分支,例如 master。也许 git_revwalk 并没有为此设计? - Fuhrmanator
3个回答

4
最后,我使用libgit2创建了一个工作版本。Carlos Martín Nieto指出了正确的方向,以下示例与libgit2 0.16非常适合。我花了一些时间学习在github上找到的libgit2-examples存储库中的general.cgit revwalk正是我正在寻找的。
我注意到git在我的提交消息末尾添加了一个换行符,可能是因为我总是使用nano来编写它们,所以在我的示例代码中没有printf出最后一个字符。
如果有人阅读此文并遇到与我相同的问题,则可以使用以下工作代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <git2.h>

#define REPO ".git"

int main(void){
    git_repository *repo;
    if(git_repository_open(&repo, REPO) != GIT_SUCCESS){
        fprintf(stderr, "Failed opening repository: '%s'\n", REPO);
        return 1;
    }

    // Read HEAD on master
    char head_filepath[512];
    FILE *head_fileptr;
    char head_rev[41];

    strcpy(head_filepath, REPO);

    if(strrchr(REPO, '/') != (REPO+strlen(REPO)))
        strcat(head_filepath, "/refs/heads/master");
    else
        strcat(head_filepath, "refs/heads/master");


    if((head_fileptr = fopen(head_filepath, "r")) == NULL){
        fprintf(stderr, "Error opening '%s'\n", head_filepath);
        return 1;
    }

    if(fread(head_rev, 40, 1, head_fileptr) != 1){
        fprintf(stderr, "Error reading from '%s'\n", head_filepath);
        fclose(head_fileptr);
        return 1;
    }   

    fclose(head_fileptr);


    git_oid oid;
    git_revwalk *walker;
    git_commit *commit;

    if(git_oid_fromstr(&oid, head_rev) != GIT_SUCCESS){
        fprintf(stderr, "Invalid git object: '%s'\n", head_rev);
        return 1;
    }

    git_revwalk_new(&walker, repo);
    git_revwalk_sorting(walker, GIT_SORT_TOPOLOGICAL);
    git_revwalk_push(walker, &oid);

    const char *commit_message;
    const git_signature *commit_author;

    while(git_revwalk_next(&oid, walker) == GIT_SUCCESS) {
        if(git_commit_lookup(&commit, repo, &oid)){
            fprintf(stderr, "Failed to lookup the next object\n");
            return 1;
        }

        commit_message  = git_commit_message(commit);
        commit_author = git_commit_committer(commit);

        // Don't print the \n in the commit_message 
        printf("'%.*s' by %s <%s>\n", strlen(commit_message)-1, commit_message, commit_author->name, commit_author->email);

        git_commit_free(commit);
    }

    git_revwalk_free(walker);

    return 0;

}

谢谢!


2
嗨,我刚刚发现了你的这个列表,并想说非常感谢,因为我也遇到了完全相同的问题。感谢您的分享,好人! - There is nothing we can do
很高兴听到它仍然有效。在解决了这个问题之后,我尝试将我的东西实现到C语言之外的另一种语言中。libgit2进行了一些API更改,因此您确实必须意识到这些更改,因为它们改变了一些基本事情。此外,我认为该库在那个时候并不是非常开发人员友好的...很高兴我能帮助你! - 0xpentix
1
你好,希望你一切都好。我注意到 walker 会从分支 A 走回主干,也就是说会在一个循环中列出来自分支 A 和主干的提交。有没有办法仅列出来自分支 A 的提交呢?谢谢。 - There is nothing we can do
就像@Thereisnothingwecando所说的那样,这个答案并没有按照问题的要求进行回答。也就是说,它没有限制自己只在一个分支中提交。 - Fuhrmanator
你是不是忘了调用 git_repository_free 函数呢? - rodrigocfd

3
我已经有以下代码,但它无法编译。 git_commit 是一种不透明类型,这意味着您的编译器不知道它是什么,只知道它存在。因此,您不能在堆栈上分配 git_commit。库将为您在堆上分配它。
您必须在代码中使用指针,并将指向该指针的指针传递给库函数,正如您可以在 其文档链接到 的示例中看到的那样。
有没有快速的方法遍历所有提交,从根提交开始?
这些revwalk tests演示了不同的遍历策略,可能会为您提供一些帮助。

它仍然无法编译,使用了示例中的所有头文件。编译器标志是-lgit2,对吗? - 0xpentix
它仍未编译。我相信提供错误消息对读者可能会非常有帮助。 - nulltoken
头文件不是问题,问题在于你没有使用指针。如果你能更新你的答案并提供新的代码,或许我们可以帮助你。 - Carlos Martín Nieto

1
在Pentax的答案中补充一点。如果你只想“走”头部,而不是做所有工作来使旧的头部初始化行走器,请执行以下操作:
 git_revwalk_push(walker, &oid);

可以简单地使用:

 git_revwalk_push_head(walker);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接