summaryrefslogtreecommitdiff
path: root/src/merge_file.c
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@microsoft.com>2013-04-01 22:16:21 -0500
committerEdward Thomson <ethomson@edwardthomson.com>2013-04-30 15:31:31 -0500
commitbec65a5e994bc4701216c9ca2c7dae83770b3edc (patch)
tree1e941e76b80245dcfb4853d7c6dc231c7aab7699 /src/merge_file.c
parent5e2261aca86310aa180eab5ccdc345b1539b024d (diff)
downloadlibgit2-bec65a5e994bc4701216c9ca2c7dae83770b3edc.tar.gz
merge!
Diffstat (limited to 'src/merge_file.c')
-rw-r--r--src/merge_file.c175
1 files changed, 175 insertions, 0 deletions
diff --git a/src/merge_file.c b/src/merge_file.c
new file mode 100644
index 000000000..4b3f3730b
--- /dev/null
+++ b/src/merge_file.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "repository.h"
+#include "merge_file.h"
+
+#include "git2/repository.h"
+#include "git2/object.h"
+#include "git2/index.h"
+
+#include "xdiff/xdiff.h"
+
+#define GIT_MERGE_FILE_SIDE_EXISTS(X) ((X)->mode != 0)
+
+GIT_INLINE(const char *) merge_file_best_path(
+ const git_merge_file_input *ancestor,
+ const git_merge_file_input *ours,
+ const git_merge_file_input *theirs)
+{
+ if (!GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
+ if (strcmp(ours->path, theirs->path) == 0)
+ return ours->path;
+
+ return NULL;
+ }
+
+ if (strcmp(ancestor->path, ours->path) == 0)
+ return theirs->path;
+ else if(strcmp(ancestor->path, theirs->path) == 0)
+ return ours->path;
+
+ return NULL;
+}
+
+GIT_INLINE(int) merge_file_best_mode(
+ const git_merge_file_input *ancestor,
+ const git_merge_file_input *ours,
+ const git_merge_file_input *theirs)
+{
+ /*
+ * If ancestor didn't exist and either ours or theirs is executable,
+ * assume executable. Otherwise, if any mode changed from the ancestor,
+ * use that one.
+ */
+ if (GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
+ if (ours->mode == GIT_FILEMODE_BLOB_EXECUTABLE ||
+ theirs->mode == GIT_FILEMODE_BLOB_EXECUTABLE)
+ return GIT_FILEMODE_BLOB_EXECUTABLE;
+
+ return GIT_FILEMODE_BLOB;
+ }
+
+ if (ancestor->mode == ours->mode)
+ return theirs->mode;
+ else if(ancestor->mode == theirs->mode)
+ return ours->mode;
+
+ return 0;
+}
+
+int git_merge_file_input_from_index_entry(
+ git_merge_file_input *input,
+ git_repository *repo,
+ const git_index_entry *entry)
+{
+ git_odb *odb = NULL;
+ int error = 0;
+
+ assert(input && repo && entry);
+
+ if (entry->mode == 0)
+ return 0;
+
+ if ((error = git_repository_odb(&odb, repo)) < 0 ||
+ (error = git_odb_read(&input->odb_object, odb, &entry->oid)) < 0)
+ goto done;
+
+ input->mode = entry->mode;
+ input->path = git__strdup(entry->path);
+ input->mmfile.size = git_odb_object_size(input->odb_object);
+ input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object);
+
+ if (input->label == NULL)
+ input->label = entry->path;
+
+done:
+ git_odb_free(odb);
+
+ return error;
+}
+
+int git_merge_file_input_from_diff_file(
+ git_merge_file_input *input,
+ git_repository *repo,
+ const git_diff_file *file)
+{
+ git_odb *odb = NULL;
+ int error = 0;
+
+ assert(input && repo && file);
+
+ if (file->mode == 0)
+ return 0;
+
+ if ((error = git_repository_odb(&odb, repo)) < 0 ||
+ (error = git_odb_read(&input->odb_object, odb, &file->oid)) < 0)
+ goto done;
+
+ input->mode = file->mode;
+ input->path = git__strdup(file->path);
+ input->mmfile.size = git_odb_object_size(input->odb_object);
+ input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object);
+
+ if (input->label == NULL)
+ input->label = file->path;
+
+done:
+ git_odb_free(odb);
+
+ return error;
+}
+
+int git_merge_files(
+ git_merge_file_result *out,
+ git_merge_file_input *ancestor,
+ git_merge_file_input *ours,
+ git_merge_file_input *theirs,
+ git_merge_automerge_flags flags)
+{
+ xmparam_t xmparam;
+ mmbuffer_t mmbuffer;
+ int xdl_result;
+ int error = 0;
+
+ assert(out && ancestor && ours && theirs);
+
+ memset(out, 0x0, sizeof(git_merge_file_result));
+
+ if (!GIT_MERGE_FILE_SIDE_EXISTS(ours) || !GIT_MERGE_FILE_SIDE_EXISTS(theirs))
+ return 0;
+
+ memset(&xmparam, 0x0, sizeof(xmparam_t));
+ xmparam.ancestor = ancestor->label;
+ xmparam.file1 = ours->label;
+ xmparam.file2 = theirs->label;
+
+ out->path = merge_file_best_path(ancestor, ours, theirs);
+ out->mode = merge_file_best_mode(ancestor, ours, theirs);
+
+ if (flags == GIT_MERGE_AUTOMERGE_FAVOR_OURS)
+ xmparam.favor = XDL_MERGE_FAVOR_OURS;
+
+ if (flags == GIT_MERGE_AUTOMERGE_FAVOR_THEIRS)
+ xmparam.favor = XDL_MERGE_FAVOR_THEIRS;
+
+ if ((xdl_result = xdl_merge(&ancestor->mmfile, &ours->mmfile,
+ &theirs->mmfile, &xmparam, &mmbuffer)) < 0) {
+ giterr_set(GITERR_MERGE, "Failed to merge files.");
+ error = -1;
+ goto done;
+ }
+
+ out->automergeable = (xdl_result == 0);
+ out->data = (unsigned char *)mmbuffer.ptr;
+ out->len = mmbuffer.size;
+
+done:
+ return error;
+}
+