Blame src/merge.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 "posix.h"
Packit Service 20376f
#include "buffer.h"
Packit Service 20376f
#include "repository.h"
Packit Service 20376f
#include "revwalk.h"
Packit Service 20376f
#include "commit_list.h"
Packit Service 20376f
#include "merge.h"
Packit Service 20376f
#include "path.h"
Packit Service 20376f
#include "refs.h"
Packit Service 20376f
#include "object.h"
Packit Service 20376f
#include "iterator.h"
Packit Service 20376f
#include "refs.h"
Packit Service 20376f
#include "diff.h"
Packit Service 20376f
#include "diff_generate.h"
Packit Service 20376f
#include "diff_tform.h"
Packit Service 20376f
#include "checkout.h"
Packit Service 20376f
#include "tree.h"
Packit Service 20376f
#include "blob.h"
Packit Service 20376f
#include "oid.h"
Packit Service 20376f
#include "index.h"
Packit Service 20376f
#include "filebuf.h"
Packit Service 20376f
#include "config.h"
Packit Service 20376f
#include "oidarray.h"
Packit Service 20376f
#include "annotated_commit.h"
Packit Service 20376f
#include "commit.h"
Packit Service 20376f
#include "oidarray.h"
Packit Service 20376f
#include "merge_driver.h"
Packit Service 20376f
Packit Service 20376f
#include "git2/types.h"
Packit Service 20376f
#include "git2/repository.h"
Packit Service 20376f
#include "git2/object.h"
Packit Service 20376f
#include "git2/commit.h"
Packit Service 20376f
#include "git2/merge.h"
Packit Service 20376f
#include "git2/refs.h"
Packit Service 20376f
#include "git2/reset.h"
Packit Service 20376f
#include "git2/checkout.h"
Packit Service 20376f
#include "git2/signature.h"
Packit Service 20376f
#include "git2/config.h"
Packit Service 20376f
#include "git2/tree.h"
Packit Service 20376f
#include "git2/oidarray.h"
Packit Service 20376f
#include "git2/annotated_commit.h"
Packit Service 20376f
#include "git2/sys/index.h"
Packit Service 20376f
#include "git2/sys/hashsig.h"
Packit Service 20376f
Packit Service 20376f
#define GIT_MERGE_INDEX_ENTRY_EXISTS(X)	((X).mode != 0)
Packit Service 20376f
#define GIT_MERGE_INDEX_ENTRY_ISFILE(X) S_ISREG((X).mode)
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
typedef enum {
Packit Service 20376f
	TREE_IDX_ANCESTOR = 0,
Packit Service 20376f
	TREE_IDX_OURS = 1,
Packit Service 20376f
	TREE_IDX_THEIRS = 2
Packit Service 20376f
} merge_tree_index_t;
Packit Service 20376f
Packit Service 20376f
/* Tracks D/F conflicts */
Packit Service 20376f
struct merge_diff_df_data {
Packit Service 20376f
	const char *df_path;
Packit Service 20376f
	const char *prev_path;
Packit Service 20376f
	git_merge_diff *prev_conflict;
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
/* Merge base computation */
Packit Service 20376f
Packit Service 20376f
int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, size_t length, const git_oid input_array[])
Packit Service 20376f
{
Packit Service 20376f
	git_revwalk *walk = NULL;
Packit Service 20376f
	git_vector list;
Packit Service 20376f
	git_commit_list *result = NULL;
Packit Service 20376f
	git_commit_list_node *commit;
Packit Service 20376f
	int error = -1;
Packit Service 20376f
	unsigned int i;
Packit Service 20376f
Packit Service 20376f
	if (length < 2) {
Packit Service 20376f
		giterr_set(GITERR_INVALID, "at least two commits are required to find an ancestor");
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (git_vector_init(&list, length - 1, NULL) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	if (git_revwalk_new(&walk, repo) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	for (i = 1; i < length; i++) {
Packit Service 20376f
		commit = git_revwalk__commit_lookup(walk, &input_array[i]);
Packit Service 20376f
		if (commit == NULL)
Packit Service 20376f
			goto on_error;
Packit Service 20376f
Packit Service 20376f
		git_vector_insert(&list, commit);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	commit = git_revwalk__commit_lookup(walk, &input_array[0]);
Packit Service 20376f
	if (commit == NULL)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	if (git_merge__bases_many(&result, walk, commit, &list) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	if (!result) {
Packit Service 20376f
		giterr_set(GITERR_MERGE, "no merge base found");
Packit Service 20376f
		error = GIT_ENOTFOUND;
Packit Service 20376f
		goto on_error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*out = result;
Packit Service 20376f
	*walk_out = walk;
Packit Service 20376f
Packit Service 20376f
	git_vector_free(&list);
Packit Service 20376f
	return 0;
Packit Service 20376f
Packit Service 20376f
on_error:
Packit Service 20376f
	git_vector_free(&list);
Packit Service 20376f
	git_revwalk_free(walk);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[])
Packit Service 20376f
{
Packit Service 20376f
	git_revwalk *walk;
Packit Service 20376f
	git_commit_list *result = NULL;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	assert(out && repo && input_array);
Packit Service 20376f
Packit Service 20376f
	if ((error = merge_bases_many(&result, &walk, repo, length, input_array)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	git_oid_cpy(out, &result->item->oid);
Packit Service 20376f
Packit Service 20376f
	git_commit_list_free(&result);
Packit Service 20376f
	git_revwalk_free(walk);
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge_bases_many(git_oidarray *out, git_repository *repo, size_t length, const git_oid input_array[])
Packit Service 20376f
{
Packit Service 20376f
	git_revwalk *walk;
Packit Service 20376f
	git_commit_list *list, *result = NULL;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
	git_array_oid_t array;
Packit Service 20376f
Packit Service 20376f
	assert(out && repo && input_array);
Packit Service 20376f
Packit Service 20376f
	if ((error = merge_bases_many(&result, &walk, repo, length, input_array)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	git_array_init(array);
Packit Service 20376f
Packit Service 20376f
	list = result;
Packit Service 20376f
	while (list) {
Packit Service 20376f
		git_oid *id = git_array_alloc(array);
Packit Service 20376f
		if (id == NULL) {
Packit Service 20376f
			error = -1;
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		git_oid_cpy(id, &list->item->oid);
Packit Service 20376f
		list = list->next;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_oidarray__from_array(out, &array);
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	git_commit_list_free(&result);
Packit Service 20376f
	git_revwalk_free(walk);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge_base_octopus(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[])
Packit Service 20376f
{
Packit Service 20376f
	git_oid result;
Packit Service 20376f
	unsigned int i;
Packit Service 20376f
	int error = -1;
Packit Service 20376f
Packit Service 20376f
	assert(out && repo && input_array);
Packit Service 20376f
Packit Service 20376f
	if (length < 2) {
Packit Service 20376f
		giterr_set(GITERR_INVALID, "at least two commits are required to find an ancestor");
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	result = input_array[0];
Packit Service 20376f
	for (i = 1; i < length; i++) {
Packit Service 20376f
		error = git_merge_base(&result, repo, &result, &input_array[i]);
Packit Service 20376f
		if (error < 0)
Packit Service 20376f
			return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*out = result;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_bases(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, const git_oid *one, const git_oid *two)
Packit Service 20376f
{
Packit Service 20376f
	git_revwalk *walk;
Packit Service 20376f
	git_vector list;
Packit Service 20376f
	git_commit_list *result = NULL;
Packit Service 20376f
	git_commit_list_node *commit;
Packit Service 20376f
	void *contents[1];
Packit Service 20376f
Packit Service 20376f
	if (git_revwalk_new(&walk, repo) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	commit = git_revwalk__commit_lookup(walk, two);
Packit Service 20376f
	if (commit == NULL)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	/* This is just one value, so we can do it on the stack */
Packit Service 20376f
	memset(&list, 0x0, sizeof(git_vector));
Packit Service 20376f
	contents[0] = commit;
Packit Service 20376f
	list.length = 1;
Packit Service 20376f
	list.contents = contents;
Packit Service 20376f
Packit Service 20376f
	commit = git_revwalk__commit_lookup(walk, one);
Packit Service 20376f
	if (commit == NULL)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	if (git_merge__bases_many(&result, walk, commit, &list) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	if (!result) {
Packit Service 20376f
		git_revwalk_free(walk);
Packit Service 20376f
		giterr_set(GITERR_MERGE, "no merge base found");
Packit Service 20376f
		return GIT_ENOTFOUND;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*out = result;
Packit Service 20376f
	*walk_out = walk;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
Packit Service 20376f
on_error:
Packit Service 20376f
	git_revwalk_free(walk);
Packit Service 20376f
	return -1;
Packit Service 20376f
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const git_oid *two)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_revwalk *walk;
Packit Service 20376f
	git_commit_list *result;
Packit Service 20376f
Packit Service 20376f
	if ((error = merge_bases(&result, &walk, repo, one, two)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	git_oid_cpy(out, &result->item->oid);
Packit Service 20376f
	git_commit_list_free(&result);
Packit Service 20376f
	git_revwalk_free(walk);
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge_bases(git_oidarray *out, git_repository *repo, const git_oid *one, const git_oid *two)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_revwalk *walk;
Packit Service 20376f
	git_commit_list *result, *list;
Packit Service 20376f
	git_array_oid_t array;
Packit Service 20376f
Packit Service 20376f
	git_array_init(array);
Packit Service 20376f
Packit Service 20376f
	if ((error = merge_bases(&result, &walk, repo, one, two)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	list = result;
Packit Service 20376f
	while (list) {
Packit Service 20376f
		git_oid *id = git_array_alloc(array);
Packit Service 20376f
		if (id == NULL)
Packit Service 20376f
			goto on_error;
Packit Service 20376f
Packit Service 20376f
		git_oid_cpy(id, &list->item->oid);
Packit Service 20376f
		list = list->next;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_oidarray__from_array(out, &array);
Packit Service 20376f
	git_commit_list_free(&result);
Packit Service 20376f
	git_revwalk_free(walk);
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
Packit Service 20376f
on_error:
Packit Service 20376f
	git_commit_list_free(&result);
Packit Service 20376f
	git_revwalk_free(walk);
Packit Service 20376f
	return -1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int interesting(git_pqueue *list)
Packit Service 20376f
{
Packit Service 20376f
	size_t i;
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < git_pqueue_size(list); i++) {
Packit Service 20376f
		git_commit_list_node *commit = git_pqueue_get(list, i);
Packit Service 20376f
		if ((commit->flags & STALE) == 0)
Packit Service 20376f
			return 1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void clear_commit_marks_1(git_commit_list **plist,
Packit Service 20376f
		git_commit_list_node *commit, unsigned int mark)
Packit Service 20376f
{
Packit Service 20376f
	while (commit) {
Packit Service 20376f
		unsigned int i;
Packit Service 20376f
Packit Service 20376f
		if (!(mark & commit->flags))
Packit Service 20376f
			return;
Packit Service 20376f
Packit Service 20376f
		commit->flags &= ~mark;
Packit Service 20376f
Packit Service 20376f
		for (i = 1; i < commit->out_degree; i++) {
Packit Service 20376f
			git_commit_list_node *p = commit->parents[i];
Packit Service 20376f
			git_commit_list_insert(p, plist);
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		commit = commit->out_degree ? commit->parents[0] : NULL;
Packit Service 20376f
	}
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void clear_commit_marks_many(git_vector *commits, unsigned int mark)
Packit Service 20376f
{
Packit Service 20376f
	git_commit_list *list = NULL;
Packit Service 20376f
	git_commit_list_node *c;
Packit Service 20376f
	unsigned int i;
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(commits, i, c) {
Packit Service 20376f
		git_commit_list_insert(c, &list);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	while (list)
Packit Service 20376f
		clear_commit_marks_1(&list, git_commit_list_pop(&list), mark);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void clear_commit_marks(git_commit_list_node *commit, unsigned int mark)
Packit Service 20376f
{
Packit Service 20376f
	git_commit_list *list = NULL;
Packit Service 20376f
	git_commit_list_insert(commit, &list);
Packit Service 20376f
	while (list)
Packit Service 20376f
		clear_commit_marks_1(&list, git_commit_list_pop(&list), mark);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int paint_down_to_common(
Packit Service 20376f
	git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos)
Packit Service 20376f
{
Packit Service 20376f
	git_pqueue list;
Packit Service 20376f
	git_commit_list *result = NULL;
Packit Service 20376f
	git_commit_list_node *two;
Packit Service 20376f
Packit Service 20376f
	int error;
Packit Service 20376f
	unsigned int i;
Packit Service 20376f
Packit Service 20376f
	if (git_pqueue_init(&list, 0, twos->length * 2, git_commit_list_time_cmp) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	one->flags |= PARENT1;
Packit Service 20376f
	if (git_pqueue_insert(&list, one) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(twos, i, two) {
Packit Service 20376f
		if (git_commit_list_parse(walk, two) < 0)
Packit Service 20376f
			return -1;
Packit Service 20376f
Packit Service 20376f
		two->flags |= PARENT2;
Packit Service 20376f
Packit Service 20376f
		if (git_pqueue_insert(&list, two) < 0)
Packit Service 20376f
			return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* as long as there are non-STALE commits */
Packit Service 20376f
	while (interesting(&list)) {
Packit Service 20376f
		git_commit_list_node *commit = git_pqueue_pop(&list);
Packit Service 20376f
		int flags;
Packit Service 20376f
Packit Service 20376f
		if (commit == NULL)
Packit Service 20376f
			break;
Packit Service 20376f
Packit Service 20376f
		flags = commit->flags & (PARENT1 | PARENT2 | STALE);
Packit Service 20376f
		if (flags == (PARENT1 | PARENT2)) {
Packit Service 20376f
			if (!(commit->flags & RESULT)) {
Packit Service 20376f
				commit->flags |= RESULT;
Packit Service 20376f
				if (git_commit_list_insert(commit, &result) == NULL)
Packit Service 20376f
					return -1;
Packit Service 20376f
			}
Packit Service 20376f
			/* we mark the parents of a merge stale */
Packit Service 20376f
			flags |= STALE;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		for (i = 0; i < commit->out_degree; i++) {
Packit Service 20376f
			git_commit_list_node *p = commit->parents[i];
Packit Service 20376f
			if ((p->flags & flags) == flags)
Packit Service 20376f
				continue;
Packit Service 20376f
Packit Service 20376f
			if ((error = git_commit_list_parse(walk, p)) < 0)
Packit Service 20376f
				return error;
Packit Service 20376f
Packit Service 20376f
			p->flags |= flags;
Packit Service 20376f
			if (git_pqueue_insert(&list, p) < 0)
Packit Service 20376f
				return -1;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_pqueue_free(&list);
Packit Service 20376f
	*out = result;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int remove_redundant(git_revwalk *walk, git_vector *commits)
Packit Service 20376f
{
Packit Service 20376f
	git_vector work = GIT_VECTOR_INIT;
Packit Service 20376f
	unsigned char *redundant;
Packit Service 20376f
	unsigned int *filled_index;
Packit Service 20376f
	unsigned int i, j;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	redundant = git__calloc(commits->length, 1);
Packit Service 20376f
	GITERR_CHECK_ALLOC(redundant);
Packit Service 20376f
	filled_index = git__calloc((commits->length - 1), sizeof(unsigned int));
Packit Service 20376f
	GITERR_CHECK_ALLOC(filled_index);
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < commits->length; ++i) {
Packit Service 20376f
		if ((error = git_commit_list_parse(walk, commits->contents[i])) < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < commits->length; ++i) {
Packit Service 20376f
		git_commit_list *common = NULL;
Packit Service 20376f
		git_commit_list_node *commit = commits->contents[i];
Packit Service 20376f
Packit Service 20376f
		if (redundant[i])
Packit Service 20376f
			continue;
Packit Service 20376f
Packit Service 20376f
		git_vector_clear(&work);
Packit Service 20376f
Packit Service 20376f
		for (j = 0; j < commits->length; j++) {
Packit Service 20376f
			if (i == j || redundant[j])
Packit Service 20376f
				continue;
Packit Service 20376f
Packit Service 20376f
			filled_index[work.length] = j;
Packit Service 20376f
			if ((error = git_vector_insert(&work, commits->contents[j])) < 0)
Packit Service 20376f
				goto done;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		error = paint_down_to_common(&common, walk, commit, &work);
Packit Service 20376f
		if (error < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
Packit Service 20376f
		if (commit->flags & PARENT2)
Packit Service 20376f
			redundant[i] = 1;
Packit Service 20376f
Packit Service 20376f
		for (j = 0; j < work.length; j++) {
Packit Service 20376f
			git_commit_list_node *w = work.contents[j];
Packit Service 20376f
			if (w->flags & PARENT1)
Packit Service 20376f
				redundant[filled_index[j]] = 1;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		clear_commit_marks(commit, ALL_FLAGS);
Packit Service 20376f
		clear_commit_marks_many(&work, ALL_FLAGS);
Packit Service 20376f
Packit Service 20376f
		git_commit_list_free(&common);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < commits->length; ++i) {
Packit Service 20376f
		if (redundant[i])
Packit Service 20376f
			commits->contents[i] = NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git__free(redundant);
Packit Service 20376f
	git__free(filled_index);
Packit Service 20376f
	git_vector_free(&work);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	unsigned int i;
Packit Service 20376f
	git_commit_list_node *two;
Packit Service 20376f
	git_commit_list *result = NULL, *tmp = NULL;
Packit Service 20376f
Packit Service 20376f
	/* If there's only the one commit, there can be no merge bases */
Packit Service 20376f
	if (twos->length == 0) {
Packit Service 20376f
		*out = NULL;
Packit Service 20376f
		return 0;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* if the commit is repeated, we have a our merge base already */
Packit Service 20376f
	git_vector_foreach(twos, i, two) {
Packit Service 20376f
		if (one == two)
Packit Service 20376f
			return git_commit_list_insert(one, out) ? 0 : -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (git_commit_list_parse(walk, one) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	error = paint_down_to_common(&result, walk, one, twos);
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	/* filter out any stale commits in the results */
Packit Service 20376f
	tmp = result;
Packit Service 20376f
	result = NULL;
Packit Service 20376f
Packit Service 20376f
	while (tmp) {
Packit Service 20376f
		git_commit_list_node *c = git_commit_list_pop(&tmp);
Packit Service 20376f
		if (!(c->flags & STALE))
Packit Service 20376f
			if (git_commit_list_insert_by_date(c, &result) == NULL)
Packit Service 20376f
				return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * more than one merge base -- see if there are redundant merge
Packit Service 20376f
	 * bases and remove them
Packit Service 20376f
	 */
Packit Service 20376f
	if (result && result->next) {
Packit Service 20376f
		git_vector redundant = GIT_VECTOR_INIT;
Packit Service 20376f
Packit Service 20376f
		while (result)
Packit Service 20376f
			git_vector_insert(&redundant, git_commit_list_pop(&result));
Packit Service 20376f
Packit Service 20376f
		clear_commit_marks(one, ALL_FLAGS);
Packit Service 20376f
		clear_commit_marks_many(twos, ALL_FLAGS);
Packit Service 20376f
Packit Service 20376f
		if ((error = remove_redundant(walk, &redundant)) < 0) {
Packit Service 20376f
			git_vector_free(&redundant);
Packit Service 20376f
			return error;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		git_vector_foreach(&redundant, i, two) {
Packit Service 20376f
			if (two != NULL)
Packit Service 20376f
				git_commit_list_insert_by_date(two, &result);
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		git_vector_free(&redundant);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*out = result;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_repository_mergehead_foreach(
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	git_repository_mergehead_foreach_cb cb,
Packit Service 20376f
	void *payload)
Packit Service 20376f
{
Packit Service 20376f
	git_buf merge_head_path = GIT_BUF_INIT, merge_head_file = GIT_BUF_INIT;
Packit Service 20376f
	char *buffer, *line;
Packit Service 20376f
	size_t line_num = 1;
Packit Service 20376f
	git_oid oid;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	assert(repo && cb);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_buf_joinpath(&merge_head_path, repo->gitdir,
Packit Service 20376f
		GIT_MERGE_HEAD_FILE)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_futils_readbuffer(&merge_head_file,
Packit Service 20376f
		git_buf_cstr(&merge_head_path))) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	buffer = merge_head_file.ptr;
Packit Service 20376f
Packit Service 20376f
	while ((line = git__strsep(&buffer, "\n")) != NULL) {
Packit Service 20376f
		if (strlen(line) != GIT_OID_HEXSZ) {
Packit Service 20376f
			giterr_set(GITERR_INVALID, "unable to parse OID - invalid length");
Packit Service 20376f
			error = -1;
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if ((error = git_oid_fromstr(&oid, line)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
Packit Service 20376f
		if ((error = cb(&oid, payload)) != 0) {
Packit Service 20376f
			giterr_set_after_callback(error);
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		++line_num;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (*buffer) {
Packit Service 20376f
		giterr_set(GITERR_MERGE, "no EOL at line %"PRIuZ, line_num);
Packit Service 20376f
		error = -1;
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	git_buf_free(&merge_head_path);
Packit Service 20376f
	git_buf_free(&merge_head_file);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
GIT_INLINE(int) index_entry_cmp(const git_index_entry *a, const git_index_entry *b)
Packit Service 20376f
{
Packit Service 20376f
	int value = 0;
Packit Service 20376f
Packit Service 20376f
	if (a->path == NULL)
Packit Service 20376f
		return (b->path == NULL) ? 0 : 1;
Packit Service 20376f
Packit Service 20376f
	if ((value = a->mode - b->mode) == 0 &&
Packit Service 20376f
		(value = git_oid__cmp(&a->id, &b->id)) == 0)
Packit Service 20376f
		value = strcmp(a->path, b->path);
Packit Service 20376f
Packit Service 20376f
	return value;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/* Conflict resolution */
Packit Service 20376f
Packit Service 20376f
static int merge_conflict_resolve_trivial(
Packit Service 20376f
	int *resolved,
Packit Service 20376f
	git_merge_diff_list *diff_list,
Packit Service 20376f
	const git_merge_diff *conflict)
Packit Service 20376f
{
Packit Service 20376f
	int ours_empty, theirs_empty;
Packit Service 20376f
	int ours_changed, theirs_changed, ours_theirs_differ;
Packit Service 20376f
	git_index_entry const *result = NULL;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	assert(resolved && diff_list && conflict);
Packit Service 20376f
Packit Service 20376f
	*resolved = 0;
Packit Service 20376f
Packit Service 20376f
	if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE ||
Packit Service 20376f
		conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	if (conflict->our_status == GIT_DELTA_RENAMED ||
Packit Service 20376f
		conflict->their_status == GIT_DELTA_RENAMED)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	ours_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry);
Packit Service 20376f
	theirs_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry);
Packit Service 20376f
Packit Service 20376f
	ours_changed = (conflict->our_status != GIT_DELTA_UNMODIFIED);
Packit Service 20376f
	theirs_changed = (conflict->their_status != GIT_DELTA_UNMODIFIED);
Packit Service 20376f
	ours_theirs_differ = ours_changed && theirs_changed &&
Packit Service 20376f
		index_entry_cmp(&conflict->our_entry, &conflict->their_entry);
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * Note: with only one ancestor, some cases are not distinct:
Packit Service 20376f
	 *
Packit Service 20376f
	 * 16: ancest:anc1/anc2, head:anc1, remote:anc2 = result:no merge
Packit Service 20376f
	 * 3: ancest:(empty)^, head:head, remote:(empty) = result:no merge
Packit Service 20376f
	 * 2: ancest:(empty)^, head:(empty), remote:remote = result:no merge
Packit Service 20376f
	 *
Packit Service 20376f
	 * Note that the two cases that take D/F conflicts into account
Packit Service 20376f
	 * specifically do not need to be explicitly tested, as D/F conflicts
Packit Service 20376f
	 * would fail the *empty* test:
Packit Service 20376f
	 *
Packit Service 20376f
	 * 3ALT: ancest:(empty)+, head:head, remote:*empty* = result:head
Packit Service 20376f
	 * 2ALT: ancest:(empty)+, head:*empty*, remote:remote = result:remote
Packit Service 20376f
	 *
Packit Service 20376f
	 * Note that many of these cases need not be explicitly tested, as
Packit Service 20376f
	 * they simply degrade to "all different" cases (eg, 11):
Packit Service 20376f
	 *
Packit Service 20376f
	 * 4: ancest:(empty)^, head:head, remote:remote = result:no merge
Packit Service 20376f
	 * 7: ancest:ancest+, head:(empty), remote:remote = result:no merge
Packit Service 20376f
	 * 9: ancest:ancest+, head:head, remote:(empty) = result:no merge
Packit Service 20376f
	 * 11: ancest:ancest+, head:head, remote:remote = result:no merge
Packit Service 20376f
	 */
Packit Service 20376f
Packit Service 20376f
	/* 5ALT: ancest:*, head:head, remote:head = result:head */
Packit Service 20376f
	if (ours_changed && !ours_empty && !ours_theirs_differ)
Packit Service 20376f
		result = &conflict->our_entry;
Packit Service 20376f
	/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
Packit Service 20376f
	else if (ours_changed && ours_empty && theirs_empty)
Packit Service 20376f
		*resolved = 0;
Packit Service 20376f
	/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
Packit Service 20376f
	else if (ours_empty && !theirs_changed)
Packit Service 20376f
		*resolved = 0;
Packit Service 20376f
	/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
Packit Service 20376f
	else if (!ours_changed && theirs_empty)
Packit Service 20376f
		*resolved = 0;
Packit Service 20376f
	/* 13: ancest:ancest+, head:head, remote:ancest = result:head */
Packit Service 20376f
	else if (ours_changed && !theirs_changed)
Packit Service 20376f
		result = &conflict->our_entry;
Packit Service 20376f
	/* 14: ancest:ancest+, head:ancest, remote:remote = result:remote */
Packit Service 20376f
	else if (!ours_changed && theirs_changed)
Packit Service 20376f
		result = &conflict->their_entry;
Packit Service 20376f
	else
Packit Service 20376f
		*resolved = 0;
Packit Service 20376f
Packit Service 20376f
	if (result != NULL &&
Packit Service 20376f
		GIT_MERGE_INDEX_ENTRY_EXISTS(*result) &&
Packit Service 20376f
		(error = git_vector_insert(&diff_list->staged, (void *)result)) >= 0)
Packit Service 20376f
		*resolved = 1;
Packit Service 20376f
Packit Service 20376f
	/* Note: trivial resolution does not update the REUC. */
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_conflict_resolve_one_removed(
Packit Service 20376f
	int *resolved,
Packit Service 20376f
	git_merge_diff_list *diff_list,
Packit Service 20376f
	const git_merge_diff *conflict)
Packit Service 20376f
{
Packit Service 20376f
	int ours_empty, theirs_empty;
Packit Service 20376f
	int ours_changed, theirs_changed;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	assert(resolved && diff_list && conflict);
Packit Service 20376f
Packit Service 20376f
	*resolved = 0;
Packit Service 20376f
Packit Service 20376f
	if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE ||
Packit Service 20376f
		conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	ours_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry);
Packit Service 20376f
	theirs_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry);
Packit Service 20376f
Packit Service 20376f
	ours_changed = (conflict->our_status != GIT_DELTA_UNMODIFIED);
Packit Service 20376f
	theirs_changed = (conflict->their_status != GIT_DELTA_UNMODIFIED);
Packit Service 20376f
Packit Service 20376f
	/* Removed in both */
Packit Service 20376f
	if (ours_changed && ours_empty && theirs_empty)
Packit Service 20376f
		*resolved = 1;
Packit Service 20376f
	/* Removed in ours */
Packit Service 20376f
	else if (ours_empty && !theirs_changed)
Packit Service 20376f
		*resolved = 1;
Packit Service 20376f
	/* Removed in theirs */
Packit Service 20376f
	else if (!ours_changed && theirs_empty)
Packit Service 20376f
		*resolved = 1;
Packit Service 20376f
Packit Service 20376f
	if (*resolved)
Packit Service 20376f
		git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_conflict_resolve_one_renamed(
Packit Service 20376f
	int *resolved,
Packit Service 20376f
	git_merge_diff_list *diff_list,
Packit Service 20376f
	const git_merge_diff *conflict)
Packit Service 20376f
{
Packit Service 20376f
	int ours_renamed, theirs_renamed;
Packit Service 20376f
	int ours_changed, theirs_changed;
Packit Service 20376f
	git_index_entry *merged;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	assert(resolved && diff_list && conflict);
Packit Service 20376f
Packit Service 20376f
	*resolved = 0;
Packit Service 20376f
Packit Service 20376f
	if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ||
Packit Service 20376f
		!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry))
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	ours_renamed = (conflict->our_status == GIT_DELTA_RENAMED);
Packit Service 20376f
	theirs_renamed = (conflict->their_status == GIT_DELTA_RENAMED);
Packit Service 20376f
Packit Service 20376f
	if (!ours_renamed && !theirs_renamed)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	/* Reject one file in a 2->1 conflict */
Packit Service 20376f
	if (conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 ||
Packit Service 20376f
		conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2 ||
Packit Service 20376f
		conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	ours_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->our_entry.id) != 0);
Packit Service 20376f
	theirs_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->their_entry.id) != 0);
Packit Service 20376f
Packit Service 20376f
	/* if both are modified (and not to a common target) require a merge */
Packit Service 20376f
	if (ours_changed && theirs_changed &&
Packit Service 20376f
		git_oid__cmp(&conflict->our_entry.id, &conflict->their_entry.id) != 0)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	if ((merged = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	if (ours_changed)
Packit Service 20376f
		memcpy(merged, &conflict->our_entry, sizeof(git_index_entry));
Packit Service 20376f
	else
Packit Service 20376f
		memcpy(merged, &conflict->their_entry, sizeof(git_index_entry));
Packit Service 20376f
Packit Service 20376f
	if (ours_renamed)
Packit Service 20376f
		merged->path = conflict->our_entry.path;
Packit Service 20376f
	else
Packit Service 20376f
		merged->path = conflict->their_entry.path;
Packit Service 20376f
Packit Service 20376f
	*resolved = 1;
Packit Service 20376f
Packit Service 20376f
	git_vector_insert(&diff_list->staged, merged);
Packit Service 20376f
	git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static bool merge_conflict_can_resolve_contents(
Packit Service 20376f
	const git_merge_diff *conflict)
Packit Service 20376f
{
Packit Service 20376f
	if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ||
Packit Service 20376f
		!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry))
Packit Service 20376f
		return false;
Packit Service 20376f
Packit Service 20376f
	/* Reject D/F conflicts */
Packit Service 20376f
	if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE)
Packit Service 20376f
		return false;
Packit Service 20376f
Packit Service 20376f
	/* Reject submodules. */
Packit Service 20376f
	if (S_ISGITLINK(conflict->ancestor_entry.mode) ||
Packit Service 20376f
		S_ISGITLINK(conflict->our_entry.mode) ||
Packit Service 20376f
		S_ISGITLINK(conflict->their_entry.mode))
Packit Service 20376f
		return false;
Packit Service 20376f
Packit Service 20376f
	/* Reject link/file conflicts. */
Packit Service 20376f
	if ((S_ISLNK(conflict->ancestor_entry.mode) ^
Packit Service 20376f
			S_ISLNK(conflict->our_entry.mode)) ||
Packit Service 20376f
		(S_ISLNK(conflict->ancestor_entry.mode) ^
Packit Service 20376f
			S_ISLNK(conflict->their_entry.mode)))
Packit Service 20376f
		return false;
Packit Service 20376f
Packit Service 20376f
	/* Reject name conflicts */
Packit Service 20376f
	if (conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 ||
Packit Service 20376f
		conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
Packit Service 20376f
		return false;
Packit Service 20376f
Packit Service 20376f
	if ((conflict->our_status & GIT_DELTA_RENAMED) == GIT_DELTA_RENAMED &&
Packit Service 20376f
		(conflict->their_status & GIT_DELTA_RENAMED) == GIT_DELTA_RENAMED &&
Packit Service 20376f
		strcmp(conflict->ancestor_entry.path, conflict->their_entry.path) != 0)
Packit Service 20376f
		return false;
Packit Service 20376f
Packit Service 20376f
	return true;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_conflict_invoke_driver(
Packit Service 20376f
	git_index_entry **out,
Packit Service 20376f
	const char *name,
Packit Service 20376f
	git_merge_driver *driver,
Packit Service 20376f
	git_merge_diff_list *diff_list,
Packit Service 20376f
	git_merge_driver_source *src)
Packit Service 20376f
{
Packit Service 20376f
	git_index_entry *result;
Packit Service 20376f
	git_buf buf = GIT_BUF_INIT;
Packit Service 20376f
	const char *path;
Packit Service 20376f
	uint32_t mode;
Packit Service 20376f
	git_odb *odb = NULL;
Packit Service 20376f
	git_oid oid;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	*out = NULL;
Packit Service 20376f
Packit Service 20376f
	if ((error = driver->apply(driver, &path, &mode, &buf, name, src)) < 0 ||
Packit Service 20376f
		(error = git_repository_odb(&odb, src->repo)) < 0 ||
Packit Service 20376f
		(error = git_odb_write(&oid, odb, buf.ptr, buf.size, GIT_OBJ_BLOB)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	result = git_pool_mallocz(&diff_list->pool, sizeof(git_index_entry));
Packit Service 20376f
	GITERR_CHECK_ALLOC(result);
Packit Service 20376f
Packit Service 20376f
	git_oid_cpy(&result->id, &oid;;
Packit Service 20376f
	result->mode = mode;
Packit Service 20376f
	result->file_size = buf.size;
Packit Service 20376f
Packit Service 20376f
	result->path = git_pool_strdup(&diff_list->pool, path);
Packit Service 20376f
	GITERR_CHECK_ALLOC(result->path);
Packit Service 20376f
Packit Service 20376f
	*out = result;
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_buf_free(&buf;;
Packit Service 20376f
	git_odb_free(odb);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_conflict_resolve_contents(
Packit Service 20376f
	int *resolved,
Packit Service 20376f
	git_merge_diff_list *diff_list,
Packit Service 20376f
	const git_merge_diff *conflict,
Packit Service 20376f
	const git_merge_options *merge_opts,
Packit Service 20376f
	const git_merge_file_options *file_opts)
Packit Service 20376f
{
Packit Service 20376f
	git_merge_driver_source source = {0};
Packit Service 20376f
	git_merge_file_result result = {0};
Packit Service 20376f
	git_merge_driver *driver;
Packit Service 20376f
	git_merge_driver__builtin builtin = {{0}};
Packit Service 20376f
	git_index_entry *merge_result;
Packit Service 20376f
	git_odb *odb = NULL;
Packit Service 20376f
	const char *name;
Packit Service 20376f
	bool fallback = false;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	assert(resolved && diff_list && conflict);
Packit Service 20376f
Packit Service 20376f
	*resolved = 0;
Packit Service 20376f
Packit Service 20376f
	if (!merge_conflict_can_resolve_contents(conflict))
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	source.repo = diff_list->repo;
Packit Service 20376f
	source.default_driver = merge_opts->default_driver;
Packit Service 20376f
	source.file_opts = file_opts;
Packit Service 20376f
	source.ancestor = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
Packit Service 20376f
		&conflict->ancestor_entry : NULL;
Packit Service 20376f
	source.ours = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
Packit Service 20376f
		&conflict->our_entry : NULL;
Packit Service 20376f
	source.theirs = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
Packit Service 20376f
		&conflict->their_entry : NULL;
Packit Service 20376f
Packit Service 20376f
	if (file_opts->favor != GIT_MERGE_FILE_FAVOR_NORMAL) {
Packit Service 20376f
		/* if the user requested a particular type of resolution (via the
Packit Service 20376f
		 * favor flag) then let that override the gitattributes and use
Packit Service 20376f
		 * the builtin driver.
Packit Service 20376f
		 */
Packit Service 20376f
		name = "text";
Packit Service 20376f
		builtin.base.apply = git_merge_driver__builtin_apply;
Packit Service 20376f
		builtin.favor = file_opts->favor;
Packit Service 20376f
Packit Service 20376f
		driver = &builtin.base;
Packit Service 20376f
	} else {
Packit Service 20376f
		/* find the merge driver for this file */
Packit Service 20376f
		if ((error = git_merge_driver_for_source(&name, &driver, &source)) < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
Packit Service 20376f
		if (driver == NULL)
Packit Service 20376f
			fallback = true;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (driver) {
Packit Service 20376f
		error = merge_conflict_invoke_driver(&merge_result, name, driver,
Packit Service 20376f
			diff_list, &source);
Packit Service 20376f
Packit Service 20376f
		if (error == GIT_PASSTHROUGH)
Packit Service 20376f
			fallback = true;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (fallback) {
Packit Service 20376f
		error = merge_conflict_invoke_driver(&merge_result, "text",
Packit Service 20376f
			&git_merge_driver__text.base, diff_list, &source);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (error < 0) {
Packit Service 20376f
		if (error == GIT_EMERGECONFLICT)
Packit Service 20376f
			error = 0;
Packit Service 20376f
Packit Service 20376f
		goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_vector_insert(&diff_list->staged, merge_result);
Packit Service 20376f
	git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict);
Packit Service 20376f
Packit Service 20376f
	*resolved = 1;
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_merge_file_result_free(&result);
Packit Service 20376f
	git_odb_free(odb);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_conflict_resolve(
Packit Service 20376f
	int *out,
Packit Service 20376f
	git_merge_diff_list *diff_list,
Packit Service 20376f
	const git_merge_diff *conflict,
Packit Service 20376f
	const git_merge_options *merge_opts,
Packit Service 20376f
	const git_merge_file_options *file_opts)
Packit Service 20376f
{
Packit Service 20376f
	int resolved = 0;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	*out = 0;
Packit Service 20376f
Packit Service 20376f
	if ((error = merge_conflict_resolve_trivial(
Packit Service 20376f
			&resolved, diff_list, conflict)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if (!resolved && (error = merge_conflict_resolve_one_removed(
Packit Service 20376f
			&resolved, diff_list, conflict)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if (!resolved && (error = merge_conflict_resolve_one_renamed(
Packit Service 20376f
			&resolved, diff_list, conflict)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if (!resolved && (error = merge_conflict_resolve_contents(
Packit Service 20376f
			&resolved, diff_list, conflict, merge_opts, file_opts)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	*out = resolved;
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/* Rename detection and coalescing */
Packit Service 20376f
Packit Service 20376f
struct merge_diff_similarity {
Packit Service 20376f
	unsigned char similarity;
Packit Service 20376f
	size_t other_idx;
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
static int index_entry_similarity_exact(
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	git_index_entry *a,
Packit Service 20376f
	size_t a_idx,
Packit Service 20376f
	git_index_entry *b,
Packit Service 20376f
	size_t b_idx,
Packit Service 20376f
	void **cache,
Packit Service 20376f
	const git_merge_options *opts)
Packit Service 20376f
{
Packit Service 20376f
	GIT_UNUSED(repo);
Packit Service 20376f
	GIT_UNUSED(a_idx);
Packit Service 20376f
	GIT_UNUSED(b_idx);
Packit Service 20376f
	GIT_UNUSED(cache);
Packit Service 20376f
	GIT_UNUSED(opts);
Packit Service 20376f
Packit Service 20376f
	if (git_oid__cmp(&a->id, &b->id) == 0)
Packit Service 20376f
		return 100;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int index_entry_similarity_calc(
Packit Service 20376f
	void **out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	git_index_entry *entry,
Packit Service 20376f
	const git_merge_options *opts)
Packit Service 20376f
{
Packit Service 20376f
	git_blob *blob;
Packit Service 20376f
	git_diff_file diff_file = {{{0}}};
Packit Service 20376f
	git_off_t blobsize;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	*out = NULL;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_blob_lookup(&blob, repo, &entry->id)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	git_oid_cpy(&diff_file.id, &entry->id);
Packit Service 20376f
	diff_file.path = entry->path;
Packit Service 20376f
	diff_file.size = entry->file_size;
Packit Service 20376f
	diff_file.mode = entry->mode;
Packit Service 20376f
	diff_file.flags = 0;
Packit Service 20376f
Packit Service 20376f
	blobsize = git_blob_rawsize(blob);
Packit Service 20376f
Packit Service 20376f
	/* file too big for rename processing */
Packit Service 20376f
	if (!git__is_sizet(blobsize))
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	error = opts->metric->buffer_signature(out, &diff_file,
Packit Service 20376f
		git_blob_rawcontent(blob), (size_t)blobsize,
Packit Service 20376f
		opts->metric->payload);
Packit Service 20376f
Packit Service 20376f
	git_blob_free(blob);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int index_entry_similarity_inexact(
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	git_index_entry *a,
Packit Service 20376f
	size_t a_idx,
Packit Service 20376f
	git_index_entry *b,
Packit Service 20376f
	size_t b_idx,
Packit Service 20376f
	void **cache,
Packit Service 20376f
	const git_merge_options *opts)
Packit Service 20376f
{
Packit Service 20376f
	int score = 0;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	if (!GIT_MODE_ISBLOB(a->mode) || !GIT_MODE_ISBLOB(b->mode))
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	/* update signature cache if needed */
Packit Service 20376f
	if (!cache[a_idx] && (error = index_entry_similarity_calc(&cache[a_idx], repo, a, opts)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
	if (!cache[b_idx] && (error = index_entry_similarity_calc(&cache[b_idx], repo, b, opts)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	/* some metrics may not wish to process this file (too big / too small) */
Packit Service 20376f
	if (!cache[a_idx] || !cache[b_idx])
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	/* compare signatures */
Packit Service 20376f
	if (opts->metric->similarity(
Packit Service 20376f
		&score, cache[a_idx], cache[b_idx], opts->metric->payload) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	/* clip score */
Packit Service 20376f
	if (score < 0)
Packit Service 20376f
		score = 0;
Packit Service 20376f
	else if (score > 100)
Packit Service 20376f
		score = 100;
Packit Service 20376f
Packit Service 20376f
	return score;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_diff_mark_similarity(
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	git_merge_diff_list *diff_list,
Packit Service 20376f
	struct merge_diff_similarity *similarity_ours,
Packit Service 20376f
	struct merge_diff_similarity *similarity_theirs,
Packit Service 20376f
	int (*similarity_fn)(git_repository *, git_index_entry *, size_t, git_index_entry *, size_t, void **, const git_merge_options *),
Packit Service 20376f
	void **cache,
Packit Service 20376f
	const git_merge_options *opts)
Packit Service 20376f
{
Packit Service 20376f
	size_t i, j;
Packit Service 20376f
	git_merge_diff *conflict_src, *conflict_tgt;
Packit Service 20376f
	int similarity;
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(&diff_list->conflicts, i, conflict_src) {
Packit Service 20376f
		/* Items can be the source of a rename iff they have an item in the
Packit Service 20376f
		 * ancestor slot and lack an item in the ours or theirs slot. */
Packit Service 20376f
		if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->ancestor_entry) ||
Packit Service 20376f
			(GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry) &&
Packit Service 20376f
			 GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)))
Packit Service 20376f
			continue;
Packit Service 20376f
Packit Service 20376f
		git_vector_foreach(&diff_list->conflicts, j, conflict_tgt) {
Packit Service 20376f
			size_t our_idx = diff_list->conflicts.length + j;
Packit Service 20376f
			size_t their_idx = (diff_list->conflicts.length * 2) + j;
Packit Service 20376f
Packit Service 20376f
			if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->ancestor_entry))
Packit Service 20376f
				continue;
Packit Service 20376f
Packit Service 20376f
			if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->our_entry) &&
Packit Service 20376f
				!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry)) {
Packit Service 20376f
				similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->our_entry, our_idx, cache, opts);
Packit Service 20376f
Packit Service 20376f
				if (similarity == GIT_EBUFS)
Packit Service 20376f
					continue;
Packit Service 20376f
				else if (similarity < 0)
Packit Service 20376f
					return similarity;
Packit Service 20376f
Packit Service 20376f
				if (similarity > similarity_ours[i].similarity &&
Packit Service 20376f
					similarity > similarity_ours[j].similarity) {
Packit Service 20376f
					/* Clear previous best similarity */
Packit Service 20376f
					if (similarity_ours[i].similarity > 0)
Packit Service 20376f
						similarity_ours[similarity_ours[i].other_idx].similarity = 0;
Packit Service 20376f
Packit Service 20376f
					if (similarity_ours[j].similarity > 0)
Packit Service 20376f
						similarity_ours[similarity_ours[j].other_idx].similarity = 0;
Packit Service 20376f
Packit Service 20376f
					similarity_ours[i].similarity = similarity;
Packit Service 20376f
					similarity_ours[i].other_idx = j;
Packit Service 20376f
Packit Service 20376f
					similarity_ours[j].similarity = similarity;
Packit Service 20376f
					similarity_ours[j].other_idx = i;
Packit Service 20376f
				}
Packit Service 20376f
			}
Packit Service 20376f
Packit Service 20376f
			if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->their_entry) &&
Packit Service 20376f
				!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)) {
Packit Service 20376f
				similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->their_entry, their_idx, cache, opts);
Packit Service 20376f
Packit Service 20376f
				if (similarity > similarity_theirs[i].similarity &&
Packit Service 20376f
					similarity > similarity_theirs[j].similarity) {
Packit Service 20376f
					/* Clear previous best similarity */
Packit Service 20376f
					if (similarity_theirs[i].similarity > 0)
Packit Service 20376f
						similarity_theirs[similarity_theirs[i].other_idx].similarity = 0;
Packit Service 20376f
Packit Service 20376f
					if (similarity_theirs[j].similarity > 0)
Packit Service 20376f
						similarity_theirs[similarity_theirs[j].other_idx].similarity = 0;
Packit Service 20376f
Packit Service 20376f
					similarity_theirs[i].similarity = similarity;
Packit Service 20376f
					similarity_theirs[i].other_idx = j;
Packit Service 20376f
Packit Service 20376f
					similarity_theirs[j].similarity = similarity;
Packit Service 20376f
					similarity_theirs[j].other_idx = i;
Packit Service 20376f
				}
Packit Service 20376f
			}
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/*
Packit Service 20376f
 * Rename conflicts:
Packit Service 20376f
 *
Packit Service 20376f
 *      Ancestor   Ours   Theirs
Packit Service 20376f
 *
Packit Service 20376f
 * 0a   A          A      A        No rename
Packit Service 20376f
 *  b   A          A*     A        No rename (ours was rewritten)
Packit Service 20376f
 *  c   A          A      A*       No rename (theirs rewritten)
Packit Service 20376f
 * 1a   A          A      B[A]     Rename or rename/edit
Packit Service 20376f
 *  b   A          B[A]   A        (automergeable)
Packit Service 20376f
 * 2    A          B[A]   B[A]     Both renamed (automergeable)
Packit Service 20376f
 * 3a   A          B[A]            Rename/delete
Packit Service 20376f
 *  b   A                 B[A]      (same)
Packit Service 20376f
 * 4a   A          B[A]   B        Rename/add [B~ours B~theirs]
Packit Service 20376f
 *  b   A          B      B[A]      (same)
Packit Service 20376f
 * 5    A          B[A]   C[A]     Both renamed ("1 -> 2")
Packit Service 20376f
 * 6    A          C[A]            Both renamed ("2 -> 1")
Packit Service 20376f
 *      B                 C[B]     [C~ours C~theirs]    (automergeable)
Packit Service 20376f
 */
Packit Service 20376f
static void merge_diff_mark_rename_conflict(
Packit Service 20376f
	git_merge_diff_list *diff_list,
Packit Service 20376f
	struct merge_diff_similarity *similarity_ours,
Packit Service 20376f
	bool ours_renamed,
Packit Service 20376f
	size_t ours_source_idx,
Packit Service 20376f
	struct merge_diff_similarity *similarity_theirs,
Packit Service 20376f
	bool theirs_renamed,
Packit Service 20376f
	size_t theirs_source_idx,
Packit Service 20376f
	git_merge_diff *target,
Packit Service 20376f
	const git_merge_options *opts)
Packit Service 20376f
{
Packit Service 20376f
	git_merge_diff *ours_source = NULL, *theirs_source = NULL;
Packit Service 20376f
Packit Service 20376f
	if (ours_renamed)
Packit Service 20376f
		ours_source = diff_list->conflicts.contents[ours_source_idx];
Packit Service 20376f
Packit Service 20376f
	if (theirs_renamed)
Packit Service 20376f
		theirs_source = diff_list->conflicts.contents[theirs_source_idx];
Packit Service 20376f
Packit Service 20376f
	/* Detect 2->1 conflicts */
Packit Service 20376f
	if (ours_renamed && theirs_renamed) {
Packit Service 20376f
		/* Both renamed to the same target name. */
Packit Service 20376f
		if (ours_source_idx == theirs_source_idx)
Packit Service 20376f
			ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED;
Packit Service 20376f
		else {
Packit Service 20376f
			ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1;
Packit Service 20376f
			theirs_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1;
Packit Service 20376f
		}
Packit Service 20376f
	} else if (ours_renamed) {
Packit Service 20376f
		/* If our source was also renamed in theirs, this is a 1->2 */
Packit Service 20376f
		if (similarity_theirs[ours_source_idx].similarity >= opts->rename_threshold)
Packit Service 20376f
			ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2;
Packit Service 20376f
Packit Service 20376f
		else if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->their_entry)) {
Packit Service 20376f
			ours_source->type = GIT_MERGE_DIFF_RENAMED_ADDED;
Packit Service 20376f
			target->type = GIT_MERGE_DIFF_RENAMED_ADDED;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(ours_source->their_entry))
Packit Service 20376f
			ours_source->type = GIT_MERGE_DIFF_RENAMED_DELETED;
Packit Service 20376f
Packit Service 20376f
		else if (ours_source->type == GIT_MERGE_DIFF_MODIFIED_DELETED)
Packit Service 20376f
			ours_source->type = GIT_MERGE_DIFF_RENAMED_MODIFIED;
Packit Service 20376f
	} else if (theirs_renamed) {
Packit Service 20376f
		/* If their source was also renamed in ours, this is a 1->2 */
Packit Service 20376f
		if (similarity_ours[theirs_source_idx].similarity >= opts->rename_threshold)
Packit Service 20376f
			theirs_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2;
Packit Service 20376f
Packit Service 20376f
		else if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->our_entry)) {
Packit Service 20376f
			theirs_source->type = GIT_MERGE_DIFF_RENAMED_ADDED;
Packit Service 20376f
			target->type = GIT_MERGE_DIFF_RENAMED_ADDED;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(theirs_source->our_entry))
Packit Service 20376f
			theirs_source->type = GIT_MERGE_DIFF_RENAMED_DELETED;
Packit Service 20376f
Packit Service 20376f
		else if (theirs_source->type == GIT_MERGE_DIFF_MODIFIED_DELETED)
Packit Service 20376f
			theirs_source->type = GIT_MERGE_DIFF_RENAMED_MODIFIED;
Packit Service 20376f
	}
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
GIT_INLINE(void) merge_diff_coalesce_rename(
Packit Service 20376f
	git_index_entry *source_entry,
Packit Service 20376f
	git_delta_t *source_status,
Packit Service 20376f
	git_index_entry *target_entry,
Packit Service 20376f
	git_delta_t *target_status)
Packit Service 20376f
{
Packit Service 20376f
	/* Coalesce the rename target into the rename source. */
Packit Service 20376f
	memcpy(source_entry, target_entry, sizeof(git_index_entry));
Packit Service 20376f
	*source_status = GIT_DELTA_RENAMED;
Packit Service 20376f
Packit Service 20376f
	memset(target_entry, 0x0, sizeof(git_index_entry));
Packit Service 20376f
	*target_status = GIT_DELTA_UNMODIFIED;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void merge_diff_list_coalesce_renames(
Packit Service 20376f
	git_merge_diff_list *diff_list,
Packit Service 20376f
	struct merge_diff_similarity *similarity_ours,
Packit Service 20376f
	struct merge_diff_similarity *similarity_theirs,
Packit Service 20376f
	const git_merge_options *opts)
Packit Service 20376f
{
Packit Service 20376f
	size_t i;
Packit Service 20376f
	bool ours_renamed = 0, theirs_renamed = 0;
Packit Service 20376f
	size_t ours_source_idx = 0, theirs_source_idx = 0;
Packit Service 20376f
	git_merge_diff *ours_source, *theirs_source, *target;
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < diff_list->conflicts.length; i++) {
Packit Service 20376f
		target = diff_list->conflicts.contents[i];
Packit Service 20376f
Packit Service 20376f
		ours_renamed = 0;
Packit Service 20376f
		theirs_renamed = 0;
Packit Service 20376f
Packit Service 20376f
		if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->our_entry) &&
Packit Service 20376f
			similarity_ours[i].similarity >= opts->rename_threshold) {
Packit Service 20376f
			ours_source_idx = similarity_ours[i].other_idx;
Packit Service 20376f
Packit Service 20376f
			ours_source = diff_list->conflicts.contents[ours_source_idx];
Packit Service 20376f
Packit Service 20376f
			merge_diff_coalesce_rename(
Packit Service 20376f
				&ours_source->our_entry,
Packit Service 20376f
				&ours_source->our_status,
Packit Service 20376f
				&target->our_entry,
Packit Service 20376f
				&target->our_status);
Packit Service 20376f
Packit Service 20376f
			similarity_ours[ours_source_idx].similarity = 0;
Packit Service 20376f
			similarity_ours[i].similarity = 0;
Packit Service 20376f
Packit Service 20376f
			ours_renamed = 1;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		/* insufficient to determine direction */
Packit Service 20376f
		if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->their_entry) &&
Packit Service 20376f
			similarity_theirs[i].similarity >= opts->rename_threshold) {
Packit Service 20376f
			theirs_source_idx = similarity_theirs[i].other_idx;
Packit Service 20376f
Packit Service 20376f
			theirs_source = diff_list->conflicts.contents[theirs_source_idx];
Packit Service 20376f
Packit Service 20376f
			merge_diff_coalesce_rename(
Packit Service 20376f
				&theirs_source->their_entry,
Packit Service 20376f
				&theirs_source->their_status,
Packit Service 20376f
				&target->their_entry,
Packit Service 20376f
				&target->their_status);
Packit Service 20376f
Packit Service 20376f
			similarity_theirs[theirs_source_idx].similarity = 0;
Packit Service 20376f
			similarity_theirs[i].similarity = 0;
Packit Service 20376f
Packit Service 20376f
			theirs_renamed = 1;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		merge_diff_mark_rename_conflict(diff_list,
Packit Service 20376f
			similarity_ours, ours_renamed, ours_source_idx,
Packit Service 20376f
			similarity_theirs, theirs_renamed, theirs_source_idx,
Packit Service 20376f
			target, opts);
Packit Service 20376f
	}
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_diff_empty(const git_vector *conflicts, size_t idx, void *p)
Packit Service 20376f
{
Packit Service 20376f
	git_merge_diff *conflict = conflicts->contents[idx];
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(p);
Packit Service 20376f
Packit Service 20376f
	return (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) &&
Packit Service 20376f
		!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) &&
Packit Service 20376f
		!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry));
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void merge_diff_list_count_candidates(
Packit Service 20376f
	git_merge_diff_list *diff_list,
Packit Service 20376f
	size_t *src_count,
Packit Service 20376f
	size_t *tgt_count)
Packit Service 20376f
{
Packit Service 20376f
	git_merge_diff *entry;
Packit Service 20376f
	size_t i;
Packit Service 20376f
Packit Service 20376f
	*src_count = 0;
Packit Service 20376f
	*tgt_count = 0;
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(&diff_list->conflicts, i, entry) {
Packit Service 20376f
		if (GIT_MERGE_INDEX_ENTRY_EXISTS(entry->ancestor_entry) &&
Packit Service 20376f
			(!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->our_entry) ||
Packit Service 20376f
			!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->their_entry)))
Packit Service 20376f
			(*src_count)++;
Packit Service 20376f
		else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->ancestor_entry))
Packit Service 20376f
			(*tgt_count)++;
Packit Service 20376f
	}
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge_diff_list__find_renames(
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	git_merge_diff_list *diff_list,
Packit Service 20376f
	const git_merge_options *opts)
Packit Service 20376f
{
Packit Service 20376f
	struct merge_diff_similarity *similarity_ours, *similarity_theirs;
Packit Service 20376f
	void **cache = NULL;
Packit Service 20376f
	size_t cache_size = 0;
Packit Service 20376f
	size_t src_count, tgt_count, i;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	assert(diff_list && opts);
Packit Service 20376f
Packit Service 20376f
	if ((opts->flags & GIT_MERGE_FIND_RENAMES) == 0)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	similarity_ours = git__calloc(diff_list->conflicts.length,
Packit Service 20376f
		sizeof(struct merge_diff_similarity));
Packit Service 20376f
	GITERR_CHECK_ALLOC(similarity_ours);
Packit Service 20376f
Packit Service 20376f
	similarity_theirs = git__calloc(diff_list->conflicts.length,
Packit Service 20376f
		sizeof(struct merge_diff_similarity));
Packit Service 20376f
	GITERR_CHECK_ALLOC(similarity_theirs);
Packit Service 20376f
Packit Service 20376f
	/* Calculate similarity between items that were deleted from the ancestor
Packit Service 20376f
	 * and added in the other branch.
Packit Service 20376f
	 */
Packit Service 20376f
	if ((error = merge_diff_mark_similarity(repo, diff_list, similarity_ours,
Packit Service 20376f
		similarity_theirs, index_entry_similarity_exact, NULL, opts)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if (diff_list->conflicts.length <= opts->target_limit) {
Packit Service 20376f
		GITERR_CHECK_ALLOC_MULTIPLY(&cache_size, diff_list->conflicts.length, 3);
Packit Service 20376f
		cache = git__calloc(cache_size, sizeof(void *));
Packit Service 20376f
		GITERR_CHECK_ALLOC(cache);
Packit Service 20376f
Packit Service 20376f
		merge_diff_list_count_candidates(diff_list, &src_count, &tgt_count);
Packit Service 20376f
Packit Service 20376f
		if (src_count > opts->target_limit || tgt_count > opts->target_limit) {
Packit Service 20376f
			/* TODO: report! */
Packit Service 20376f
		} else {
Packit Service 20376f
			if ((error = merge_diff_mark_similarity(
Packit Service 20376f
				repo, diff_list, similarity_ours, similarity_theirs,
Packit Service 20376f
				index_entry_similarity_inexact, cache, opts)) < 0)
Packit Service 20376f
				goto done;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* For entries that are appropriately similar, merge the new name's entry
Packit Service 20376f
	 * into the old name.
Packit Service 20376f
	 */
Packit Service 20376f
	merge_diff_list_coalesce_renames(diff_list, similarity_ours, similarity_theirs, opts);
Packit Service 20376f
Packit Service 20376f
	/* And remove any entries that were merged and are now empty. */
Packit Service 20376f
	git_vector_remove_matching(&diff_list->conflicts, merge_diff_empty, NULL);
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	if (cache != NULL) {
Packit Service 20376f
		for (i = 0; i < cache_size; ++i) {
Packit Service 20376f
			if (cache[i] != NULL)
Packit Service 20376f
				opts->metric->free_signature(cache[i], opts->metric->payload);
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		git__free(cache);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git__free(similarity_ours);
Packit Service 20376f
	git__free(similarity_theirs);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/* Directory/file conflict handling */
Packit Service 20376f
Packit Service 20376f
GIT_INLINE(const char *) merge_diff_path(
Packit Service 20376f
	const git_merge_diff *conflict)
Packit Service 20376f
{
Packit Service 20376f
	if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry))
Packit Service 20376f
		return conflict->ancestor_entry.path;
Packit Service 20376f
	else if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry))
Packit Service 20376f
		return conflict->our_entry.path;
Packit Service 20376f
	else if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry))
Packit Service 20376f
		return conflict->their_entry.path;
Packit Service 20376f
Packit Service 20376f
	return NULL;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
GIT_INLINE(bool) merge_diff_any_side_added_or_modified(
Packit Service 20376f
	const git_merge_diff *conflict)
Packit Service 20376f
{
Packit Service 20376f
	if (conflict->our_status == GIT_DELTA_ADDED ||
Packit Service 20376f
		conflict->our_status == GIT_DELTA_MODIFIED ||
Packit Service 20376f
		conflict->their_status == GIT_DELTA_ADDED ||
Packit Service 20376f
		conflict->their_status == GIT_DELTA_MODIFIED)
Packit Service 20376f
		return true;
Packit Service 20376f
Packit Service 20376f
	return false;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
GIT_INLINE(bool) path_is_prefixed(const char *parent, const char *child)
Packit Service 20376f
{
Packit Service 20376f
	size_t child_len = strlen(child);
Packit Service 20376f
	size_t parent_len = strlen(parent);
Packit Service 20376f
Packit Service 20376f
	if (child_len < parent_len ||
Packit Service 20376f
		strncmp(parent, child, parent_len) != 0)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	return (child[parent_len] == '/');
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
GIT_INLINE(int) merge_diff_detect_df_conflict(
Packit Service 20376f
	struct merge_diff_df_data *df_data,
Packit Service 20376f
	git_merge_diff *conflict)
Packit Service 20376f
{
Packit Service 20376f
	const char *cur_path = merge_diff_path(conflict);
Packit Service 20376f
Packit Service 20376f
	/* Determine if this is a D/F conflict or the child of one */
Packit Service 20376f
	if (df_data->df_path &&
Packit Service 20376f
		path_is_prefixed(df_data->df_path, cur_path))
Packit Service 20376f
		conflict->type = GIT_MERGE_DIFF_DF_CHILD;
Packit Service 20376f
	else if(df_data->df_path)
Packit Service 20376f
		df_data->df_path = NULL;
Packit Service 20376f
	else if (df_data->prev_path &&
Packit Service 20376f
		merge_diff_any_side_added_or_modified(df_data->prev_conflict) &&
Packit Service 20376f
		merge_diff_any_side_added_or_modified(conflict) &&
Packit Service 20376f
		path_is_prefixed(df_data->prev_path, cur_path)) {
Packit Service 20376f
		conflict->type = GIT_MERGE_DIFF_DF_CHILD;
Packit Service 20376f
Packit Service 20376f
		df_data->prev_conflict->type = GIT_MERGE_DIFF_DIRECTORY_FILE;
Packit Service 20376f
		df_data->df_path = df_data->prev_path;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	df_data->prev_path = cur_path;
Packit Service 20376f
	df_data->prev_conflict = conflict;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/* Conflict handling */
Packit Service 20376f
Packit Service 20376f
GIT_INLINE(int) merge_diff_detect_type(
Packit Service 20376f
	git_merge_diff *conflict)
Packit Service 20376f
{
Packit Service 20376f
	if (conflict->our_status == GIT_DELTA_ADDED &&
Packit Service 20376f
		conflict->their_status == GIT_DELTA_ADDED)
Packit Service 20376f
		conflict->type = GIT_MERGE_DIFF_BOTH_ADDED;
Packit Service 20376f
	else if (conflict->our_status == GIT_DELTA_MODIFIED &&
Packit Service 20376f
			 conflict->their_status == GIT_DELTA_MODIFIED)
Packit Service 20376f
		conflict->type = GIT_MERGE_DIFF_BOTH_MODIFIED;
Packit Service 20376f
	else if (conflict->our_status == GIT_DELTA_DELETED &&
Packit Service 20376f
			 conflict->their_status == GIT_DELTA_DELETED)
Packit Service 20376f
		conflict->type = GIT_MERGE_DIFF_BOTH_DELETED;
Packit Service 20376f
	else if (conflict->our_status == GIT_DELTA_MODIFIED &&
Packit Service 20376f
			 conflict->their_status == GIT_DELTA_DELETED)
Packit Service 20376f
		conflict->type = GIT_MERGE_DIFF_MODIFIED_DELETED;
Packit Service 20376f
	else if (conflict->our_status == GIT_DELTA_DELETED &&
Packit Service 20376f
			 conflict->their_status == GIT_DELTA_MODIFIED)
Packit Service 20376f
		conflict->type = GIT_MERGE_DIFF_MODIFIED_DELETED;
Packit Service 20376f
	else
Packit Service 20376f
		conflict->type = GIT_MERGE_DIFF_NONE;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
GIT_INLINE(int) index_entry_dup_pool(
Packit Service 20376f
	git_index_entry *out,
Packit Service 20376f
	git_pool *pool,
Packit Service 20376f
	const git_index_entry *src)
Packit Service 20376f
{
Packit Service 20376f
	if (src != NULL) {
Packit Service 20376f
		memcpy(out, src, sizeof(git_index_entry));
Packit Service 20376f
		if ((out->path = git_pool_strdup(pool, src->path)) == NULL)
Packit Service 20376f
			return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
GIT_INLINE(int) merge_delta_type_from_index_entries(
Packit Service 20376f
	const git_index_entry *ancestor,
Packit Service 20376f
	const git_index_entry *other)
Packit Service 20376f
{
Packit Service 20376f
	if (ancestor == NULL && other == NULL)
Packit Service 20376f
		return GIT_DELTA_UNMODIFIED;
Packit Service 20376f
	else if (ancestor == NULL && other != NULL)
Packit Service 20376f
		return GIT_DELTA_ADDED;
Packit Service 20376f
	else if (ancestor != NULL && other == NULL)
Packit Service 20376f
		return GIT_DELTA_DELETED;
Packit Service 20376f
	else if (S_ISDIR(ancestor->mode) ^ S_ISDIR(other->mode))
Packit Service 20376f
		return GIT_DELTA_TYPECHANGE;
Packit Service 20376f
	else if(S_ISLNK(ancestor->mode) ^ S_ISLNK(other->mode))
Packit Service 20376f
		return GIT_DELTA_TYPECHANGE;
Packit Service 20376f
	else if (git_oid__cmp(&ancestor->id, &other->id) ||
Packit Service 20376f
			 ancestor->mode != other->mode)
Packit Service 20376f
		return GIT_DELTA_MODIFIED;
Packit Service 20376f
Packit Service 20376f
	return GIT_DELTA_UNMODIFIED;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static git_merge_diff *merge_diff_from_index_entries(
Packit Service 20376f
	git_merge_diff_list *diff_list,
Packit Service 20376f
	const git_index_entry **entries)
Packit Service 20376f
{
Packit Service 20376f
	git_merge_diff *conflict;
Packit Service 20376f
	git_pool *pool = &diff_list->pool;
Packit Service 20376f
Packit Service 20376f
	if ((conflict = git_pool_mallocz(pool, sizeof(git_merge_diff))) == NULL)
Packit Service 20376f
		return NULL;
Packit Service 20376f
Packit Service 20376f
	if (index_entry_dup_pool(&conflict->ancestor_entry, pool, entries[TREE_IDX_ANCESTOR]) < 0 ||
Packit Service 20376f
		index_entry_dup_pool(&conflict->our_entry, pool, entries[TREE_IDX_OURS]) < 0 ||
Packit Service 20376f
		index_entry_dup_pool(&conflict->their_entry, pool, entries[TREE_IDX_THEIRS]) < 0)
Packit Service 20376f
		return NULL;
Packit Service 20376f
Packit Service 20376f
	conflict->our_status = merge_delta_type_from_index_entries(
Packit Service 20376f
		entries[TREE_IDX_ANCESTOR], entries[TREE_IDX_OURS]);
Packit Service 20376f
	conflict->their_status = merge_delta_type_from_index_entries(
Packit Service 20376f
		entries[TREE_IDX_ANCESTOR], entries[TREE_IDX_THEIRS]);
Packit Service 20376f
Packit Service 20376f
	return conflict;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/* Merge trees */
Packit Service 20376f
Packit Service 20376f
static int merge_diff_list_insert_conflict(
Packit Service 20376f
	git_merge_diff_list *diff_list,
Packit Service 20376f
	struct merge_diff_df_data *merge_df_data,
Packit Service 20376f
	const git_index_entry *tree_items[3])
Packit Service 20376f
{
Packit Service 20376f
	git_merge_diff *conflict;
Packit Service 20376f
Packit Service 20376f
	if ((conflict = merge_diff_from_index_entries(diff_list, tree_items)) == NULL ||
Packit Service 20376f
		merge_diff_detect_type(conflict) < 0 ||
Packit Service 20376f
		merge_diff_detect_df_conflict(merge_df_data, conflict) < 0 ||
Packit Service 20376f
		git_vector_insert(&diff_list->conflicts, conflict) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_diff_list_insert_unmodified(
Packit Service 20376f
	git_merge_diff_list *diff_list,
Packit Service 20376f
	const git_index_entry *tree_items[3])
Packit Service 20376f
{
Packit Service 20376f
	int error = 0;
Packit Service 20376f
	git_index_entry *entry;
Packit Service 20376f
Packit Service 20376f
	entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry));
Packit Service 20376f
	GITERR_CHECK_ALLOC(entry);
Packit Service 20376f
Packit Service 20376f
	if ((error = index_entry_dup_pool(entry, &diff_list->pool, tree_items[0])) >= 0)
Packit Service 20376f
		error = git_vector_insert(&diff_list->staged, entry);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
struct merge_diff_find_data {
Packit Service 20376f
	git_merge_diff_list *diff_list;
Packit Service 20376f
	struct merge_diff_df_data df_data;
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
static int queue_difference(const git_index_entry **entries, void *data)
Packit Service 20376f
{
Packit Service 20376f
	struct merge_diff_find_data *find_data = data;
Packit Service 20376f
	bool item_modified = false;
Packit Service 20376f
	size_t i;
Packit Service 20376f
Packit Service 20376f
	if (!entries[0] || !entries[1] || !entries[2]) {
Packit Service 20376f
		item_modified = true;
Packit Service 20376f
	} else {
Packit Service 20376f
		for (i = 1; i < 3; i++) {
Packit Service 20376f
			if (index_entry_cmp(entries[0], entries[i]) != 0) {
Packit Service 20376f
				item_modified = true;
Packit Service 20376f
				break;
Packit Service 20376f
			}
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return item_modified ?
Packit Service 20376f
		merge_diff_list_insert_conflict(
Packit Service 20376f
			find_data->diff_list, &find_data->df_data, entries) :
Packit Service 20376f
		merge_diff_list_insert_unmodified(find_data->diff_list, entries);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge_diff_list__find_differences(
Packit Service 20376f
	git_merge_diff_list *diff_list,
Packit Service 20376f
	git_iterator *ancestor_iter,
Packit Service 20376f
	git_iterator *our_iter,
Packit Service 20376f
	git_iterator *their_iter)
Packit Service 20376f
{
Packit Service 20376f
	git_iterator *iterators[3] = { ancestor_iter, our_iter, their_iter };
Packit Service 20376f
	struct merge_diff_find_data find_data = { diff_list };
Packit Service 20376f
Packit Service 20376f
	return git_iterator_walk(iterators, 3, queue_difference, &find_data);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo)
Packit Service 20376f
{
Packit Service 20376f
	git_merge_diff_list *diff_list = git__calloc(1, sizeof(git_merge_diff_list));
Packit Service 20376f
Packit Service 20376f
	if (diff_list == NULL)
Packit Service 20376f
		return NULL;
Packit Service 20376f
Packit Service 20376f
	diff_list->repo = repo;
Packit Service 20376f
Packit Service 20376f
	git_pool_init(&diff_list->pool, 1);
Packit Service 20376f
Packit Service 20376f
	if (git_vector_init(&diff_list->staged, 0, NULL) < 0 ||
Packit Service 20376f
		git_vector_init(&diff_list->conflicts, 0, NULL) < 0 ||
Packit Service 20376f
		git_vector_init(&diff_list->resolved, 0, NULL) < 0) {
Packit Service 20376f
		git_merge_diff_list__free(diff_list);
Packit Service 20376f
		return NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return diff_list;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
void git_merge_diff_list__free(git_merge_diff_list *diff_list)
Packit Service 20376f
{
Packit Service 20376f
	if (!diff_list)
Packit Service 20376f
		return;
Packit Service 20376f
Packit Service 20376f
	git_vector_free(&diff_list->staged);
Packit Service 20376f
	git_vector_free(&diff_list->conflicts);
Packit Service 20376f
	git_vector_free(&diff_list->resolved);
Packit Service 20376f
	git_pool_clear(&diff_list->pool);
Packit Service 20376f
	git__free(diff_list);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_normalize_opts(
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	git_merge_options *opts,
Packit Service 20376f
	const git_merge_options *given)
Packit Service 20376f
{
Packit Service 20376f
	git_config *cfg = NULL;
Packit Service 20376f
	git_config_entry *entry = NULL;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	assert(repo && opts);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if (given != NULL) {
Packit Service 20376f
		memcpy(opts, given, sizeof(git_merge_options));
Packit Service 20376f
	} else {
Packit Service 20376f
		git_merge_options init = GIT_MERGE_OPTIONS_INIT;
Packit Service 20376f
		memcpy(opts, &init, sizeof(init));
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((opts->flags & GIT_MERGE_FIND_RENAMES) && !opts->rename_threshold)
Packit Service 20376f
		opts->rename_threshold = GIT_MERGE_DEFAULT_RENAME_THRESHOLD;
Packit Service 20376f
Packit Service 20376f
	if (given && given->default_driver) {
Packit Service 20376f
		opts->default_driver = git__strdup(given->default_driver);
Packit Service 20376f
		GITERR_CHECK_ALLOC(opts->default_driver);
Packit Service 20376f
	} else {
Packit Service 20376f
		error = git_config_get_entry(&entry, cfg, "merge.default");
Packit Service 20376f
Packit Service 20376f
		if (error == 0) {
Packit Service 20376f
			opts->default_driver = git__strdup(entry->value);
Packit Service 20376f
			GITERR_CHECK_ALLOC(opts->default_driver);
Packit Service 20376f
		} else if (error == GIT_ENOTFOUND) {
Packit Service 20376f
			error = 0;
Packit Service 20376f
		} else {
Packit Service 20376f
			goto done;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (!opts->target_limit) {
Packit Service 20376f
		int limit = git_config__get_int_force(cfg, "merge.renamelimit", 0);
Packit Service 20376f
Packit Service 20376f
		if (!limit)
Packit Service 20376f
			limit = git_config__get_int_force(cfg, "diff.renamelimit", 0);
Packit Service 20376f
Packit Service 20376f
		opts->target_limit = (limit <= 0) ?
Packit Service 20376f
			GIT_MERGE_DEFAULT_TARGET_LIMIT : (unsigned int)limit;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* assign the internal metric with whitespace flag as payload */
Packit Service 20376f
	if (!opts->metric) {
Packit Service 20376f
		opts->metric = git__malloc(sizeof(git_diff_similarity_metric));
Packit Service 20376f
		GITERR_CHECK_ALLOC(opts->metric);
Packit Service 20376f
Packit Service 20376f
		opts->metric->file_signature = git_diff_find_similar__hashsig_for_file;
Packit Service 20376f
		opts->metric->buffer_signature = git_diff_find_similar__hashsig_for_buf;
Packit Service 20376f
		opts->metric->free_signature = git_diff_find_similar__hashsig_free;
Packit Service 20376f
		opts->metric->similarity = git_diff_find_similar__calc_similarity;
Packit Service 20376f
		opts->metric->payload = (void *)GIT_HASHSIG_SMART_WHITESPACE;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_config_entry_free(entry);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
static int merge_index_insert_reuc(
Packit Service 20376f
	git_index *index,
Packit Service 20376f
	size_t idx,
Packit Service 20376f
	const git_index_entry *entry)
Packit Service 20376f
{
Packit Service 20376f
	const git_index_reuc_entry *reuc;
Packit Service 20376f
	int mode[3] = { 0, 0, 0 };
Packit Service 20376f
	git_oid const *oid[3] = { NULL, NULL, NULL };
Packit Service 20376f
	size_t i;
Packit Service 20376f
Packit Service 20376f
	if (!GIT_MERGE_INDEX_ENTRY_EXISTS(*entry))
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	if ((reuc = git_index_reuc_get_bypath(index, entry->path)) != NULL) {
Packit Service 20376f
		for (i = 0; i < 3; i++) {
Packit Service 20376f
			mode[i] = reuc->mode[i];
Packit Service 20376f
			oid[i] = &reuc->oid[i];
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	mode[idx] = entry->mode;
Packit Service 20376f
	oid[idx] = &entry->id;
Packit Service 20376f
Packit Service 20376f
	return git_index_reuc_add(index, entry->path,
Packit Service 20376f
		mode[0], oid[0], mode[1], oid[1], mode[2], oid[2]);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int index_update_reuc(git_index *index, git_merge_diff_list *diff_list)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	size_t i;
Packit Service 20376f
	git_merge_diff *conflict;
Packit Service 20376f
Packit Service 20376f
	/* Add each entry in the resolved conflict to the REUC independently, since
Packit Service 20376f
	 * the paths may differ due to renames. */
Packit Service 20376f
	git_vector_foreach(&diff_list->resolved, i, conflict) {
Packit Service 20376f
		const git_index_entry *ancestor =
Packit Service 20376f
			GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
Packit Service 20376f
			&conflict->ancestor_entry : NULL;
Packit Service 20376f
Packit Service 20376f
		const git_index_entry *ours =
Packit Service 20376f
			GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
Packit Service 20376f
			&conflict->our_entry : NULL;
Packit Service 20376f
Packit Service 20376f
		const git_index_entry *theirs =
Packit Service 20376f
			GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
Packit Service 20376f
			&conflict->their_entry : NULL;
Packit Service 20376f
Packit Service 20376f
		if (ancestor != NULL &&
Packit Service 20376f
			(error = merge_index_insert_reuc(index, TREE_IDX_ANCESTOR, ancestor)) < 0)
Packit Service 20376f
			return error;
Packit Service 20376f
Packit Service 20376f
		if (ours != NULL &&
Packit Service 20376f
			(error = merge_index_insert_reuc(index, TREE_IDX_OURS, ours)) < 0)
Packit Service 20376f
			return error;
Packit Service 20376f
Packit Service 20376f
		if (theirs != NULL &&
Packit Service 20376f
			(error = merge_index_insert_reuc(index, TREE_IDX_THEIRS, theirs)) < 0)
Packit Service 20376f
			return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int index_from_diff_list(git_index **out,
Packit Service 20376f
	git_merge_diff_list *diff_list, bool skip_reuc)
Packit Service 20376f
{
Packit Service 20376f
	git_index *index;
Packit Service 20376f
	size_t i;
Packit Service 20376f
	git_merge_diff *conflict;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	*out = NULL;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_index_new(&index)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_index__fill(index, &diff_list->staged)) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(&diff_list->conflicts, i, conflict) {
Packit Service 20376f
		const git_index_entry *ancestor =
Packit Service 20376f
			GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
Packit Service 20376f
			&conflict->ancestor_entry : NULL;
Packit Service 20376f
Packit Service 20376f
		const git_index_entry *ours =
Packit Service 20376f
			GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
Packit Service 20376f
			&conflict->our_entry : NULL;
Packit Service 20376f
Packit Service 20376f
		const git_index_entry *theirs =
Packit Service 20376f
			GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
Packit Service 20376f
			&conflict->their_entry : NULL;
Packit Service 20376f
Packit Service 20376f
		if ((error = git_index_conflict_add(index, ancestor, ours, theirs)) < 0)
Packit Service 20376f
			goto on_error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* Add each rename entry to the rename portion of the index. */
Packit Service 20376f
	git_vector_foreach(&diff_list->conflicts, i, conflict) {
Packit Service 20376f
		const char *ancestor_path, *our_path, *their_path;
Packit Service 20376f
Packit Service 20376f
		if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry))
Packit Service 20376f
			continue;
Packit Service 20376f
Packit Service 20376f
		ancestor_path = conflict->ancestor_entry.path;
Packit Service 20376f
Packit Service 20376f
		our_path =
Packit Service 20376f
			GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
Packit Service 20376f
			conflict->our_entry.path : NULL;
Packit Service 20376f
Packit Service 20376f
		their_path =
Packit Service 20376f
			GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
Packit Service 20376f
			conflict->their_entry.path : NULL;
Packit Service 20376f
Packit Service 20376f
		if ((our_path && strcmp(ancestor_path, our_path) != 0) ||
Packit Service 20376f
			(their_path && strcmp(ancestor_path, their_path) != 0)) {
Packit Service 20376f
			if ((error = git_index_name_add(index, ancestor_path, our_path, their_path)) < 0)
Packit Service 20376f
				goto on_error;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (!skip_reuc) {
Packit Service 20376f
		if ((error = index_update_reuc(index, diff_list)) < 0)
Packit Service 20376f
			goto on_error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*out = index;
Packit Service 20376f
	return 0;
Packit Service 20376f
Packit Service 20376f
on_error:
Packit Service 20376f
	git_index_free(index);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static git_iterator *iterator_given_or_empty(git_iterator **empty, git_iterator *given)
Packit Service 20376f
{
Packit Service 20376f
	git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT;
Packit Service 20376f
Packit Service 20376f
	if (given)
Packit Service 20376f
		return given;
Packit Service 20376f
Packit Service 20376f
	opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
Packit Service 20376f
Packit Service 20376f
	if (git_iterator_for_nothing(empty, &opts) < 0)
Packit Service 20376f
		return NULL;
Packit Service 20376f
Packit Service 20376f
	return *empty;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge__iterators(
Packit Service 20376f
	git_index **out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	git_iterator *ancestor_iter,
Packit Service 20376f
	git_iterator *our_iter,
Packit Service 20376f
	git_iterator *theirs_iter,
Packit Service 20376f
	const git_merge_options *given_opts)
Packit Service 20376f
{
Packit Service 20376f
	git_iterator *empty_ancestor = NULL,
Packit Service 20376f
		*empty_ours = NULL,
Packit Service 20376f
		*empty_theirs = NULL;
Packit Service 20376f
	git_merge_diff_list *diff_list;
Packit Service 20376f
	git_merge_options opts;
Packit Service 20376f
	git_merge_file_options file_opts = GIT_MERGE_FILE_OPTIONS_INIT;
Packit Service 20376f
	git_merge_diff *conflict;
Packit Service 20376f
	git_vector changes;
Packit Service 20376f
	size_t i;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	assert(out && repo);
Packit Service 20376f
Packit Service 20376f
	*out = NULL;
Packit Service 20376f
Packit Service 20376f
	GITERR_CHECK_VERSION(
Packit Service 20376f
		given_opts, GIT_MERGE_OPTIONS_VERSION, "git_merge_options");
Packit Service 20376f
Packit Service 20376f
	if ((error = merge_normalize_opts(repo, &opts, given_opts)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	file_opts.favor = opts.file_favor;
Packit Service 20376f
	file_opts.flags = opts.file_flags;
Packit Service 20376f
Packit Service 20376f
	/* use the git-inspired labels when virtual base building */
Packit Service 20376f
	if (opts.flags & GIT_MERGE__VIRTUAL_BASE) {
Packit Service 20376f
		file_opts.ancestor_label = "merged common ancestors";
Packit Service 20376f
		file_opts.our_label = "Temporary merge branch 1";
Packit Service 20376f
		file_opts.their_label = "Temporary merge branch 2";
Packit Service 20376f
		file_opts.flags |= GIT_MERGE_FILE_FAVOR__CONFLICTED;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	diff_list = git_merge_diff_list__alloc(repo);
Packit Service 20376f
	GITERR_CHECK_ALLOC(diff_list);
Packit Service 20376f
Packit Service 20376f
	ancestor_iter = iterator_given_or_empty(&empty_ancestor, ancestor_iter);
Packit Service 20376f
	our_iter = iterator_given_or_empty(&empty_ours, our_iter);
Packit Service 20376f
	theirs_iter = iterator_given_or_empty(&empty_theirs, theirs_iter);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_merge_diff_list__find_differences(
Packit Service 20376f
			diff_list, ancestor_iter, our_iter, theirs_iter)) < 0 ||
Packit Service 20376f
		(error = git_merge_diff_list__find_renames(repo, diff_list, &opts)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	memcpy(&changes, &diff_list->conflicts, sizeof(git_vector));
Packit Service 20376f
	git_vector_clear(&diff_list->conflicts);
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(&changes, i, conflict) {
Packit Service 20376f
		int resolved = 0;
Packit Service 20376f
Packit Service 20376f
		if ((error = merge_conflict_resolve(
Packit Service 20376f
			&resolved, diff_list, conflict, &opts, &file_opts)) < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
Packit Service 20376f
		if (!resolved) {
Packit Service 20376f
			if ((opts.flags & GIT_MERGE_FAIL_ON_CONFLICT)) {
Packit Service 20376f
				giterr_set(GITERR_MERGE, "merge conflicts exist");
Packit Service 20376f
				error = GIT_EMERGECONFLICT;
Packit Service 20376f
				goto done;
Packit Service 20376f
			}
Packit Service 20376f
Packit Service 20376f
			git_vector_insert(&diff_list->conflicts, conflict);
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	error = index_from_diff_list(out, diff_list,
Packit Service 20376f
		(opts.flags & GIT_MERGE_SKIP_REUC));
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	if (!given_opts || !given_opts->metric)
Packit Service 20376f
		git__free(opts.metric);
Packit Service 20376f
Packit Service 20376f
	git__free((char *)opts.default_driver);
Packit Service 20376f
Packit Service 20376f
	git_merge_diff_list__free(diff_list);
Packit Service 20376f
	git_iterator_free(empty_ancestor);
Packit Service 20376f
	git_iterator_free(empty_ours);
Packit Service 20376f
	git_iterator_free(empty_theirs);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge_trees(
Packit Service 20376f
	git_index **out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const git_tree *ancestor_tree,
Packit Service 20376f
	const git_tree *our_tree,
Packit Service 20376f
	const git_tree *their_tree,
Packit Service 20376f
	const git_merge_options *merge_opts)
Packit Service 20376f
{
Packit Service 20376f
	git_iterator *ancestor_iter = NULL, *our_iter = NULL, *their_iter = NULL;
Packit Service 20376f
	git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	assert(out && repo);
Packit Service 20376f
Packit Service 20376f
	/* if one side is treesame to the ancestor, take the other side */
Packit Service 20376f
	if (ancestor_tree && merge_opts && (merge_opts->flags & GIT_MERGE_SKIP_REUC)) {
Packit Service 20376f
		const git_tree *result = NULL;
Packit Service 20376f
		const git_oid *ancestor_tree_id = git_tree_id(ancestor_tree);
Packit Service 20376f
Packit Service 20376f
		if (our_tree && !git_oid_cmp(ancestor_tree_id, git_tree_id(our_tree)))
Packit Service 20376f
			result = their_tree;
Packit Service 20376f
		else if (their_tree && !git_oid_cmp(ancestor_tree_id, git_tree_id(their_tree)))
Packit Service 20376f
			result = our_tree;
Packit Service 20376f
Packit Service 20376f
		if (result) {
Packit Service 20376f
			if ((error = git_index_new(out)) == 0)
Packit Service 20376f
    			error = git_index_read_tree(*out, result);
Packit Service 20376f
Packit Service 20376f
			return error;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_iterator_for_tree(
Packit Service 20376f
			&ancestor_iter, (git_tree *)ancestor_tree, &iter_opts)) < 0 ||
Packit Service 20376f
		(error = git_iterator_for_tree(
Packit Service 20376f
			&our_iter, (git_tree *)our_tree, &iter_opts)) < 0 ||
Packit Service 20376f
		(error = git_iterator_for_tree(
Packit Service 20376f
			&their_iter, (git_tree *)their_tree, &iter_opts)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	error = git_merge__iterators(
Packit Service 20376f
		out, repo, ancestor_iter, our_iter, their_iter, merge_opts);
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_iterator_free(ancestor_iter);
Packit Service 20376f
	git_iterator_free(our_iter);
Packit Service 20376f
	git_iterator_free(their_iter);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_annotated_commits(
Packit Service 20376f
	git_index **index_out,
Packit Service 20376f
	git_annotated_commit **base_out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	git_annotated_commit *our_commit,
Packit Service 20376f
	git_annotated_commit *their_commit,
Packit Service 20376f
	size_t recursion_level,
Packit Service 20376f
	const git_merge_options *opts);
Packit Service 20376f
Packit Service 20376f
GIT_INLINE(int) insert_head_ids(
Packit Service 20376f
	git_array_oid_t *ids,
Packit Service 20376f
	const git_annotated_commit *annotated_commit)
Packit Service 20376f
{
Packit Service 20376f
	git_oid *id;
Packit Service 20376f
	size_t i;
Packit Service 20376f
Packit Service 20376f
	if (annotated_commit->type == GIT_ANNOTATED_COMMIT_REAL) {
Packit Service 20376f
		id = git_array_alloc(*ids);
Packit Service 20376f
		GITERR_CHECK_ALLOC(id);
Packit Service 20376f
Packit Service 20376f
		git_oid_cpy(id, git_commit_id(annotated_commit->commit));
Packit Service 20376f
	} else {
Packit Service 20376f
		for (i = 0; i < annotated_commit->parents.size; i++) {
Packit Service 20376f
			id = git_array_alloc(*ids);
Packit Service 20376f
			GITERR_CHECK_ALLOC(id);
Packit Service 20376f
Packit Service 20376f
			git_oid_cpy(id, &annotated_commit->parents.ptr[i]);
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int create_virtual_base(
Packit Service 20376f
	git_annotated_commit **out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	git_annotated_commit *one,
Packit Service 20376f
	git_annotated_commit *two,
Packit Service 20376f
	const git_merge_options *opts,
Packit Service 20376f
	size_t recursion_level)
Packit Service 20376f
{
Packit Service 20376f
	git_annotated_commit *result = NULL;
Packit Service 20376f
	git_index *index = NULL;
Packit Service 20376f
	git_merge_options virtual_opts = GIT_MERGE_OPTIONS_INIT;
Packit Service 20376f
Packit Service 20376f
	/* Conflicts in the merge base creation do not propagate to conflicts
Packit Service 20376f
	 * in the result; the conflicted base will act as the common ancestor.
Packit Service 20376f
	 */
Packit Service 20376f
	if (opts)
Packit Service 20376f
		memcpy(&virtual_opts, opts, sizeof(git_merge_options));
Packit Service 20376f
Packit Service 20376f
	virtual_opts.flags &= ~GIT_MERGE_FAIL_ON_CONFLICT;
Packit Service 20376f
	virtual_opts.flags |= GIT_MERGE__VIRTUAL_BASE;
Packit Service 20376f
Packit Service 20376f
	if ((merge_annotated_commits(&index, NULL, repo, one, two,
Packit Service 20376f
			recursion_level + 1, &virtual_opts)) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	result = git__calloc(1, sizeof(git_annotated_commit));
Packit Service 20376f
	GITERR_CHECK_ALLOC(result);
Packit Service 20376f
	result->type = GIT_ANNOTATED_COMMIT_VIRTUAL;
Packit Service 20376f
	result->index = index;
Packit Service 20376f
Packit Service 20376f
	insert_head_ids(&result->parents, one);
Packit Service 20376f
	insert_head_ids(&result->parents, two);
Packit Service 20376f
Packit Service 20376f
	*out = result;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int compute_base(
Packit Service 20376f
	git_annotated_commit **out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const git_annotated_commit *one,
Packit Service 20376f
	const git_annotated_commit *two,
Packit Service 20376f
	const git_merge_options *given_opts,
Packit Service 20376f
	size_t recursion_level)
Packit Service 20376f
{
Packit Service 20376f
	git_array_oid_t head_ids = GIT_ARRAY_INIT;
Packit Service 20376f
	git_oidarray bases = {0};
Packit Service 20376f
	git_annotated_commit *base = NULL, *other = NULL, *new_base = NULL;
Packit Service 20376f
	git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
Packit Service 20376f
	size_t i, base_count;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	*out = NULL;
Packit Service 20376f
Packit Service 20376f
	if (given_opts)
Packit Service 20376f
		memcpy(&opts, given_opts, sizeof(git_merge_options));
Packit Service 20376f
Packit Service 20376f
	/* With more than two commits, merge_bases_many finds the base of
Packit Service 20376f
	 * the first commit and a hypothetical merge of the others. Since
Packit Service 20376f
	 * "one" may itself be a virtual commit, which insert_head_ids
Packit Service 20376f
	 * substitutes multiple ancestors for, it needs to be added
Packit Service 20376f
	 * after "two" which is always a single real commit.
Packit Service 20376f
	 */
Packit Service 20376f
	if ((error = insert_head_ids(&head_ids, two)) < 0 ||
Packit Service 20376f
		(error = insert_head_ids(&head_ids, one)) < 0 ||
Packit Service 20376f
		(error = git_merge_bases_many(&bases, repo,
Packit Service 20376f
			head_ids.size, head_ids.ptr)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	base_count = (opts.flags & GIT_MERGE_NO_RECURSIVE) ? 0 : bases.count;
Packit Service 20376f
Packit Service 20376f
	if (base_count)
Packit Service 20376f
		git_oidarray__reverse(&bases);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_annotated_commit_lookup(&base, repo, &bases.ids[0])) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	for (i = 1; i < base_count; i++) {
Packit Service 20376f
		recursion_level++;
Packit Service 20376f
Packit Service 20376f
		if (opts.recursion_limit && recursion_level > opts.recursion_limit)
Packit Service 20376f
			break;
Packit Service 20376f
Packit Service 20376f
		if ((error = git_annotated_commit_lookup(&other, repo,
Packit Service 20376f
				&bases.ids[i])) < 0 ||
Packit Service 20376f
			(error = create_virtual_base(&new_base, repo, base, other, &opts,
Packit Service 20376f
				recursion_level)) < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
Packit Service 20376f
		git_annotated_commit_free(base);
Packit Service 20376f
		git_annotated_commit_free(other);
Packit Service 20376f
Packit Service 20376f
		base = new_base;
Packit Service 20376f
		new_base = NULL;
Packit Service 20376f
		other = NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	if (error == 0)
Packit Service 20376f
		*out = base;
Packit Service 20376f
	else
Packit Service 20376f
		git_annotated_commit_free(base);
Packit Service 20376f
Packit Service 20376f
	git_annotated_commit_free(other);
Packit Service 20376f
	git_annotated_commit_free(new_base);
Packit Service 20376f
	git_oidarray_free(&bases);
Packit Service 20376f
	git_array_clear(head_ids);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int iterator_for_annotated_commit(
Packit Service 20376f
	git_iterator **out,
Packit Service 20376f
	git_annotated_commit *commit)
Packit Service 20376f
{
Packit Service 20376f
	git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
Packit Service 20376f
Packit Service 20376f
	if (commit == NULL) {
Packit Service 20376f
		error = git_iterator_for_nothing(out, &opts);
Packit Service 20376f
	} else if (commit->type == GIT_ANNOTATED_COMMIT_VIRTUAL) {
Packit Service 20376f
		error = git_iterator_for_index(out, git_index_owner(commit->index), commit->index, &opts);
Packit Service 20376f
	} else {
Packit Service 20376f
		if (!commit->tree &&
Packit Service 20376f
			(error = git_commit_tree(&commit->tree, commit->commit)) < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
Packit Service 20376f
		error = git_iterator_for_tree(out, commit->tree, &opts);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_annotated_commits(
Packit Service 20376f
	git_index **index_out,
Packit Service 20376f
	git_annotated_commit **base_out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	git_annotated_commit *ours,
Packit Service 20376f
	git_annotated_commit *theirs,
Packit Service 20376f
	size_t recursion_level,
Packit Service 20376f
	const git_merge_options *opts)
Packit Service 20376f
{
Packit Service 20376f
	git_annotated_commit *base = NULL;
Packit Service 20376f
	git_iterator *base_iter = NULL, *our_iter = NULL, *their_iter = NULL;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	if ((error = compute_base(&base, repo, ours, theirs, opts,
Packit Service 20376f
		recursion_level)) < 0) {
Packit Service 20376f
Packit Service 20376f
		if (error != GIT_ENOTFOUND)
Packit Service 20376f
			goto done;
Packit Service 20376f
Packit Service 20376f
		giterr_clear();
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = iterator_for_annotated_commit(&base_iter, base)) < 0 ||
Packit Service 20376f
		(error = iterator_for_annotated_commit(&our_iter, ours)) < 0 ||
Packit Service 20376f
		(error = iterator_for_annotated_commit(&their_iter, theirs)) < 0 ||
Packit Service 20376f
		(error = git_merge__iterators(index_out, repo, base_iter, our_iter,
Packit Service 20376f
			their_iter, opts)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if (base_out) {
Packit Service 20376f
		*base_out = base;
Packit Service 20376f
		base = NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_annotated_commit_free(base);
Packit Service 20376f
	git_iterator_free(base_iter);
Packit Service 20376f
	git_iterator_free(our_iter);
Packit Service 20376f
	git_iterator_free(their_iter);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
int git_merge_commits(
Packit Service 20376f
	git_index **out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const git_commit *our_commit,
Packit Service 20376f
	const git_commit *their_commit,
Packit Service 20376f
	const git_merge_options *opts)
Packit Service 20376f
{
Packit Service 20376f
	git_annotated_commit *ours = NULL, *theirs = NULL, *base = NULL;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_annotated_commit_from_commit(&ours, (git_commit *)our_commit)) < 0 ||
Packit Service 20376f
		(error = git_annotated_commit_from_commit(&theirs, (git_commit *)their_commit)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	error = merge_annotated_commits(out, &base, repo, ours, theirs, 0, opts);
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_annotated_commit_free(ours);
Packit Service 20376f
	git_annotated_commit_free(theirs);
Packit Service 20376f
	git_annotated_commit_free(base);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/* Merge setup / cleanup */
Packit Service 20376f
Packit Service 20376f
static int write_merge_head(
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const git_annotated_commit *heads[],
Packit Service 20376f
	size_t heads_len)
Packit Service 20376f
{
Packit Service 20376f
	git_filebuf file = GIT_FILEBUF_INIT;
Packit Service 20376f
	git_buf file_path = GIT_BUF_INIT;
Packit Service 20376f
	size_t i;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	assert(repo && heads);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_HEAD_FILE)) < 0 ||
Packit Service 20376f
		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < heads_len; i++) {
Packit Service 20376f
		if ((error = git_filebuf_printf(&file, "%s\n", heads[i]->id_str)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	error = git_filebuf_commit(&file;;
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		git_filebuf_cleanup(&file;;
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&file_path);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int write_merge_mode(git_repository *repo)
Packit Service 20376f
{
Packit Service 20376f
	git_filebuf file = GIT_FILEBUF_INIT;
Packit Service 20376f
	git_buf file_path = GIT_BUF_INIT;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	assert(repo);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MODE_FILE)) < 0 ||
Packit Service 20376f
		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_filebuf_write(&file, "no-ff", 5)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	error = git_filebuf_commit(&file;;
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		git_filebuf_cleanup(&file;;
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&file_path);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
struct merge_msg_entry {
Packit Service 20376f
	const git_annotated_commit *merge_head;
Packit Service 20376f
	bool written;
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
static int msg_entry_is_branch(
Packit Service 20376f
	const struct merge_msg_entry *entry,
Packit Service 20376f
	git_vector *entries)
Packit Service 20376f
{
Packit Service 20376f
	GIT_UNUSED(entries);
Packit Service 20376f
Packit Service 20376f
	return (entry->written == 0 &&
Packit Service 20376f
		entry->merge_head->remote_url == NULL &&
Packit Service 20376f
		entry->merge_head->ref_name != NULL &&
Packit Service 20376f
		git__strncmp(GIT_REFS_HEADS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_HEADS_DIR)) == 0);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int msg_entry_is_tracking(
Packit Service 20376f
	const struct merge_msg_entry *entry,
Packit Service 20376f
	git_vector *entries)
Packit Service 20376f
{
Packit Service 20376f
	GIT_UNUSED(entries);
Packit Service 20376f
Packit Service 20376f
	return (entry->written == 0 &&
Packit Service 20376f
		entry->merge_head->remote_url == NULL &&
Packit Service 20376f
		entry->merge_head->ref_name != NULL &&
Packit Service 20376f
		git__strncmp(GIT_REFS_REMOTES_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_REMOTES_DIR)) == 0);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int msg_entry_is_tag(
Packit Service 20376f
	const struct merge_msg_entry *entry,
Packit Service 20376f
	git_vector *entries)
Packit Service 20376f
{
Packit Service 20376f
	GIT_UNUSED(entries);
Packit Service 20376f
Packit Service 20376f
	return (entry->written == 0 &&
Packit Service 20376f
		entry->merge_head->remote_url == NULL &&
Packit Service 20376f
		entry->merge_head->ref_name != NULL &&
Packit Service 20376f
		git__strncmp(GIT_REFS_TAGS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_TAGS_DIR)) == 0);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int msg_entry_is_remote(
Packit Service 20376f
	const struct merge_msg_entry *entry,
Packit Service 20376f
	git_vector *entries)
Packit Service 20376f
{
Packit Service 20376f
	if (entry->written == 0 &&
Packit Service 20376f
		entry->merge_head->remote_url != NULL &&
Packit Service 20376f
		entry->merge_head->ref_name != NULL &&
Packit Service 20376f
		git__strncmp(GIT_REFS_HEADS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_HEADS_DIR)) == 0)
Packit Service 20376f
	{
Packit Service 20376f
		struct merge_msg_entry *existing;
Packit Service 20376f
Packit Service 20376f
		/* Match only branches from the same remote */
Packit Service 20376f
		if (entries->length == 0)
Packit Service 20376f
			return 1;
Packit Service 20376f
Packit Service 20376f
		existing = git_vector_get(entries, 0);
Packit Service 20376f
Packit Service 20376f
		return (git__strcmp(existing->merge_head->remote_url,
Packit Service 20376f
			entry->merge_head->remote_url) == 0);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int msg_entry_is_oid(
Packit Service 20376f
	const struct merge_msg_entry *merge_msg_entry)
Packit Service 20376f
{
Packit Service 20376f
	return (merge_msg_entry->written == 0 &&
Packit Service 20376f
		merge_msg_entry->merge_head->ref_name == NULL &&
Packit Service 20376f
		merge_msg_entry->merge_head->remote_url == NULL);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_msg_entry_written(
Packit Service 20376f
	const struct merge_msg_entry *merge_msg_entry)
Packit Service 20376f
{
Packit Service 20376f
	return (merge_msg_entry->written == 1);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_msg_entries(
Packit Service 20376f
	git_vector *v,
Packit Service 20376f
	const struct merge_msg_entry *entries,
Packit Service 20376f
	size_t len,
Packit Service 20376f
	int (*match)(const struct merge_msg_entry *entry, git_vector *entries))
Packit Service 20376f
{
Packit Service 20376f
	size_t i;
Packit Service 20376f
	int matches, total = 0;
Packit Service 20376f
Packit Service 20376f
	git_vector_clear(v);
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < len; i++) {
Packit Service 20376f
		if ((matches = match(&entries[i], v)) < 0)
Packit Service 20376f
			return matches;
Packit Service 20376f
		else if (!matches)
Packit Service 20376f
			continue;
Packit Service 20376f
Packit Service 20376f
		git_vector_insert(v, (struct merge_msg_entry *)&entries[i]);
Packit Service 20376f
		total++;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return total;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_msg_write_entries(
Packit Service 20376f
	git_filebuf *file,
Packit Service 20376f
	git_vector *entries,
Packit Service 20376f
	const char *item_name,
Packit Service 20376f
	const char *item_plural_name,
Packit Service 20376f
	size_t ref_name_skip,
Packit Service 20376f
	const char *source,
Packit Service 20376f
	char sep)
Packit Service 20376f
{
Packit Service 20376f
	struct merge_msg_entry *entry;
Packit Service 20376f
	size_t i;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	if (entries->length == 0)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	if (sep && (error = git_filebuf_printf(file, "%c ", sep)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_filebuf_printf(file, "%s ",
Packit Service 20376f
		(entries->length == 1) ? item_name : item_plural_name)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(entries, i, entry) {
Packit Service 20376f
		if (i > 0 &&
Packit Service 20376f
			(error = git_filebuf_printf(file, "%s", (i == entries->length - 1) ? " and " : ", ")) < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
Packit Service 20376f
		if ((error = git_filebuf_printf(file, "'%s'", entry->merge_head->ref_name + ref_name_skip)) < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
Packit Service 20376f
		entry->written = 1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (source)
Packit Service 20376f
		error = git_filebuf_printf(file, " of %s", source);
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_msg_write_branches(
Packit Service 20376f
	git_filebuf *file,
Packit Service 20376f
	git_vector *entries,
Packit Service 20376f
	char sep)
Packit Service 20376f
{
Packit Service 20376f
	return merge_msg_write_entries(file, entries,
Packit Service 20376f
		"branch", "branches", strlen(GIT_REFS_HEADS_DIR), NULL, sep);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_msg_write_tracking(
Packit Service 20376f
	git_filebuf *file,
Packit Service 20376f
	git_vector *entries,
Packit Service 20376f
	char sep)
Packit Service 20376f
{
Packit Service 20376f
	return merge_msg_write_entries(file, entries,
Packit Service 20376f
		"remote-tracking branch", "remote-tracking branches", 0, NULL, sep);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_msg_write_tags(
Packit Service 20376f
	git_filebuf *file,
Packit Service 20376f
	git_vector *entries,
Packit Service 20376f
	char sep)
Packit Service 20376f
{
Packit Service 20376f
	return merge_msg_write_entries(file, entries,
Packit Service 20376f
		"tag", "tags", strlen(GIT_REFS_TAGS_DIR), NULL, sep);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_msg_write_remotes(
Packit Service 20376f
	git_filebuf *file,
Packit Service 20376f
	git_vector *entries,
Packit Service 20376f
	char sep)
Packit Service 20376f
{
Packit Service 20376f
	const char *source;
Packit Service 20376f
Packit Service 20376f
	if (entries->length == 0)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	source = ((struct merge_msg_entry *)entries->contents[0])->merge_head->remote_url;
Packit Service 20376f
Packit Service 20376f
	return merge_msg_write_entries(file, entries,
Packit Service 20376f
		"branch", "branches", strlen(GIT_REFS_HEADS_DIR), source, sep);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int write_merge_msg(
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const git_annotated_commit *heads[],
Packit Service 20376f
	size_t heads_len)
Packit Service 20376f
{
Packit Service 20376f
	git_filebuf file = GIT_FILEBUF_INIT;
Packit Service 20376f
	git_buf file_path = GIT_BUF_INIT;
Packit Service 20376f
	struct merge_msg_entry *entries;
Packit Service 20376f
	git_vector matching = GIT_VECTOR_INIT;
Packit Service 20376f
	size_t i;
Packit Service 20376f
	char sep = 0;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	assert(repo && heads);
Packit Service 20376f
Packit Service 20376f
	entries = git__calloc(heads_len, sizeof(struct merge_msg_entry));
Packit Service 20376f
	GITERR_CHECK_ALLOC(entries);
Packit Service 20376f
Packit Service 20376f
	if (git_vector_init(&matching, heads_len, NULL) < 0) {
Packit Service 20376f
		git__free(entries);
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < heads_len; i++)
Packit Service 20376f
		entries[i].merge_head = heads[i];
Packit Service 20376f
Packit Service 20376f
	if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 ||
Packit Service 20376f
		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0 ||
Packit Service 20376f
		(error = git_filebuf_write(&file, "Merge ", 6)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * This is to emulate the format of MERGE_MSG by core git.
Packit Service 20376f
	 *
Packit Service 20376f
	 * Core git will write all the commits specified by OID, in the order
Packit Service 20376f
	 * provided, until the first named branch or tag is reached, at which
Packit Service 20376f
	 * point all branches will be written in the order provided, then all
Packit Service 20376f
	 * tags, then all remote tracking branches and finally all commits that
Packit Service 20376f
	 * were specified by OID that were not already written.
Packit Service 20376f
	 *
Packit Service 20376f
	 * Yes.  Really.
Packit Service 20376f
	 */
Packit Service 20376f
	for (i = 0; i < heads_len; i++) {
Packit Service 20376f
		if (!msg_entry_is_oid(&entries[i]))
Packit Service 20376f
			break;
Packit Service 20376f
Packit Service 20376f
		if ((error = git_filebuf_printf(&file,
Packit Service 20376f
			"%scommit '%s'", (i > 0) ? "; " : "",
Packit Service 20376f
			entries[i].merge_head->id_str)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
Packit Service 20376f
		entries[i].written = 1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (i)
Packit Service 20376f
		sep = ';';
Packit Service 20376f
Packit Service 20376f
	if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_branch)) < 0 ||
Packit Service 20376f
		(error = merge_msg_write_branches(&file, &matching, sep)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if (matching.length)
Packit Service 20376f
		sep =',';
Packit Service 20376f
Packit Service 20376f
	if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_tracking)) < 0 ||
Packit Service 20376f
		(error = merge_msg_write_tracking(&file, &matching, sep)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if (matching.length)
Packit Service 20376f
		sep =',';
Packit Service 20376f
Packit Service 20376f
	if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_tag)) < 0 ||
Packit Service 20376f
		(error = merge_msg_write_tags(&file, &matching, sep)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if (matching.length)
Packit Service 20376f
		sep =',';
Packit Service 20376f
Packit Service 20376f
	/* We should never be called with multiple remote branches, but handle
Packit Service 20376f
	 * it in case we are... */
Packit Service 20376f
	while ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_remote)) > 0) {
Packit Service 20376f
		if ((error = merge_msg_write_remotes(&file, &matching, sep)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
Packit Service 20376f
		if (matching.length)
Packit Service 20376f
			sep =',';
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < heads_len; i++) {
Packit Service 20376f
		if (merge_msg_entry_written(&entries[i]))
Packit Service 20376f
			continue;
Packit Service 20376f
Packit Service 20376f
		if ((error = git_filebuf_printf(&file, "; commit '%s'",
Packit Service 20376f
			entries[i].merge_head->id_str)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = git_filebuf_printf(&file, "\n")) < 0 ||
Packit Service 20376f
		(error = git_filebuf_commit(&file)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		git_filebuf_cleanup(&file;;
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&file_path);
Packit Service 20376f
Packit Service 20376f
	git_vector_free(&matching);
Packit Service 20376f
	git__free(entries);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge__setup(
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const git_annotated_commit *our_head,
Packit Service 20376f
	const git_annotated_commit *heads[],
Packit Service 20376f
	size_t heads_len)
Packit Service 20376f
{
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	assert (repo && our_head && heads);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository__set_orig_head(repo, git_annotated_commit_id(our_head))) == 0 &&
Packit Service 20376f
		(error = write_merge_head(repo, heads, heads_len)) == 0 &&
Packit Service 20376f
		(error = write_merge_mode(repo)) == 0) {
Packit Service 20376f
		error = write_merge_msg(repo, heads, heads_len);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/* Merge branches */
Packit Service 20376f
Packit Service 20376f
static int merge_ancestor_head(
Packit Service 20376f
	git_annotated_commit **ancestor_head,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const git_annotated_commit *our_head,
Packit Service 20376f
	const git_annotated_commit **their_heads,
Packit Service 20376f
	size_t their_heads_len)
Packit Service 20376f
{
Packit Service 20376f
	git_oid *oids, ancestor_oid;
Packit Service 20376f
	size_t i, alloc_len;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	assert(repo && our_head && their_heads);
Packit Service 20376f
Packit Service 20376f
	GITERR_CHECK_ALLOC_ADD(&alloc_len, their_heads_len, 1);
Packit Service 20376f
	oids = git__calloc(alloc_len, sizeof(git_oid));
Packit Service 20376f
	GITERR_CHECK_ALLOC(oids);
Packit Service 20376f
Packit Service 20376f
	git_oid_cpy(&oids[0], git_commit_id(our_head->commit));
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < their_heads_len; i++)
Packit Service 20376f
		git_oid_cpy(&oids[i + 1], git_annotated_commit_id(their_heads[i]));
Packit Service 20376f
Packit Service 20376f
	if ((error = git_merge_base_many(&ancestor_oid, repo, their_heads_len + 1, oids)) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	error = git_annotated_commit_lookup(ancestor_head, repo, &ancestor_oid);
Packit Service 20376f
Packit Service 20376f
on_error:
Packit Service 20376f
	git__free(oids);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
const char *merge_their_label(const char *branchname)
Packit Service 20376f
{
Packit Service 20376f
	const char *slash;
Packit Service 20376f
Packit Service 20376f
	if ((slash = strrchr(branchname, '/')) == NULL)
Packit Service 20376f
		return branchname;
Packit Service 20376f
Packit Service 20376f
	if (*(slash+1) == '\0')
Packit Service 20376f
		return "theirs";
Packit Service 20376f
Packit Service 20376f
	return slash+1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_normalize_checkout_opts(
Packit Service 20376f
	git_checkout_options *out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const git_checkout_options *given_checkout_opts,
Packit Service 20376f
	unsigned int checkout_strategy,
Packit Service 20376f
	git_annotated_commit *ancestor,
Packit Service 20376f
	const git_annotated_commit *our_head,
Packit Service 20376f
	const git_annotated_commit **their_heads,
Packit Service 20376f
	size_t their_heads_len)
Packit Service 20376f
{
Packit Service 20376f
	git_checkout_options default_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(repo);
Packit Service 20376f
Packit Service 20376f
	if (given_checkout_opts != NULL)
Packit Service 20376f
		memcpy(out, given_checkout_opts, sizeof(git_checkout_options));
Packit Service 20376f
	else
Packit Service 20376f
		memcpy(out, &default_checkout_opts, sizeof(git_checkout_options));
Packit Service 20376f
Packit Service 20376f
	out->checkout_strategy = checkout_strategy;
Packit Service 20376f
Packit Service 20376f
	if (!out->ancestor_label) {
Packit Service 20376f
		if (ancestor && ancestor->type == GIT_ANNOTATED_COMMIT_REAL)
Packit Service 20376f
			out->ancestor_label = git_commit_summary(ancestor->commit);
Packit Service 20376f
		else if (ancestor)
Packit Service 20376f
			out->ancestor_label = "merged common ancestors";
Packit Service 20376f
		else
Packit Service 20376f
			out->ancestor_label = "empty base";
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (!out->our_label) {
Packit Service 20376f
		if (our_head && our_head->ref_name)
Packit Service 20376f
			out->our_label = our_head->ref_name;
Packit Service 20376f
		else
Packit Service 20376f
			out->our_label = "ours";
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (!out->their_label) {
Packit Service 20376f
		if (their_heads_len == 1 && their_heads[0]->ref_name)
Packit Service 20376f
			out->their_label = merge_their_label(their_heads[0]->ref_name);
Packit Service 20376f
		else if (their_heads_len == 1)
Packit Service 20376f
			out->their_label = their_heads[0]->id_str;
Packit Service 20376f
		else
Packit Service 20376f
			out->their_label = "theirs";
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_check_index(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths)
Packit Service 20376f
{
Packit Service 20376f
	git_tree *head_tree = NULL;
Packit Service 20376f
	git_index *index_repo = NULL;
Packit Service 20376f
	git_iterator *iter_repo = NULL, *iter_new = NULL;
Packit Service 20376f
	git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
Packit Service 20376f
	git_diff *staged_diff_list = NULL, *index_diff_list = NULL;
Packit Service 20376f
	git_diff_delta *delta;
Packit Service 20376f
	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
Packit Service 20376f
	git_vector staged_paths = GIT_VECTOR_INIT;
Packit Service 20376f
	size_t i;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(merged_paths);
Packit Service 20376f
Packit Service 20376f
	*conflicts = 0;
Packit Service 20376f
Packit Service 20376f
	/* No staged changes may exist unless the change staged is identical to
Packit Service 20376f
	 * the result of the merge.  This allows one to apply to merge manually,
Packit Service 20376f
	 * then run merge.  Any other staged change would be overwritten by
Packit Service 20376f
	 * a reset merge.
Packit Service 20376f
	 */
Packit Service 20376f
	if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
Packit Service 20376f
		(error = git_repository_index(&index_repo, repo)) < 0 ||
Packit Service 20376f
		(error = git_diff_tree_to_index(&staged_diff_list, repo, head_tree, index_repo, &opts)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if (staged_diff_list->deltas.length == 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(&staged_diff_list->deltas, i, delta) {
Packit Service 20376f
		if ((error = git_vector_insert(&staged_paths, (char *)delta->new_file.path)) < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
Packit Service 20376f
	iter_opts.pathlist.strings = (char **)staged_paths.contents;
Packit Service 20376f
	iter_opts.pathlist.count = staged_paths.length;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_iterator_for_index(&iter_repo, repo, index_repo, &iter_opts)) < 0 ||
Packit Service 20376f
		(error = git_iterator_for_index(&iter_new, repo, index_new, &iter_opts)) < 0 ||
Packit Service 20376f
		(error = git_diff__from_iterators(&index_diff_list, repo, iter_repo, iter_new, &opts)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	*conflicts = index_diff_list->deltas.length;
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_tree_free(head_tree);
Packit Service 20376f
	git_index_free(index_repo);
Packit Service 20376f
	git_iterator_free(iter_repo);
Packit Service 20376f
	git_iterator_free(iter_new);
Packit Service 20376f
	git_diff_free(staged_diff_list);
Packit Service 20376f
	git_diff_free(index_diff_list);
Packit Service 20376f
	git_vector_free(&staged_paths);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_check_workdir(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths)
Packit Service 20376f
{
Packit Service 20376f
	git_diff *wd_diff_list = NULL;
Packit Service 20376f
	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(index_new);
Packit Service 20376f
Packit Service 20376f
	*conflicts = 0;
Packit Service 20376f
Packit Service 20376f
	/* We need to have merged at least 1 file for the possibility to exist to
Packit Service 20376f
	 * have conflicts with the workdir. Passing 0 as the pathspec count paramter
Packit Service 20376f
	 * will consider all files in the working directory, that is, we may detect
Packit Service 20376f
	 * a conflict if there were untracked files in the workdir prior to starting
Packit Service 20376f
	 * the merge. This typically happens when cherry-picking a commmit whose
Packit Service 20376f
	 * changes have already been applied.
Packit Service 20376f
	 */
Packit Service 20376f
	if (merged_paths->length == 0)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
Packit Service 20376f
Packit Service 20376f
	/* Workdir changes may exist iff they do not conflict with changes that
Packit Service 20376f
	 * will be applied by the merge (including conflicts).  Ensure that there
Packit Service 20376f
	 * are no changes in the workdir to these paths.
Packit Service 20376f
	 */
Packit Service 20376f
	opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH;
Packit Service 20376f
	opts.pathspec.count = merged_paths->length;
Packit Service 20376f
	opts.pathspec.strings = (char **)merged_paths->contents;
Packit Service 20376f
	opts.ignore_submodules = GIT_SUBMODULE_IGNORE_ALL;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_diff_index_to_workdir(&wd_diff_list, repo, NULL, &opts)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	*conflicts = wd_diff_list->deltas.length;
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_diff_free(wd_diff_list);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge__check_result(git_repository *repo, git_index *index_new)
Packit Service 20376f
{
Packit Service 20376f
	git_tree *head_tree = NULL;
Packit Service 20376f
	git_iterator *iter_head = NULL, *iter_new = NULL;
Packit Service 20376f
	git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
Packit Service 20376f
	git_diff *merged_list = NULL;
Packit Service 20376f
	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
Packit Service 20376f
	git_diff_delta *delta;
Packit Service 20376f
	git_vector paths = GIT_VECTOR_INIT;
Packit Service 20376f
	size_t i, index_conflicts = 0, wd_conflicts = 0, conflicts;
Packit Service 20376f
	const git_index_entry *e;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
Packit Service 20376f
		(error = git_iterator_for_tree(&iter_head, head_tree, &iter_opts)) < 0 ||
Packit Service 20376f
		(error = git_iterator_for_index(&iter_new, repo, index_new, &iter_opts)) < 0 ||
Packit Service 20376f
		(error = git_diff__from_iterators(&merged_list, repo, iter_head, iter_new, &opts)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	git_vector_foreach(&merged_list->deltas, i, delta) {
Packit Service 20376f
		if ((error = git_vector_insert(&paths, (char *)delta->new_file.path)) < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < git_index_entrycount(index_new); i++) {
Packit Service 20376f
		e = git_index_get_byindex(index_new, i);
Packit Service 20376f
Packit Service 20376f
		if (git_index_entry_is_conflict(e) &&
Packit Service 20376f
			(git_vector_last(&paths) == NULL ||
Packit Service 20376f
			strcmp(git_vector_last(&paths), e->path) != 0)) {
Packit Service 20376f
Packit Service 20376f
			if ((error = git_vector_insert(&paths, (char *)e->path)) < 0)
Packit Service 20376f
				goto done;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* Make sure the index and workdir state do not prevent merging */
Packit Service 20376f
	if ((error = merge_check_index(&index_conflicts, repo, index_new, &paths)) < 0 ||
Packit Service 20376f
		(error = merge_check_workdir(&wd_conflicts, repo, index_new, &paths)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if ((conflicts = index_conflicts + wd_conflicts) > 0) {
Packit Service 20376f
		giterr_set(GITERR_MERGE, "%" PRIuZ " uncommitted change%s would be overwritten by merge",
Packit Service 20376f
			conflicts, (conflicts != 1) ? "s" : "");
Packit Service 20376f
		error = GIT_ECONFLICT;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_vector_free(&paths);
Packit Service 20376f
	git_tree_free(head_tree);
Packit Service 20376f
	git_iterator_free(iter_head);
Packit Service 20376f
	git_iterator_free(iter_new);
Packit Service 20376f
	git_diff_free(merged_list);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge__append_conflicts_to_merge_msg(
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	git_index *index)
Packit Service 20376f
{
Packit Service 20376f
	git_filebuf file = GIT_FILEBUF_INIT;
Packit Service 20376f
	git_buf file_path = GIT_BUF_INIT;
Packit Service 20376f
	const char *last = NULL;
Packit Service 20376f
	size_t i;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	if (!git_index_has_conflicts(index))
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 ||
Packit Service 20376f
		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_APPEND, GIT_MERGE_FILE_MODE)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	git_filebuf_printf(&file, "\nConflicts:\n");
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < git_index_entrycount(index); i++) {
Packit Service 20376f
		const git_index_entry *e = git_index_get_byindex(index, i);
Packit Service 20376f
Packit Service 20376f
		if (!git_index_entry_is_conflict(e))
Packit Service 20376f
			continue;
Packit Service 20376f
Packit Service 20376f
		if (last == NULL || strcmp(e->path, last) != 0)
Packit Service 20376f
			git_filebuf_printf(&file, "\t%s\n", e->path);
Packit Service 20376f
Packit Service 20376f
		last = e->path;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	error = git_filebuf_commit(&file;;
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		git_filebuf_cleanup(&file;;
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&file_path);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_state_cleanup(git_repository *repo)
Packit Service 20376f
{
Packit Service 20376f
	const char *state_files[] = {
Packit Service 20376f
		GIT_MERGE_HEAD_FILE,
Packit Service 20376f
		GIT_MERGE_MODE_FILE,
Packit Service 20376f
		GIT_MERGE_MSG_FILE,
Packit Service 20376f
	};
Packit Service 20376f
Packit Service 20376f
	return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_heads(
Packit Service 20376f
	git_annotated_commit **ancestor_head_out,
Packit Service 20376f
	git_annotated_commit **our_head_out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const git_annotated_commit **their_heads,
Packit Service 20376f
	size_t their_heads_len)
Packit Service 20376f
{
Packit Service 20376f
	git_annotated_commit *ancestor_head = NULL, *our_head = NULL;
Packit Service 20376f
	git_reference *our_ref = NULL;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	*ancestor_head_out = NULL;
Packit Service 20376f
	*our_head_out = NULL;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository__ensure_not_bare(repo, "merge")) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_reference_lookup(&our_ref, repo, GIT_HEAD_FILE)) < 0 ||
Packit Service 20376f
		(error = git_annotated_commit_from_ref(&our_head, repo, our_ref)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if ((error = merge_ancestor_head(&ancestor_head, repo, our_head, their_heads, their_heads_len)) < 0) {
Packit Service 20376f
		if (error != GIT_ENOTFOUND)
Packit Service 20376f
			goto done;
Packit Service 20376f
Packit Service 20376f
		giterr_clear();
Packit Service 20376f
		error = 0;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*ancestor_head_out = ancestor_head;
Packit Service 20376f
	*our_head_out = our_head;
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	if (error < 0) {
Packit Service 20376f
		git_annotated_commit_free(ancestor_head);
Packit Service 20376f
		git_annotated_commit_free(our_head);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_reference_free(our_ref);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int merge_preference(git_merge_preference_t *out, git_repository *repo)
Packit Service 20376f
{
Packit Service 20376f
	git_config *config;
Packit Service 20376f
	const char *value;
Packit Service 20376f
	int bool_value, error = 0;
Packit Service 20376f
Packit Service 20376f
	*out = GIT_MERGE_PREFERENCE_NONE;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository_config_snapshot(&config, repo)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_config_get_string(&value, config, "merge.ff")) < 0) {
Packit Service 20376f
		if (error == GIT_ENOTFOUND) {
Packit Service 20376f
			giterr_clear();
Packit Service 20376f
			error = 0;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (git_config_parse_bool(&bool_value, value) == 0) {
Packit Service 20376f
		if (!bool_value)
Packit Service 20376f
			*out |= GIT_MERGE_PREFERENCE_NO_FASTFORWARD;
Packit Service 20376f
	} else {
Packit Service 20376f
		if (strcasecmp(value, "only") == 0)
Packit Service 20376f
			*out |= GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_config_free(config);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge_analysis(
Packit Service 20376f
	git_merge_analysis_t *analysis_out,
Packit Service 20376f
	git_merge_preference_t *preference_out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const git_annotated_commit **their_heads,
Packit Service 20376f
	size_t their_heads_len)
Packit Service 20376f
{
Packit Service 20376f
	git_annotated_commit *ancestor_head = NULL, *our_head = NULL;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	assert(analysis_out && preference_out && repo && their_heads);
Packit Service 20376f
Packit Service 20376f
	if (their_heads_len != 1) {
Packit Service 20376f
		giterr_set(GITERR_MERGE, "can only merge a single branch");
Packit Service 20376f
		error = -1;
Packit Service 20376f
		goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*analysis_out = GIT_MERGE_ANALYSIS_NONE;
Packit Service 20376f
Packit Service 20376f
	if ((error = merge_preference(preference_out, repo)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if (git_repository_head_unborn(repo)) {
Packit Service 20376f
		*analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_UNBORN;
Packit Service 20376f
		goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = merge_heads(&ancestor_head, &our_head, repo, their_heads, their_heads_len)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	/* We're up-to-date if we're trying to merge our own common ancestor. */
Packit Service 20376f
	if (ancestor_head && git_oid_equal(
Packit Service 20376f
		git_annotated_commit_id(ancestor_head), git_annotated_commit_id(their_heads[0])))
Packit Service 20376f
		*analysis_out |= GIT_MERGE_ANALYSIS_UP_TO_DATE;
Packit Service 20376f
Packit Service 20376f
	/* We're fastforwardable if we're our own common ancestor. */
Packit Service 20376f
	else if (ancestor_head && git_oid_equal(
Packit Service 20376f
		git_annotated_commit_id(ancestor_head), git_annotated_commit_id(our_head)))
Packit Service 20376f
		*analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_NORMAL;
Packit Service 20376f
Packit Service 20376f
	/* Otherwise, just a normal merge is possible. */
Packit Service 20376f
	else
Packit Service 20376f
		*analysis_out |= GIT_MERGE_ANALYSIS_NORMAL;
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_annotated_commit_free(ancestor_head);
Packit Service 20376f
	git_annotated_commit_free(our_head);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge(
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const git_annotated_commit **their_heads,
Packit Service 20376f
	size_t their_heads_len,
Packit Service 20376f
	const git_merge_options *merge_opts,
Packit Service 20376f
	const git_checkout_options *given_checkout_opts)
Packit Service 20376f
{
Packit Service 20376f
	git_reference *our_ref = NULL;
Packit Service 20376f
	git_checkout_options checkout_opts;
Packit Service 20376f
	git_annotated_commit *our_head = NULL, *base = NULL;
Packit Service 20376f
	git_index *index = NULL;
Packit Service 20376f
	git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
Packit Service 20376f
	unsigned int checkout_strategy;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	assert(repo && their_heads);
Packit Service 20376f
Packit Service 20376f
	if (their_heads_len != 1) {
Packit Service 20376f
		giterr_set(GITERR_MERGE, "can only merge a single branch");
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository__ensure_not_bare(repo, "merge")) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	checkout_strategy = given_checkout_opts ?
Packit Service 20376f
		given_checkout_opts->checkout_strategy :
Packit Service 20376f
		GIT_CHECKOUT_SAFE;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_indexwriter_init_for_operation(&indexwriter, repo,
Packit Service 20376f
		&checkout_strategy)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	/* Write the merge setup files to the repository. */
Packit Service 20376f
	if ((error = git_annotated_commit_from_head(&our_head, repo)) < 0 ||
Packit Service 20376f
		(error = git_merge__setup(repo, our_head, their_heads,
Packit Service 20376f
			their_heads_len)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	/* TODO: octopus */
Packit Service 20376f
Packit Service 20376f
	if ((error = merge_annotated_commits(&index, &base, repo, our_head,
Packit Service 20376f
			(git_annotated_commit *)their_heads[0], 0, merge_opts)) < 0 ||
Packit Service 20376f
		(error = git_merge__check_result(repo, index)) < 0 ||
Packit Service 20376f
		(error = git_merge__append_conflicts_to_merge_msg(repo, index)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	/* check out the merge results */
Packit Service 20376f
Packit Service 20376f
	if ((error = merge_normalize_checkout_opts(&checkout_opts, repo,
Packit Service 20376f
			given_checkout_opts, checkout_strategy,
Packit Service 20376f
			base, our_head, their_heads, their_heads_len)) < 0 ||
Packit Service 20376f
		(error = git_checkout_index(repo, index, &checkout_opts)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	error = git_indexwriter_commit(&indexwriter);
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		merge_state_cleanup(repo);
Packit Service 20376f
Packit Service 20376f
	git_indexwriter_cleanup(&indexwriter);
Packit Service 20376f
	git_index_free(index);
Packit Service 20376f
	git_annotated_commit_free(our_head);
Packit Service 20376f
	git_annotated_commit_free(base);
Packit Service 20376f
	git_reference_free(our_ref);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge_init_options(git_merge_options *opts, unsigned int version)
Packit Service 20376f
{
Packit Service 20376f
	GIT_INIT_STRUCTURE_FROM_TEMPLATE(
Packit Service 20376f
		opts, version, git_merge_options, GIT_MERGE_OPTIONS_INIT);
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge_file_init_input(git_merge_file_input *input, unsigned int version)
Packit Service 20376f
{
Packit Service 20376f
	GIT_INIT_STRUCTURE_FROM_TEMPLATE(
Packit Service 20376f
		input, version, git_merge_file_input, GIT_MERGE_FILE_INPUT_INIT);
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_merge_file_init_options(
Packit Service 20376f
	git_merge_file_options *opts, unsigned int version)
Packit Service 20376f
{
Packit Service 20376f
	GIT_INIT_STRUCTURE_FROM_TEMPLATE(
Packit Service 20376f
		opts, version, git_merge_file_options, GIT_MERGE_FILE_OPTIONS_INIT);
Packit Service 20376f
	return 0;
Packit Service 20376f
}