More tests and fixed for merging reversed diffs There were a lot more cases to deal with to make sure that our merged (i.e. workdir-to-tree-to-index) diffs were matching the output of core Git.
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
diff --git a/src/diff_tform.c b/src/diff_tform.c
index 0aec754..28a9cc7 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -46,7 +46,6 @@ fail:
}
static git_diff_delta *diff_delta__merge_like_cgit(
- uint16_t flags,
const git_diff_delta *a,
const git_diff_delta *b,
git_pool *pool)
@@ -99,15 +98,46 @@ static git_diff_delta *diff_delta__merge_like_cgit(
return dup;
}
-int git_diff_merge(
- git_diff *onto,
- const git_diff *from)
+static git_diff_delta *diff_delta__merge_like_cgit_reversed(
+ const git_diff_delta *a,
+ const git_diff_delta *b,
+ git_pool *pool)
+{
+ git_diff_delta *dup;
+
+ /* reversed version of above logic */
+
+ if (a->status == GIT_DELTA_UNMODIFIED)
+ return diff_delta__dup(b, pool);
+
+ if ((dup = diff_delta__dup(a, pool)) == NULL)
+ return NULL;
+
+ if (b->status == GIT_DELTA_UNMODIFIED || b->status == GIT_DELTA_UNTRACKED)
+ return dup;
+
+ if (dup->status == GIT_DELTA_DELETED) {
+ if (b->status == GIT_DELTA_ADDED)
+ dup->status = GIT_DELTA_UNMODIFIED;
+ } else {
+ dup->status = b->status;
+ }
+
+ git_oid_cpy(&dup->old_file.oid, &b->old_file.oid);
+ dup->old_file.mode = b->old_file.mode;
+ dup->old_file.size = b->old_file.size;
+ dup->old_file.flags = b->old_file.flags;
+
+ return dup;
+}
+
+int git_diff_merge(git_diff *onto, const git_diff *from)
{
int error = 0;
git_pool onto_pool;
git_vector onto_new;
git_diff_delta *delta;
- bool ignore_case = false;
+ bool ignore_case, reversed;
unsigned int i, j;
assert(onto && from);
@@ -115,11 +145,11 @@ int git_diff_merge(
if (!from->deltas.length)
return 0;
- if ((onto->opts.flags & GIT_DIFF_IGNORE_CASE) !=
- (from->opts.flags & GIT_DIFF_IGNORE_CASE) ||
- (onto->opts.flags & GIT_DIFF_REVERSE) !=
- (from->opts.flags & GIT_DIFF_REVERSE))
- {
+ ignore_case = ((onto->opts.flags & GIT_DIFF_IGNORE_CASE) != 0);
+ reversed = ((onto->opts.flags & GIT_DIFF_REVERSE) != 0);
+
+ if (ignore_case != ((from->opts.flags & GIT_DIFF_IGNORE_CASE) != 0) ||
+ reversed != ((from->opts.flags & GIT_DIFF_REVERSE) != 0)) {
giterr_set(GITERR_INVALID,
"Attempt to merge diffs created with conflicting options");
return -1;
@@ -130,8 +160,6 @@ int git_diff_merge(
git_pool_init(&onto_pool, 1, 0) < 0)
return -1;
- ignore_case = ((onto->opts.flags & GIT_DIFF_IGNORE_CASE) != 0);
-
for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
@@ -145,8 +173,9 @@ int git_diff_merge(
delta = diff_delta__dup(f, &onto_pool);
j++;
} else {
- delta = diff_delta__merge_like_cgit(
- onto->opts.flags, o, f, &onto_pool);
+ delta = reversed ?
+ diff_delta__merge_like_cgit_reversed(o, f, &onto_pool) :
+ diff_delta__merge_like_cgit(o, f, &onto_pool);
i++;
j++;
}
diff --git a/tests-clar/diff/workdir.c b/tests-clar/diff/workdir.c
index 8611be8..fba64ef 100644
--- a/tests-clar/diff/workdir.c
+++ b/tests-clar/diff/workdir.c
@@ -196,6 +196,38 @@ void test_diff_workdir__to_tree(void)
git_diff_free(diff);
+ /* Let's try that once more with a reversed diff */
+
+ opts.flags |= GIT_DIFF_REVERSE;
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
+ cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
+ cl_git_pass(git_diff_merge(diff, diff2));
+ git_diff_free(diff2);
+
+ 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(16, exp.files);
+ cl_assert_equal_i(5, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ cl_assert_equal_i(12, exp.hunks);
+
+ cl_assert_equal_i(19, exp.lines);
+ cl_assert_equal_i(3, exp.line_ctxt);
+ cl_assert_equal_i(12, exp.line_dels);
+ cl_assert_equal_i(4, exp.line_adds);
+
+ git_diff_free(diff);
+
+ /* all done now */
+
git_tree_free(a);
git_tree_free(b);
}