diff_tform: account for whitespace options When comparing seemingly blank files, take whitespace options into account.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
diff --git a/src/hashsig.c b/src/hashsig.c
index cbe56e7..e99637d 100644
--- a/src/hashsig.c
+++ b/src/hashsig.c
@@ -34,6 +34,7 @@ typedef struct {
struct git_hashsig {
hashsig_heap mins;
hashsig_heap maxs;
+ size_t lines;
git_hashsig_option_t opt;
};
@@ -185,8 +186,10 @@ static int hashsig_add_hashes(
++scan;
/* check run terminator */
- if (ch == '\n' || ch == '\0')
+ if (ch == '\n' || ch == '\0') {
+ sig->lines++;
break;
+ }
++len;
HASHSIG_HASH_MIX(state, ch);
@@ -314,9 +317,6 @@ static int hashsig_heap_compare(const hashsig_heap *a, const hashsig_heap *b)
assert(a->cmp == b->cmp);
- if (a->size + b->size == 0)
- return 0;
-
/* hash heaps are sorted - just look for overlap vs total */
for (i = 0, j = 0; i < a->size && j < b->size; ) {
@@ -336,6 +336,18 @@ static int hashsig_heap_compare(const hashsig_heap *a, const hashsig_heap *b)
int git_hashsig_compare(const git_hashsig *a, const git_hashsig *b)
{
+ /* if we have no elements in either file then each file is either
+ * empty or blank. if we're ignoring whitespace then the files are
+ * similar, otherwise they're dissimilar.
+ */
+ if (a->mins.size == 0 && b->mins.size == 0) {
+ if ((!a->lines && !b->lines) ||
+ (a->opt & GIT_HASHSIG_IGNORE_WHITESPACE))
+ return HASHSIG_SCALE;
+ else
+ return 0;
+ }
+
/* if we have fewer than the maximum number of elements, then just use
* one array since the two arrays will be the same
*/
diff --git a/tests/diff/rename.c b/tests/diff/rename.c
index 6592446..8a327f0 100644
--- a/tests/diff/rename.c
+++ b/tests/diff/rename.c
@@ -1603,8 +1603,7 @@ void test_diff_rename__by_config_doesnt_mess_with_whitespace_settings(void)
git_tree_free(tree2);
}
-/* test some variations on empty files */
-void test_diff_rename__empty_files(void)
+static void expect_files_renamed(const char *one, const char *two, uint32_t whitespace_flags)
{
git_index *index;
git_diff *diff = NULL;
@@ -1613,18 +1612,73 @@ void test_diff_rename__empty_files(void)
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;
+ GIT_DIFF_FIND_RENAMES_FROM_REWRITES |
+ whitespace_flags;
cl_git_pass(git_repository_index(&index, g_repo));
- cl_git_rewritefile("renames/ikeepsix.txt", "");
+ cl_git_rewritefile("renames/ikeepsix.txt", one);
cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
cl_git_rmfile("renames/ikeepsix.txt");
- cl_git_rewritefile("renames/ikeepsix2.txt", "\n\n\n");
+ cl_git_rewritefile("renames/ikeepsix2.txt", two);
+
+ 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(1, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+ git_index_free(index);
+}
+
+/* test some variations on empty and blank files */
+void test_diff_rename__empty_files_renamed(void)
+{
+ /* empty files are identical when ignoring whitespace or not */
+ expect_files_renamed("", "", GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE);
+ expect_files_renamed("", "", GIT_DIFF_FIND_IGNORE_WHITESPACE);
+}
+
+/* test that blank files are similar when ignoring whitespace */
+void test_diff_rename__blank_files_renamed_when_ignoring_whitespace(void)
+{
+ expect_files_renamed("", "\n\n", GIT_DIFF_FIND_IGNORE_WHITESPACE);
+ expect_files_renamed("", "\r\n\r\n", GIT_DIFF_FIND_IGNORE_WHITESPACE);
+ expect_files_renamed("\r\n\r\n", "\n\n\n", GIT_DIFF_FIND_IGNORE_WHITESPACE);
+
+ expect_files_renamed(" ", "\n\n", GIT_DIFF_FIND_IGNORE_WHITESPACE);
+ expect_files_renamed(" \n \n", "\n\n", GIT_DIFF_FIND_IGNORE_WHITESPACE);
+}
+
+/* blank files are not similar when whitespace is not ignored */
+static void expect_files_not_renamed(const char *one, const char *two, uint32_t whitespace_flags)
+{
+ git_index *index;
+ git_diff *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 |
+ whitespace_flags;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_rewritefile("renames/ikeepsix.txt", one);
+ cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
+
+ cl_git_rmfile("renames/ikeepsix.txt");
+ cl_git_rewritefile("renames/ikeepsix2.txt", two);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &diffopts));
cl_git_pass(git_diff_find_similar(diff, &findopts));
@@ -1640,3 +1694,11 @@ void test_diff_rename__empty_files(void)
git_diff_free(diff);
git_index_free(index);
}
+
+/* test that blank files are similar when ignoring renames */
+void test_diff_rename__blank_files_not_renamed_when_not_ignoring_whitespace(void)
+{
+ expect_files_not_renamed("", "\r\n\r\n\r\n", GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE);
+ expect_files_not_renamed("", "\n\n\n\n", GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE);
+ expect_files_not_renamed("\n\n\n\n", "\r\n\r\n\r\n", GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE);
+}