merge_helpers.c 9.71 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

int merge_trees_from_branches(
	git_index **index, git_repository *repo,
	const char *ours_name, const char *theirs_name,
13
	git_merge_options *opts)
Edward Thomson committed
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
{
	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
int merge_commits_from_branches(
	git_index **index, git_repository *repo,
	const char *ours_name, const char *theirs_name,
58
	git_merge_options *opts)
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
{
	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
int merge_branches(git_repository *repo,
83
	const char *ours_branch, const char *theirs_branch,
84
	git_merge_options *merge_opts, git_checkout_options *checkout_opts)
85 86 87
{
	git_reference *head_ref, *theirs_ref;
	git_merge_head *theirs_head;
88
	git_checkout_options head_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
89 90 91

	head_checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;

92
	cl_git_pass(git_reference_symbolic_create(&head_ref, repo, "HEAD", ours_branch, 1, NULL, NULL));
93 94 95 96 97
	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));

98
	cl_git_pass(git_merge(repo, (const git_merge_head **)&theirs_head, 1, merge_opts, checkout_opts));
99 100 101 102 103 104 105 106

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

	return 0;
}

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

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

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

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

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

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

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

	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
169

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

Edward Thomson committed
175 176 177 178 179
	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
180

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

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

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

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

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

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

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

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

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

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

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

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

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

	return 1;
Edward Thomson committed
255 256
}

Edward Thomson committed
257 258 259 260
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
261

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

Edward Thomson committed
277 278 279
	return 1;
}

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

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

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

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

	return 1;
Edward Thomson committed
326 327 328 329 330 331
}

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

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

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

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

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

Edward Thomson committed
351 352 353
	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
354

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

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

Edward Thomson committed
361 362
	return 1;
}