Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Listing all commits in a branch using libgit2

Tags:

git

c

libgit2

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'
like image 858
pentix Avatar asked Jul 29 '13 00:07

pentix


3 Answers

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!

like image 52
pentix Avatar answered Nov 09 '22 10:11

pentix


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.

like image 24
Carlos Martín Nieto Avatar answered Nov 09 '22 09:11

Carlos Martín Nieto


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);
like image 1
SteveEng Avatar answered Nov 09 '22 09:11

SteveEng