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