Skip to content

Commit e0af48e

Browse files
committed
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>
1 parent f2b25dd commit e0af48e

File tree

4 files changed

+135
-21
lines changed

4 files changed

+135
-21
lines changed

builtin-merge-file.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#include "xdiff-interface.h"
55

66
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";
88

99
int cmd_merge_file(int argc, const char **argv, const char *prefix)
1010
{
@@ -13,6 +13,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
1313
mmbuffer_t result = {NULL, 0};
1414
xpparam_t xpp = {XDF_NEED_MINIMAL};
1515
int ret = 0, i = 0, to_stdout = 0;
16+
int merge_level = XDL_MERGE_ZEALOUS_ALNUM;
17+
int merge_style = 0;
1618

1719
while (argc > 4) {
1820
if (!strcmp(argv[1], "-L") && i < 3) {
@@ -25,6 +27,10 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
2527
else if (!strcmp(argv[1], "-q") ||
2628
!strcmp(argv[1], "--quiet"))
2729
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+
}
2834
else
2935
usage(merge_file_usage);
3036
argc--;
@@ -46,7 +52,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
4652
}
4753

4854
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);
5056

5157
for (i = 0; i < 3; i++)
5258
free(mmfs[i].ptr);

t/t6023-merge-file.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,41 @@ test_expect_success 'ZEALOUS_ALNUM' '
161161
162162
'
163163

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+
164201
test_done

xdiff/xdiff.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,16 @@ extern "C" {
5050
#define XDL_BDOP_CPY 2
5151
#define XDL_BDOP_INSB 3
5252

53+
/* merge simplification levels */
5354
#define XDL_MERGE_MINIMAL 0
5455
#define XDL_MERGE_EAGER 1
5556
#define XDL_MERGE_ZEALOUS 2
5657
#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
5763

5864
typedef struct s_mmfile {
5965
char *ptr;

xdiff/xmerge.c

Lines changed: 84 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,32 @@ typedef struct s_xdmerge {
3030
* 2 = no conflict, take second.
3131
*/
3232
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+
*/
3339
long i1, i2;
3440
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;
3547
} xdmerge_t;
3648

3749
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)
3953
{
4054
xdmerge_t *m = *merge;
4155
if (m && (i1 <= m->i1 + m->chg1 || i2 <= m->i2 + m->chg2)) {
4256
if (mode != m->mode)
4357
m->mode = 0;
58+
m->chg0 = i0 + chg0 - m->i0;
4459
m->chg1 = i1 + chg1 - m->i1;
4560
m->chg2 = i2 + chg2 - m->i2;
4661
} else {
@@ -49,6 +64,8 @@ static int xdl_append_merge(xdmerge_t **merge, int mode,
4964
return -1;
5065
m->next = NULL;
5166
m->mode = mode;
67+
m->i0 = i0;
68+
m->chg0 = chg0;
5269
m->i1 = i1;
5370
m->chg1 = chg1;
5471
m->i2 = i2;
@@ -91,11 +108,13 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,
91108
return 0;
92109
}
93110

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)
95112
{
96-
xrecord_t **recs = xe->xdf2.recs + i;
113+
xrecord_t **recs;
97114
int size = 0;
98115

116+
recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;
117+
99118
if (count < 1)
100119
return 0;
101120

@@ -113,9 +132,19 @@ static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
113132
return size;
114133
}
115134

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+
116145
static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
117146
xdfenv_t *xe2, const char *name2,
118-
int size, int i,
147+
int size, int i, int style,
119148
xdmerge_t *m, char *dest)
120149
{
121150
const int marker_size = 7;
@@ -143,6 +172,20 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
143172
/* Postimage from side #1 */
144173
size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
145174
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+
146189
if (!dest) {
147190
size += marker_size + 1;
148191
} else {
@@ -170,14 +213,15 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
170213
}
171214

172215
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)
174218
{
175219
int size, i;
176220

177221
for (size = i = 0; m; m = m->next) {
178222
if (m->mode == 0)
179223
size = fill_conflict_hunk(xe1, name1, xe2, name2,
180-
size, i, m, dest);
224+
size, i, style, m, dest);
181225
else if (m->mode == 1)
182226
size += xdl_recs_copy(xe1, i, m->i1 + m->chg1 - i, 0,
183227
dest ? dest + size : NULL);
@@ -342,33 +386,41 @@ static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,
342386
*/
343387
static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
344388
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) {
346390
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;
348394

349395
c = changes = NULL;
350396

351397
while (xscr1 && xscr2) {
352398
if (!changes)
353399
changes = c;
354400
if (xscr1->i1 + xscr1->chg1 < xscr2->i1) {
401+
i0 = xscr1->i1;
355402
i1 = xscr1->i2;
356403
i2 = xscr2->i2 - xscr2->i1 + xscr1->i1;
404+
chg0 = xscr1->chg1;
357405
chg1 = xscr1->chg2;
358406
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)) {
360409
xdl_cleanup_merge(changes);
361410
return -1;
362411
}
363412
xscr1 = xscr1->next;
364413
continue;
365414
}
366415
if (xscr2->i1 + xscr2->chg1 < xscr1->i1) {
416+
i0 = xscr2->i1;
367417
i1 = xscr1->i2 - xscr1->i1 + xscr2->i1;
368418
i2 = xscr2->i2;
419+
chg0 = xscr2->chg1;
369420
chg1 = xscr2->chg1;
370421
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)) {
372424
xdl_cleanup_merge(changes);
373425
return -1;
374426
}
@@ -385,19 +437,26 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
385437
int off = xscr1->i1 - xscr2->i1;
386438
int ffo = off + xscr1->chg1 - xscr2->chg1;
387439

440+
i0 = xscr1->i1;
388441
i1 = xscr1->i2;
389442
i2 = xscr2->i2;
390-
if (off > 0)
443+
if (off > 0) {
444+
i0 -= off;
391445
i1 -= off;
446+
}
392447
else
393448
i2 += off;
449+
chg0 = xscr1->i1 + xscr1->chg1 - i0;
394450
chg1 = xscr1->i2 + xscr1->chg2 - i1;
395451
chg2 = xscr2->i2 + xscr2->chg2 - i2;
396452
if (ffo > 0)
397453
chg2 += ffo;
398-
else
454+
else {
455+
chg0 -= ffo;
399456
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)) {
401460
xdl_cleanup_merge(changes);
402461
return -1;
403462
}
@@ -414,11 +473,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
414473
while (xscr1) {
415474
if (!changes)
416475
changes = c;
476+
i0 = xscr1->i1;
417477
i1 = xscr1->i2;
418478
i2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec;
479+
chg0 = xscr1->chg1;
419480
chg1 = xscr1->chg2;
420481
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)) {
422484
xdl_cleanup_merge(changes);
423485
return -1;
424486
}
@@ -427,11 +489,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
427489
while (xscr2) {
428490
if (!changes)
429491
changes = c;
492+
i0 = xscr2->i1;
430493
i1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec;
431494
i2 = xscr2->i2;
495+
chg0 = xscr2->chg1;
432496
chg1 = xscr2->chg1;
433497
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)) {
435500
xdl_cleanup_merge(changes);
436501
return -1;
437502
}
@@ -449,22 +514,22 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
449514
/* output */
450515
if (result) {
451516
int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2,
452-
changes, NULL);
517+
changes, NULL, style);
453518
result->ptr = xdl_malloc(size);
454519
if (!result->ptr) {
455520
xdl_cleanup_merge(changes);
456521
return -1;
457522
}
458523
result->size = size;
459524
xdl_fill_merge_buffer(xe1, name1, xe2, name2, changes,
460-
result->ptr);
525+
result->ptr, style);
461526
}
462527
return xdl_cleanup_merge(changes);
463528
}
464529

465530
int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
466531
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) {
468533
xdchange_t *xscr1, *xscr2;
469534
xdfenv_t xe1, xe2;
470535
int status;
@@ -501,7 +566,7 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
501566
} else {
502567
status = xdl_do_merge(&xe1, xscr1, name1,
503568
&xe2, xscr2, name2,
504-
level, xpp, result);
569+
flags, xpp, result);
505570
}
506571
xdl_free_script(xscr1);
507572
xdl_free_script(xscr2);

0 commit comments

Comments
 (0)