#include #include #include #include #include #include enum match_result { MATCH_RESULT_INITIAL, MATCH_RESULT_NOTFOUND, MATCH_RESULT_FOUND, }; size_t min(const size_t lhs, const size_t rhs) { return lhs > rhs ? rhs : lhs; } char* relative_path_in_current_dir(git_repository* repo, const char* filename) { const long maximum = pathconf(".", _PC_PATH_MAX); size_t len = 0 > maximum ? 1024 : maximum; char* buf = NULL; char* cwd = NULL; for (; NULL == cwd && 1024 * 50 > len; len *= 2) { if (NULL == (cwd = realloc(buf, len))) { if (NULL != buf) free(buf); return NULL; } buf = cwd; cwd = getcwd(buf, len); if (NULL == cwd && ERANGE != errno) { free(buf); return NULL; } } const char* workdir = git_repository_workdir(repo); const size_t prefix = min(strlen(cwd), strlen(workdir)); if (0 != strncmp(cwd, workdir, prefix)) { free(cwd); return NULL; } const size_t relative = strlen(cwd + prefix); if (NULL == (buf = malloc(relative + strlen(filename) + 2))) { free(cwd); return NULL; } strcpy(buf, cwd + prefix); if (0 < relative) strcat(buf, "/"); strcat(buf, filename); free(cwd); return buf; } int try_git(const int err, const char* optional_prefix) { if (0 > err) { const git_error* e = git_error_last(); if (NULL != optional_prefix) dprintf(2, "%s: ", optional_prefix); dprintf(2, "%s\n", e->message); exit(1); } return err; } int line_line(const git_diff_delta* delta, const git_diff_hunk* hunk, const git_diff_line* line, void* payload) { const char* const pkgver = "pkgver"; const size_t len = strlen(pkgver); enum match_result* matched = (enum match_result*) payload; switch (line->origin) { case GIT_DIFF_LINE_ADDITION: case GIT_DIFF_LINE_DELETION: if (len <= line->content_len && 0 == strncmp(line->content, pkgver, len)) *matched = MATCH_RESULT_FOUND; break; default: if (MATCH_RESULT_FOUND != *matched) *matched = MATCH_RESULT_NOTFOUND; break; } return 0; } int main(int argc, char* argv[]) { git_libgit2_init(); git_repository* repo = NULL; try_git(git_repository_open_ext(&repo, NULL, GIT_REPOSITORY_OPEN_FROM_ENV, NULL), "opening repo"); git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; diffopts.pathspec.strings = (char*[1]){relative_path_in_current_dir(repo, "PKGBUILD")}; diffopts.pathspec.count = 1; if (NULL == diffopts.pathspec.strings[0]) { dprintf(2, "finding PKGBUILD in repo\n"); exit(1); } git_pathspec* pathspec = NULL; try_git(git_pathspec_new(&pathspec, &diffopts.pathspec), "creating pathspec"); git_revwalk* walker = NULL; try_git(git_revwalk_new(&walker, repo), "creating revwalker"); try_git(git_revwalk_push_head(walker), "finding head"); git_oid oid; git_tree* earlier = NULL; git_tree* later = NULL; int rel = 1; int occurred = 0; while (0 == git_revwalk_next(&oid, walker)) { git_commit* commit; try_git(git_commit_lookup(&commit, repo, &oid), "finding commit"); if (NULL != earlier) git_tree_free(earlier); earlier = later; try_git(git_commit_tree(&later, commit), "finding tree"); git_diff* diff; if (NULL == earlier) try_git(git_diff_tree_to_workdir_with_index(&diff, repo, later, &diffopts), "diff"); else try_git(git_diff_tree_to_tree(&diff, repo, later, earlier, &diffopts), "diff"); enum match_result matched = MATCH_RESULT_INITIAL; try_git(git_diff_foreach(diff, NULL, NULL, NULL, &line_line, &matched), "searching lines"); git_diff_free(diff); git_commit_free(commit); if (MATCH_RESULT_INITIAL != matched) occurred = 1; if (MATCH_RESULT_FOUND == matched) break; if (MATCH_RESULT_NOTFOUND == matched) rel++; } if (0 == occurred) dprintf(2, "PKGBUILD not found\n"); else printf("%d\n", rel); }