merge_helpers.c 7.98 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;
}

Vicent Marti committed
55
void merge__dump_index_entries(git_vector *index_entries)
Edward Thomson committed
56 57 58
{
	size_t i;
	const git_index_entry *index_entry;
nulltoken committed
59

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

Edward Thomson committed
64 65 66 67 68 69 70 71 72
		printf("%o ", index_entry->mode);
		printf("%s ", git_oid_allocfmt(&index_entry->oid));
		printf("%d ", git_index_entry_stage(index_entry));
		printf("%s ", index_entry->path);
		printf("\n");
	}
	printf("\n");
}

Vicent Marti committed
73
void merge__dump_names(git_index *index)
Edward Thomson committed
74 75 76 77 78 79
{
	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
80

Edward Thomson committed
81 82 83 84 85
		printf("%s %s %s\n", conflict_name->ancestor, conflict_name->ours, conflict_name->theirs);
	}
	printf("\n");
}

Vicent Marti committed
86
void merge__dump_reuc(git_index *index)
Edward Thomson committed
87 88 89 90 91 92 93
{
	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
94

Edward Thomson committed
95 96 97 98 99 100 101 102 103 104 105 106
		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
107 108 109
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
110
	bool test_oid;
Edward Thomson committed
111 112 113 114 115 116

	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
117

Edward Thomson committed
118 119 120 121
	if (actual->mode != expected->mode ||
		(test_oid && git_oid_cmp(&actual->oid, &expected_oid) != 0) ||
		git_index_entry_stage(actual) != expected->stage)
		return 0;
nulltoken committed
122

Edward Thomson committed
123 124 125 126 127
	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
128

Edward Thomson committed
129 130 131 132 133 134 135
	return 1;
}

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

Edward Thomson committed
137 138 139
	return (strcmp(expected, actual) == 0) ? 1 : 0;
}

Edward Thomson committed
140 141 142 143 144 145 146 147 148 149
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
150 151
static int index_conflict_data_eq_merge_diff(const struct merge_index_conflict_data *expected, git_merge_diff *actual)
{
152 153 154
	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
155
		return 0;
nulltoken committed
156

Edward Thomson committed
157 158 159
	if (expected->ours.status != actual->our_status ||
		expected->theirs.status != actual->their_status)
		return 0;
nulltoken committed
160

Edward Thomson committed
161 162 163 164 165 166 167
	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
168

Edward Thomson committed
169 170 171 172 173
	if (conflicts->length != expected_len)
		return 0;

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

Edward Thomson committed
175 176 177 178
		if (!index_conflict_data_eq_merge_diff(&expected[i], actual))
			return 0;
	}

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

int merge_test_index(git_index *index, const struct merge_index_entry expected[], size_t expected_len)
{
nulltoken committed
184 185 186
	size_t i;
	const git_index_entry *index_entry;

Edward Thomson committed
187 188 189 190
	/*
	dump_index_entries(&index->entries);
	*/

nulltoken committed
191 192 193 194 195 196 197
	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
198 199
		if (!index_entry_eq_merge_index_entry(&expected[i], index_entry))
			return 0;
nulltoken committed
200 201 202
	}

	return 1;
Edward Thomson committed
203 204
}

Edward Thomson committed
205 206 207 208
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
209

Edward Thomson committed
210 211 212 213 214 215 216 217 218 219 220 221 222 223
	/*
	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
224

Edward Thomson committed
225 226 227
	return 1;
}

Edward Thomson committed
228 229
int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[], size_t expected_len)
{
nulltoken committed
230
	size_t i;
Edward Thomson committed
231
	const git_index_reuc_entry *reuc_entry;
nulltoken committed
232 233
	git_oid expected_oid;

Edward Thomson committed
234 235 236
	/*
	dump_reuc(index);
	*/
nulltoken committed
237 238 239 240 241 242 243

	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
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270

		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
271 272 273
	}

	return 1;
Edward Thomson committed
274 275 276 277 278 279
}

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

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

Edward Thomson committed
284 285 286 287 288 289 290 291
	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
292 293

	git_buf_puts(&wd, repo->workdir);
Edward Thomson committed
294
	git_path_direach(&wd, dircount, &actual_len);
nulltoken committed
295

Edward Thomson committed
296 297
	if (actual_len != expected_len)
		return 0;
nulltoken committed
298

Edward Thomson committed
299 300 301
	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
302

Edward Thomson committed
303 304 305
		if (git_oid_cmp(&actual_oid, &expected_oid) != 0)
			return 0;
	}
nulltoken committed
306

Edward Thomson committed
307
	git_buf_free(&wd);
nulltoken committed
308

Edward Thomson committed
309 310
	return 1;
}