1 #include "clar_libgit2.h"
2 #include "futils.h"
3 #include "refs.h"
4 #include "tree.h"
5 #include "merge_helpers.h"
6 #include "merge.h"
7 #include "index.h"
8 #include "git2/merge.h"
9 #include "git2/sys/index.h"
10 #include "git2/annotated_commit.h"
11 
merge_trees_from_branches(git_index ** index,git_repository * repo,const char * ours_name,const char * theirs_name,git_merge_options * opts)12 int merge_trees_from_branches(
13 	git_index **index, git_repository *repo,
14 	const char *ours_name, const char *theirs_name,
15 	git_merge_options *opts)
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;
20 	git_buf branch_buf = GIT_BUF_INIT;
21 	int error;
22 
23 	git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name);
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 
27 	git_buf_clear(&branch_buf);
28 	git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs_name);
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 
44 	error = git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts);
45 
46 	git_buf_dispose(&branch_buf);
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 
54 	return error;
55 }
56 
merge_commits_from_branches(git_index ** index,git_repository * repo,const char * ours_name,const char * theirs_name,git_merge_options * opts)57 int merge_commits_from_branches(
58 	git_index **index, git_repository *repo,
59 	const char *ours_name, const char *theirs_name,
60 	git_merge_options *opts)
61 {
62 	git_commit *our_commit, *their_commit;
63 	git_oid our_oid, their_oid;
64 	git_buf branch_buf = GIT_BUF_INIT;
65 	int error;
66 
67 	git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name);
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 
71 	git_buf_clear(&branch_buf);
72 	git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs_name);
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 
76 	error = git_merge_commits(index, repo, our_commit, their_commit, opts);
77 
78 	git_buf_dispose(&branch_buf);
79 	git_commit_free(our_commit);
80 	git_commit_free(their_commit);
81 
82 	return error;
83 }
84 
merge_branches(git_repository * repo,const char * ours_branch,const char * theirs_branch,git_merge_options * merge_opts,git_checkout_options * checkout_opts)85 int merge_branches(git_repository *repo,
86 	const char *ours_branch, const char *theirs_branch,
87 	git_merge_options *merge_opts, git_checkout_options *checkout_opts)
88 {
89 	git_reference *head_ref, *theirs_ref;
90 	git_annotated_commit *theirs_head;
91 	git_checkout_options head_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
92 
93 	head_checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
94 
95 	cl_git_pass(git_reference_symbolic_create(&head_ref, repo, "HEAD", ours_branch, 1, NULL));
96 	cl_git_pass(git_checkout_head(repo, &head_checkout_opts));
97 
98 	cl_git_pass(git_reference_lookup(&theirs_ref, repo, theirs_branch));
99 	cl_git_pass(git_annotated_commit_from_ref(&theirs_head, repo, theirs_ref));
100 
101 	cl_git_pass(git_merge(repo, (const git_annotated_commit **)&theirs_head, 1, merge_opts, checkout_opts));
102 
103 	git_reference_free(head_ref);
104 	git_reference_free(theirs_ref);
105 	git_annotated_commit_free(theirs_head);
106 
107 	return 0;
108 }
109 
merge__dump_index_entries(git_vector * index_entries)110 void merge__dump_index_entries(git_vector *index_entries)
111 {
112 	size_t i;
113 	const git_index_entry *index_entry;
114 
115 	printf ("\nINDEX [%"PRIuZ"]:\n", index_entries->length);
116 	for (i = 0; i < index_entries->length; i++) {
117 		index_entry = index_entries->contents[i];
118 
119 		printf("%o ", index_entry->mode);
120 		printf("%s ", git_oid_allocfmt(&index_entry->id));
121 		printf("%d ", git_index_entry_stage(index_entry));
122 		printf("%s ", index_entry->path);
123 		printf("\n");
124 	}
125 	printf("\n");
126 }
127 
merge__dump_names(git_index * index)128 void merge__dump_names(git_index *index)
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);
135 
136 		printf("%s %s %s\n", conflict_name->ancestor, conflict_name->ours, conflict_name->theirs);
137 	}
138 	printf("\n");
139 }
140 
merge__dump_reuc(git_index * index)141 void merge__dump_reuc(git_index *index)
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);
149 
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 
index_entry_eq_merge_index_entry(const struct merge_index_entry * expected,const git_index_entry * actual)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;
165 	bool test_oid;
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;
172 
173 	if (actual->mode != expected->mode ||
174 		(test_oid && git_oid_cmp(&actual->id, &expected_oid) != 0) ||
175 		git_index_entry_stage(actual) != expected->stage)
176 		return 0;
177 
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;
183 
184 	return 1;
185 }
186 
name_entry_eq(const char * expected,const char * actual)187 static int name_entry_eq(const char *expected, const char *actual)
188 {
189 	if (strlen(expected) == 0)
190 		return (actual == NULL) ? 1 : 0;
191 
192 	return (strcmp(expected, actual) == 0) ? 1 : 0;
193 }
194 
name_entry_eq_merge_name_entry(const struct merge_name_entry * expected,const git_index_name_entry * actual)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 
index_conflict_data_eq_merge_diff(const struct merge_index_conflict_data * expected,git_merge_diff * actual)205 static int index_conflict_data_eq_merge_diff(const struct merge_index_conflict_data *expected, git_merge_diff *actual)
206 {
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))
210 		return 0;
211 
212 	if (expected->ours.status != actual->our_status ||
213 		expected->theirs.status != actual->their_status)
214 		return 0;
215 
216 	return 1;
217 }
218 
merge_test_merge_conflicts(git_vector * conflicts,const struct merge_index_conflict_data expected[],size_t expected_len)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;
223 
224 	if (conflicts->length != expected_len)
225 		return 0;
226 
227 	for (i = 0; i < expected_len; i++) {
228 		actual = conflicts->contents[i];
229 
230 		if (!index_conflict_data_eq_merge_diff(&expected[i], actual))
231 			return 0;
232 	}
233 
234 	return 1;
235 }
236 
merge_test_index(git_index * index,const struct merge_index_entry expected[],size_t expected_len)237 int merge_test_index(git_index *index, const struct merge_index_entry expected[], size_t expected_len)
238 {
239 	size_t i;
240 	const git_index_entry *index_entry;
241 
242 	/*
243 	merge__dump_index_entries(&index->entries);
244 	*/
245 
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 
253 		if (!index_entry_eq_merge_index_entry(&expected[i], index_entry))
254 			return 0;
255 	}
256 
257 	return 1;
258 }
259 
merge_test_names(git_index * index,const struct merge_name_entry expected[],size_t expected_len)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;
264 
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 	}
279 
280 	return 1;
281 }
282 
merge_test_reuc(git_index * index,const struct merge_reuc_entry expected[],size_t expected_len)283 int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[], size_t expected_len)
284 {
285 	size_t i;
286 	const git_index_reuc_entry *reuc_entry;
287 	git_oid expected_oid;
288 
289 	/*
290 	dump_reuc(index);
291 	*/
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;
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 		}
326 	}
327 
328 	return 1;
329 }
330 
dircount(void * payload,git_buf * pathbuf)331 int dircount(void *payload, git_buf *pathbuf)
332 {
333 	size_t *entries = payload;
334 	size_t len = git_buf_len(pathbuf);
335 
336 	if (len < 5 || strcmp(pathbuf->ptr + (git_buf_len(pathbuf) - 5), "/.git") != 0)
337 		(*entries)++;
338 
339 	return 0;
340 }
341 
merge_test_workdir(git_repository * repo,const struct merge_index_entry expected[],size_t expected_len)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;
346 	git_buf wd = GIT_BUF_INIT;
347 
348 	git_buf_puts(&wd, repo->workdir);
349 	git_path_direach(&wd, 0, dircount, &actual_len);
350 
351 	if (actual_len != expected_len)
352 		return 0;
353 
354 	for (i = 0; i < expected_len; i++) {
355 		git_blob_create_from_workdir(&actual_oid, repo, expected[i].path);
356 		git_oid_fromstr(&expected_oid, expected[i].oid_str);
357 
358 		if (git_oid_cmp(&actual_oid, &expected_oid) != 0)
359 			return 0;
360 	}
361 
362 	git_buf_dispose(&wd);
363 
364 	return 1;
365 }
366