Blame src/merge_driver.c

Packit Service 20376f
/*
Packit Service 20376f
 * Copyright (C) the libgit2 contributors. All rights reserved.
Packit Service 20376f
 *
Packit Service 20376f
 * This file is part of libgit2, distributed under the GNU GPL v2 with
Packit Service 20376f
 * a Linking Exception. For full terms see the included COPYING file.
Packit Service 20376f
 */
Packit Service 20376f
Packit Service 20376f
#include "common.h"
Packit Service 20376f
#include "vector.h"
Packit Service 20376f
#include "global.h"
Packit Service 20376f
#include "merge.h"
Packit Service 20376f
#include "merge_driver.h"
Packit Service 20376f
#include "git2/merge.h"
Packit Service 20376f
#include "git2/sys/merge.h"
Packit Service 20376f
Packit Service 20376f
static const char *merge_driver_name__text = "text";
Packit Service 20376f
static const char *merge_driver_name__union = "union";
Packit Service 20376f
static const char *merge_driver_name__binary = "binary";
Packit Service 20376f
Packit Service 20376f
struct merge_driver_registry {
Packit Service 20376f
	git_rwlock lock;
Packit Service 20376f
	git_vector drivers;
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
typedef struct {
Packit Service 20376f
	git_merge_driver *driver;
Packit Service 20376f
	int initialized;
Packit Service 20376f
	char name[GIT_FLEX_ARRAY];
Packit Service 20376f
} git_merge_driver_entry;
Packit Service 20376f
Packit Service 20376f
static struct merge_driver_registry merge_driver_registry;
Packit Service 20376f
Packit Service 20376f
static void git_merge_driver_global_shutdown(void);
Packit Service 20376f
Packit Service 20376f
const git_repository* git_merge_driver_source_repo(const git_merge_driver_source *src)
Packit Service 20376f
{
Packit Service 20376f
	assert(src);
Packit Service 20376f
	return src->repo;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
const git_index_entry* git_merge_driver_source_ancestor(const git_merge_driver_source *src)
Packit Service 20376f
{
Packit Service 20376f
	assert(src);
Packit Service 20376f
	return src->ancestor;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
const git_index_entry* git_merge_driver_source_ours(const git_merge_driver_source *src)
Packit Service 20376f
{
Packit Service 20376f
	assert(src);
Packit Service 20376f
	return src->ours;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
const git_index_entry* git_merge_driver_source_theirs(const git_merge_driver_source *src)
Packit Service 20376f
{
Packit Service 20376f
	assert(src);
Packit Service 20376f
	return src->theirs;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
const git_merge_file_options* git_merge_driver_source_file_options(const git_merge_driver_source *src)
Packit Service 20376f
{
Packit Service 20376f
	assert(src);
Packit Service 20376f
	return src->file_opts;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge_driver__builtin_apply(
Packit Service 20376f
	git_merge_driver *self,
Packit Service 20376f
	const char **path_out,
Packit Service 20376f
	uint32_t *mode_out,
Packit Service 20376f
	git_buf *merged_out,
Packit Service 20376f
	const char *filter_name,
Packit Service 20376f
	const git_merge_driver_source *src)
Packit Service 20376f
{
Packit Service 20376f
	git_merge_driver__builtin *driver = (git_merge_driver__builtin *)self;
Packit Service 20376f
	git_merge_file_options file_opts = GIT_MERGE_FILE_OPTIONS_INIT;
Packit Service 20376f
	git_merge_file_result result = {0};
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(filter_name);
Packit Service 20376f
Packit Service 20376f
	if (src->file_opts)
Packit Service 20376f
		memcpy(&file_opts, src->file_opts, sizeof(git_merge_file_options));
Packit Service 20376f
Packit Service 20376f
	if (driver->favor)
Packit Service 20376f
		file_opts.favor = driver->favor;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_merge_file_from_index(&result, src->repo,
Packit Service 20376f
		src->ancestor, src->ours, src->theirs, &file_opts)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if (!result.automergeable &&
Packit Service 20376f
		!(file_opts.flags & GIT_MERGE_FILE_FAVOR__CONFLICTED)) {
Packit Service 20376f
		error = GIT_EMERGECONFLICT;
Packit Service 20376f
		goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*path_out = git_merge_file__best_path(
Packit Service 20376f
		src->ancestor ? src->ancestor->path : NULL,
Packit Service 20376f
		src->ours ? src->ours->path : NULL,
Packit Service 20376f
		src->theirs ? src->theirs->path : NULL);
Packit Service 20376f
Packit Service 20376f
	*mode_out = git_merge_file__best_mode(
Packit Service 20376f
		src->ancestor ? src->ancestor->mode : 0,
Packit Service 20376f
		src->ours ? src->ours->mode : 0,
Packit Service 20376f
		src->theirs ? src->theirs->mode : 0);
Packit Service 20376f
Packit Service 20376f
	merged_out->ptr = (char *)result.ptr;
Packit Service 20376f
	merged_out->size = result.len;
Packit Service 20376f
	merged_out->asize = result.len;
Packit Service 20376f
	result.ptr = NULL;
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_merge_file_result_free(&result);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_driver_binary_apply(
Packit Service 20376f
	git_merge_driver *self,
Packit Service 20376f
	const char **path_out,
Packit Service 20376f
	uint32_t *mode_out,
Packit Service 20376f
	git_buf *merged_out,
Packit Service 20376f
	const char *filter_name,
Packit Service 20376f
	const git_merge_driver_source *src)
Packit Service 20376f
{
Packit Service 20376f
	GIT_UNUSED(self);
Packit Service 20376f
	GIT_UNUSED(path_out);
Packit Service 20376f
	GIT_UNUSED(mode_out);
Packit Service 20376f
	GIT_UNUSED(merged_out);
Packit Service 20376f
	GIT_UNUSED(filter_name);
Packit Service 20376f
	GIT_UNUSED(src);
Packit Service 20376f
Packit Service 20376f
	return GIT_EMERGECONFLICT;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_driver_entry_cmp(const void *a, const void *b)
Packit Service 20376f
{
Packit Service 20376f
	const git_merge_driver_entry *entry_a = a;
Packit Service 20376f
	const git_merge_driver_entry *entry_b = b;
Packit Service 20376f
Packit Service 20376f
	return strcmp(entry_a->name, entry_b->name);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_driver_entry_search(const void *a, const void *b)
Packit Service 20376f
{
Packit Service 20376f
	const char *name_a = a;
Packit Service 20376f
	const git_merge_driver_entry *entry_b = b;
Packit Service 20376f
Packit Service 20376f
	return strcmp(name_a, entry_b->name);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
git_merge_driver__builtin git_merge_driver__text = {
Packit Service 20376f
	{
Packit Service 20376f
		GIT_MERGE_DRIVER_VERSION,
Packit Service 20376f
		NULL,
Packit Service 20376f
		NULL,
Packit Service 20376f
		git_merge_driver__builtin_apply,
Packit Service 20376f
	},
Packit Service 20376f
	GIT_MERGE_FILE_FAVOR_NORMAL
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
git_merge_driver__builtin git_merge_driver__union = {
Packit Service 20376f
	{
Packit Service 20376f
		GIT_MERGE_DRIVER_VERSION,
Packit Service 20376f
		NULL,
Packit Service 20376f
		NULL,
Packit Service 20376f
		git_merge_driver__builtin_apply,
Packit Service 20376f
	},
Packit Service 20376f
	GIT_MERGE_FILE_FAVOR_UNION
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
git_merge_driver git_merge_driver__binary = {
Packit Service 20376f
	GIT_MERGE_DRIVER_VERSION,
Packit Service 20376f
	NULL,
Packit Service 20376f
	NULL,
Packit Service 20376f
	merge_driver_binary_apply
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
/* Note: callers must lock the registry before calling this function */
Packit Service 20376f
static int merge_driver_registry_insert(
Packit Service 20376f
	const char *name, git_merge_driver *driver)
Packit Service 20376f
{
Packit Service 20376f
	git_merge_driver_entry *entry;
Packit Service 20376f
Packit Service 20376f
	entry = git__calloc(1, sizeof(git_merge_driver_entry) + strlen(name) + 1);
Packit Service 20376f
	GITERR_CHECK_ALLOC(entry);
Packit Service 20376f
Packit Service 20376f
	strcpy(entry->name, name);
Packit Service 20376f
	entry->driver = driver;
Packit Service 20376f
Packit Service 20376f
	return git_vector_insert_sorted(
Packit Service 20376f
		&merge_driver_registry.drivers, entry, NULL);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge_driver_global_init(void)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	if (git_rwlock_init(&merge_driver_registry.lock) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_vector_init(&merge_driver_registry.drivers, 3,
Packit Service 20376f
		merge_driver_entry_cmp)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if ((error = merge_driver_registry_insert(
Packit Service 20376f
			merge_driver_name__text, &git_merge_driver__text.base)) < 0 ||
Packit Service 20376f
		(error = merge_driver_registry_insert(
Packit Service 20376f
			merge_driver_name__union, &git_merge_driver__union.base)) < 0 ||
Packit Service 20376f
		(error = merge_driver_registry_insert(
Packit Service 20376f
			merge_driver_name__binary, &git_merge_driver__binary)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	git__on_shutdown(git_merge_driver_global_shutdown);
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		git_vector_free_deep(&merge_driver_registry.drivers);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void git_merge_driver_global_shutdown(void)
Packit Service 20376f
{
Packit Service 20376f
	git_merge_driver_entry *entry;
Packit Service 20376f
	size_t i;
Packit Service 20376f
Packit Service 20376f
	if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0)
Packit Service 20376f
		return;
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(&merge_driver_registry.drivers, i, entry) {
Packit Service 20376f
		if (entry->driver->shutdown)
Packit Service 20376f
			entry->driver->shutdown(entry->driver);
Packit Service 20376f
Packit Service 20376f
		git__free(entry);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_vector_free(&merge_driver_registry.drivers);
Packit Service 20376f
Packit Service 20376f
	git_rwlock_wrunlock(&merge_driver_registry.lock);
Packit Service 20376f
	git_rwlock_free(&merge_driver_registry.lock);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/* Note: callers must lock the registry before calling this function */
Packit Service 20376f
static int merge_driver_registry_find(size_t *pos, const char *name)
Packit Service 20376f
{
Packit Service 20376f
	return git_vector_search2(pos, &merge_driver_registry.drivers,
Packit Service 20376f
		merge_driver_entry_search, name);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/* Note: callers must lock the registry before calling this function */
Packit Service 20376f
static git_merge_driver_entry *merge_driver_registry_lookup(
Packit Service 20376f
	size_t *pos, const char *name)
Packit Service 20376f
{
Packit Service 20376f
	git_merge_driver_entry *entry = NULL;
Packit Service 20376f
Packit Service 20376f
	if (!merge_driver_registry_find(pos, name))
Packit Service 20376f
		entry = git_vector_get(&merge_driver_registry.drivers, *pos);
Packit Service 20376f
Packit Service 20376f
	return entry;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge_driver_register(const char *name, git_merge_driver *driver)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	assert(name && driver);
Packit Service 20376f
Packit Service 20376f
	if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0) {
Packit Service 20376f
		giterr_set(GITERR_OS, "failed to lock merge driver registry");
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (!merge_driver_registry_find(NULL, name)) {
Packit Service 20376f
		giterr_set(GITERR_MERGE, "attempt to reregister existing driver '%s'",
Packit Service 20376f
			name);
Packit Service 20376f
		error = GIT_EEXISTS;
Packit Service 20376f
		goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	error = merge_driver_registry_insert(name, driver);
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_rwlock_wrunlock(&merge_driver_registry.lock);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge_driver_unregister(const char *name)
Packit Service 20376f
{
Packit Service 20376f
	git_merge_driver_entry *entry;
Packit Service 20376f
	size_t pos;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0) {
Packit Service 20376f
		giterr_set(GITERR_OS, "failed to lock merge driver registry");
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((entry = merge_driver_registry_lookup(&pos, name)) == NULL) {
Packit Service 20376f
		giterr_set(GITERR_MERGE, "cannot find merge driver '%s' to unregister",
Packit Service 20376f
			name);
Packit Service 20376f
		error = GIT_ENOTFOUND;
Packit Service 20376f
		goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_vector_remove(&merge_driver_registry.drivers, pos);
Packit Service 20376f
Packit Service 20376f
	if (entry->initialized && entry->driver->shutdown) {
Packit Service 20376f
		entry->driver->shutdown(entry->driver);
Packit Service 20376f
		entry->initialized = false;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git__free(entry);
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_rwlock_wrunlock(&merge_driver_registry.lock);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
git_merge_driver *git_merge_driver_lookup(const char *name)
Packit Service 20376f
{
Packit Service 20376f
	git_merge_driver_entry *entry;
Packit Service 20376f
	size_t pos;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	/* If we've decided the merge driver to use internally - and not
Packit Service 20376f
	 * based on user configuration (in merge_driver_name_for_path)
Packit Service 20376f
	 * then we can use a hardcoded name to compare instead of bothering
Packit Service 20376f
	 * to take a lock and look it up in the vector.
Packit Service 20376f
	 */
Packit Service 20376f
	if (name == merge_driver_name__text)
Packit Service 20376f
		return &git_merge_driver__text.base;
Packit Service 20376f
	else if (name == merge_driver_name__binary)
Packit Service 20376f
		return &git_merge_driver__binary;
Packit Service 20376f
Packit Service 20376f
	if (git_rwlock_rdlock(&merge_driver_registry.lock) < 0) {
Packit Service 20376f
		giterr_set(GITERR_OS, "failed to lock merge driver registry");
Packit Service 20376f
		return NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	entry = merge_driver_registry_lookup(&pos, name);
Packit Service 20376f
Packit Service 20376f
	git_rwlock_rdunlock(&merge_driver_registry.lock);
Packit Service 20376f
Packit Service 20376f
	if (entry == NULL) {
Packit Service 20376f
		giterr_set(GITERR_MERGE, "cannot use an unregistered filter");
Packit Service 20376f
		return NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (!entry->initialized) {
Packit Service 20376f
		if (entry->driver->initialize &&
Packit Service 20376f
			(error = entry->driver->initialize(entry->driver)) < 0)
Packit Service 20376f
			return NULL;
Packit Service 20376f
Packit Service 20376f
		entry->initialized = 1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return entry->driver;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_driver_name_for_path(
Packit Service 20376f
	const char **out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const char *path,
Packit Service 20376f
	const char *default_driver)
Packit Service 20376f
{
Packit Service 20376f
	const char *value;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	*out = NULL;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_attr_get(&value, repo, 0, path, "merge")) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	/* set: use the built-in 3-way merge driver ("text") */
Packit Service 20376f
	if (GIT_ATTR_TRUE(value))
Packit Service 20376f
		*out = merge_driver_name__text;
Packit Service 20376f
Packit Service 20376f
	/* unset: do not merge ("binary") */
Packit Service 20376f
	else if (GIT_ATTR_FALSE(value))
Packit Service 20376f
		*out = merge_driver_name__binary;
Packit Service 20376f
Packit Service 20376f
	else if (GIT_ATTR_UNSPECIFIED(value) && default_driver)
Packit Service 20376f
		*out = default_driver;
Packit Service 20376f
Packit Service 20376f
	else if (GIT_ATTR_UNSPECIFIED(value))
Packit Service 20376f
		*out = merge_driver_name__text;
Packit Service 20376f
Packit Service 20376f
	else
Packit Service 20376f
		*out = value;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
GIT_INLINE(git_merge_driver *) merge_driver_lookup_with_wildcard(
Packit Service 20376f
	const char *name)
Packit Service 20376f
{
Packit Service 20376f
	git_merge_driver *driver = git_merge_driver_lookup(name);
Packit Service 20376f
Packit Service 20376f
	if (driver == NULL)
Packit Service 20376f
		driver = git_merge_driver_lookup("*");
Packit Service 20376f
Packit Service 20376f
	return driver;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge_driver_for_source(
Packit Service 20376f
	const char **name_out,
Packit Service 20376f
	git_merge_driver **driver_out,
Packit Service 20376f
	const git_merge_driver_source *src)
Packit Service 20376f
{
Packit Service 20376f
	const char *path, *driver_name;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	path = git_merge_file__best_path(
Packit Service 20376f
		src->ancestor ? src->ancestor->path : NULL,
Packit Service 20376f
		src->ours ? src->ours->path : NULL,
Packit Service 20376f
		src->theirs ? src->theirs->path : NULL);
Packit Service 20376f
Packit Service 20376f
	if ((error = merge_driver_name_for_path(
Packit Service 20376f
			&driver_name, src->repo, path, src->default_driver)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	*name_out = driver_name;
Packit Service 20376f
	*driver_out = merge_driver_lookup_with_wildcard(driver_name);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f