Commit d0628e2feeaeebf78a6b67fa4ca3fc658aa4744a

Russell Belfer 2013-06-25T15:39:13

More progress on log example

diff --git a/examples/log.c b/examples/log.c
index 83e5cbc..5603d74 100644
--- a/examples/log.c
+++ b/examples/log.c
@@ -3,21 +3,15 @@
 #include <stdlib.h>
 #include <string.h>
 
-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);
 }
 
-int main(int argc, char *argv[])
-{
-	int i, j, last_nonoption, force_files = -1;
-	char *a;
-	const char *dir = ".";
+struct log_state {
 	git_repository *repo;
+	const char *repodir;
 	git_revwalk *walker;
-	git_revspec revs;
+	int hide;
+	int sorting;
+};
 
-	git_threads_init();
+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);
+	}
 
-	for (i = 1, last_nonoption = 1; i < argc; ++i) {
-		a = argv[i];
+	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);
+}
 
-		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++;
+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);
 		}
-		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);
+
+		push_rev(s, revs.from, !hide);
 	}
 
-	check(git_repository_open_ext(&repo, dir, 0, NULL),
-		"Could not open repository");
-	check(git_revwalk_new(&walker, repo),
-		"Could not create revision walker");
+	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;
 
-	if (force_files < 0)
-		force_files = last_nonoption;
+};
 
-	for (i = 1; i < force_files; ) {
-		printf("option '%s'\n", argv[i]);
+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];
 
-		if (!git_revparse(&revs, repo, argv[i])) {
-			char str[GIT_OID_HEXSZ+1];
+	git_threads_init();
 
-			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);
-			}
+	memset(&s, 0, sizeof(s));
 
-			/* push / hide / merge-base in revwalker */
+	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;
-		} 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;
 
-	for (i = force_files; i < last_nonoption; ++i)
-		printf("file %s\n", argv[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_repository_free(repo);
+	git_revwalk_free(s.walker);
+	git_repository_free(s.repo);
 	git_threads_shutdown();
 
 	return 0;