From 034dd2d54f2e7e33897cfd5ede7c50b3d67d18d3 Mon Sep 17 00:00:00 2001 From: Tom Jones Date: Tue, 19 Apr 2022 16:20:24 +0100 Subject: [PATCH] diff3: Add support for -m diff3 in -m mode generates a complete file with changes bracketed with conflict markers. This adds support for diff3 to generate version control style three way merge output. The output format was inferred from looking at the gnu diff3 output on a selection of test files as a specification of what diff3 -m should output is not available. It is likely there are cases where the -m output differs from other tools and I am happy to update diff3 to address these. Discussed with: pstef, kevans Sponsored by: Klara, Inc. --- usr.bin/diff3/diff3.1 | 4 +- usr.bin/diff3/diff3.c | 85 +++++++++++++++++++++++++++++- usr.bin/diff3/tests/Makefile | 3 +- usr.bin/diff3/tests/diff3_test.sh | 13 +++++ usr.bin/diff3/tests/long-merge.out | 35 ++++++++++++ 5 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 usr.bin/diff3/tests/long-merge.out diff --git a/usr.bin/diff3/diff3.1 b/usr.bin/diff3/diff3.1 index f0fd481c75f1..6968af44bfbd 100644 --- a/usr.bin/diff3/diff3.1 +++ b/usr.bin/diff3/diff3.1 @@ -38,7 +38,7 @@ .Nd 3-way differential file comparison .Sh SYNOPSIS .Nm diff3 -.Op Fl 3AaEeiTXx +.Op Fl 3AaEeimTXx .Op Fl Fl diff-program Ar program .Op Fl Fl strip-trailing-cr .Op Fl L | Fl Fl label Ar label1 @@ -117,6 +117,8 @@ Defines labels to print instead of file names .Ar file2 and .Ar file3 . +.It Fl m, Fl Fl merge +Merge output instead of generating ed script. .It Fl T, Fl Fl initial-tab In the normal listing, use a tab instead of two spaces diff --git a/usr.bin/diff3/diff3.c b/usr.bin/diff3/diff3.c index 5df6357065fc..629e23288875 100644 --- a/usr.bin/diff3/diff3.c +++ b/usr.bin/diff3/diff3.c @@ -153,6 +153,7 @@ static void prange(struct range *, bool); static void repos(int); static void edscript(int) __dead2; static void Ascript(int) __dead2; +static void mergescript(int) __dead2; static void increase(void); static void usage(void) __dead2; static void printrange(FILE *, struct range *); @@ -389,7 +390,9 @@ merge(int m1, int m2) } } - if (Aflag) + if (mflag) + mergescript(j); + else if (Aflag) Ascript(j); else if (eflag) edscript(j); @@ -687,6 +690,86 @@ Ascript(int n) exit(overlapcnt > 0); } +/* + * Output the merged file directly (don't generate an ed script). When + * regurgitating diffs we need to walk forward through the file and print any + * inbetween lines. + */ +static void +mergescript(int i) +{ + struct range r; + int n; + + r.from = 1; + r.to = 1; + + for (n = 1; n < i+1; n++) { + /* print any lines leading up to here */ + r.to = de[n].old.from; + printrange(fp[0], &r); + + if (de[n].type == DIFF_TYPE2) { + printf("%s %s\n", oldmark, f2mark); + printrange(fp[1], &de[n].old); + printf("=======\n"); + printrange(fp[2], &de[n].new); + printf("%s %s\n", newmark, f3mark); + } else if (de[n].type == DIFF_TYPE3) { + if (!oflag || !overlap[n]) { + printrange(fp[2], &de[n].new); + } else { + + printf("%s %s\n", oldmark, f1mark); + printrange(fp[0], &de[n].old); + + printf("%s %s\n", orgmark, f2mark); + if (de[n].old.from == de[n].old.to) { + struct range or; + or.from = de[n].old.from -1; + or.to = de[n].new.to; + printrange(fp[1], &or); + } else + printrange(fp[1], &de[n].old); + + printf("=======\n"); + + printrange(fp[2], &de[n].new); + printf("%s %s\n", newmark, f3mark); + } + } + + if (de[n].old.from == de[n].old.to) + r.from = de[n].new.to; + else + r.from = de[n].old.to; + } + /* + * Print from the final range to the end of 'myfile'. Any deletions or + * additions to this file should have been handled by now. + * + * If the ranges are the same we need to rewind a line. + * If the new range is 0 length (from == to), we need to use the old + * range. + */ + if ((de[n-1].old.from == de[n-1].new.from) && + (de[n-1].old.to == de[n-1].new.to)) + r.from--; + else if (de[n-1].new.from == de[n-1].new.to) + r.from = de[n-1].old.from; + + /* + * If the range is a 3 way merge then we need to skip a line in the + * trailing output. + */ + if (de[n-1].type == DIFF_TYPE3) + r.from++; + + r.to = INT_MAX; + printrange(fp[0], &r); + exit(overlapcnt > 0); +} + static void increase(void) { diff --git a/usr.bin/diff3/tests/Makefile b/usr.bin/diff3/tests/Makefile index fc69dea260e8..0785e78aaee3 100644 --- a/usr.bin/diff3/tests/Makefile +++ b/usr.bin/diff3/tests/Makefile @@ -22,6 +22,7 @@ ${PACKAGE}FILES+= \ 8.out \ 9.out \ long-ed.out \ - long-A.out + long-A.out \ + long-merge.out \ .include diff --git a/usr.bin/diff3/tests/diff3_test.sh b/usr.bin/diff3/tests/diff3_test.sh index 8df84b0d8ff2..c30f258128af 100755 --- a/usr.bin/diff3/tests/diff3_test.sh +++ b/usr.bin/diff3/tests/diff3_test.sh @@ -4,6 +4,7 @@ atf_test_case diff3 atf_test_case diff3_lesssimple atf_test_case diff3_ed atf_test_case diff3_A +atf_test_case diff3_merge diff3_body() { @@ -56,10 +57,22 @@ diff3_A_body() diff3 -A -L long-m.txt -L long-o.txt -L long-y.txt $(atf_get_srcdir)/long-m.txt $(atf_get_srcdir)/long-o.txt $(atf_get_srcdir)/long-y.txt } + +diff3_merge_body() +{ + atf_check -s exit:1 -o file:$(atf_get_srcdir)/9.out \ + diff3 -m -L 1 -L 2 -L 3 $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt + atf_check -s exit:1 -o file:$(atf_get_srcdir)/tao-merge.out \ + diff3 -m -L lao.txt -L tzu.txt -L tao.txt $(atf_get_srcdir)/lao.txt $(atf_get_srcdir)/tzu.txt $(atf_get_srcdir)/tao.txt + atf_check -s exit:1 -o file:$(atf_get_srcdir)/long-merge.out \ + diff3 -m -L long-m.txt -L long-o.txt -L long-y.txt $(atf_get_srcdir)/long-m.txt $(atf_get_srcdir)/long-o.txt $(atf_get_srcdir)/long-y.txt +} + atf_init_test_cases() { atf_add_test_case diff3 # atf_add_test_case diff3_lesssimple atf_add_test_case diff3_ed atf_add_test_case diff3_A + atf_add_test_case diff3_merge } diff --git a/usr.bin/diff3/tests/long-merge.out b/usr.bin/diff3/tests/long-merge.out new file mode 100644 index 000000000000..5139a48fa429 --- /dev/null +++ b/usr.bin/diff3/tests/long-merge.out @@ -0,0 +1,35 @@ +This is a long file +These lines are the same in all three files +These lines are the same in all three files +This line is different in mine, not better +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +This line is different in yours, much butter +These lines are the same in all three files +These lines are the same in all three files +<<<<<<< long-o.txt +This line is different in yours and mine, but the same +======= +This line is different in yours and mine, the same is in both +>>>>>>> long-y.txt +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +<<<<<<< long-m.txt +This line is different in yours and mine, best change in mine +||||||| long-o.txt +This line is different in yours and mine, but the different in each +======= +This line is different in yours and mine, but the best in yours +>>>>>>> long-y.txt +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files