8

How can I walk through all the commits of a branch using libgit2?

I have already the following bit of code, but it doesn't compile.

#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 reports:

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

I compiled with the -lgit2 flag. Is there a fast possibility to walk through all the commits, beginning from the root-commit?

Update The new code looks like this:

#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;
}

I get the following error messages:

log.c:11: undefined reference to `git_repository_database'
log.c:14: undefined reference to `git_revparse_single'
0xpentix
  • 732
  • 9
  • 22
  • 2
    Make sure you're using the latest library and looking at documentation for that version. `git_repository_database` doesn't exist. Presumably you want `git_repository_odb`, though you shouldn't need it for log. `git_revparse_single` was introduced in version 0.18 which suggests you have an ancient library intalled on your system. – Carlos Martín Nieto Jul 29 '13 at 12:48
  • The accepted answer doesn't seem to limit itself to a single branch, e.g., `master`. Perhaps `git_revwalk` wasn't designed for that? – Fuhrmanator Jan 10 '19 at 14:30

3 Answers3

4

Finally, I created a working version using libgit2. Carlos Martín Nieto pointed in the right direction, the following example works great with libgit2 0.16. It took me some time to study the general.c I found in the libgit2-examples repository on github. git revwalk was exactly what I was looking for.

I noted that git adds an newline at the end of my commit messages, probably because I'm always using nano to write them, so I don't printf out the last character in my example code.

If anyone reads this and has the same problem as I had, here's the working code:

#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;

}

Thanks!

0xpentix
  • 732
  • 9
  • 22
  • 2
    Hi, I've just found this listing by you and wanna say big thank you as I had exactly the same problem. Thanks for sharing, good man! – There is nothing we can do Apr 27 '16 at 15:00
  • Great to hear it still works. After having this problem fixed I tried to implement my stuff in another language than C. libgit2 did some API changes anyway and you really had to be aware of those as they changed some fundamental things. Also i think the library was not very developer friendly at this time... Glad I could help you! – 0xpentix Apr 27 '16 at 15:03
  • 1
    Hi again, I hope you're doing well. I observed that the walker will walk from branch let's say A back to a master, i.e. will list in one loop commits from branch A and master. Is there a way to list commits only from the branch A? Thank you. – There is nothing we can do May 05 '16 at 12:47
  • Like @Thereisnothingwecando says, this answer doesn't answer the question as it was written. That is, it doesn't limit itself to commits in one branch only. – Fuhrmanator Jan 10 '19 at 14:26
  • Didn't you forget to call `git_repository_free`? – rodrigocfd Jun 12 '19 at 15:01
3

I have already the following bit of code, but it doesn't compile

A git_commit is an opaque type, which means that your compiler doesn't know what it is, only that it exists. Thus you cannot allocate a git_commit on the stack. The library will allocate it on the heap for you.

You must use a pointer in your code and pass a pointer to that to the library's functions, as you can see in its documentation and the examples it links to.

Is there a fast possibility to walk through all the commits, beginning from the root-commit?

Those revwalk tests, demonstrating different walking strategies, may provide you with some help.

nulltoken
  • 64,429
  • 20
  • 138
  • 130
Carlos Martín Nieto
  • 5,207
  • 1
  • 15
  • 16
  • It does still not compile, using all the headers from the example. The compiler flag is `-lgit2`, isn't it? – 0xpentix Jul 29 '13 at 10:56
  • *It does still not compile* -> I'm pretty sure that providing the error messages might be quite helpful to the reader – nulltoken Jul 29 '13 at 12:02
  • The headers wouldn't be the issue, it's that you aren't (or weren't) using a pointer. If you would update your answer with newer code, maybe we could help. – Carlos Martín Nieto Jul 29 '13 at 12:41
1

Adding to Pentax's answer above. If you just want to 'walk' the head, instead of doing all that work to get the old to the head to initialize the walker with:

 git_revwalk_push(walker, &oid);

One could simply use:

 git_revwalk_push_head(walker);
VLL
  • 9,634
  • 1
  • 29
  • 54
SteveEng
  • 331
  • 2
  • 6