Re: git: cf73401c4f7a - main - diff3: Fix merge mode.
- In reply to: Dag-Erling Smørgrav : "git: cf73401c4f7a - main - diff3: Fix merge mode."
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 26 Sep 2024 20:56:01 UTC
On Wed 25 Sep 17:15, Dag-Erling Smørgrav wrote: > The branch main has been updated by des: > > URL: https://cgit.FreeBSD.org/src/commit/?id=cf73401c4f7af063c91a0c8e4d5b46e08f06db87 > > commit cf73401c4f7af063c91a0c8e4d5b46e08f06db87 > Author: Dag-Erling Smørgrav <des@FreeBSD.org> > AuthorDate: 2024-09-25 17:14:32 +0000 > Commit: Dag-Erling Smørgrav <des@FreeBSD.org> > CommitDate: 2024-09-25 17:14:55 +0000 > > diff3: Fix merge mode. > > This is mostly thj@'s work, with some tweaks and cleanup by me. There > are still some cases where our output differs from GNU diff3, but it's > much better than before and I'd rather commit what I have now than let > it continue to languish in a metaphorical drawer. > > MFC after 3 weeks > Sponsored by: Klara, Inc. > Reviewed by: thj > Differential Revision: https://reviews.freebsd.org/D46762 > --- > usr.bin/diff3/diff3.c | 255 +++++++++++++++++++++++++++++++++++--------------- > 1 file changed, 179 insertions(+), 76 deletions(-) > > diff --git a/usr.bin/diff3/diff3.c b/usr.bin/diff3/diff3.c > index c72ea0747589..f20b1d74678d 100644 > --- a/usr.bin/diff3/diff3.c > +++ b/usr.bin/diff3/diff3.c > @@ -79,7 +79,6 @@ > #include <string.h> > #include <unistd.h> > > - > /* > * "from" is first in range of changed lines; "to" is last+1 > * from=to=line after point of insertion for added lines. > @@ -90,6 +89,7 @@ struct range { > }; > > struct diff { > +#define DIFF_TYPE1 1 > #define DIFF_TYPE2 2 > #define DIFF_TYPE3 3 > int type; > @@ -147,6 +147,7 @@ static void keep(int, struct range *); > static void merge(int, int); > static void prange(struct range *, bool); > static void repos(int); > +static void separate(const char *); > static void edscript(int) __dead2; > static void Ascript(int) __dead2; > static void mergescript(int) __dead2; > @@ -154,7 +155,7 @@ static void increase(void); > static void usage(void); > static void printrange(FILE *, struct range *); > > -static const char diff3_version[] = "FreeBSD diff3 20220517"; > +static const char diff3_version[] = "FreeBSD diff3 20240925"; > > enum { > DIFFPROG_OPT, > @@ -189,49 +190,110 @@ usage(void) > "[-L label3] file1 file2 file3\n"); > } > > +static int > +strtoi(char *str, char **end) > +{ > + intmax_t num; > + > + errno = 0; > + num = strtoimax(str, end, 10); > + if ((end != NULL && *end == str) || > + num < 0 || num > INT_MAX || > + errno == EINVAL || errno == ERANGE) > + err(1, "error in diff output"); > + return (int)num; > +} > + > +/* > + * Read diff hunks into the array pointed to by *dd. > + * > + * The output from `diff foo bar` consists of a series of hunks describing > + * an addition (lines in bar not present in foo), change (lines in bar > + * different from lines in foo), or deletion (lines in foo not present in > + * bar). Each record starts with a line of the form: > + * > + * a[,b]xc[,d] > + * > + * where a, b, c, and d are nonnegative integers (b and d are printed only > + * if they differ from a and c, respectively), and x is either 'a' for an > + * addition, 'c' for a change, or 'd' for a deletion. This is then > + * followed by a series of lines (which we ignore) giving the added, > + * changed, or deleted text. > + * > + * For an addition, a == b is the last line in 'foo' before the addition, > + * while c through d is the range of lines in 'bar' to be added to 'foo'. > + * > + * For a change, a through b is the range of lines in 'foo' to be replaced > + * and c through d is the range of lines in 'bar' to replace them with. > + * > + * For a deletion, a through b is the range of lines in 'foo' to remove > + * and c == d is the line in 'bar' which corresponds to the last line > + * before the deletion. > + * > + * The observant reader will have noticed that x is not really needed and > + * that we can fully describe any hunk using only a, b, c, and d: > + * > + * - an addition replaces a zero-length range in one file with a > + * non-zero-length range from the other > + * > + * - a change replaces a non-zero-length range in one file with a > + * non-zero-length range from the other > + * > + * - a deletion replaces a non-zero-length range in one file with a > + * zero-length range from the other > + */ > static int > readin(int fd, struct diff **dd) > { > int a, b, c, d; > - size_t i; > + int i; > char kind, *p; > FILE *f; > > f = fdopen(fd, "r"); > if (f == NULL) > err(2, "fdopen"); > - for (i = 0; (p = getchange(f)); i++) { > + for (i = 0; (p = getchange(f)) != NULL; i++) { > + if ((size_t)i >= szchanges - 1) > + increase(); > #if DEBUG > (*dd)[i].line = strdup(p); > #endif /* DEBUG */ > > - if (i >= szchanges - 1) > - increase(); > - a = b = (int)strtoimax(p, &p, 10); > - if (*p == ',') { > - p++; > - b = (int)strtoimax(p, &p, 10); > - } > + a = b = strtoi(p, &p); > + if (*p == ',') > + b = strtoi(p + 1, &p); > kind = *p++; > - c = d = (int)strtoimax(p, &p, 10); > - if (*p == ',') { > - p++; > - d = (int)strtoimax(p, &p, 10); > - } > + c = d = strtoi(p, &p); > + if (*p == ',') > + d = strtoi(p + 1, &p); > + if (*p != '\n') > + errx(1, "error in diff output"); > if (kind == 'a') > a++; > - if (kind == 'd') > + else if (kind == 'c') > + /* nothing */ ; > + else if (kind == 'd') > c++; > + else > + errx(1, "error in diff output"); > b++; > d++; > + if (b < a || d < c) > + errx(1, "error in diff output"); > (*dd)[i].old.from = a; > (*dd)[i].old.to = b; > (*dd)[i].new.from = c; > (*dd)[i].new.to = d; > + if (i > 0) { > + if ((*dd)[i].old.from < (*dd)[i - 1].old.to || > + (*dd)[i].new.from < (*dd)[i - 1].new.to) > + errx(1, "diff output out of order"); > + } > } > - if (i) { > - (*dd)[i].old.from = (*dd)[i - 1].old.to; > - (*dd)[i].new.from = (*dd)[i - 1].new.to; > + if (i > 0) { > + (*dd)[i].old.from = (*dd)[i].old.to = (*dd)[i - 1].old.to; > + (*dd)[i].new.from = (*dd)[i].new.to = (*dd)[i - 1].new.to; > } > fclose(f); > return (i); > @@ -264,7 +326,7 @@ getchange(FILE *b) > { > char *line; > > - while ((line = get_line(b, NULL))) { > + while ((line = get_line(b, NULL)) != NULL) { > if (isdigit((unsigned char)line[0])) > return (line); > } > @@ -305,15 +367,23 @@ merge(int m1, int m2) > d2 = d23; > j = 0; > > - while (t1 = d1 < d13 + m1, t2 = d2 < d23 + m2, t1 || t2) { > + for (;;) { > + t1 = (d1 < d13 + m1); > + t2 = (d2 < d23 + m2); > + if (!t1 && !t2) > + break; > + > /* first file is different from the others */ > if (!t2 || (t1 && d1->new.to < d2->new.from)) { > /* stuff peculiar to 1st file */ > if (eflag == EFLAG_NONE) { > - printf("====1\n"); > + separate("1"); > change(1, &d1->old, false); > keep(2, &d1->new); > change(3, &d1->new, false); > + } else if (eflag == EFLAG_OVERLAP) { > + j = edit(d2, dup, j, DIFF_TYPE1); > + printdiff(d2); This is defined nowhere, so the code does not build. Best regards, Bapt