xdiff-merge: optionally show conflicts in "diff3 -m" style
authorJunio C Hamano <gitster@pobox.com>
Thu, 28 Aug 2008 08:10:04 +0000 (01:10 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 31 Aug 2008 02:41:43 +0000 (19:41 -0700)
When showing conflicting merges, we traditionally followed RCS's merge
output format.  The output shows:

 <<<<<<<
 postimage from one side;
 =======
 postimage of the other side; and
 >>>>>>>

Some poeple find it easier to be able to understand what is going on when
they can view the common ancestor's version, which is used by "diff3 -m",
which shows:

 <<<<<<<
 postimage from one side;
 |||||||
 shared preimage;
 =======
 postimage of the other side; and
 >>>>>>>

This is an initial step to bring that as an optional feature to git.
Only "git merge-file" has been converted, with "--diff3" option.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin-merge-file.c
t/t6023-merge-file.sh
xdiff/xdiff.h
xdiff/xmerge.c

index 3605960c2d9692514a6df0f344f3c3269cf1de3c..5b4f020e38d3f97e05c1d8b1c465e8fcc2a7b851 100644 (file)
@@ -4,7 +4,7 @@
 #include "xdiff-interface.h"
 
 static const char merge_file_usage[] =
-"git merge-file [-p | --stdout] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
+"git merge-file [-p | --stdout] [--diff3] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
 
 int cmd_merge_file(int argc, const char **argv, const char *prefix)
 {
@@ -13,6 +13,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        mmbuffer_t result = {NULL, 0};
        xpparam_t xpp = {XDF_NEED_MINIMAL};
        int ret = 0, i = 0, to_stdout = 0;
+       int merge_level = XDL_MERGE_ZEALOUS_ALNUM;
+       int merge_style = 0;
 
        while (argc > 4) {
                if (!strcmp(argv[1], "-L") && i < 3) {
@@ -25,6 +27,10 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
                else if (!strcmp(argv[1], "-q") ||
                                !strcmp(argv[1], "--quiet"))
                        freopen("/dev/null", "w", stderr);
+               else if (!strcmp(argv[1], "--diff3")) {
+                       merge_style = XDL_MERGE_DIFF3;
+                       merge_level = XDL_MERGE_EAGER;
+               }
                else
                        usage(merge_file_usage);
                argc--;
@@ -46,7 +52,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        }
 
        ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
-                       &xpp, XDL_MERGE_ZEALOUS_ALNUM, &result);
+                       &xpp, merge_level | merge_style, &result);
 
        for (i = 0; i < 3; i++)
                free(mmfs[i].ptr);
index f674c48cab3e80d63b5a5831c667b8e08b542905..20786ce79da82456ba7603b868e8ca1130fe06bd 100755 (executable)
@@ -161,4 +161,41 @@ test_expect_success 'ZEALOUS_ALNUM' '
 
 '
 
+cat >expect <<\EOF
+Dominus regit me,
+<<<<<<< new8.txt
+et nihil mihi deerit;
+
+
+
+
+In loco pascuae ibi me collocavit;
+super aquam refectionis educavit me.
+|||||||
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+=======
+et nihil mihi deerit,
+
+
+
+
+In loco pascuae ibi me collocavit --
+super aquam refectionis educavit me,
+>>>>>>> new9.txt
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam TU mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+
+test_expect_success '"diff3 -m" style output' '
+       test_must_fail git merge-file -p --diff3 \
+               new8.txt new5.txt new9.txt >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 413082e1fdf537d230a0f58940cee7466b965d0e..deebe02cd3489ef3ce94b407ff9c3de4c21a0eb1 100644 (file)
@@ -50,10 +50,16 @@ extern "C" {
 #define XDL_BDOP_CPY 2
 #define XDL_BDOP_INSB 3
 
+/* merge simplification levels */
 #define XDL_MERGE_MINIMAL 0
 #define XDL_MERGE_EAGER 1
 #define XDL_MERGE_ZEALOUS 2
 #define XDL_MERGE_ZEALOUS_ALNUM 3
+#define XDL_MERGE_LEVEL_MASK 0x0f
+
+/* merge output styles */
+#define XDL_MERGE_DIFF3 0x8000
+#define XDL_MERGE_STYLE_MASK 0x8000
 
 typedef struct s_mmfile {
        char *ptr;
index 6ffaa4f9e5ae10e9690a611fc849e3a95a256c10..29cdbea1ee32a71fa6f6ddffb4866ec3b67d6150 100644 (file)
@@ -30,17 +30,32 @@ typedef struct s_xdmerge {
         * 2 = no conflict, take second.
         */
        int mode;
+       /*
+        * These point at the respective postimages.  E.g. <i1,chg1> is
+        * how side #1 wants to change the common ancestor; if there is no
+        * overlap, lines before i1 in the postimage of side #1 appear
+        * in the merge result as a region touched by neither side.
+        */
        long i1, i2;
        long chg1, chg2;
+       /*
+        * These point at the preimage; of course there is just one
+        * preimage, that is from the shared common ancestor.
+        */
+       long i0;
+       long chg0;
 } xdmerge_t;
 
 static int xdl_append_merge(xdmerge_t **merge, int mode,
-               long i1, long chg1, long i2, long chg2)
+                           long i0, long chg0,
+                           long i1, long chg1,
+                           long i2, long chg2)
 {
        xdmerge_t *m = *merge;
        if (m && (i1 <= m->i1 + m->chg1 || i2 <= m->i2 + m->chg2)) {
                if (mode != m->mode)
                        m->mode = 0;
+               m->chg0 = i0 + chg0 - m->i0;
                m->chg1 = i1 + chg1 - m->i1;
                m->chg2 = i2 + chg2 - m->i2;
        } else {
@@ -49,6 +64,8 @@ static int xdl_append_merge(xdmerge_t **merge, int mode,
                        return -1;
                m->next = NULL;
                m->mode = mode;
+               m->i0 = i0;
+               m->chg0 = chg0;
                m->i1 = i1;
                m->chg1 = chg1;
                m->i2 = i2;
@@ -91,11 +108,13 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,
        return 0;
 }
 
-static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
 {
-       xrecord_t **recs = xe->xdf2.recs + i;
+       xrecord_t **recs;
        int size = 0;
 
+       recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;
+
        if (count < 1)
                return 0;
 
@@ -113,9 +132,19 @@ static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
        return size;
 }
 
+static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+{
+       return xdl_recs_copy_0(0, xe, i, count, add_nl, dest);
+}
+
+static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+{
+       return xdl_recs_copy_0(1, xe, i, count, add_nl, dest);
+}
+
 static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
                              xdfenv_t *xe2, const char *name2,
-                             int size, int i,
+                             int size, int i, int style,
                              xdmerge_t *m, char *dest)
 {
        const int marker_size = 7;
@@ -143,6 +172,20 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
        /* Postimage from side #1 */
        size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
                              dest ? dest + size : NULL);
+
+       if (style == XDL_MERGE_DIFF3) {
+               /* Shared preimage */
+               if (!dest) {
+                       size += marker_size + 1;
+               } else {
+                       for (j = 0; j < marker_size; j++)
+                               dest[size++] = '|';
+                       dest[size++] = '\n';
+               }
+               size += xdl_orig_copy(xe1, m->i0, m->chg0, 1,
+                                     dest ? dest + size : NULL);
+       }
+
        if (!dest) {
                size += marker_size + 1;
        } else {
@@ -170,14 +213,15 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
 }
 
 static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
-               xdfenv_t *xe2, const char *name2, xdmerge_t *m, char *dest)
+                                xdfenv_t *xe2, const char *name2,
+                                xdmerge_t *m, char *dest, int style)
 {
        int size, i;
 
        for (size = i = 0; m; m = m->next) {
                if (m->mode == 0)
                        size = fill_conflict_hunk(xe1, name1, xe2, name2,
-                                                 size, i, m, dest);
+                                                 size, i, style, m, dest);
                else if (m->mode == 1)
                        size += xdl_recs_copy(xe1, i, m->i1 + m->chg1 - i, 0,
                                              dest ? dest + size : NULL);
@@ -342,9 +386,11 @@ static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,
  */
 static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
                xdfenv_t *xe2, xdchange_t *xscr2, const char *name2,
-               int level, xpparam_t const *xpp, mmbuffer_t *result) {
+               int flags, xpparam_t const *xpp, mmbuffer_t *result) {
        xdmerge_t *changes, *c;
-       int i1, i2, chg1, chg2;
+       int i0, i1, i2, chg0, chg1, chg2;
+       int level = flags & XDL_MERGE_LEVEL_MASK;
+       int style = flags & XDL_MERGE_STYLE_MASK;
 
        c = changes = NULL;
 
@@ -352,11 +398,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
                if (!changes)
                        changes = c;
                if (xscr1->i1 + xscr1->chg1 < xscr2->i1) {
+                       i0 = xscr1->i1;
                        i1 = xscr1->i2;
                        i2 = xscr2->i2 - xscr2->i1 + xscr1->i1;
+                       chg0 = xscr1->chg1;
                        chg1 = xscr1->chg2;
                        chg2 = xscr1->chg1;
-                       if (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) {
+                       if (xdl_append_merge(&c, 1,
+                                            i0, chg0, i1, chg1, i2, chg2)) {
                                xdl_cleanup_merge(changes);
                                return -1;
                        }
@@ -364,11 +413,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
                        continue;
                }
                if (xscr2->i1 + xscr2->chg1 < xscr1->i1) {
+                       i0 = xscr2->i1;
                        i1 = xscr1->i2 - xscr1->i1 + xscr2->i1;
                        i2 = xscr2->i2;
+                       chg0 = xscr2->chg1;
                        chg1 = xscr2->chg1;
                        chg2 = xscr2->chg2;
-                       if (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) {
+                       if (xdl_append_merge(&c, 2,
+                                            i0, chg0, i1, chg1, i2, chg2)) {
                                xdl_cleanup_merge(changes);
                                return -1;
                        }
@@ -385,19 +437,26 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
                        int off = xscr1->i1 - xscr2->i1;
                        int ffo = off + xscr1->chg1 - xscr2->chg1;
 
+                       i0 = xscr1->i1;
                        i1 = xscr1->i2;
                        i2 = xscr2->i2;
-                       if (off > 0)
+                       if (off > 0) {
+                               i0 -= off;
                                i1 -= off;
+                       }
                        else
                                i2 += off;
+                       chg0 = xscr1->i1 + xscr1->chg1 - i0;
                        chg1 = xscr1->i2 + xscr1->chg2 - i1;
                        chg2 = xscr2->i2 + xscr2->chg2 - i2;
                        if (ffo > 0)
                                chg2 += ffo;
-                       else
+                       else {
+                               chg0 -= ffo;
                                chg1 -= ffo;
-                       if (xdl_append_merge(&c, 0, i1, chg1, i2, chg2)) {
+                       }
+                       if (xdl_append_merge(&c, 0,
+                                            i0, chg0, i1, chg1, i2, chg2)) {
                                xdl_cleanup_merge(changes);
                                return -1;
                        }
@@ -414,11 +473,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
        while (xscr1) {
                if (!changes)
                        changes = c;
+               i0 = xscr1->i1;
                i1 = xscr1->i2;
                i2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec;
+               chg0 = xscr1->chg1;
                chg1 = xscr1->chg2;
                chg2 = xscr1->chg1;
-               if (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) {
+               if (xdl_append_merge(&c, 1,
+                                    i0, chg0, i1, chg1, i2, chg2)) {
                        xdl_cleanup_merge(changes);
                        return -1;
                }
@@ -427,11 +489,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
        while (xscr2) {
                if (!changes)
                        changes = c;
+               i0 = xscr2->i1;
                i1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec;
                i2 = xscr2->i2;
+               chg0 = xscr2->chg1;
                chg1 = xscr2->chg1;
                chg2 = xscr2->chg2;
-               if (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) {
+               if (xdl_append_merge(&c, 2,
+                                    i0, chg0, i1, chg1, i2, chg2)) {
                        xdl_cleanup_merge(changes);
                        return -1;
                }
@@ -449,7 +514,7 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
        /* output */
        if (result) {
                int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2,
-                       changes, NULL);
+                       changes, NULL, style);
                result->ptr = xdl_malloc(size);
                if (!result->ptr) {
                        xdl_cleanup_merge(changes);
@@ -457,14 +522,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
                }
                result->size = size;
                xdl_fill_merge_buffer(xe1, name1, xe2, name2, changes,
-                               result->ptr);
+                                     result->ptr, style);
        }
        return xdl_cleanup_merge(changes);
 }
 
 int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
                mmfile_t *mf2, const char *name2,
-               xpparam_t const *xpp, int level, mmbuffer_t *result) {
+               xpparam_t const *xpp, int flags, mmbuffer_t *result) {
        xdchange_t *xscr1, *xscr2;
        xdfenv_t xe1, xe2;
        int status;
@@ -501,7 +566,7 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
                } else {
                        status = xdl_do_merge(&xe1, xscr1, name1,
                                              &xe2, xscr2, name2,
-                                             level, xpp, result);
+                                             flags, xpp, result);
                }
                xdl_free_script(xscr1);
                xdl_free_script(xscr2);