xdiff-merge: optionally show conflicts in "diff3 -m" style
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>
Junio C Hamano committed
Aug 28, 2008 at 01:10 UTC
e0af48e49682ea3345f932f6b9e7fa7c5e5e611a
4 files changed
+135
-21
builtin-merge-file.c
+8
-2
@@ -4,7 +4,7 @@
4
#include "xdiff-interface.h"
5
6
static const char merge_file_usage[] =
7
-"git merge-file [-p | --stdout] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
7
+"git merge-file [-p | --stdout] [--diff3] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
8
9
int cmd_merge_file(int argc, const char **argv, const char *prefix)
10
{
@@ -13,6 +13,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
13
mmbuffer_t result = {NULL, 0};
14
xpparam_t xpp = {XDF_NEED_MINIMAL};
15
int ret = 0, i = 0, to_stdout = 0;
16
+ int merge_level = XDL_MERGE_ZEALOUS_ALNUM;
17
+ int merge_style = 0;
18
19
while (argc > 4) {
20
if (!strcmp(argv[1], "-L") && i < 3) {
@@ -25,6 +27,10 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
27
else if (!strcmp(argv[1], "-q") ||
28
!strcmp(argv[1], "--quiet"))
29
freopen("/dev/null", "w", stderr);
30
+ else if (!strcmp(argv[1], "--diff3")) {
31
+ merge_style = XDL_MERGE_DIFF3;
32
+ merge_level = XDL_MERGE_EAGER;
33
+ }
34
else
35
usage(merge_file_usage);
36
argc--;
@@ -46,7 +52,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
52
}
53
54
ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
49
- &xpp, XDL_MERGE_ZEALOUS_ALNUM, &result);
55
+ &xpp, merge_level | merge_style, &result);
56
57
for (i = 0; i < 3; i++)
58
free(mmfs[i].ptr);
t/t6023-merge-file.sh
+37
@@ -161,4 +161,41 @@ test_expect_success 'ZEALOUS_ALNUM' '
161
162
'
163
164
+cat >expect <<\EOF
165
+Dominus regit me,
166
+<<<<<<< new8.txt
167
+et nihil mihi deerit;
168
+
169
+
170
+
171
+
172
+In loco pascuae ibi me collocavit;
173
+super aquam refectionis educavit me.
174
+|||||||
175
+et nihil mihi deerit.
176
+In loco pascuae ibi me collocavit,
177
+super aquam refectionis educavit me;
178
+=======
179
+et nihil mihi deerit,
180
+
181
+
182
+
183
+
184
+In loco pascuae ibi me collocavit --
185
+super aquam refectionis educavit me,
186
+>>>>>>> new9.txt
187
+animam meam convertit,
188
+deduxit me super semitas jusitiae,
189
+propter nomen suum.
190
+Nam et si ambulavero in medio umbrae mortis,
191
+non timebo mala, quoniam TU mecum es:
192
+virga tua et baculus tuus ipsa me consolata sunt.
193
+EOF
194
+
195
+test_expect_success '"diff3 -m" style output' '
196
+ test_must_fail git merge-file -p --diff3 \
197
+ new8.txt new5.txt new9.txt >actual &&
198
+ test_cmp expect actual
199
+'
200
+
201
test_done
xdiff/xdiff.h
+6
@@ -50,10 +50,16 @@ extern "C" {
50
#define XDL_BDOP_CPY 2
51
#define XDL_BDOP_INSB 3
52
53
+/* merge simplification levels */
54
#define XDL_MERGE_MINIMAL 0
55
#define XDL_MERGE_EAGER 1
56
#define XDL_MERGE_ZEALOUS 2
57
#define XDL_MERGE_ZEALOUS_ALNUM 3
58
+#define XDL_MERGE_LEVEL_MASK 0x0f
59
+
60
+/* merge output styles */
61
+#define XDL_MERGE_DIFF3 0x8000
62
+#define XDL_MERGE_STYLE_MASK 0x8000
63
64
typedef struct s_mmfile {
65
char *ptr;
xdiff/xmerge.c
+84
-19
@@ -30,17 +30,32 @@ typedef struct s_xdmerge {
30
* 2 = no conflict, take second.
31
*/
32
int mode;
33
+ /*
34
+ * These point at the respective postimages. E.g. <i1,chg1> is
35
+ * how side #1 wants to change the common ancestor; if there is no
36
+ * overlap, lines before i1 in the postimage of side #1 appear
37
+ * in the merge result as a region touched by neither side.
38
+ */
39
long i1, i2;
40
long chg1, chg2;
41
+ /*
42
+ * These point at the preimage; of course there is just one
43
+ * preimage, that is from the shared common ancestor.
44
+ */
45
+ long i0;
46
+ long chg0;
47
} xdmerge_t;
48
49
static int xdl_append_merge(xdmerge_t **merge, int mode,
38
- long i1, long chg1, long i2, long chg2)
50
+ long i0, long chg0,
51
+ long i1, long chg1,
52
+ long i2, long chg2)
53
{
54
xdmerge_t *m = *merge;
55
if (m && (i1 <= m->i1 + m->chg1 || i2 <= m->i2 + m->chg2)) {
56
if (mode != m->mode)
57
m->mode = 0;
58
+ m->chg0 = i0 + chg0 - m->i0;
59
m->chg1 = i1 + chg1 - m->i1;
60
m->chg2 = i2 + chg2 - m->i2;
61
} else {
@@ -49,6 +64,8 @@ static int xdl_append_merge(xdmerge_t **merge, int mode,
64
return -1;
65
m->next = NULL;
66
m->mode = mode;
67
+ m->i0 = i0;
68
+ m->chg0 = chg0;
69
m->i1 = i1;
70
m->chg1 = chg1;
71
m->i2 = i2;
@@ -91,11 +108,13 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,
108
return 0;
109
}
110
94
-static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
111
+static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
112
{
96
- xrecord_t **recs = xe->xdf2.recs + i;
113
+ xrecord_t **recs;
114
int size = 0;
115
116
+ recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;
117
+
118
if (count < 1)
119
return 0;
120
@@ -113,9 +132,19 @@ static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
132
return size;
133
}
134
135
+static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
136
+{
137
+ return xdl_recs_copy_0(0, xe, i, count, add_nl, dest);
138
+}
139
+
140
+static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
141
+{
142
+ return xdl_recs_copy_0(1, xe, i, count, add_nl, dest);
143
+}
144
+
145
static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
146
xdfenv_t *xe2, const char *name2,
118
- int size, int i,
147
+ int size, int i, int style,
148
xdmerge_t *m, char *dest)
149
{
150
const int marker_size = 7;
@@ -143,6 +172,20 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
172
/* Postimage from side #1 */
173
size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
174
dest ? dest + size : NULL);
175
+
176
+ if (style == XDL_MERGE_DIFF3) {
177
+ /* Shared preimage */
178
+ if (!dest) {
179
+ size += marker_size + 1;
180
+ } else {
181
+ for (j = 0; j < marker_size; j++)
182
+ dest[size++] = '|';
183
+ dest[size++] = '\n';
184
+ }
185
+ size += xdl_orig_copy(xe1, m->i0, m->chg0, 1,
186
+ dest ? dest + size : NULL);
187
+ }
188
+
189
if (!dest) {
190
size += marker_size + 1;
191
} else {
@@ -170,14 +213,15 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
213
}
214
215
static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
173
- xdfenv_t *xe2, const char *name2, xdmerge_t *m, char *dest)
216
+ xdfenv_t *xe2, const char *name2,
217
+ xdmerge_t *m, char *dest, int style)
218
{
219
int size, i;
220
221
for (size = i = 0; m; m = m->next) {
222
if (m->mode == 0)
223
size = fill_conflict_hunk(xe1, name1, xe2, name2,
180
- size, i, m, dest);
224
+ size, i, style, m, dest);
225
else if (m->mode == 1)
226
size += xdl_recs_copy(xe1, i, m->i1 + m->chg1 - i, 0,
227
dest ? dest + size : NULL);
@@ -342,9 +386,11 @@ static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,
386
*/
387
static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
388
xdfenv_t *xe2, xdchange_t *xscr2, const char *name2,
345
- int level, xpparam_t const *xpp, mmbuffer_t *result) {
389
+ int flags, xpparam_t const *xpp, mmbuffer_t *result) {
390
xdmerge_t *changes, *c;
347
- int i1, i2, chg1, chg2;
391
+ int i0, i1, i2, chg0, chg1, chg2;
392
+ int level = flags & XDL_MERGE_LEVEL_MASK;
393
+ int style = flags & XDL_MERGE_STYLE_MASK;
394
395
c = changes = NULL;
396
@@ -352,11 +398,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
398
if (!changes)
399
changes = c;
400
if (xscr1->i1 + xscr1->chg1 < xscr2->i1) {
401
+ i0 = xscr1->i1;
402
i1 = xscr1->i2;
403
i2 = xscr2->i2 - xscr2->i1 + xscr1->i1;
404
+ chg0 = xscr1->chg1;
405
chg1 = xscr1->chg2;
406
chg2 = xscr1->chg1;
359
- if (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) {
407
+ if (xdl_append_merge(&c, 1,
408
+ i0, chg0, i1, chg1, i2, chg2)) {
409
xdl_cleanup_merge(changes);
410
return -1;
411
}
@@ -364,11 +413,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
413
continue;
414
}
415
if (xscr2->i1 + xscr2->chg1 < xscr1->i1) {
416
+ i0 = xscr2->i1;
417
i1 = xscr1->i2 - xscr1->i1 + xscr2->i1;
418
i2 = xscr2->i2;
419
+ chg0 = xscr2->chg1;
420
chg1 = xscr2->chg1;
421
chg2 = xscr2->chg2;
371
- if (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) {
422
+ if (xdl_append_merge(&c, 2,
423
+ i0, chg0, i1, chg1, i2, chg2)) {
424
xdl_cleanup_merge(changes);
425
return -1;
426
}
@@ -385,19 +437,26 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
437
int off = xscr1->i1 - xscr2->i1;
438
int ffo = off + xscr1->chg1 - xscr2->chg1;
439
440
+ i0 = xscr1->i1;
441
i1 = xscr1->i2;
442
i2 = xscr2->i2;
390
- if (off > 0)
443
+ if (off > 0) {
444
+ i0 -= off;
445
i1 -= off;
446
+ }
447
else
448
i2 += off;
449
+ chg0 = xscr1->i1 + xscr1->chg1 - i0;
450
chg1 = xscr1->i2 + xscr1->chg2 - i1;
451
chg2 = xscr2->i2 + xscr2->chg2 - i2;
452
if (ffo > 0)
453
chg2 += ffo;
398
- else
454
+ else {
455
+ chg0 -= ffo;
456
chg1 -= ffo;
400
- if (xdl_append_merge(&c, 0, i1, chg1, i2, chg2)) {
457
+ }
458
+ if (xdl_append_merge(&c, 0,
459
+ i0, chg0, i1, chg1, i2, chg2)) {
460
xdl_cleanup_merge(changes);
461
return -1;
462
}
@@ -414,11 +473,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
473
while (xscr1) {
474
if (!changes)
475
changes = c;
476
+ i0 = xscr1->i1;
477
i1 = xscr1->i2;
478
i2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec;
479
+ chg0 = xscr1->chg1;
480
chg1 = xscr1->chg2;
481
chg2 = xscr1->chg1;
421
- if (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) {
482
+ if (xdl_append_merge(&c, 1,
483
+ i0, chg0, i1, chg1, i2, chg2)) {
484
xdl_cleanup_merge(changes);
485
return -1;
486
}
@@ -427,11 +489,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
489
while (xscr2) {
490
if (!changes)
491
changes = c;
492
+ i0 = xscr2->i1;
493
i1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec;
494
i2 = xscr2->i2;
495
+ chg0 = xscr2->chg1;
496
chg1 = xscr2->chg1;
497
chg2 = xscr2->chg2;
434
- if (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) {
498
+ if (xdl_append_merge(&c, 2,
499
+ i0, chg0, i1, chg1, i2, chg2)) {
500
xdl_cleanup_merge(changes);
501
return -1;
502
}
@@ -449,7 +514,7 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
514
/* output */
515
if (result) {
516
int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2,
452
- changes, NULL);
517
+ changes, NULL, style);
518
result->ptr = xdl_malloc(size);
519
if (!result->ptr) {
520
xdl_cleanup_merge(changes);
@@ -457,14 +522,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
522
}
523
result->size = size;
524
xdl_fill_merge_buffer(xe1, name1, xe2, name2, changes,
460
- result->ptr);
525
+ result->ptr, style);
526
}
527
return xdl_cleanup_merge(changes);
528
}
529
530
int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
531
mmfile_t *mf2, const char *name2,
467
- xpparam_t const *xpp, int level, mmbuffer_t *result) {
532
+ xpparam_t const *xpp, int flags, mmbuffer_t *result) {
533
xdchange_t *xscr1, *xscr2;
534
xdfenv_t xe1, xe2;
535
int status;
@@ -501,7 +566,7 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
566
} else {
567
status = xdl_do_merge(&xe1, xscr1, name1,
568
&xe2, xscr2, name2,
504
- level, xpp, result);
569
+ flags, xpp, result);
570
}
571
xdl_free_script(xscr1);
572
xdl_free_script(xscr2);