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