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

int merge_trees_from_branches(
	git_index **index, git_repository *repo,
	const char *ours_name, const char *theirs_name,
15
	git_merge_options *opts)
Edward Thomson committed
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
{
	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));

44
	error = git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts);
Edward Thomson committed
45 46 47 48 49 50 51 52 53

	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);

54
	return error;
Edward Thomson committed
55 56
}

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

	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));

76
	error = git_merge_commits(index, repo, our_commit, their_commit, opts);
77 78 79 80 81

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

82
	return error;
83 84
}

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 94

	head_checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;

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

	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 104

	git_reference_free(head_ref);
	git_reference_free(theirs_ref);
105
	git_annotated_commit_free(theirs_head);
106 107 108 109

	return 0;
}

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

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

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

Vicent Marti committed
128
void merge__dump_names(git_index *index)
Edward Thomson committed
129 130 131 132 133 134
{
	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
135

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

Vicent Marti committed
141
void merge__dump_reuc(git_index *index)
Edward Thomson committed
142 143 144 145 146 147 148
{
	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
149

Edward Thomson committed
150 151 152 153 154 155 156 157 158 159 160 161
		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
162 163 164
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
165
	bool test_oid;
Edward Thomson committed
166 167 168 169 170 171

	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
172

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

Edward Thomson committed
178 179 180 181 182
	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
183

Edward Thomson committed
184 185 186 187 188 189 190
	return 1;
}

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

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

Edward Thomson committed
195 196 197 198 199 200 201 202 203 204
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
205 206
static int index_conflict_data_eq_merge_diff(const struct merge_index_conflict_data *expected, git_merge_diff *actual)
{
207 208 209
	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
210
		return 0;
nulltoken committed
211

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

Edward Thomson committed
216 217 218 219 220 221 222
	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
223

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

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

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

nulltoken committed
234
	return 1;
Edward Thomson committed
235 236 237 238
}

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

Edward Thomson committed
242
	/*
243
	merge__dump_index_entries(&index->entries);
Edward Thomson committed
244 245
	*/

nulltoken committed
246 247 248 249 250 251 252
	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
253 254
		if (!index_entry_eq_merge_index_entry(&expected[i], index_entry))
			return 0;
nulltoken committed
255 256 257
	}

	return 1;
Edward Thomson committed
258 259
}

Edward Thomson committed
260 261 262 263
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
264

Edward Thomson committed
265 266 267 268 269 270 271 272 273 274 275 276 277 278
	/*
	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
279

Edward Thomson committed
280 281 282
	return 1;
}

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

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

	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
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 324 325

		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
326 327 328
	}

	return 1;
Edward Thomson committed
329 330 331 332
}

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

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

Edward Thomson committed
339 340 341 342 343 344 345 346
	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
347 348

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

Edward Thomson committed
351 352
	if (actual_len != expected_len)
		return 0;
nulltoken committed
353

Edward Thomson committed
354 355 356
	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
357

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

Edward Thomson committed
362
	git_buf_free(&wd);
nulltoken committed
363

Edward Thomson committed
364 365
	return 1;
}