diff --git a/examples/log.c b/examples/log.c index ba411d7a4..50e81efad 100644 --- a/examples/log.c +++ b/examples/log.c @@ -158,15 +158,73 @@ struct log_options { char *committer; }; +static void print_commit(git_commit *commit) +{ + char buf[GIT_OID_HEXSZ + 1]; + int i, count; + const git_signature *sig; + const char *scan, *eol; + + git_oid_tostr(buf, sizeof(buf), git_commit_id(commit)); + printf("commit %s\n", buf); + + if ((count = (int)git_commit_parentcount(commit)) > 1) { + printf("Merge:"); + for (i = 0; i < count; ++i) { + git_oid_tostr(buf, 8, git_commit_parent_id(commit, i)); + printf(" %s", buf); + } + printf("\n"); + } + + if ((sig = git_commit_author(commit)) != NULL) { + printf("Author: %s <%s>\n", sig->name, sig->email); + print_time(&sig->when, "Date: "); + } + printf("\n"); + + for (scan = git_commit_message(commit); scan && *scan; ) { + for (eol = scan; *eol && *eol != '\n'; ++eol) /* find eol */; + + printf(" %.*s\n", (int)(eol - scan), scan); + scan = *eol ? eol + 1 : NULL; + } + printf("\n"); +} + +static int match_with_parent( + git_commit *commit, int i, git_diff_options *opts) +{ + git_commit *parent; + git_tree *a, *b; + git_diff_list *diff; + int ndeltas; + + check(git_commit_parent(&parent, commit, (size_t)i), "Get parent", NULL); + check(git_commit_tree(&a, parent), "Tree for parent", NULL); + check(git_commit_tree(&b, commit), "Tree for commit", NULL); + check(git_diff_tree_to_tree(&diff, git_commit_owner(commit), a, b, opts), + "Checking diff between parent and commit", NULL); + + ndeltas = (int)git_diff_num_deltas(diff); + + git_diff_list_free(diff); + git_tree_free(a); + git_tree_free(b); + git_commit_free(parent); + + return ndeltas > 0; +} + int main(int argc, char *argv[]) { - int i, count = 0; + int i, count = 0, parents; char *a; struct log_state s; - git_strarray paths; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; git_oid oid; git_commit *commit; - char buf[GIT_OID_HEXSZ + 1]; + git_pathspec *ps = NULL; git_threads_init(); @@ -200,45 +258,47 @@ int main(int argc, char *argv[]) if (!count) add_revision(&s, NULL); - paths.strings = &argv[i]; - paths.count = argc - i; - - while (!git_revwalk_next(&oid, s.walker)) { - const git_signature *sig; - const char *scan, *eol; + diffopts.pathspec.strings = &argv[i]; + diffopts.pathspec.count = argc - i; + count = 0; + if (diffopts.pathspec.count > 0) + check(git_pathspec_new(&ps, &diffopts.pathspec), + "Building pathspec", NULL); + for (; !git_revwalk_next(&oid, s.walker); git_commit_free(commit)) { check(git_commit_lookup(&commit, s.repo, &oid), "Failed to look up commit", NULL); - git_oid_tostr(buf, sizeof(buf), &oid); - printf("commit %s\n", buf); + parents = (int)git_commit_parentcount(commit); - if ((count = (int)git_commit_parentcount(commit)) > 1) { - printf("Merge:"); - for (i = 0; i < count; ++i) { - git_oid_tostr(buf, 8, git_commit_parent_id(commit, i)); - printf(" %s", buf); + if (diffopts.pathspec.count > 0) { + int unmatched = parents; + + if (parents == 0) { + git_tree *tree; + check(git_commit_tree(&tree, commit), "Get tree", NULL); + if (git_pathspec_match_tree( + NULL, tree, GIT_PATHSPEC_NO_MATCH_ERROR, ps) != 0) + unmatched = 1; + git_tree_free(tree); + } else if (parents == 1) { + unmatched = match_with_parent(commit, 0, &diffopts) ? 0 : 1; + } else { + for (i = 0; i < parents; ++i) { + if (match_with_parent(commit, i, &diffopts)) + unmatched--; + } } - printf("\n"); + + if (unmatched > 0) + continue; } - if ((sig = git_commit_author(commit)) != NULL) { - printf("Author: %s <%s>\n", sig->name, sig->email); - print_time(&sig->when, "Date: "); - } - printf("\n"); - - for (scan = git_commit_message(commit); scan && *scan; ) { - for (eol = scan; *eol && *eol != '\n'; ++eol) /* find eol */; - - printf(" %.*s\n", (int)(eol - scan), scan); - scan = *eol ? eol + 1 : NULL; - } - printf("\n"); - - git_commit_free(commit); + print_commit(commit); + ++count; } + git_pathspec_free(ps); git_revwalk_free(s.walker); git_repository_free(s.repo); git_threads_shutdown(); diff --git a/include/git2.h b/include/git2.h index 5f9fc4824..e8638a830 100644 --- a/include/git2.h +++ b/include/git2.h @@ -56,5 +56,6 @@ #include "git2/message.h" #include "git2/pack.h" #include "git2/stash.h" +#include "git2/pathspec.h" #endif diff --git a/src/pathspec.c b/src/pathspec.c index 35421dbef..021f38f1c 100644 --- a/src/pathspec.c +++ b/src/pathspec.c @@ -311,7 +311,7 @@ static int pathspec_match_from_iterator( git_pathspec *ps) { int error = 0; - git_pathspec_match_list *m; + git_pathspec_match_list *m = NULL; const git_index_entry *entry = NULL; struct pathspec_match_context ctxt; git_vector *patterns = &ps->pathspec; @@ -322,8 +322,13 @@ static int pathspec_match_from_iterator( uint8_t *used_patterns = NULL; char **file; - *out = m = pathspec_match_alloc(ps); - GITERR_CHECK_ALLOC(m); + if (out) { + *out = m = pathspec_match_alloc(ps); + GITERR_CHECK_ALLOC(m); + } else { + failures_only = true; + find_failures = false; + } if ((error = git_iterator_reset(iter, ps->prefix, ps->prefix)) < 0) goto done; @@ -385,7 +390,7 @@ static int pathspec_match_from_iterator( } /* if only looking at failures, exit early or just continue */ - if (failures_only) { + if (failures_only || !out) { if (used_ct == patterns->length) break; continue; @@ -429,7 +434,7 @@ done: if (error < 0) { pathspec_match_free(m); - *out = NULL; + if (out) *out = NULL; } return error; @@ -456,7 +461,7 @@ int git_pathspec_match_workdir( int error = 0; git_iterator *iter; - assert(out && repo); + assert(repo); if (!(error = git_iterator_for_workdir( &iter, repo, pathspec_match_iter_flags(flags), NULL, NULL))) { @@ -478,7 +483,7 @@ int git_pathspec_match_index( int error = 0; git_iterator *iter; - assert(out && index); + assert(index); if (!(error = git_iterator_for_index( &iter, index, pathspec_match_iter_flags(flags), NULL, NULL))) { @@ -500,7 +505,7 @@ int git_pathspec_match_tree( int error = 0; git_iterator *iter; - assert(out && tree); + assert(tree); if (!(error = git_iterator_for_tree( &iter, tree, pathspec_match_iter_flags(flags), NULL, NULL))) {