@@ -34,6 +34,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")),
OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3),
+ OPT_SET_INT(0, "zdiff3", &xmp.style, N_("use a zealous diff3 based merge"),
+ XDL_MERGE_ZEALOUS_DIFF3),
OPT_SET_INT(0, "ours", &xmp.favor, N_("for conflicts, use our version"),
XDL_MERGE_FAVOR_OURS),
OPT_SET_INT(0, "theirs", &xmp.favor, N_("for conflicts, use their version"),
@@ -1566,7 +1566,7 @@ _git_checkout ()
case "$cur" in
--conflict=*)
- __gitcomp "diff3 merge" "" "${cur##--conflict=}"
+ __gitcomp "diff3 merge zdiff3" "" "${cur##--conflict=}"
;;
--*)
__gitcomp_builtin checkout
@@ -2446,7 +2446,7 @@ _git_switch ()
case "$cur" in
--conflict=*)
- __gitcomp "diff3 merge" "" "${cur##--conflict=}"
+ __gitcomp "diff3 merge zdiff3" "" "${cur##--conflict=}"
;;
--*)
__gitcomp_builtin switch
@@ -2887,7 +2887,7 @@ _git_restore ()
case "$cur" in
--conflict=*)
- __gitcomp "diff3 merge" "" "${cur##--conflict=}"
+ __gitcomp "diff3 merge zdiff3" "" "${cur##--conflict=}"
;;
--source=*)
__git_complete_refs --cur="${cur##--source=}"
@@ -211,4 +211,94 @@ test_expect_success 'rebase --apply describes fake ancestor base' '
)
'
+test_setup_zdiff3 () {
+ test_create_repo zdiff3 &&
+ (
+ cd zdiff3 &&
+
+ test_write_lines 1 2 3 4 5 6 7 8 9 >basic &&
+ test_write_lines 1 2 3 AA 4 5 BB 6 7 8 >middle-common &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 >interesting &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 >evil &&
+
+ git add basic middle-common interesting evil &&
+ git commit -m base &&
+
+ git branch left &&
+ git branch right &&
+
+ git checkout left &&
+ test_write_lines 1 2 3 4 A B C D E 7 8 9 >basic &&
+ test_write_lines 1 2 3 CC 4 5 DD 6 7 8 >middle-common &&
+ test_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >interesting &&
+ test_write_lines 1 2 3 4 X A B C 7 8 9 >evil &&
+ git add -u &&
+ git commit -m letters &&
+
+ git checkout right &&
+ test_write_lines 1 2 3 4 A X C Y E 7 8 9 >basic &&
+ test_write_lines 1 2 3 EE 4 5 FF 6 7 8 >middle-common &&
+ test_write_lines 1 2 3 4 A B C 5 6 G H I J 7 8 9 >interesting &&
+ test_write_lines 1 2 3 4 Y A B C B C 7 8 9 >evil &&
+ git add -u &&
+ git commit -m permuted
+ )
+}
+
+test_expect_success 'check zdiff3 markers' '
+ test_setup_zdiff3 &&
+ (
+ cd zdiff3 &&
+
+ git checkout left^0 &&
+
+ base=$(git rev-parse --short HEAD^1) &&
+ test_must_fail git -c merge.conflictstyle=zdiff3 merge -s recursive right^0 &&
+
+ test_write_lines 1 2 3 4 A \
+ "<<<<<<< HEAD" B C D \
+ "||||||| $base" 5 6 \
+ ======= X C Y \
+ ">>>>>>> right^0" \
+ E 7 8 9 \
+ >expect &&
+ test_cmp expect basic &&
+
+ test_write_lines 1 2 3 \
+ "<<<<<<< HEAD" CC \
+ "||||||| $base" AA \
+ ======= EE \
+ ">>>>>>> right^0" \
+ 4 5 \
+ "<<<<<<< HEAD" DD \
+ "||||||| $base" BB \
+ ======= FF \
+ ">>>>>>> right^0" \
+ 6 7 8 \
+ >expect &&
+ test_cmp expect middle-common &&
+
+ test_write_lines 1 2 3 4 A B C \
+ "<<<<<<< HEAD" D E F \
+ "||||||| $base" 5 6 \
+ ======= 5 6 \
+ ">>>>>>> right^0" \
+ G H I J 7 8 9 \
+ >expect &&
+ test_cmp expect interesting &&
+
+ # Not passing this one yet; the common "B C" lines is still
+ # being left in the conflict blocks on the left and right
+ # sides.
+ test_write_lines 1 2 3 4 \
+ "<<<<<<< HEAD" X A \
+ "||||||| $base" 5 6 \
+ ======= Y A B C \
+ ">>>>>>> right^0" \
+ B C 7 8 9 \
+ >expect &&
+ test_cmp expect evil
+ )
+'
+
test_done
@@ -313,6 +313,8 @@ int git_xmerge_config(const char *var, const char *value, void *cb)
die("'%s' is not a boolean", var);
if (!strcmp(value, "diff3"))
git_xmerge_style = XDL_MERGE_DIFF3;
+ else if (!strcmp(value, "zdiff3"))
+ git_xmerge_style = XDL_MERGE_ZEALOUS_DIFF3;
else if (!strcmp(value, "merge"))
git_xmerge_style = 0;
/*
@@ -66,6 +66,7 @@ extern "C" {
/* merge output styles */
#define XDL_MERGE_DIFF3 1
+#define XDL_MERGE_ZEALOUS_DIFF3 2
typedef struct s_mmfile {
char *ptr;
@@ -230,7 +230,7 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
size += xdl_recs_copy(xe1, m->i1, m->chg1, needs_cr, 1,
dest ? dest + size : NULL);
- if (style == XDL_MERGE_DIFF3) {
+ if (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_DIFF3) {
/* Shared preimage */
if (!dest) {
size += marker_size + 1 + needs_cr + marker3_size;
@@ -322,6 +322,40 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
return size;
}
+static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)
+{
+ return xdl_recmatch(rec1->ptr, rec1->size,
+ rec2->ptr, rec2->size, flags);
+}
+
+/*
+ * Remove any common lines from the beginning and end of the conflicted region.
+ */
+static void xdl_refine_zdiff3_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
+ xpparam_t const *xpp)
+{
+ xrecord_t **rec1 = xe1->xdf2.recs, **rec2 = xe2->xdf2.recs;
+ for (; m; m = m->next) {
+ /* let's handle just the conflicts */
+ if (m->mode)
+ continue;
+
+ while(m->chg1 && m->chg2 &&
+ recmatch(rec1[m->i1], rec2[m->i2], xpp->flags)) {
+ m->chg1--;
+ m->chg2--;
+ m->i1++;
+ m->i2++;
+ }
+ while (m->chg1 && m->chg2 &&
+ recmatch(rec1[m->i1 + m->chg1 - 1],
+ rec2[m->i2 + m->chg2 - 1], xpp->flags)) {
+ m->chg1--;
+ m->chg2--;
+ }
+ }
+}
+
/*
* Sometimes, changes are not quite identical, but differ in only a few
* lines. Try hard to show only these few lines as conflicting.
@@ -482,7 +516,22 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
int style = xmp->style;
int favor = xmp->favor;
- if (style == XDL_MERGE_DIFF3) {
+ /*
+ * XDL_MERGE_DIFF3 does not attempt to refine conflicts by looking
+ * at common areas of sides 1 & 2, because the base (side 0) does
+ * not match and is being shown. Similarly, simplification of
+ * non-conflicts is also skipped due to the skipping of conflict
+ * refinement.
+ *
+ * XDL_MERGE_ZEALOUS_DIFF3, on the other hand, will attempt to
+ * refine conflicts looking for common areas of sides 1 & 2.
+ * However, since the base is being shown and does not match,
+ * it will only look for common areas at the beginning or end
+ * of the conflict block. Since XDL_MERGE_ZEALOUS_DIFF3's
+ * conflict refinement is much more limited in this fashion, the
+ * conflict simplification will be skipped.
+ */
+ if (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_DIFF3) {
/*
* "diff3 -m" output does not make sense for anything
* more aggressive than XDL_MERGE_EAGER.
@@ -603,10 +652,12 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
if (!changes)
changes = c;
/* refine conflicts */
- if (XDL_MERGE_ZEALOUS <= level &&
- (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||
- xdl_simplify_non_conflicts(xe1, changes,
- XDL_MERGE_ZEALOUS < level) < 0)) {
+ if (style == XDL_MERGE_ZEALOUS_DIFF3) {
+ xdl_refine_zdiff3_conflicts(xe1, xe2, changes, xpp);
+ } else if (XDL_MERGE_ZEALOUS <= level &&
+ (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||
+ xdl_simplify_non_conflicts(xe1, changes,
+ XDL_MERGE_ZEALOUS < level) < 0)) {
xdl_cleanup_merge(changes);
return -1;
}