Commit 1574d3884f020c072f68c08785e199732e438e34

Ben Straub 2014-02-26T16:58:20

Merge pull request #2137 from jru/blame-first-parent Blame first-parent history

diff --git a/examples/blame.c b/examples/blame.c
index 1f5db69..6bc0581 100644
--- a/examples/blame.c
+++ b/examples/blame.c
@@ -31,6 +31,7 @@ struct opts {
 	int M;
 	int start_line;
 	int end_line;
+	int F;
 };
 static void parse_opts(struct opts *o, int argc, char *argv[]);
 
@@ -52,6 +53,7 @@ int main(int argc, char *argv[])
 	parse_opts(&o, argc, argv);
 	if (o.M) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
 	if (o.C) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES;
+	if (o.F) blameopts.flags |= GIT_BLAME_FIRST_PARENT;
 
 	/** Open the repository. */
 	check_lg2(git_repository_open_ext(&repo, ".", 0, NULL), "Couldn't open repository", NULL);
@@ -146,6 +148,7 @@ static void usage(const char *msg, const char *arg)
 	fprintf(stderr, "   -L <n,m>            process only line range n-m, counting from 1\n");
 	fprintf(stderr, "   -M                  find line moves within and across files\n");
 	fprintf(stderr, "   -C                  find line copies within and across files\n");
+	fprintf(stderr, "   -F                  follow only the first parent commits\n");
 	fprintf(stderr, "\n");
 	exit(1);
 }
@@ -174,6 +177,8 @@ static void parse_opts(struct opts *o, int argc, char *argv[])
 			o->M = 1;
 		else if (!strcasecmp(a, "-C"))
 			o->C = 1;
+		else if (!strcasecmp(a, "-F"))
+			o->F = 1;
 		else if (!strcasecmp(a, "-L")) {
 			i++; a = argv[i];
 			if (i >= argc) fatal("Not enough arguments to -L", NULL);
diff --git a/include/git2/blame.h b/include/git2/blame.h
index b98c6f0..4ad51ee 100644
--- a/include/git2/blame.h
+++ b/include/git2/blame.h
@@ -40,6 +40,9 @@ typedef enum {
 	 * commit (like `git blame -CCC`). Implies SAME_COMMIT_COPIES.
 	 * NOT IMPLEMENTED. */
 	GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = (1<<3),
+	/** Restrict the search of commits to those reachable following only the
+	 * first parents. */
+	GIT_BLAME_FIRST_PARENT = (1<<4),
 } git_blame_flag_t;
 
 /**
diff --git a/src/blame_git.c b/src/blame_git.c
index 800f1f0..72afb85 100644
--- a/src/blame_git.c
+++ b/src/blame_git.c
@@ -485,12 +485,14 @@ static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt
 	git_blame__origin *sg_buf[16];
 	git_blame__origin *porigin, **sg_origin = sg_buf;
 
-	GIT_UNUSED(opt);
-
 	num_parents = git_commit_parentcount(commit);
 	if (!git_oid_cmp(git_commit_id(commit), &blame->options.oldest_commit))
 		/* Stop at oldest specified commit */
 		num_parents = 0;
+	else if (opt & GIT_BLAME_FIRST_PARENT && num_parents > 1)
+		/* Limit search to the first parent */
+		num_parents = 1;
+
 	if (!num_parents) {
 		git_oid_cpy(&blame->options.oldest_commit, git_commit_id(commit));
 		goto finish;
diff --git a/tests/blame/simple.c b/tests/blame/simple.c
index 79bd56b..11ff4cd 100644
--- a/tests/blame/simple.c
+++ b/tests/blame/simple.c
@@ -303,3 +303,18 @@ void test_blame_simple__can_restrict_to_newish_commits(void)
 	check_blame_hunk_index(g_repo, g_blame, 0,  1, 1, 1, "be3563a", "branch_file.txt");
 	check_blame_hunk_index(g_repo, g_blame, 1,  2, 1, 0, "a65fedf", "branch_file.txt");
 }
+
+void test_blame_simple__can_restrict_to_first_parent_commits(void)
+{
+	git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+	opts.flags |= GIT_BLAME_FIRST_PARENT;
+
+	cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+
+	cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", &opts));
+	cl_assert_equal_i(4, git_blame_get_hunk_count(g_blame));
+	check_blame_hunk_index(g_repo, g_blame, 0,  1, 4, 0, "da237394", "b.txt");
+	check_blame_hunk_index(g_repo, g_blame, 1,  5, 1, 1, "b99f7ac0", "b.txt");
+	check_blame_hunk_index(g_repo, g_blame, 2,  6, 5, 0, "63d671eb", "b.txt");
+	check_blame_hunk_index(g_repo, g_blame, 3, 11, 5, 0, "bc7c5ac2", "b.txt");
+}