Commit 8f7f5e55436eb6b20c12c435eab03e9f37be2e39

Greg Price 2013-03-31T14:56:32

examples: rev-list This demonstrates parts of the interface for specifying revisions that Git users are familiar with from 'git rev-list', 'git log', and other Git commands. A similar query interface is used in out-of-core command-line programs that browse a Git repo (like 'tig'), and may be useful for an 'advanced search' interface in GUI or web applications. In this version, we parse all the query modifiers we can support with the existing logic in revwalk: basic include/exclude commits, and the ordering flags. More logic will be required to support '--grep', '--author', the pickaxe '-S', etc. Signed-off-by: Greg Price <price@mit.edu>

diff --git a/examples/.gitignore b/examples/.gitignore
index e40bfc2..e8e0820 100644
--- a/examples/.gitignore
+++ b/examples/.gitignore
@@ -1,4 +1,5 @@
 general
 showindex
 diff
+rev-list
 *.dSYM
diff --git a/examples/rev-list.c b/examples/rev-list.c
new file mode 100644
index 0000000..b7e466f
--- /dev/null
+++ b/examples/rev-list.c
@@ -0,0 +1,116 @@
+#include <stdio.h>
+#include <string.h>
+
+#include <git2.h>
+
+static void check_error(int error_code, const char *action)
+{
+	if (!error_code)
+		return;
+
+	const git_error *error = giterr_last();
+	fprintf(stderr, "Error %d %s: %s\n", -error_code, action,
+	        (error && error->message) ? error->message : "???");
+	exit(1);
+}
+
+static int push_commit(git_revwalk *walk, git_object *obj, int hide)
+{
+	if (hide)
+		return git_revwalk_hide(walk, git_object_id(obj));
+	else
+		return git_revwalk_push(walk, git_object_id(obj));
+}
+
+static int push_spec(git_repository *repo, git_revwalk *walk, const char *spec, int hide)
+{
+	int error;
+	git_object *obj;
+
+	if ((error = git_revparse_single(&obj, repo, spec)))
+		return error;
+	return push_commit(walk, obj, hide);
+}
+
+static int push_range(git_repository *repo, git_revwalk *walk, const char *range, int hide)
+{
+	git_object *left, *right;
+	int threedots;
+	int error = 0;
+
+	if ((error = git_revparse_rangelike(&left, &right, &threedots, repo, range)))
+		return error;
+	if (threedots) {
+		/* TODO: support "<commit>...<commit>" */
+		return GIT_EINVALIDSPEC;
+	}
+
+	if ((error = push_commit(walk, left, !hide)))
+		goto out;
+	error = push_commit(walk, right, hide);
+
+  out:
+	git_object_free(left);
+	git_object_free(right);
+	return error;
+}
+
+static int revwalk_parseopts(git_repository *repo, git_revwalk *walk, int nopts, const char *const *opts)
+{
+	int hide, i, error;
+	unsigned int sorting = GIT_SORT_NONE;
+
+	hide = 0;
+	for (i = 0; i < nopts; i++) {
+		if (!strcmp(opts[i], "--topo-order")) {
+			sorting = GIT_SORT_TOPOLOGICAL | (sorting & GIT_SORT_REVERSE);
+			git_revwalk_sorting(walk, sorting);
+		} else if (!strcmp(opts[i], "--date-order")) {
+			sorting = GIT_SORT_TIME | (sorting & GIT_SORT_REVERSE);
+			git_revwalk_sorting(walk, sorting);
+		} else if (!strcmp(opts[i], "--reverse")) {
+			sorting = (sorting & ~GIT_SORT_REVERSE)
+			    | ((sorting & GIT_SORT_REVERSE) ? 0 : GIT_SORT_REVERSE);
+			git_revwalk_sorting(walk, sorting);
+		} else if (!strcmp(opts[i], "--not")) {
+			hide = !hide;
+		} else if (opts[i][0] == '^') {
+			if ((error = push_spec(repo, walk, opts[i] + 1, !hide)))
+				return error;
+		} else if (strstr(opts[i], "..")) {
+			if ((error = push_range(repo, walk, opts[i], hide)))
+				return error;
+		} else {
+			if ((error = push_spec(repo, walk, opts[i], hide)))
+				return error;
+		}
+	}
+
+	return 0;
+}
+
+int main (int argc, char **argv)
+{
+	int error;
+	git_repository *repo;
+	git_revwalk *walk;
+	git_oid oid;
+	char buf[41];
+
+	error = git_repository_open_ext(&repo, ".", 0, NULL);
+	check_error(error, "opening repository");
+
+	error = git_revwalk_new(&walk, repo);
+	check_error(error, "allocating revwalk");
+	error = revwalk_parseopts(repo, walk, argc-1, argv+1);
+	check_error(error, "parsing options");
+
+	while (!git_revwalk_next(&oid, walk)) {
+		git_oid_fmt(buf, &oid);
+		buf[40] = '\0';
+		printf("%s\n", buf);
+	}
+
+	return 0;
+}
+