#include #include #include #include static void check(int error, const char *message, const char *arg) { 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 void usage(const char *message, const char *arg) { if (message && arg) fprintf(stderr, "%s: %s\n", message, arg); else if (message) fprintf(stderr, "%s\n", message); fprintf(stderr, "usage: log []\n"); 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, count = 0; char *a; struct log_state s; git_strarray paths; git_oid oid; git_commit *commit; char buf[GIT_OID_HEXSZ + 1]; git_threads_init(); memset(&s, 0, sizeof(s)); for (i = 1; i < argc; ++i) { a = argv[i]; if (a[0] != '-') { if (!add_revision(&s, a)) ++count; else /* try failed revision parse as filename */ break; } else if (!strcmp(a, "--")) { ++i; 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 (!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); } git_revwalk_free(s.walker); git_repository_free(s.repo); git_threads_shutdown(); return 0; }