diff --git a/examples/log.c b/examples/log.c index 83e5cbcc0..5603d743d 100644 --- a/examples/log.c +++ b/examples/log.c @@ -3,21 +3,15 @@ #include #include -static void check(int error, const char *message) +static void check(int error, const char *message, const char *arg) { - if (error) { + if (!error) + return; + if (arg) + fprintf(stderr, "%s '%s' (%d)\n", message, arg, error); + else fprintf(stderr, "%s (%d)\n", message, error); - exit(1); - } -} - -static int check_str_param(const char *arg, const char *pat, const char **val) -{ - size_t len = strlen(pat); - if (strncmp(arg, pat, len)) - return 0; - *val = (const char *)(arg + len); - return 1; + exit(1); } static void usage(const char *message, const char *arg) @@ -30,75 +24,165 @@ static void usage(const char *message, const char *arg) exit(1); } +struct log_state { + git_repository *repo; + const char *repodir; + git_revwalk *walker; + int hide; + int sorting; +}; + +static void set_sorting(struct log_state *s, unsigned int sort_mode) +{ + if (!s->repo) { + if (!s->repodir) s->repodir = "."; + check(git_repository_open_ext(&s->repo, s->repodir, 0, NULL), + "Could not open repository", s->repodir); + } + + if (!s->walker) + check(git_revwalk_new(&s->walker, s->repo), + "Could not create revision walker", NULL); + + if (sort_mode == GIT_SORT_REVERSE) + s->sorting = s->sorting ^ GIT_SORT_REVERSE; + else + s->sorting = sort_mode | (s->sorting & GIT_SORT_REVERSE); + + git_revwalk_sorting(s->walker, s->sorting); +} + +static void push_rev(struct log_state *s, git_object *obj, int hide) +{ + hide = s->hide ^ hide; + + if (!s->walker) + check(git_revwalk_new(&s->walker, s->repo), + "Could not create revision walker", NULL); + + if (!obj) + check(git_revwalk_push_head(s->walker), + "Could not find repository HEAD", NULL); + else if (hide) + check(git_revwalk_hide(s->walker, git_object_id(obj)), + "Reference does not refer to a commit", NULL); + else + check(git_revwalk_push(s->walker, git_object_id(obj)), + "Reference does not refer to a commit", NULL); + + git_object_free(obj); +} + +static int add_revision(struct log_state *s, const char *revstr) +{ + git_revspec revs; + int hide = 0; + + if (!s->repo) { + if (!s->repodir) s->repodir = "."; + check(git_repository_open_ext(&s->repo, s->repodir, 0, NULL), + "Could not open repository", s->repodir); + } + + if (!revstr) + push_rev(s, NULL, hide); + else if (*revstr == '^') { + revs.flags = GIT_REVPARSE_SINGLE; + hide = !hide; + if (!git_revparse_single(&revs.from, s->repo, revstr + 1)) + return -1; + } else + if (!git_revparse(&revs, s->repo, revstr)) + return -1; + + if ((revs.flags & GIT_REVPARSE_SINGLE) != 0) + push_rev(s, revs.from, hide); + else { + push_rev(s, revs.to, hide); + + if ((revs.flags & GIT_REVPARSE_MERGE_BASE) != 0) { + git_oid base; + check(git_merge_base(&base, s->repo, + git_object_id(revs.from), git_object_id(revs.to)), + "Could not find merge base", revstr); + check(git_object_lookup(&revs.to, s->repo, &base, GIT_OBJ_COMMIT), + "Could not find merge base commit", NULL); + + push_rev(s, revs.to, hide); + } + + push_rev(s, revs.from, !hide); + } + + return 0; +} + +struct log_options { + int show_diff; + int skip; + int min_parents, max_parents; + git_time_t before; + git_time_t after; + char *author; + char *committer; + +}; + int main(int argc, char *argv[]) { - int i, j, last_nonoption, force_files = -1; + int i, count = 0; char *a; - const char *dir = "."; - git_repository *repo; - git_revwalk *walker; - git_revspec revs; + struct log_state s; + git_strarray paths; + git_oid oid; + git_commit *commit; + char buf[GIT_OID_HEXSZ + 1]; git_threads_init(); - for (i = 1, last_nonoption = 1; i < argc; ++i) { + memset(&s, 0, sizeof(s)); + + for (i = 1; i < argc; ++i) { a = argv[i]; - if (a[0] != '-' || force_files > 0) { - /* condense args not prefixed with '-' to start of argv */ - if (last_nonoption != i) - argv[last_nonoption] = a; - last_nonoption++; - } - else if (!strcmp(a, "--")) - force_files = last_nonoption; /* copy all args as filenames */ - else if (!check_str_param(a, "--git-dir=", &dir)) - usage("Unknown argument", a); - } - - check(git_repository_open_ext(&repo, dir, 0, NULL), - "Could not open repository"); - check(git_revwalk_new(&walker, repo), - "Could not create revision walker"); - - if (force_files < 0) - force_files = last_nonoption; - - for (i = 1; i < force_files; ) { - printf("option '%s'\n", argv[i]); - - if (!git_revparse(&revs, repo, argv[i])) { - char str[GIT_OID_HEXSZ+1]; - - if (revs.from) { - git_oid_tostr(str, sizeof(str), git_object_id(revs.from)); - printf("revwalk from %s\n", str); - } - if (revs.to) { - git_oid_tostr(str, sizeof(str), git_object_id(revs.to)); - printf("revwalk to %s\n", str); - } - - /* push / hide / merge-base in revwalker */ - + if (a[0] != '-') { + if (!add_revision(&s, a)) + ++count; + else /* try failed revision parse as filename */ + break; + } else if (!strcmp(a, "--")) { ++i; - } else { - /* shift array down */ - for (a = argv[i], j = i + 1; j < force_files; ++j) - argv[j - 1] = argv[j]; - argv[--force_files] = a; + break; } + else if (!strcmp(a, "--date-order")) + set_sorting(&s, GIT_SORT_TIME); + else if (!strcmp(a, "--topo-order")) + set_sorting(&s, GIT_SORT_TOPOLOGICAL); + else if (!strcmp(a, "--reverse")) + set_sorting(&s, GIT_SORT_REVERSE); + else if (!strncmp(a, "--git-dir=", strlen("--git-dir="))) + s.repodir = a + strlen("--git-dir="); + else + usage("Unsupported argument", a); } - if (i == 1) { - /* no revs pushed so push HEAD */ - printf("revwalk HEAD\n"); + if (!count) + add_revision(&s, NULL); + + paths.strings = &argv[i]; + paths.count = argc - i; + + while (!git_revwalk_next(&oid, s.walker)) { + check(git_commit_lookup(&commit, s.repo, &oid), + "Failed to look up commit", NULL); + git_commit_free(commit); + + git_oid_tostr(buf, sizeof(buf), &oid); + printf("%s\n", buf); } - for (i = force_files; i < last_nonoption; ++i) - printf("file %s\n", argv[i]); - - git_repository_free(repo); + git_revwalk_free(s.walker); + git_repository_free(s.repo); git_threads_shutdown(); return 0;