Merge pull request #1643 from ethomson/rename_source Keep data about source of similarity
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 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
diff --git a/src/diff_tform.c b/src/diff_tform.c
index 597c240..53a7e63 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -688,7 +688,7 @@ int git_diff_find_similar(
git_diff_find_options opts;
size_t num_rewrites = 0, num_updates = 0;
void **cache; /* cache of similarity metric file signatures */
- diff_find_match *matches; /* cache of best matches */
+ diff_find_match *match_sources, *match_targets; /* cache of best matches */
if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0)
return error;
@@ -701,16 +701,18 @@ int git_diff_find_similar(
cache = git__calloc(cache_size, sizeof(void *));
GITERR_CHECK_ALLOC(cache);
- matches = git__calloc(diff->deltas.length, sizeof(diff_find_match));
- GITERR_CHECK_ALLOC(matches);
+ match_sources = git__calloc(diff->deltas.length, sizeof(diff_find_match));
+ match_targets = git__calloc(diff->deltas.length, sizeof(diff_find_match));
+ GITERR_CHECK_ALLOC(match_sources);
+ GITERR_CHECK_ALLOC(match_targets);
/* next find the most similar delta for each rename / copy candidate */
git_vector_foreach(&diff->deltas, i, to) {
size_t tried_sources = 0;
- matches[i].idx = i;
- matches[i].similarity = 0;
+ match_targets[i].idx = i;
+ match_targets[i].similarity = 0;
/* skip things that are not rename targets */
if (!is_rename_target(diff, &opts, i, cache))
@@ -738,9 +740,12 @@ int git_diff_find_similar(
continue;
}
- if (matches[i].similarity < (uint32_t)similarity) {
- matches[i].similarity = (uint32_t)similarity;
- matches[i].idx = j;
+ if (match_targets[i].similarity < (uint32_t)similarity &&
+ match_sources[j].similarity < (uint32_t)similarity) {
+ match_targets[i].similarity = (uint32_t)similarity;
+ match_sources[j].similarity = (uint32_t)similarity;
+ match_targets[i].idx = j;
+ match_sources[j].idx = i;
}
}
}
@@ -748,13 +753,13 @@ int git_diff_find_similar(
/* next rewrite the diffs with renames / copies */
git_vector_foreach(&diff->deltas, i, to) {
-
- /* check if this delta was matched to another one */
- if ((similarity = (int)matches[i].similarity) <= 0)
+ /* check if this delta was the target of a similarity */
+ if ((similarity = (int)match_targets[i].similarity) <= 0)
continue;
+
assert(to && (to->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) != 0);
- from = GIT_VECTOR_GET(&diff->deltas, matches[i].idx);
+ from = GIT_VECTOR_GET(&diff->deltas, match_targets[i].idx);
assert(from && (from->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) != 0);
/* possible scenarios:
@@ -851,14 +856,14 @@ int git_diff_find_similar(
/* in the off chance that we've just swapped the new
* element into the correct place, clear the SPLIT flag
*/
- if (matches[matches[i].idx].idx == i &&
- matches[matches[i].idx].similarity >
+ if (match_targets[match_targets[i].idx].idx == i &&
+ match_targets[match_targets[i].idx].similarity >
opts.rename_from_rewrite_threshold) {
from->status = GIT_DELTA_RENAMED;
from->similarity =
- (uint32_t)matches[matches[i].idx].similarity;
- matches[matches[i].idx].similarity = 0;
+ (uint32_t)match_targets[match_targets[i].idx].similarity;
+ match_targets[match_targets[i].idx].similarity = 0;
from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
num_rewrites--;
}
@@ -886,7 +891,8 @@ int git_diff_find_similar(
FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES));
cleanup:
- git__free(matches);
+ git__free(match_sources);
+ git__free(match_targets);
for (i = 0; i < cache_size; ++i) {
if (cache[i] != NULL)
diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c
index ca3f506..fd31a38 100644
--- a/tests-clar/diff/rename.c
+++ b/tests-clar/diff/rename.c
@@ -811,3 +811,96 @@ void test_diff_rename__from_deleted_to_split(void)
git_buf_free(&c1);
}
+
+struct rename_expected
+{
+ size_t len;
+ const char **sources;
+ const char **targets;
+
+ size_t idx;
+};
+
+int test_names_expected(const git_diff_delta *delta, float progress, void *p)
+{
+ struct rename_expected *expected = p;
+
+ cl_assert(expected->idx < expected->len);
+
+ cl_assert_equal_i(delta->status, GIT_DELTA_RENAMED);
+
+ cl_assert(git__strcmp(expected->sources[expected->idx],
+ delta->old_file.path) == 0);
+ cl_assert(git__strcmp(expected->targets[expected->idx],
+ delta->new_file.path) == 0);
+
+ expected->idx++;
+
+ return 0;
+}
+
+void test_diff_rename__rejected_match_can_match_others(void)
+{
+ git_reference *head, *selfsimilar;
+ git_index *index;
+ git_tree *tree;
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ git_diff_list *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+ git_buf one = GIT_BUF_INIT, two = GIT_BUF_INIT;
+ const char *sources[] = { "Class1.cs", "Class2.cs" };
+ const char *targets[] = { "ClassA.cs", "ClassB.cs" };
+ struct rename_expected expect = { 2, sources, targets };
+ char *ptr;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_git_pass(git_reference_symbolic_set_target(
+ &selfsimilar, head, "refs/heads/renames_similar"));
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(git_futils_readbuffer(&one, "renames/Class1.cs"));
+ cl_git_pass(git_futils_readbuffer(&two, "renames/Class2.cs"));
+
+ cl_git_pass(p_unlink("renames/Class1.cs"));
+ cl_git_pass(p_unlink("renames/Class2.cs"));
+
+ cl_git_pass(git_index_remove_bypath(index, "Class1.cs"));
+ cl_git_pass(git_index_remove_bypath(index, "Class2.cs"));
+
+ cl_assert(ptr = strstr(one.ptr, "Class1"));
+ ptr[5] = 'A';
+
+ cl_assert(ptr = strstr(two.ptr, "Class2"));
+ ptr[5] = 'B';
+
+ cl_git_pass(
+ git_futils_writebuffer(&one, "renames/ClassA.cs", O_RDWR|O_CREAT, 0777));
+ cl_git_pass(
+ git_futils_writebuffer(&two, "renames/ClassB.cs", O_RDWR|O_CREAT, 0777));
+
+ cl_git_pass(git_index_add_bypath(index, "ClassA.cs"));
+ cl_git_pass(git_index_add_bypath(index, "ClassB.cs"));
+
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(
+ git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+ cl_git_pass(git_diff_find_similar(diff, &findopts));
+
+ cl_git_pass(
+ git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect));
+
+ git_tree_free(tree);
+ git_index_free(index);
+ git_reference_free(head);
+ git_reference_free(selfsimilar);
+ git_buf_free(&one);
+ git_buf_free(&two);
+}
diff --git a/tests-clar/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754 b/tests-clar/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754
new file mode 100644
index 0000000..886271d
Binary files /dev/null and b/tests-clar/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754 differ
diff --git a/tests-clar/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a b/tests-clar/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a
new file mode 100644
index 0000000..5ce12a3
Binary files /dev/null and b/tests-clar/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a differ
diff --git a/tests-clar/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951 b/tests-clar/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951
new file mode 100644
index 0000000..aa91926
Binary files /dev/null and b/tests-clar/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951 differ
diff --git a/tests-clar/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7 b/tests-clar/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7
new file mode 100644
index 0000000..39105f9
Binary files /dev/null and b/tests-clar/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7 differ
diff --git a/tests-clar/resources/renames/.gitted/refs/heads/renames_similar b/tests-clar/resources/renames/.gitted/refs/heads/renames_similar
new file mode 100644
index 0000000..8ffb6d9
--- /dev/null
+++ b/tests-clar/resources/renames/.gitted/refs/heads/renames_similar
@@ -0,0 +1 @@
+444a76ed3e45b183753f49376af30da8c3fe276a