Commit 15f7b9b8d9bdfb68ca52d582be40cf6112464e77

Carlos Martín Nieto 2013-09-08T00:52:26

revwalk: allow simplifying by first-parent When enabled, only the first parent of each commit will be queued, enabling a simple way of using first-parent simplification.

diff --git a/include/git2/revwalk.h b/include/git2/revwalk.h
index 8bfe0b5..c59b799 100644
--- a/include/git2/revwalk.h
+++ b/include/git2/revwalk.h
@@ -232,6 +232,14 @@ GIT_EXTERN(void) git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode);
 GIT_EXTERN(int) git_revwalk_push_range(git_revwalk *walk, const char *range);
 
 /**
+ * Simplify the history by first-parent
+ *
+ * No parents other than the first for each commit will be enqueued.
+ */
+GIT_EXTERN(void) git_revwalk_simplify_first_parent(git_revwalk *walk);
+
+
+/**
  * Free a revision walker previously allocated.
  *
  * @param walk traversal handle to close. If NULL nothing occurs.
diff --git a/src/revwalk.c b/src/revwalk.c
index 1ff41bf..9e1e39c 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -99,10 +99,14 @@ static int process_commit(git_revwalk *walk, git_commit_list_node *commit, int h
 
 static int process_commit_parents(git_revwalk *walk, git_commit_list_node *commit)
 {
-	unsigned short i;
+	unsigned short i, max;
 	int error = 0;
 
-	for (i = 0; i < commit->out_degree && !error; ++i)
+	max = commit->out_degree;
+	if (walk->first_parent && commit->out_degree)
+		max = 1;
+
+	for (i = 0; i < max && !error; ++i)
 		error = process_commit(walk, commit->parents[i], commit->uninteresting);
 
 	return error;
@@ -333,7 +337,7 @@ static int revwalk_next_unsorted(git_commit_list_node **object_out, git_revwalk 
 static int revwalk_next_toposort(git_commit_list_node **object_out, git_revwalk *walk)
 {
 	git_commit_list_node *next;
-	unsigned short i;
+	unsigned short i, max;
 
 	for (;;) {
 		next = git_commit_list_pop(&walk->iterator_topo);
@@ -347,7 +351,12 @@ static int revwalk_next_toposort(git_commit_list_node **object_out, git_revwalk 
 			continue;
 		}
 
-		for (i = 0; i < next->out_degree; ++i) {
+
+		max = next->out_degree;
+		if (walk->first_parent && next->out_degree)
+			max = 1;
+
+		for (i = 0; i < max; ++i) {
 			git_commit_list_node *parent = next->parents[i];
 
 			if (--parent->in_degree == 0 && parent->topo_delay) {
@@ -505,6 +514,11 @@ void git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode)
 	}
 }
 
+void git_revwalk_simplify_first_parent(git_revwalk *walk)
+{
+	walk->first_parent = 1;
+}
+
 int git_revwalk_next(git_oid *oid, git_revwalk *walk)
 {
 	int error;
diff --git a/src/revwalk.h b/src/revwalk.h
index 22696df..8c821d0 100644
--- a/src/revwalk.h
+++ b/src/revwalk.h
@@ -31,7 +31,8 @@ struct git_revwalk {
 	int (*get_next)(git_commit_list_node **, git_revwalk *);
 	int (*enqueue)(git_revwalk *, git_commit_list_node *);
 
-	unsigned walking:1;
+	unsigned walking:1,
+		first_parent: 1;
 	unsigned int sorting;
 
 	/* merge base calculation */
diff --git a/tests-clar/revwalk/simplify.c b/tests-clar/revwalk/simplify.c
new file mode 100644
index 0000000..c949521
--- /dev/null
+++ b/tests-clar/revwalk/simplify.c
@@ -0,0 +1,51 @@
+#include "clar_libgit2.h"
+
+/*
+	*   a4a7dce [0] Merge branch 'master' into br2
+	|\
+	| * 9fd738e [1] a fourth commit
+	| * 4a202b3 [2] a third commit
+	* | c47800c [3] branch commit one
+	|/
+	* 5b5b025 [5] another commit
+	* 8496071 [4] testing
+*/
+static const char *commit_head = "a4a7dce85cf63874e984719f4fdd239f5145052f";
+
+static const char *expected_str[] = {
+	"a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
+	"c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
+	"8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
+	"5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
+};
+
+void test_revwalk_simplify__first_parent(void)
+{
+	git_repository *repo;
+	git_revwalk *walk;
+	git_oid id, expected[4];
+	int i, error;
+
+	for (i = 0; i < 4; i++) {
+		git_oid_fromstr(&expected[i], expected_str[i]);
+	}
+
+	repo = cl_git_sandbox_init("testrepo.git");
+	cl_git_pass(git_revwalk_new(&walk, repo));
+
+	git_oid_fromstr(&id, commit_head);
+	cl_git_pass(git_revwalk_push(walk, &id));
+	git_revwalk_simplify_first_parent(walk);
+
+	i = 0;
+	while ((error = git_revwalk_next(&id, walk)) == 0) {
+		git_oid_cmp(&id, &expected[i]);
+		i++;
+	}
+
+	cl_assert_equal_i(i, 4);
+	cl_assert_equal_i(error, GIT_ITEROVER);
+
+	git_revwalk_free(walk);
+	git_repository_free(repo);
+}