From: Junio C Hamano <junkio@cox.net>
To: git@vger.kernel.org
Subject: [PATCH 3/4] Allow specifying specialized merge-backend per path.
Date: Tue, 17 Apr 2007 01:08:37 -0700 [thread overview]
Message-ID: <11767973191314-git-send-email-junkio@cox.net> (raw)
In-Reply-To: <11767973183627-git-send-email-junkio@cox.net>
This allows 'merge' attribute to control how the file-level
three-way merge is done per path.
- If you set 'merge' to true or leave unspecified, we use the
built-in 3-way xdl-merge.
- If you set 'merge' to false, the merge result is 'ours'. But
this still leaves the path conflicted, so that the mess can
be sorted out by the user. This is probably useful for
binary files.
- 'merge=union' (this is the first example of a string valued
attribute, introduced in the previous one) uses the "union"
merge. The "union" merge takes lines in conflicted hunks
from both sides, which is useful for line-oriented files such
as .gitignore.
Currently there is no way to specify random programs but it
should be trivial for motivated contributors to add later.
There is one caveat, though. ll_merge() is called for both
internal ancestor merge and the outer "final" merge. I think an
interactive custom per-path merge backend should refrain from
going interactive when performing an internal merge (you can
tell it by checking call_depth) and instead just call either
ll_xdl_merge() if the content is text, or call ll_ours_merge()
otherwise.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
merge-recursive.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 126 insertions(+), 7 deletions(-)
diff --git a/merge-recursive.c b/merge-recursive.c
index 4eb62cf..a6c08a1 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,124 @@ 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));
+ 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_ours_merge(mmfile_t *orig,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
+ mmbuffer_t *result)
+{
+ /*
+ * Pretend the tentative merge result is "ours",
+ * but still return "conflicted merge" status.
+ */
+ result->ptr = src1->ptr;
+ result->size = src1->size;
+ src1->ptr = NULL;
+ return 1;
+}
+
+static struct {
+ const char *name;
+ ll_merge_fn fn;
+} ll_merge_fns[] = {
+ { "3way", ll_xdl_merge },
+ { "ours", ll_ours_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_ours_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 +786,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 +798,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);
--
1.5.1.1.821.g88bdb
next prev parent reply other threads:[~2007-04-17 8:08 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-04-17 8:08 [PATCH 1/4] Allow more than true/false to attributes Junio C Hamano
2007-04-17 8:08 ` [PATCH 2/4] merge-recursive: separate out xdl_merge() interface Junio C Hamano
2007-04-17 8:08 ` Junio C Hamano [this message]
2007-04-17 9:20 ` [PATCH 3/4] Allow specifying specialized merge-backend per path Junio C Hamano
2007-04-17 15:16 ` Linus Torvalds
2007-04-17 8:08 ` [PATCH 4/4] Add a demonstration/test of customized merge Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=11767973191314-git-send-email-junkio@cox.net \
--to=junkio@cox.net \
--cc=git@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).