summaryrefslogtreecommitdiff
path: root/merge-recursive.c
diff options
context:
space:
mode:
Diffstat (limited to 'merge-recursive.c')
-rw-r--r--merge-recursive.c136
1 files changed, 129 insertions, 7 deletions
diff --git a/merge-recursive.c b/merge-recursive.c
index 4eb62cf..3b34401 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -15,6 +15,7 @@
#include "unpack-trees.h"
#include "path-list.h"
#include "xdiff-interface.h"
+#include "attr.h"
static int subtree_merge;
@@ -659,6 +660,127 @@ static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
mm->size = size;
}
+/* Low-level merge functions */
+typedef int (*ll_merge_fn)(mmfile_t *orig,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
+ mmbuffer_t *result);
+
+static int ll_xdl_merge(mmfile_t *orig,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
+ mmbuffer_t *result)
+{
+ xpparam_t xpp;
+
+ memset(&xpp, 0, sizeof(xpp));
+ return xdl_merge(orig,
+ src1, name1,
+ src2, name2,
+ &xpp, XDL_MERGE_ZEALOUS,
+ result);
+}
+
+static int ll_union_merge(mmfile_t *orig,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
+ mmbuffer_t *result)
+{
+ char *src, *dst;
+ long size;
+ const int marker_size = 7;
+
+ int status = ll_xdl_merge(orig, src1, NULL, src2, NULL, result);
+ if (status <= 0)
+ return status;
+ size = result->size;
+ src = dst = result->ptr;
+ while (size) {
+ char ch;
+ if ((marker_size < size) &&
+ (*src == '<' || *src == '=' || *src == '>')) {
+ int i;
+ ch = *src;
+ for (i = 0; i < marker_size; i++)
+ if (src[i] != ch)
+ goto not_a_marker;
+ if (src[marker_size] != '\n')
+ goto not_a_marker;
+ src += marker_size + 1;
+ size -= marker_size + 1;
+ continue;
+ }
+ not_a_marker:
+ do {
+ ch = *src++;
+ *dst++ = ch;
+ size--;
+ } while (ch != '\n' && size);
+ }
+ result->size = dst - result->ptr;
+ return 0;
+}
+
+static int ll_binary_merge(mmfile_t *orig,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
+ mmbuffer_t *result)
+{
+ /*
+ * The tentative merge result is "ours" for the final round,
+ * or common ancestor for an internal merge. Still return
+ * "conflicted merge" status.
+ */
+ mmfile_t *stolen = index_only ? orig : src1;
+
+ result->ptr = stolen->ptr;
+ result->size = stolen->size;
+ stolen->ptr = NULL;
+ return 1;
+}
+
+static struct {
+ const char *name;
+ ll_merge_fn fn;
+} ll_merge_fns[] = {
+ { "text", ll_xdl_merge },
+ { "binary", ll_binary_merge },
+ { "union", ll_union_merge },
+ { NULL, NULL },
+};
+
+static ll_merge_fn find_ll_merge_fn(void *merge_attr)
+{
+ const char *name;
+ int i;
+
+ if (ATTR_TRUE(merge_attr) || ATTR_UNSET(merge_attr))
+ return ll_xdl_merge;
+ else if (ATTR_FALSE(merge_attr))
+ return ll_binary_merge;
+
+ /* Otherwise merge_attr may name the merge function */
+ name = merge_attr;
+ for (i = 0; ll_merge_fns[i].name; i++)
+ if (!strcmp(ll_merge_fns[i].name, name))
+ return ll_merge_fns[i].fn;
+
+ /* default to the 3-way */
+ return ll_xdl_merge;
+}
+
+static void *git_path_check_merge(const char *path)
+{
+ static struct git_attr_check attr_merge_check;
+
+ if (!attr_merge_check.attr)
+ attr_merge_check.attr = git_attr("merge", 5);
+
+ if (git_checkattr(path, 1, &attr_merge_check))
+ return ATTR__UNSET;
+ return attr_merge_check.value;
+}
+
static int ll_merge(mmbuffer_t *result_buf,
struct diff_filespec *o,
struct diff_filespec *a,
@@ -667,9 +789,10 @@ static int ll_merge(mmbuffer_t *result_buf,
const char *branch2)
{
mmfile_t orig, src1, src2;
- xpparam_t xpp;
char *name1, *name2;
int merge_status;
+ void *merge_attr;
+ ll_merge_fn fn;
name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
@@ -678,12 +801,11 @@ static int ll_merge(mmbuffer_t *result_buf,
fill_mm(a->sha1, &src1);
fill_mm(b->sha1, &src2);
- memset(&xpp, 0, sizeof(xpp));
- merge_status = xdl_merge(&orig,
- &src1, name1,
- &src2, name2,
- &xpp, XDL_MERGE_ZEALOUS,
- result_buf);
+ merge_attr = git_path_check_merge(a->path);
+ fn = find_ll_merge_fn(merge_attr);
+
+ merge_status = fn(&orig, &src1, name1, &src2, name2, result_buf);
+
free(name1);
free(name2);
free(orig.ptr);