merge_helpers.c 9.77 KB
Newer Older
Edward Thomson committed
1
#include "clar_libgit2.h"
2
#include "fileops.h"
Edward Thomson committed
3 4 5 6 7
#include "refs.h"
#include "tree.h"
#include "merge_helpers.h"
#include "merge.h"
#include "git2/merge.h"
8
#include "git2/sys/index.h"
9
#include "git2/annotated_commit.h"
Edward Thomson committed
10 11 12 13

int merge_trees_from_branches(
	git_index **index, git_repository *repo,
	const char *ours_name, const char *theirs_name,
14
	git_merge_options *opts)
Edward Thomson committed
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
{
	git_commit *our_commit, *their_commit, *ancestor_commit = NULL;
	git_tree *our_tree, *their_tree, *ancestor_tree = NULL;
	git_oid our_oid, their_oid, ancestor_oid;
	git_buf branch_buf = GIT_BUF_INIT;
	int error;

	git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name);
	cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr));
	cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid));

	git_buf_clear(&branch_buf);
	git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs_name);
	cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr));
	cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid));

	error = git_merge_base(&ancestor_oid, repo, git_commit_id(our_commit), git_commit_id(their_commit));

	if (error != GIT_ENOTFOUND) {
		cl_git_pass(error);

		cl_git_pass(git_commit_lookup(&ancestor_commit, repo, &ancestor_oid));
		cl_git_pass(git_commit_tree(&ancestor_tree, ancestor_commit));
	}

	cl_git_pass(git_commit_tree(&our_tree, our_commit));
	cl_git_pass(git_commit_tree(&their_tree, their_commit));

	cl_git_pass(git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts));

	git_buf_free(&branch_buf);
	git_tree_free(our_tree);
	git_tree_free(their_tree);
	git_tree_free(ancestor_tree);
	git_commit_free(our_commit);
	git_commit_free(their_commit);
	git_commit_free(ancestor_commit);

	return 0;
}

56 57 58
int merge_commits_from_branches(
	git_index **index, git_repository *repo,
	const char *ours_name, const char *theirs_name,
59
	git_merge_options *opts)
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
{
	git_commit *our_commit, *their_commit;
	git_oid our_oid, their_oid;
	git_buf branch_buf = GIT_BUF_INIT;

	git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name);
	cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr));
	cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid));

	git_buf_clear(&branch_buf);
	git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs_name);
	cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr));
	cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid));

	cl_git_pass(git_merge_commits(index, repo, our_commit, their_commit, opts));

	git_buf_free(&branch_buf);
	git_commit_free(our_commit);
	git_commit_free(their_commit);

	return 0;
}

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 92

	head_checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;

93
	cl_git_pass(git_reference_symbolic_create(&head_ref, repo, "HEAD", ours_branch, 1, NULL, NULL));
94 95 96
	cl_git_pass(git_checkout_head(repo, &head_checkout_opts));

	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 102

	git_reference_free(head_ref);
	git_reference_free(theirs_ref);
103
	git_annotated_commit_free(theirs_head);
104 105 106 107

	return 0;
}

Vicent Marti committed
108
void merge__dump_index_entries(git_vector *index_entries)
Edward Thomson committed
109 110 111
{
	size_t i;
	const git_index_entry *index_entry;
nulltoken committed
112

Edward Thomson committed
113 114 115
	printf ("\nINDEX [%d]:\n", (int)index_entries->length);
	for (i = 0; i < index_entries->length; i++) {
		index_entry = index_entries->contents[i];
nulltoken committed
116

Edward Thomson committed
117
		printf("%o ", index_entry->mode);
118
		printf("%s ", git_oid_allocfmt(&index_entry->id));
Edward Thomson committed
119 120 121 122 123 124 125
		printf("%d ", git_index_entry_stage(index_entry));
		printf("%s ", index_entry->path);
		printf("\n");
	}
	printf("\n");
}

Vicent Marti committed
126
void merge__dump_names(git_index *index)
Edward Thomson committed
127 128 129 130 131 132
{
	size_t i;
	const git_index_name_entry *conflict_name;

	for (i = 0; i < git_index_name_entrycount(index); i++) {
		conflict_name = git_index_name_get_byindex(index, i);
nulltoken committed
133

Edward Thomson committed
134 135 136 137 138
		printf("%s %s %s\n", conflict_name->ancestor, conflict_name->ours, conflict_name->theirs);
	}
	printf("\n");
}

Vicent Marti committed
139
void merge__dump_reuc(git_index *index)
Edward Thomson committed
140 141 142 143 144 145 146
{
	size_t i;
	const git_index_reuc_entry *reuc;

	printf ("\nREUC:\n");
	for (i = 0; i < git_index_reuc_entrycount(index); i++) {
		reuc = git_index_reuc_get_byindex(index, i);
nulltoken committed
147

Edward Thomson committed
148 149 150 151 152 153 154 155 156 157 158 159
		printf("%s ", reuc->path);
		printf("%o ", reuc->mode[0]);
		printf("%s\n", git_oid_allocfmt(&reuc->oid[0]));
		printf("          %o ", reuc->mode[1]);
		printf("          %s\n", git_oid_allocfmt(&reuc->oid[1]));
		printf("          %o ", reuc->mode[2]);
		printf("          %s ", git_oid_allocfmt(&reuc->oid[2]));
		printf("\n");
	}
	printf("\n");
}

Edward Thomson committed
160 161 162
static int index_entry_eq_merge_index_entry(const struct merge_index_entry *expected, const git_index_entry *actual)
{
	git_oid expected_oid;
Edward Thomson committed
163
	bool test_oid;
Edward Thomson committed
164 165 166 167 168 169

	if (strlen(expected->oid_str) != 0) {
		cl_git_pass(git_oid_fromstr(&expected_oid, expected->oid_str));
		test_oid = 1;
	} else
		test_oid = 0;
nulltoken committed
170

Edward Thomson committed
171
	if (actual->mode != expected->mode ||
172
		(test_oid && git_oid_cmp(&actual->id, &expected_oid) != 0) ||
Edward Thomson committed
173 174
		git_index_entry_stage(actual) != expected->stage)
		return 0;
nulltoken committed
175

Edward Thomson committed
176 177 178 179 180
	if (actual->mode == 0 && (actual->path != NULL || strlen(expected->path) > 0))
		return 0;

	if (actual->mode != 0 && (strcmp(actual->path, expected->path) != 0))
		return 0;
nulltoken committed
181

Edward Thomson committed
182 183 184 185 186 187 188
	return 1;
}

static int name_entry_eq(const char *expected, const char *actual)
{
	if (strlen(expected) == 0)
		return (actual == NULL) ? 1 : 0;
nulltoken committed
189

Edward Thomson committed
190 191 192
	return (strcmp(expected, actual) == 0) ? 1 : 0;
}

Edward Thomson committed
193 194 195 196 197 198 199 200 201 202
static int name_entry_eq_merge_name_entry(const struct merge_name_entry *expected, const git_index_name_entry *actual)
{
	if (name_entry_eq(expected->ancestor_path, actual->ancestor) == 0 ||
		name_entry_eq(expected->our_path, actual->ours) == 0 ||
		name_entry_eq(expected->their_path, actual->theirs) == 0)
		return 0;

	return 1;
}

Edward Thomson committed
203 204
static int index_conflict_data_eq_merge_diff(const struct merge_index_conflict_data *expected, git_merge_diff *actual)
{
205 206 207
	if (!index_entry_eq_merge_index_entry(&expected->ancestor.entry, &actual->ancestor_entry) ||
		!index_entry_eq_merge_index_entry(&expected->ours.entry, &actual->our_entry) ||
		!index_entry_eq_merge_index_entry(&expected->theirs.entry, &actual->their_entry))
Edward Thomson committed
208
		return 0;
nulltoken committed
209

Edward Thomson committed
210 211 212
	if (expected->ours.status != actual->our_status ||
		expected->theirs.status != actual->their_status)
		return 0;
nulltoken committed
213

Edward Thomson committed
214 215 216 217 218 219 220
	return 1;
}

int merge_test_merge_conflicts(git_vector *conflicts, const struct merge_index_conflict_data expected[], size_t expected_len)
{
	git_merge_diff *actual;
	size_t i;
nulltoken committed
221

Edward Thomson committed
222 223 224 225 226
	if (conflicts->length != expected_len)
		return 0;

	for (i = 0; i < expected_len; i++) {
		actual = conflicts->contents[i];
nulltoken committed
227

Edward Thomson committed
228 229 230 231
		if (!index_conflict_data_eq_merge_diff(&expected[i], actual))
			return 0;
	}

nulltoken committed
232
	return 1;
Edward Thomson committed
233 234 235 236
}

int merge_test_index(git_index *index, const struct merge_index_entry expected[], size_t expected_len)
{
nulltoken committed
237 238 239
	size_t i;
	const git_index_entry *index_entry;

Edward Thomson committed
240 241 242 243
	/*
	dump_index_entries(&index->entries);
	*/

nulltoken committed
244 245 246 247 248 249 250
	if (git_index_entrycount(index) != expected_len)
		return 0;

	for (i = 0; i < expected_len; i++) {
		if ((index_entry = git_index_get_byindex(index, i)) == NULL)
			return 0;

Edward Thomson committed
251 252
		if (!index_entry_eq_merge_index_entry(&expected[i], index_entry))
			return 0;
nulltoken committed
253 254 255
	}

	return 1;
Edward Thomson committed
256 257
}

Edward Thomson committed
258 259 260 261
int merge_test_names(git_index *index, const struct merge_name_entry expected[], size_t expected_len)
{
	size_t i;
	const git_index_name_entry *name_entry;
nulltoken committed
262

Edward Thomson committed
263 264 265 266 267 268 269 270 271 272 273 274 275 276
	/*
	dump_names(index);
	*/

	if (git_index_name_entrycount(index) != expected_len)
		return 0;

	for (i = 0; i < expected_len; i++) {
		if ((name_entry = git_index_name_get_byindex(index, i)) == NULL)
			return 0;

		if (! name_entry_eq_merge_name_entry(&expected[i], name_entry))
			return 0;
	}
nulltoken committed
277

Edward Thomson committed
278 279 280
	return 1;
}

Edward Thomson committed
281 282
int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[], size_t expected_len)
{
nulltoken committed
283
	size_t i;
Edward Thomson committed
284
	const git_index_reuc_entry *reuc_entry;
nulltoken committed
285 286
	git_oid expected_oid;

Edward Thomson committed
287 288 289
	/*
	dump_reuc(index);
	*/
nulltoken committed
290 291 292 293 294 295 296

	if (git_index_reuc_entrycount(index) != expected_len)
		return 0;

	for (i = 0; i < expected_len; i++) {
		if ((reuc_entry = git_index_reuc_get_byindex(index, i)) == NULL)
			return 0;
Edward Thomson committed
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323

		if (strcmp(reuc_entry->path, expected[i].path) != 0 ||
			reuc_entry->mode[0] != expected[i].ancestor_mode ||
			reuc_entry->mode[1] != expected[i].our_mode ||
			reuc_entry->mode[2] != expected[i].their_mode)
			return 0;

		if (expected[i].ancestor_mode > 0) {
			cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].ancestor_oid_str));

			if (git_oid_cmp(&reuc_entry->oid[0], &expected_oid) != 0)
				return 0;
		}

		if (expected[i].our_mode > 0) {
			cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].our_oid_str));

			if (git_oid_cmp(&reuc_entry->oid[1], &expected_oid) != 0)
				return 0;
		}

		if (expected[i].their_mode > 0) {
			cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].their_oid_str));

			if (git_oid_cmp(&reuc_entry->oid[2], &expected_oid) != 0)
				return 0;
		}
nulltoken committed
324 325 326
	}

	return 1;
Edward Thomson committed
327 328 329 330
}

int dircount(void *payload, git_buf *pathbuf)
{
331
	size_t *entries = payload;
Edward Thomson committed
332
	size_t len = git_buf_len(pathbuf);
nulltoken committed
333

Edward Thomson committed
334 335
	if (len < 5 || strcmp(pathbuf->ptr + (git_buf_len(pathbuf) - 5), "/.git") != 0)
		(*entries)++;
nulltoken committed
336

Edward Thomson committed
337 338 339 340 341 342 343 344
	return 0;
}

int merge_test_workdir(git_repository *repo, const struct merge_index_entry expected[], size_t expected_len)
{
	size_t actual_len = 0, i;
	git_oid actual_oid, expected_oid;
	git_buf wd = GIT_BUF_INIT;
nulltoken committed
345 346

	git_buf_puts(&wd, repo->workdir);
347
	git_path_direach(&wd, 0, dircount, &actual_len);
nulltoken committed
348

Edward Thomson committed
349 350
	if (actual_len != expected_len)
		return 0;
nulltoken committed
351

Edward Thomson committed
352 353 354
	for (i = 0; i < expected_len; i++) {
		git_blob_create_fromworkdir(&actual_oid, repo, expected[i].path);
		git_oid_fromstr(&expected_oid, expected[i].oid_str);
nulltoken committed
355

Edward Thomson committed
356 357 358
		if (git_oid_cmp(&actual_oid, &expected_oid) != 0)
			return 0;
	}
nulltoken committed
359

Edward Thomson committed
360
	git_buf_free(&wd);
nulltoken committed
361

Edward Thomson committed
362 363
	return 1;
}