]>
Commit | Line | Data |
---|---|---|
bec65a5e | 1 | #include "clar_libgit2.h" |
22a2d3d5 | 2 | #include "futils.h" |
bec65a5e ET |
3 | #include "refs.h" |
4 | #include "tree.h" | |
5 | #include "merge_helpers.h" | |
6 | #include "merge.h" | |
86c8d02c | 7 | #include "index.h" |
bec65a5e | 8 | #include "git2/merge.h" |
75d1c8c6 | 9 | #include "git2/sys/index.h" |
18b00406 | 10 | #include "git2/annotated_commit.h" |
bec65a5e ET |
11 | |
12 | int merge_trees_from_branches( | |
13 | git_index **index, git_repository *repo, | |
14 | const char *ours_name, const char *theirs_name, | |
5aa2ac6d | 15 | git_merge_options *opts) |
bec65a5e ET |
16 | { |
17 | git_commit *our_commit, *their_commit, *ancestor_commit = NULL; | |
18 | git_tree *our_tree, *their_tree, *ancestor_tree = NULL; | |
19 | git_oid our_oid, their_oid, ancestor_oid; | |
e579e0f7 | 20 | git_str branch_buf = GIT_STR_INIT; |
bec65a5e ET |
21 | int error; |
22 | ||
e579e0f7 | 23 | git_str_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name); |
bec65a5e ET |
24 | cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr)); |
25 | cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid)); | |
26 | ||
e579e0f7 MB |
27 | git_str_clear(&branch_buf); |
28 | git_str_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs_name); | |
bec65a5e ET |
29 | cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr)); |
30 | cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid)); | |
31 | ||
32 | error = git_merge_base(&ancestor_oid, repo, git_commit_id(our_commit), git_commit_id(their_commit)); | |
33 | ||
34 | if (error != GIT_ENOTFOUND) { | |
35 | cl_git_pass(error); | |
36 | ||
37 | cl_git_pass(git_commit_lookup(&ancestor_commit, repo, &ancestor_oid)); | |
38 | cl_git_pass(git_commit_tree(&ancestor_tree, ancestor_commit)); | |
39 | } | |
40 | ||
41 | cl_git_pass(git_commit_tree(&our_tree, our_commit)); | |
42 | cl_git_pass(git_commit_tree(&their_tree, their_commit)); | |
43 | ||
8683d31f | 44 | error = git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts); |
bec65a5e | 45 | |
e579e0f7 | 46 | git_str_dispose(&branch_buf); |
bec65a5e ET |
47 | git_tree_free(our_tree); |
48 | git_tree_free(their_tree); | |
49 | git_tree_free(ancestor_tree); | |
50 | git_commit_free(our_commit); | |
51 | git_commit_free(their_commit); | |
52 | git_commit_free(ancestor_commit); | |
53 | ||
8683d31f | 54 | return error; |
bec65a5e ET |
55 | } |
56 | ||
eac938d9 ET |
57 | int merge_commits_from_branches( |
58 | git_index **index, git_repository *repo, | |
59 | const char *ours_name, const char *theirs_name, | |
5aa2ac6d | 60 | git_merge_options *opts) |
eac938d9 ET |
61 | { |
62 | git_commit *our_commit, *their_commit; | |
63 | git_oid our_oid, their_oid; | |
e579e0f7 | 64 | git_str branch_buf = GIT_STR_INIT; |
8683d31f | 65 | int error; |
eac938d9 | 66 | |
e579e0f7 | 67 | git_str_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name); |
eac938d9 ET |
68 | cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr)); |
69 | cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid)); | |
70 | ||
e579e0f7 MB |
71 | git_str_clear(&branch_buf); |
72 | git_str_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs_name); | |
eac938d9 ET |
73 | cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr)); |
74 | cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid)); | |
75 | ||
8683d31f | 76 | error = git_merge_commits(index, repo, our_commit, their_commit, opts); |
eac938d9 | 77 | |
e579e0f7 | 78 | git_str_dispose(&branch_buf); |
eac938d9 ET |
79 | git_commit_free(our_commit); |
80 | git_commit_free(their_commit); | |
81 | ||
8683d31f | 82 | return error; |
eac938d9 ET |
83 | } |
84 | ||
d9fdee6e | 85 | int merge_branches(git_repository *repo, |
02105a27 | 86 | const char *ours_branch, const char *theirs_branch, |
5aa2ac6d | 87 | git_merge_options *merge_opts, git_checkout_options *checkout_opts) |
039db728 ET |
88 | { |
89 | git_reference *head_ref, *theirs_ref; | |
18b00406 | 90 | git_annotated_commit *theirs_head; |
6affd71f | 91 | git_checkout_options head_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; |
039db728 ET |
92 | |
93 | head_checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE; | |
94 | ||
659cf202 | 95 | cl_git_pass(git_reference_symbolic_create(&head_ref, repo, "HEAD", ours_branch, 1, NULL)); |
039db728 ET |
96 | cl_git_pass(git_checkout_head(repo, &head_checkout_opts)); |
97 | ||
98 | cl_git_pass(git_reference_lookup(&theirs_ref, repo, theirs_branch)); | |
18b00406 | 99 | cl_git_pass(git_annotated_commit_from_ref(&theirs_head, repo, theirs_ref)); |
039db728 | 100 | |
18b00406 | 101 | cl_git_pass(git_merge(repo, (const git_annotated_commit **)&theirs_head, 1, merge_opts, checkout_opts)); |
039db728 ET |
102 | |
103 | git_reference_free(head_ref); | |
104 | git_reference_free(theirs_ref); | |
18b00406 | 105 | git_annotated_commit_free(theirs_head); |
039db728 ET |
106 | |
107 | return 0; | |
108 | } | |
109 | ||
e1807113 | 110 | void merge__dump_index_entries(git_vector *index_entries) |
0462fba5 ET |
111 | { |
112 | size_t i; | |
113 | const git_index_entry *index_entry; | |
1fed6b07 | 114 | |
768f8be3 | 115 | printf ("\nINDEX [%"PRIuZ"]:\n", index_entries->length); |
0462fba5 ET |
116 | for (i = 0; i < index_entries->length; i++) { |
117 | index_entry = index_entries->contents[i]; | |
1fed6b07 | 118 | |
0462fba5 | 119 | printf("%o ", index_entry->mode); |
d541170c | 120 | printf("%s ", git_oid_allocfmt(&index_entry->id)); |
0462fba5 ET |
121 | printf("%d ", git_index_entry_stage(index_entry)); |
122 | printf("%s ", index_entry->path); | |
123 | printf("\n"); | |
124 | } | |
125 | printf("\n"); | |
126 | } | |
127 | ||
e1807113 | 128 | void merge__dump_names(git_index *index) |
0462fba5 ET |
129 | { |
130 | size_t i; | |
131 | const git_index_name_entry *conflict_name; | |
132 | ||
133 | for (i = 0; i < git_index_name_entrycount(index); i++) { | |
134 | conflict_name = git_index_name_get_byindex(index, i); | |
1fed6b07 | 135 | |
0462fba5 ET |
136 | printf("%s %s %s\n", conflict_name->ancestor, conflict_name->ours, conflict_name->theirs); |
137 | } | |
138 | printf("\n"); | |
139 | } | |
140 | ||
e1807113 | 141 | void merge__dump_reuc(git_index *index) |
0462fba5 ET |
142 | { |
143 | size_t i; | |
144 | const git_index_reuc_entry *reuc; | |
145 | ||
146 | printf ("\nREUC:\n"); | |
147 | for (i = 0; i < git_index_reuc_entrycount(index); i++) { | |
148 | reuc = git_index_reuc_get_byindex(index, i); | |
1fed6b07 | 149 | |
0462fba5 ET |
150 | printf("%s ", reuc->path); |
151 | printf("%o ", reuc->mode[0]); | |
152 | printf("%s\n", git_oid_allocfmt(&reuc->oid[0])); | |
153 | printf(" %o ", reuc->mode[1]); | |
154 | printf(" %s\n", git_oid_allocfmt(&reuc->oid[1])); | |
155 | printf(" %o ", reuc->mode[2]); | |
156 | printf(" %s ", git_oid_allocfmt(&reuc->oid[2])); | |
157 | printf("\n"); | |
158 | } | |
159 | printf("\n"); | |
160 | } | |
161 | ||
bec65a5e ET |
162 | static int index_entry_eq_merge_index_entry(const struct merge_index_entry *expected, const git_index_entry *actual) |
163 | { | |
164 | git_oid expected_oid; | |
0462fba5 | 165 | bool test_oid; |
bec65a5e ET |
166 | |
167 | if (strlen(expected->oid_str) != 0) { | |
168 | cl_git_pass(git_oid_fromstr(&expected_oid, expected->oid_str)); | |
169 | test_oid = 1; | |
170 | } else | |
171 | test_oid = 0; | |
1fed6b07 | 172 | |
bec65a5e | 173 | if (actual->mode != expected->mode || |
d541170c | 174 | (test_oid && git_oid_cmp(&actual->id, &expected_oid) != 0) || |
bec65a5e ET |
175 | git_index_entry_stage(actual) != expected->stage) |
176 | return 0; | |
1fed6b07 | 177 | |
bec65a5e ET |
178 | if (actual->mode == 0 && (actual->path != NULL || strlen(expected->path) > 0)) |
179 | return 0; | |
180 | ||
181 | if (actual->mode != 0 && (strcmp(actual->path, expected->path) != 0)) | |
182 | return 0; | |
1fed6b07 | 183 | |
bec65a5e ET |
184 | return 1; |
185 | } | |
186 | ||
187 | static int name_entry_eq(const char *expected, const char *actual) | |
188 | { | |
189 | if (strlen(expected) == 0) | |
190 | return (actual == NULL) ? 1 : 0; | |
1fed6b07 | 191 | |
bec65a5e ET |
192 | return (strcmp(expected, actual) == 0) ? 1 : 0; |
193 | } | |
194 | ||
0462fba5 ET |
195 | static int name_entry_eq_merge_name_entry(const struct merge_name_entry *expected, const git_index_name_entry *actual) |
196 | { | |
197 | if (name_entry_eq(expected->ancestor_path, actual->ancestor) == 0 || | |
198 | name_entry_eq(expected->our_path, actual->ours) == 0 || | |
199 | name_entry_eq(expected->their_path, actual->theirs) == 0) | |
200 | return 0; | |
201 | ||
202 | return 1; | |
203 | } | |
204 | ||
bec65a5e ET |
205 | static int index_conflict_data_eq_merge_diff(const struct merge_index_conflict_data *expected, git_merge_diff *actual) |
206 | { | |
4e7c1560 ET |
207 | if (!index_entry_eq_merge_index_entry(&expected->ancestor.entry, &actual->ancestor_entry) || |
208 | !index_entry_eq_merge_index_entry(&expected->ours.entry, &actual->our_entry) || | |
209 | !index_entry_eq_merge_index_entry(&expected->theirs.entry, &actual->their_entry)) | |
bec65a5e | 210 | return 0; |
1fed6b07 | 211 | |
bec65a5e ET |
212 | if (expected->ours.status != actual->our_status || |
213 | expected->theirs.status != actual->their_status) | |
214 | return 0; | |
1fed6b07 | 215 | |
bec65a5e ET |
216 | return 1; |
217 | } | |
218 | ||
219 | int merge_test_merge_conflicts(git_vector *conflicts, const struct merge_index_conflict_data expected[], size_t expected_len) | |
220 | { | |
221 | git_merge_diff *actual; | |
222 | size_t i; | |
1fed6b07 | 223 | |
bec65a5e ET |
224 | if (conflicts->length != expected_len) |
225 | return 0; | |
226 | ||
227 | for (i = 0; i < expected_len; i++) { | |
228 | actual = conflicts->contents[i]; | |
1fed6b07 | 229 | |
bec65a5e ET |
230 | if (!index_conflict_data_eq_merge_diff(&expected[i], actual)) |
231 | return 0; | |
232 | } | |
233 | ||
1fed6b07 | 234 | return 1; |
bec65a5e ET |
235 | } |
236 | ||
237 | int merge_test_index(git_index *index, const struct merge_index_entry expected[], size_t expected_len) | |
238 | { | |
1fed6b07 | 239 | size_t i; |
240 | const git_index_entry *index_entry; | |
241 | ||
bec65a5e | 242 | /* |
86c8d02c | 243 | merge__dump_index_entries(&index->entries); |
bec65a5e ET |
244 | */ |
245 | ||
1fed6b07 | 246 | if (git_index_entrycount(index) != expected_len) |
247 | return 0; | |
248 | ||
249 | for (i = 0; i < expected_len; i++) { | |
250 | if ((index_entry = git_index_get_byindex(index, i)) == NULL) | |
251 | return 0; | |
252 | ||
bec65a5e ET |
253 | if (!index_entry_eq_merge_index_entry(&expected[i], index_entry)) |
254 | return 0; | |
1fed6b07 | 255 | } |
256 | ||
257 | return 1; | |
bec65a5e ET |
258 | } |
259 | ||
0462fba5 ET |
260 | int merge_test_names(git_index *index, const struct merge_name_entry expected[], size_t expected_len) |
261 | { | |
262 | size_t i; | |
263 | const git_index_name_entry *name_entry; | |
1fed6b07 | 264 | |
0462fba5 ET |
265 | /* |
266 | dump_names(index); | |
267 | */ | |
268 | ||
269 | if (git_index_name_entrycount(index) != expected_len) | |
270 | return 0; | |
271 | ||
272 | for (i = 0; i < expected_len; i++) { | |
273 | if ((name_entry = git_index_name_get_byindex(index, i)) == NULL) | |
274 | return 0; | |
275 | ||
276 | if (! name_entry_eq_merge_name_entry(&expected[i], name_entry)) | |
277 | return 0; | |
278 | } | |
1fed6b07 | 279 | |
0462fba5 ET |
280 | return 1; |
281 | } | |
282 | ||
bec65a5e ET |
283 | int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[], size_t expected_len) |
284 | { | |
1fed6b07 | 285 | size_t i; |
bec65a5e | 286 | const git_index_reuc_entry *reuc_entry; |
1fed6b07 | 287 | git_oid expected_oid; |
288 | ||
bec65a5e ET |
289 | /* |
290 | dump_reuc(index); | |
291 | */ | |
1fed6b07 | 292 | |
293 | if (git_index_reuc_entrycount(index) != expected_len) | |
294 | return 0; | |
295 | ||
296 | for (i = 0; i < expected_len; i++) { | |
297 | if ((reuc_entry = git_index_reuc_get_byindex(index, i)) == NULL) | |
298 | return 0; | |
bec65a5e ET |
299 | |
300 | if (strcmp(reuc_entry->path, expected[i].path) != 0 || | |
301 | reuc_entry->mode[0] != expected[i].ancestor_mode || | |
302 | reuc_entry->mode[1] != expected[i].our_mode || | |
303 | reuc_entry->mode[2] != expected[i].their_mode) | |
304 | return 0; | |
305 | ||
306 | if (expected[i].ancestor_mode > 0) { | |
307 | cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].ancestor_oid_str)); | |
308 | ||
309 | if (git_oid_cmp(&reuc_entry->oid[0], &expected_oid) != 0) | |
310 | return 0; | |
311 | } | |
312 | ||
313 | if (expected[i].our_mode > 0) { | |
314 | cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].our_oid_str)); | |
315 | ||
316 | if (git_oid_cmp(&reuc_entry->oid[1], &expected_oid) != 0) | |
317 | return 0; | |
318 | } | |
319 | ||
320 | if (expected[i].their_mode > 0) { | |
321 | cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].their_oid_str)); | |
322 | ||
323 | if (git_oid_cmp(&reuc_entry->oid[2], &expected_oid) != 0) | |
324 | return 0; | |
325 | } | |
1fed6b07 | 326 | } |
327 | ||
328 | return 1; | |
bec65a5e ET |
329 | } |
330 | ||
e579e0f7 | 331 | static int dircount(void *payload, git_str *pathbuf) |
bec65a5e | 332 | { |
d99e5471 | 333 | size_t *entries = payload; |
e579e0f7 | 334 | size_t len = git_str_len(pathbuf); |
1fed6b07 | 335 | |
e579e0f7 | 336 | if (len < 5 || strcmp(pathbuf->ptr + (git_str_len(pathbuf) - 5), "/.git") != 0) |
bec65a5e | 337 | (*entries)++; |
1fed6b07 | 338 | |
bec65a5e ET |
339 | return 0; |
340 | } | |
341 | ||
342 | int merge_test_workdir(git_repository *repo, const struct merge_index_entry expected[], size_t expected_len) | |
343 | { | |
344 | size_t actual_len = 0, i; | |
345 | git_oid actual_oid, expected_oid; | |
e579e0f7 | 346 | git_str wd = GIT_STR_INIT; |
1fed6b07 | 347 | |
e579e0f7 MB |
348 | git_str_puts(&wd, repo->workdir); |
349 | git_fs_path_direach(&wd, 0, dircount, &actual_len); | |
1fed6b07 | 350 | |
bec65a5e ET |
351 | if (actual_len != expected_len) |
352 | return 0; | |
1fed6b07 | 353 | |
bec65a5e | 354 | for (i = 0; i < expected_len; i++) { |
22a2d3d5 | 355 | git_blob_create_from_workdir(&actual_oid, repo, expected[i].path); |
bec65a5e | 356 | git_oid_fromstr(&expected_oid, expected[i].oid_str); |
1fed6b07 | 357 | |
bec65a5e ET |
358 | if (git_oid_cmp(&actual_oid, &expected_oid) != 0) |
359 | return 0; | |
360 | } | |
1fed6b07 | 361 | |
e579e0f7 | 362 | git_str_dispose(&wd); |
1fed6b07 | 363 | |
bec65a5e ET |
364 | return 1; |
365 | } |