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