merge_helpers.c 9.68 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"
Edward Thomson committed
9 10 11 12 13 14 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

int merge_trees_from_branches(
	git_index **index, git_repository *repo,
	const char *ours_name, const char *theirs_name,
	git_merge_tree_opts *opts)
{
	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;
}

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
int merge_commits_from_branches(
	git_index **index, git_repository *repo,
	const char *ours_name, const char *theirs_name,
	git_merge_tree_opts *opts)
{
	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;
}

82 83 84 85
int merge_branches(git_merge_result **result, git_repository *repo, const char *ours_branch, const char *theirs_branch, git_merge_opts *opts)
{
	git_reference *head_ref, *theirs_ref;
	git_merge_head *theirs_head;
86
	git_checkout_options head_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
87 88 89

	head_checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;

90
	cl_git_pass(git_reference_symbolic_create(&head_ref, repo, "HEAD", ours_branch, 1, NULL, NULL));
91 92 93 94 95 96 97 98 99 100 101 102 103 104
	cl_git_pass(git_checkout_head(repo, &head_checkout_opts));

	cl_git_pass(git_reference_lookup(&theirs_ref, repo, theirs_branch));
	cl_git_pass(git_merge_head_from_ref(&theirs_head, repo, theirs_ref));

	cl_git_pass(git_merge(result, repo, (const git_merge_head **)&theirs_head, 1, opts));

	git_reference_free(head_ref);
	git_reference_free(theirs_ref);
	git_merge_head_free(theirs_head);

	return 0;
}

Vicent Marti committed
105
void merge__dump_index_entries(git_vector *index_entries)
Edward Thomson committed
106 107 108
{
	size_t i;
	const git_index_entry *index_entry;
nulltoken committed
109

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

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

Vicent Marti committed
123
void merge__dump_names(git_index *index)
Edward Thomson committed
124 125 126 127 128 129
{
	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
130

Edward Thomson committed
131 132 133 134 135
		printf("%s %s %s\n", conflict_name->ancestor, conflict_name->ours, conflict_name->theirs);
	}
	printf("\n");
}

Vicent Marti committed
136
void merge__dump_reuc(git_index *index)
Edward Thomson committed
137 138 139 140 141 142 143
{
	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
144

Edward Thomson committed
145 146 147 148 149 150 151 152 153 154 155 156
		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
157 158 159
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
160
	bool test_oid;
Edward Thomson committed
161 162 163 164 165 166

	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
167

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

Edward Thomson committed
173 174 175 176 177
	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
178

Edward Thomson committed
179 180 181 182 183 184 185
	return 1;
}

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

Edward Thomson committed
187 188 189
	return (strcmp(expected, actual) == 0) ? 1 : 0;
}

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

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

Edward Thomson committed
211 212 213 214 215 216 217
	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
218

Edward Thomson committed
219 220 221 222 223
	if (conflicts->length != expected_len)
		return 0;

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

Edward Thomson committed
225 226 227 228
		if (!index_conflict_data_eq_merge_diff(&expected[i], actual))
			return 0;
	}

nulltoken committed
229
	return 1;
Edward Thomson committed
230 231 232 233
}

int merge_test_index(git_index *index, const struct merge_index_entry expected[], size_t expected_len)
{
nulltoken committed
234 235 236
	size_t i;
	const git_index_entry *index_entry;

Edward Thomson committed
237 238 239 240
	/*
	dump_index_entries(&index->entries);
	*/

nulltoken committed
241 242 243 244 245 246 247
	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
248 249
		if (!index_entry_eq_merge_index_entry(&expected[i], index_entry))
			return 0;
nulltoken committed
250 251 252
	}

	return 1;
Edward Thomson committed
253 254
}

Edward Thomson committed
255 256 257 258
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
259

Edward Thomson committed
260 261 262 263 264 265 266 267 268 269 270 271 272 273
	/*
	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
274

Edward Thomson committed
275 276 277
	return 1;
}

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

Edward Thomson committed
284 285 286
	/*
	dump_reuc(index);
	*/
nulltoken committed
287 288 289 290 291 292 293

	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
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320

		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
321 322 323
	}

	return 1;
Edward Thomson committed
324 325 326 327 328 329
}

int dircount(void *payload, git_buf *pathbuf)
{
	int *entries = payload;
	size_t len = git_buf_len(pathbuf);
nulltoken committed
330

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

Edward Thomson committed
334 335 336 337 338 339 340 341
	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
342 343

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

Edward Thomson committed
346 347
	if (actual_len != expected_len)
		return 0;
nulltoken committed
348

Edward Thomson committed
349 350 351
	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
352

Edward Thomson committed
353 354 355
		if (git_oid_cmp(&actual_oid, &expected_oid) != 0)
			return 0;
	}
nulltoken committed
356

Edward Thomson committed
357
	git_buf_free(&wd);
nulltoken committed
358

Edward Thomson committed
359 360
	return 1;
}