merge-file: add --diff-algorithm option
Make it possible to use other diff algorithms than the 'myers' default algorithm, when using the 'git merge-file' command, to help avoid spurious conflicts by selecting a more recent algorithm such as 'histogram', for instance when using 'git merge-file' as part of a custom merge driver. Signed-off-by: Antonin Delpeuch <antonin@delpeuch.eu> Reviewed-by: Phillip Wood <phillip.wood@dunelm.org.uk> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Antonin Delpeuch committed
Nov 20, 2023 at 19:18 UTC
4f7fd79e57f483282bc21bf5b0669f9c8229d32a
3 files changed
+157
-1
Documentation/git-merge-file.txt
+6
@@ -92,6 +92,12 @@ object store and the object ID of its blob is written to standard output.
92
Instead of leaving conflicts in the file, resolve conflicts
93
favouring our (or their or both) side of the lines.
94
95
+--diff-algorithm={patience|minimal|histogram|myers}::
96
+ Use a different diff algorithm while merging. The current default is "myers",
97
+ but selecting more recent algorithm such as "histogram" can help
98
+ avoid mismerges that occur due to unimportant matching lines
99
+ (such as braces from distinct functions). See also
100
+ linkgit:git-diff[1] `--diff-algorithm`.
101
102
EXAMPLES
103
--------
builtin/merge-file.c
+28
@@ -1,5 +1,6 @@
1
#include "builtin.h"
2
#include "abspath.h"
3
+#include "diff.h"
4
#include "hex.h"
5
#include "object-name.h"
6
#include "object-store.h"
@@ -28,6 +29,30 @@ static int label_cb(const struct option *opt, const char *arg, int unset)
29
return 0;
30
}
31
32
+static int set_diff_algorithm(xpparam_t *xpp,
33
+ const char *alg)
34
+{
35
+ long diff_algorithm = parse_algorithm_value(alg);
36
+ if (diff_algorithm < 0)
37
+ return -1;
38
+ xpp->flags = (xpp->flags & ~XDF_DIFF_ALGORITHM_MASK) | diff_algorithm;
39
+ return 0;
40
+}
41
+
42
+static int diff_algorithm_cb(const struct option *opt,
43
+ const char *arg, int unset)
44
+{
45
+ xpparam_t *xpp = opt->value;
46
+
47
+ BUG_ON_OPT_NEG(unset);
48
+
49
+ if (set_diff_algorithm(xpp, arg))
50
+ return error(_("option diff-algorithm accepts \"myers\", "
51
+ "\"minimal\", \"patience\" and \"histogram\""));
52
+
53
+ return 0;
54
+}
55
+
56
int cmd_merge_file(int argc, const char **argv, const char *prefix)
57
{
58
const char *names[3] = { 0 };
@@ -48,6 +73,9 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
73
XDL_MERGE_FAVOR_THEIRS),
74
OPT_SET_INT(0, "union", &xmp.favor, N_("for conflicts, use a union version"),
75
XDL_MERGE_FAVOR_UNION),
76
+ OPT_CALLBACK_F(0, "diff-algorithm", &xmp.xpp, N_("<algorithm>"),
77
+ N_("choose a diff algorithm"),
78
+ PARSE_OPT_NONEG, diff_algorithm_cb),
79
OPT_INTEGER(0, "marker-size", &xmp.marker_size,
80
N_("for conflicts, use this marker size")),
81
OPT__QUIET(&quiet, N_("do not warn about conflicts")),
t/t6403-merge-file.sh
+123
-1
@@ -56,7 +56,67 @@ test_expect_success 'setup' '
56
deduxit me super semitas jusitiae,
57
EOF
58
59
- printf "propter nomen suum." >>new4.txt
59
+ printf "propter nomen suum." >>new4.txt &&
60
+
61
+ cat >base.c <<-\EOF &&
62
+ int f(int x, int y)
63
+ {
64
+ if (x == 0)
65
+ {
66
+ return y;
67
+ }
68
+ return x;
69
+ }
70
+
71
+ int g(size_t u)
72
+ {
73
+ while (u < 30)
74
+ {
75
+ u++;
76
+ }
77
+ return u;
78
+ }
79
+ EOF
80
+
81
+ cat >ours.c <<-\EOF &&
82
+ int g(size_t u)
83
+ {
84
+ while (u < 30)
85
+ {
86
+ u++;
87
+ }
88
+ return u;
89
+ }
90
+
91
+ int h(int x, int y, int z)
92
+ {
93
+ if (z == 0)
94
+ {
95
+ return x;
96
+ }
97
+ return y;
98
+ }
99
+ EOF
100
+
101
+ cat >theirs.c <<-\EOF
102
+ int f(int x, int y)
103
+ {
104
+ if (x == 0)
105
+ {
106
+ return y;
107
+ }
108
+ return x;
109
+ }
110
+
111
+ int g(size_t u)
112
+ {
113
+ while (u > 34)
114
+ {
115
+ u--;
116
+ }
117
+ return u;
118
+ }
119
+ EOF
120
'
121
122
test_expect_success 'merge with no changes' '
@@ -447,4 +507,66 @@ test_expect_success '--object-id fails without repository' '
507
grep "not a git repository" err
508
'
509
510
+test_expect_success 'merging C files with "myers" diff algorithm creates some spurious conflicts' '
511
+ cat >expect.c <<-\EOF &&
512
+ int g(size_t u)
513
+ {
514
+ while (u < 30)
515
+ {
516
+ u++;
517
+ }
518
+ return u;
519
+ }
520
+
521
+ int h(int x, int y, int z)
522
+ {
523
+ <<<<<<< ours.c
524
+ if (z == 0)
525
+ ||||||| base.c
526
+ while (u < 30)
527
+ =======
528
+ while (u > 34)
529
+ >>>>>>> theirs.c
530
+ {
531
+ <<<<<<< ours.c
532
+ return x;
533
+ ||||||| base.c
534
+ u++;
535
+ =======
536
+ u--;
537
+ >>>>>>> theirs.c
538
+ }
539
+ return y;
540
+ }
541
+ EOF
542
+
543
+ test_must_fail git merge-file -p --diff3 --diff-algorithm myers ours.c base.c theirs.c >myers_output.c &&
544
+ test_cmp expect.c myers_output.c
545
+'
546
+
547
+test_expect_success 'merging C files with "histogram" diff algorithm avoids some spurious conflicts' '
548
+ cat >expect.c <<-\EOF &&
549
+ int g(size_t u)
550
+ {
551
+ while (u > 34)
552
+ {
553
+ u--;
554
+ }
555
+ return u;
556
+ }
557
+
558
+ int h(int x, int y, int z)
559
+ {
560
+ if (z == 0)
561
+ {
562
+ return x;
563
+ }
564
+ return y;
565
+ }
566
+ EOF
567
+
568
+ git merge-file -p --diff3 --diff-algorithm histogram ours.c base.c theirs.c >histogram_output.c &&
569
+ test_cmp expect.c histogram_output.c
570
+'
571
+
572
test_done