Commit 17c7fbf6d276443344c54f55800367b9837c0259

Edward Thomson 2013-08-21T14:07:53

Split rewrites, status doesn't return rewrites Ensure that we apply splits to rewrites, even if we're not interested in examining it closely for rename/copy detection. In keeping with core git, status should not display rewrites, it should simply show files as "modified".

diff --git a/include/git2/diff.h b/include/git2/diff.h
index c989ba4..5960985 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -454,6 +454,9 @@ typedef enum {
 	GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE = (1 << 13),
 	/** measure similarity only by comparing SHAs (fast and cheap) */
 	GIT_DIFF_FIND_EXACT_MATCH_ONLY = (1 << 14),
+
+	/** do not break rewrites unless they contribute to a rename */
+	GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY  = (1 << 15),
 } git_diff_find_t;
 
 /**
diff --git a/src/diff_tform.c b/src/diff_tform.c
index ba35d3c..ca3c771 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -799,6 +799,9 @@ int git_diff_find_similar(
 
 		if (is_rename_target(diff, &opts, t, sigcache))
 			++num_tgts;
+
+		if ((tgt->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0)
+			num_rewrites++;
 	}
 
 	/* if there are no candidate srcs or tgts, we're done */
@@ -1036,7 +1039,8 @@ find_best_matches:
 	if (num_rewrites > 0 || num_updates > 0)
 		error = apply_splits_and_deletes(
 			diff, diff->deltas.length - num_rewrites,
-			FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES));
+			FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES) &&
+			!FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY));
 
 cleanup:
 	git__free(tgt2src);
diff --git a/src/status.c b/src/status.c
index b235325..4a0d650 100644
--- a/src/status.c
+++ b/src/status.c
@@ -284,8 +284,10 @@ int git_status_list_new(
 		diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES;
 
 	if ((flags & GIT_STATUS_OPT_RENAMES_FROM_REWRITES) != 0)
-		findopt.flags = findopt.flags | GIT_DIFF_FIND_AND_BREAK_REWRITES |
-			GIT_DIFF_FIND_RENAMES_FROM_REWRITES;
+		findopt.flags = findopt.flags |
+			GIT_DIFF_FIND_AND_BREAK_REWRITES |
+			GIT_DIFF_FIND_RENAMES_FROM_REWRITES |
+			GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY;
 
 	if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) {
 		if ((error = git_diff_tree_to_index(
diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c
index 5a35495..ac3814d 100644
--- a/tests-clar/diff/rename.c
+++ b/tests-clar/diff/rename.c
@@ -1235,3 +1235,52 @@ void test_diff_rename__unmodified_can_be_renamed(void)
 	git_index_free(index);
 	git_tree_free(tree);
 }
+
+void test_diff_rename__rewrite_on_single_file(void)
+{
+	git_index *index;
+	git_diff_list *diff = NULL;
+	diff_expects exp;
+	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+	git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+	diffopts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+
+	findopts.flags = GIT_DIFF_FIND_FOR_UNTRACKED |
+		GIT_DIFF_FIND_AND_BREAK_REWRITES |
+		GIT_DIFF_FIND_RENAMES_FROM_REWRITES;
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+	cl_git_rewritefile("renames/ikeepsix.txt",
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n");
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &diffopts));
+	cl_git_pass(git_diff_find_similar(diff, &findopts));
+
+	memset(&exp, 0, sizeof(exp));
+
+	cl_git_pass(git_diff_foreach(
+		diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+	cl_assert_equal_i(2, exp.files);
+	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+	git_diff_list_free(diff);
+	git_index_free(index);
+}
diff --git a/tests-clar/status/renames.c b/tests-clar/status/renames.c
index 836e65c..d72e563 100644
--- a/tests-clar/status/renames.c
+++ b/tests-clar/status/renames.c
@@ -403,6 +403,47 @@ void test_status_renames__both_rename_from_rewrite(void)
 	git_index_free(index);
 }
 
+void test_status_renames__rewrites_only_for_renames(void)
+{
+	git_index *index;
+	git_status_list *statuslist;
+	git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+	struct status_entry expected[] = {
+		{ GIT_STATUS_WT_MODIFIED, "ikeepsix.txt", "ikeepsix.txt" },
+	};
+
+	opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+	opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+	opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+	opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+	cl_git_rewritefile("renames/ikeepsix.txt",
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n");
+
+	cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+	test_status(statuslist, expected, 1);
+	git_status_list_free(statuslist);
+
+	git_index_free(index);
+}
+
 void test_status_renames__both_casechange_one(void)
 {
 	git_index *index;