merge_driver.c 9.86 KB
Newer Older
1 2 3 4 5 6 7
/*
 * Copyright (C) the libgit2 contributors. All rights reserved.
 *
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
 */

8 9
#include "merge_driver.h"

10 11 12 13 14 15 16 17 18 19 20
#include "vector.h"
#include "global.h"
#include "merge.h"
#include "git2/merge.h"
#include "git2/sys/merge.h"

static const char *merge_driver_name__text = "text";
static const char *merge_driver_name__union = "union";
static const char *merge_driver_name__binary = "binary";

struct merge_driver_registry {
21
	git_rwlock lock;
22 23 24 25 26 27 28 29 30
	git_vector drivers;
};

typedef struct {
	git_merge_driver *driver;
	int initialized;
	char name[GIT_FLEX_ARRAY];
} git_merge_driver_entry;

31
static struct merge_driver_registry merge_driver_registry;
32

33 34
static void git_merge_driver_global_shutdown(void);

35
const git_repository* git_merge_driver_source_repo(const git_merge_driver_source *src)
36 37 38 39 40
{
	assert(src);
	return src->repo;
}

41
const git_index_entry* git_merge_driver_source_ancestor(const git_merge_driver_source *src)
42 43 44 45 46
{
	assert(src);
	return src->ancestor;
}

47
const git_index_entry* git_merge_driver_source_ours(const git_merge_driver_source *src)
48 49 50 51 52
{
	assert(src);
	return src->ours;
}

53
const git_index_entry* git_merge_driver_source_theirs(const git_merge_driver_source *src)
54 55 56 57 58
{
	assert(src);
	return src->theirs;
}

59
const git_merge_file_options* git_merge_driver_source_file_options(const git_merge_driver_source *src)
60 61 62 63
{
	assert(src);
	return src->file_opts;
}
64

65
int git_merge_driver__builtin_apply(
66 67 68 69
	git_merge_driver *self,
	const char **path_out,
	uint32_t *mode_out,
	git_buf *merged_out,
70
	const char *filter_name,
71 72
	const git_merge_driver_source *src)
{
73
	git_merge_driver__builtin *driver = (git_merge_driver__builtin *)self;
74 75 76 77
	git_merge_file_options file_opts = GIT_MERGE_FILE_OPTIONS_INIT;
	git_merge_file_result result = {0};
	int error;

78
	GIT_UNUSED(filter_name);
79 80 81 82

	if (src->file_opts)
		memcpy(&file_opts, src->file_opts, sizeof(git_merge_file_options));

83 84
	if (driver->favor)
		file_opts.favor = driver->favor;
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120

	if ((error = git_merge_file_from_index(&result, src->repo,
		src->ancestor, src->ours, src->theirs, &file_opts)) < 0)
		goto done;

	if (!result.automergeable &&
		!(file_opts.flags & GIT_MERGE_FILE_FAVOR__CONFLICTED)) {
		error = GIT_EMERGECONFLICT;
		goto done;
	}

	*path_out = git_merge_file__best_path(
		src->ancestor ? src->ancestor->path : NULL,
		src->ours ? src->ours->path : NULL,
		src->theirs ? src->theirs->path : NULL);

	*mode_out = git_merge_file__best_mode(
		src->ancestor ? src->ancestor->mode : 0,
		src->ours ? src->ours->mode : 0,
		src->theirs ? src->theirs->mode : 0);

	merged_out->ptr = (char *)result.ptr;
	merged_out->size = result.len;
	merged_out->asize = result.len;
	result.ptr = NULL;

done:
	git_merge_file_result_free(&result);
	return error;
}

static int merge_driver_binary_apply(
	git_merge_driver *self,
	const char **path_out,
	uint32_t *mode_out,
	git_buf *merged_out,
121
	const char *filter_name,
122 123 124 125 126 127
	const git_merge_driver_source *src)
{
	GIT_UNUSED(self);
	GIT_UNUSED(path_out);
	GIT_UNUSED(mode_out);
	GIT_UNUSED(merged_out);
128
	GIT_UNUSED(filter_name);
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
	GIT_UNUSED(src);

	return GIT_EMERGECONFLICT;
}

static int merge_driver_entry_cmp(const void *a, const void *b)
{
	const git_merge_driver_entry *entry_a = a;
	const git_merge_driver_entry *entry_b = b;

	return strcmp(entry_a->name, entry_b->name);
}

static int merge_driver_entry_search(const void *a, const void *b)
{
	const char *name_a = a;
	const git_merge_driver_entry *entry_b = b;

	return strcmp(name_a, entry_b->name);
}

150 151 152 153 154 155 156 157
git_merge_driver__builtin git_merge_driver__text = {
	{
		GIT_MERGE_DRIVER_VERSION,
		NULL,
		NULL,
		git_merge_driver__builtin_apply,
	},
	GIT_MERGE_FILE_FAVOR_NORMAL
158 159
};

160 161 162 163 164 165 166 167
git_merge_driver__builtin git_merge_driver__union = {
	{
		GIT_MERGE_DRIVER_VERSION,
		NULL,
		NULL,
		git_merge_driver__builtin_apply,
	},
	GIT_MERGE_FILE_FAVOR_UNION
168 169 170 171 172 173 174 175 176
};

git_merge_driver git_merge_driver__binary = {
	GIT_MERGE_DRIVER_VERSION,
	NULL,
	NULL,
	merge_driver_binary_apply
};

177 178 179
/* Note: callers must lock the registry before calling this function */
static int merge_driver_registry_insert(
	const char *name, git_merge_driver *driver)
180
{
181
	git_merge_driver_entry *entry;
182

183 184
	entry = git__calloc(1, sizeof(git_merge_driver_entry) + strlen(name) + 1);
	GITERR_CHECK_ALLOC(entry);
185

186 187
	strcpy(entry->name, name);
	entry->driver = driver;
188

189 190 191
	return git_vector_insert_sorted(
		&merge_driver_registry.drivers, entry, NULL);
}
192

193 194 195
int git_merge_driver_global_init(void)
{
	int error;
196

197 198
	if (git_rwlock_init(&merge_driver_registry.lock) < 0)
		return -1;
199

200 201 202 203 204
	if ((error = git_vector_init(&merge_driver_registry.drivers, 3,
		merge_driver_entry_cmp)) < 0)
		goto done;

	if ((error = merge_driver_registry_insert(
205
			merge_driver_name__text, &git_merge_driver__text.base)) < 0 ||
206
		(error = merge_driver_registry_insert(
207
			merge_driver_name__union, &git_merge_driver__union.base)) < 0 ||
208
		(error = merge_driver_registry_insert(
209
			merge_driver_name__binary, &git_merge_driver__binary)) < 0)
210
		goto done;
211 212

	git__on_shutdown(git_merge_driver_global_shutdown);
213 214 215

done:
	if (error < 0)
216
		git_vector_free_deep(&merge_driver_registry.drivers);
217 218 219 220

	return error;
}

221
static void git_merge_driver_global_shutdown(void)
222 223
{
	git_merge_driver_entry *entry;
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
	size_t i;

	if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0)
		return;

	git_vector_foreach(&merge_driver_registry.drivers, i, entry) {
		if (entry->driver->shutdown)
			entry->driver->shutdown(entry->driver);

		git__free(entry);
	}

	git_vector_free(&merge_driver_registry.drivers);

	git_rwlock_wrunlock(&merge_driver_registry.lock);
	git_rwlock_free(&merge_driver_registry.lock);
}

/* Note: callers must lock the registry before calling this function */
static int merge_driver_registry_find(size_t *pos, const char *name)
{
	return git_vector_search2(pos, &merge_driver_registry.drivers,
		merge_driver_entry_search, name);
}

/* Note: callers must lock the registry before calling this function */
static git_merge_driver_entry *merge_driver_registry_lookup(
	size_t *pos, const char *name)
{
	git_merge_driver_entry *entry = NULL;

	if (!merge_driver_registry_find(pos, name))
		entry = git_vector_get(&merge_driver_registry.drivers, *pos);

	return entry;
}

int git_merge_driver_register(const char *name, git_merge_driver *driver)
{
	int error;
264 265 266

	assert(name && driver);

267 268
	if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0) {
		giterr_set(GITERR_OS, "failed to lock merge driver registry");
269
		return -1;
270
	}
271

272 273 274 275 276 277
	if (!merge_driver_registry_find(NULL, name)) {
		giterr_set(GITERR_MERGE, "attempt to reregister existing driver '%s'",
			name);
		error = GIT_EEXISTS;
		goto done;
	}
278

279
	error = merge_driver_registry_insert(name, driver);
280

281 282 283
done:
	git_rwlock_wrunlock(&merge_driver_registry.lock);
	return error;
284 285 286 287 288 289
}

int git_merge_driver_unregister(const char *name)
{
	git_merge_driver_entry *entry;
	size_t pos;
290 291 292 293 294 295
	int error = 0;

	if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0) {
		giterr_set(GITERR_OS, "failed to lock merge driver registry");
		return -1;
	}
296

297 298 299 300 301 302
	if ((entry = merge_driver_registry_lookup(&pos, name)) == NULL) {
		giterr_set(GITERR_MERGE, "cannot find merge driver '%s' to unregister",
			name);
		error = GIT_ENOTFOUND;
		goto done;
	}
303

304
	git_vector_remove(&merge_driver_registry.drivers, pos);
305 306 307 308 309 310 311 312

	if (entry->initialized && entry->driver->shutdown) {
		entry->driver->shutdown(entry->driver);
		entry->initialized = false;
	}

	git__free(entry);

313 314 315
done:
	git_rwlock_wrunlock(&merge_driver_registry.lock);
	return error;
316 317 318 319 320 321 322 323 324 325
}

git_merge_driver *git_merge_driver_lookup(const char *name)
{
	git_merge_driver_entry *entry;
	size_t pos;
	int error;

	/* If we've decided the merge driver to use internally - and not
	 * based on user configuration (in merge_driver_name_for_path)
326 327
	 * then we can use a hardcoded name to compare instead of bothering
	 * to take a lock and look it up in the vector.
328 329
	 */
	if (name == merge_driver_name__text)
330
		return &git_merge_driver__text.base;
331 332 333
	else if (name == merge_driver_name__binary)
		return &git_merge_driver__binary;

334 335
	if (git_rwlock_rdlock(&merge_driver_registry.lock) < 0) {
		giterr_set(GITERR_OS, "failed to lock merge driver registry");
336
		return NULL;
337
	}
338

339
	entry = merge_driver_registry_lookup(&pos, name);
340

341
	git_rwlock_rdunlock(&merge_driver_registry.lock);
342

343 344 345 346
	if (entry == NULL) {
		giterr_set(GITERR_MERGE, "cannot use an unregistered filter");
		return NULL;
	}
347 348 349 350 351 352 353 354 355

	if (!entry->initialized) {
		if (entry->driver->initialize &&
			(error = entry->driver->initialize(entry->driver)) < 0)
			return NULL;

		entry->initialized = 1;
	}

356
	return entry->driver;
357 358 359 360 361
}

static int merge_driver_name_for_path(
	const char **out,
	git_repository *repo,
362 363
	const char *path,
	const char *default_driver)
364 365 366 367 368 369 370 371 372 373
{
	const char *value;
	int error;

	*out = NULL;

	if ((error = git_attr_get(&value, repo, 0, path, "merge")) < 0)
		return error;

	/* set: use the built-in 3-way merge driver ("text") */
374
	if (GIT_ATTR_TRUE(value))
375 376 377
		*out = merge_driver_name__text;

	/* unset: do not merge ("binary") */
378
	else if (GIT_ATTR_FALSE(value))
379 380
		*out = merge_driver_name__binary;

381 382 383 384
	else if (GIT_ATTR_UNSPECIFIED(value) && default_driver)
		*out = default_driver;

	else if (GIT_ATTR_UNSPECIFIED(value))
385
		*out = merge_driver_name__text;
386 387 388 389

	else
		*out = value;

390 391 392
	return 0;
}

393 394 395 396 397 398 399 400 401 402 403 404

GIT_INLINE(git_merge_driver *) merge_driver_lookup_with_wildcard(
	const char *name)
{
	git_merge_driver *driver = git_merge_driver_lookup(name);

	if (driver == NULL)
		driver = git_merge_driver_lookup("*");

	return driver;
}

405
int git_merge_driver_for_source(
406
	const char **name_out,
407 408 409 410 411 412 413 414 415 416 417
	git_merge_driver **driver_out,
	const git_merge_driver_source *src)
{
	const char *path, *driver_name;
	int error = 0;

	path = git_merge_file__best_path(
		src->ancestor ? src->ancestor->path : NULL,
		src->ours ? src->ours->path : NULL,
		src->theirs ? src->theirs->path : NULL);

418 419
	if ((error = merge_driver_name_for_path(
			&driver_name, src->repo, path, src->default_driver)) < 0)
420 421
		return error;

422 423
	*name_out = driver_name;
	*driver_out = merge_driver_lookup_with_wildcard(driver_name);
424 425 426
	return error;
}