bisect--helper: implement "git bisect--helper"

This patch implements a new "git bisect--helper" builtin plumbing
command that will be used to migrate "git-bisect.sh" to C.

We start by implementing only the "--next-vars" option that will
read bisect refs from "refs/bisect/", and then compute the next
bisect step, and output shell variables ready to be eval'ed by
the shell.

At this step, "git bisect--helper" ignores the paths that may
have been put in "$GIT_DIR/BISECT_NAMES". This will be fixed in a
later patch.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff --git a/bisect.c b/bisect.c
index 47120c1..94ec011 100644
--- a/bisect.c
+++ b/bisect.c
@@ -2,11 +2,18 @@
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
+#include "refs.h"
+#include "list-objects.h"
 #include "sha1-lookup.h"
 #include "bisect.h"
 
 static unsigned char (*skipped_sha1)[20];
 static int skipped_sha1_nr;
+static int skipped_sha1_alloc;
+
+static const char **rev_argv;
+static int rev_argv_nr;
+static int rev_argv_alloc;
 
 /* bits #0-15 in revision.h */
 
@@ -390,6 +397,33 @@
 	return best;
 }
 
+static int register_ref(const char *refname, const unsigned char *sha1,
+			int flags, void *cb_data)
+{
+	if (!strcmp(refname, "bad")) {
+		ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
+		rev_argv[rev_argv_nr++] = xstrdup(sha1_to_hex(sha1));
+	} else if (!prefixcmp(refname, "good-")) {
+		const char *hex = sha1_to_hex(sha1);
+		char *good = xmalloc(strlen(hex) + 2);
+		*good = '^';
+		memcpy(good + 1, hex, strlen(hex) + 1);
+		ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
+		rev_argv[rev_argv_nr++] = good;
+	} else if (!prefixcmp(refname, "skip-")) {
+		ALLOC_GROW(skipped_sha1, skipped_sha1_nr + 1,
+			   skipped_sha1_alloc);
+		hashcpy(skipped_sha1[skipped_sha1_nr++], sha1);
+	}
+
+	return 0;
+}
+
+static int read_bisect_refs(void)
+{
+	return for_each_ref_in("refs/bisect/", register_ref, NULL);
+}
+
 static int skipcmp(const void *a, const void *b)
 {
 	return hashcmp(a, b);
@@ -444,3 +478,37 @@
 
 	return filtered;
 }
+
+int bisect_next_vars(const char *prefix)
+{
+	struct rev_info revs;
+	int reaches = 0, all = 0;
+
+	init_revisions(&revs, prefix);
+	revs.abbrev = 0;
+	revs.commit_format = CMIT_FMT_UNSPECIFIED;
+
+	/* argv[0] will be ignored by setup_revisions */
+	ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
+	rev_argv[rev_argv_nr++] = xstrdup("bisect_rev_setup");
+
+	if (read_bisect_refs())
+		die("reading bisect refs failed");
+
+	ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
+	rev_argv[rev_argv_nr++] = xstrdup("--");
+
+	setup_revisions(rev_argv_nr, rev_argv, &revs, NULL);
+
+	revs.limited = 1;
+
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
+	if (revs.tree_objects)
+		mark_edges_uninteresting(revs.commits, &revs, NULL);
+
+	revs.commits = find_bisection(revs.commits, &reaches, &all,
+				      !!skipped_sha1_nr);
+
+	return show_bisect_vars(&revs, reaches, all, 0, 1);
+}