2 * wiggle - apply rejected patches
4 * Copyright (C) 2003 Neil Brown <neilb@cse.unsw.edu.au>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * Email: <neilb@cse.unsw.edu.au>
24 * School of Computer Science and Engineering
25 * The University of New South Wales
31 * Wiggle is a tool for working with patches that don't quite apply properly.
32 * It provides functionality similar to 'diff' and 'merge' but can
33 * work at the level of individual words thus allowing the merging of
34 * two changes that affect the same line, but not the same parts of that line.
36 * Wiggle can also read patch and merge files. Unlike 'merge' it does not
37 * need to be given three separate files, but can be given a file and a patch
38 * and it will extract the pieces of the two two other files that it needs from
41 * Wiggle performs one of three core function:
42 * --extract -x extract part of a patch or merge file
43 * --diff -d report differences between two files
44 * --merge -m merge the changes between two files into a third file
46 * To perform these, wiggle requires 1, 2, or 3 input streams respectively.
47 * I can get there from individual files, from a diff (unified or context) or
51 * If one file is given, it is a merge file (output of 'merge').
52 * If two files are given, the second is assumed to be a patch, the first is a normal file.
53 * If three files are given, they are taken to be normal files.
56 * If one file is given, it is a patch
57 * If two files are given, they are normal files.
60 * Only one file can be given. -p indicates it is a patch, otherwise it is a merge.
61 * One of the flags -1 -2 or -3 must also be given and they indicate which
62 * part of the patch or merge to extract.
64 * Difference calculate and merging is performed on lines (-l) or words (-w).
65 * In the case of -w, an initial diff is computed based on non-trivial words.
66 * i.e. spaces are ignored
67 * This diff is computed from the ends of the file and is used to find a suitable
68 * starting point and range. Then a more precise diff is computed over that
71 * Other options available are:
72 * --replace -r replace first file with result of merge.
73 * --help -h provide help
74 * --version -v version
76 * Defaults are --merge --words
90 fprintf(stderr
,"wiggle: fatal error\n");
95 void printword(FILE *f
, struct elmnt e
)
98 fprintf(f
, "%.*s", e
.len
, e
.start
);
101 sscanf(e
.start
+1, "%d %d %d", &a
, &b
, &c
);
102 fprintf(f
, "*** %d,%d **** %d\n", b
,c
,a
);
106 static void printsep(struct elmnt e1
, struct elmnt e2
)
109 sscanf(e1
.start
+1, "%d %d %d", &a
, &b
, &c
);
110 sscanf(e2
.start
+1, "%d %d %d", &d
, &e
, &f
);
111 printf("@@ -%d,%d +%d,%d @@\n", b
,c
,e
,f
);
115 /* Remove any entries from the common-sublist that are
116 * just spaces, tabs, or newlines
118 void cleanlist(struct file a
, struct file b
, struct csl
*list
)
120 struct csl
*new = list
;
125 for( ap
= list
->a
; ap
< list
->a
+list
->len
; ap
++) {
126 for (i
=0; i
<a
.list
[ap
].len
; i
++) {
127 char c
= a
.list
[ap
].start
[i
];
131 if (i
!= a
.list
[ap
].len
)
134 if (ap
== list
->a
+list
->len
)
142 int main(int argc
, char *argv
[])
149 char *replacename
=NULL
, *orignew
=NULL
;
153 int verbose
=0, quiet
=0;
155 int chunks1
=0, chunks2
=0, chunks3
=0;
157 FILE *outfile
= stdout
;
160 struct stream f
, flist
[3];
162 struct csl
*csl1
, *csl2
;
164 while ((opt
= getopt_long(argc
, argv
,
165 short_options
, long_options
,
166 &option_index
)) != -1)
171 case 'x': helpmsg
= HelpExtract
; break;
172 case 'd': helpmsg
= HelpDiff
; break;
173 case 'm': helpmsg
= HelpMerge
; break;
175 fputs(helpmsg
, stderr
);
179 fputs(Version
, stderr
);
184 fputs(Usage
, stderr
);
194 fprintf(stderr
, "wiggle: mode is '%c' - cannot set to '%c'\n",
200 if (obj
== 0 || obj
== opt
) {
204 fprintf(stderr
, "wiggle: cannot select both words and lines.\n");
217 if (which
== 0 || which
== opt
) {
221 fprintf(stderr
, "wiggle: can only select one of -1, -2, -3\n");
228 case 'v': verbose
++; continue;
229 case 'q': quiet
=1 ; continue;
234 if (obj
&& mode
== 'x') {
235 fprintf(stderr
,"wiggle: cannot specify --line or --word with --extract\n");
238 if (mode
!= 'm' && !obj
) obj
= 'w';
239 if (replace
&& mode
!= 'm') {
240 fprintf(stderr
, "wiggle: --replace only allowed with --merge\n");
243 if (mode
== 'x' && !which
) {
244 fprintf(stderr
, "wiggle: must specify -1, -2 or -3 with --extract\n");
247 if (mode
!= 'x' && mode
!= 'd' && which
) {
248 fprintf(stderr
, "wiggle: -1, -2 or -3 only allowed with --extract or --diff\n");
251 if (ispatch
&& (mode
!= 'x' && mode
!= 'd')) {
252 fprintf(stderr
, "wiggle: --patch only allowed with --extract or --diff\n");
255 if (ispatch
&& which
== '3') {
256 fprintf(stderr
, "wiggle: cannot extract -3 from a patch.\n");
262 /* extract a branch of a diff or diff3 or merge output
265 if (optind
== argc
) {
266 fprintf(stderr
, "wiggle: no file given for --extract\n");
269 if (optind
< argc
-1) {
270 fprintf(stderr
, "wiggle: only give one file for --extract\n");
273 f
= load_file(argv
[optind
]);
275 fprintf(stderr
, "wiggle: cannot load file '%s' - %s\n",
276 argv
[optind
], strerror(errno
));
280 chunks1
= chunks2
= split_patch(f
, &flist
[0], &flist
[1]);
282 if (!split_merge(f
, &flist
[0], &flist
[1], &flist
[2])) {
283 fprintf(stderr
, "wiggle: merge file %s looks bad.\n",
288 if (flist
[which
-'1'].body
== NULL
) {
289 fprintf(stderr
, "wiggle: %s has no -%c component.\n",
290 argv
[optind
], which
);
293 write(1, flist
[which
-'1'].body
, flist
[which
-'1'].len
);
298 /* create a diff (line or char) of two streams */
299 switch (argc
-optind
) {
301 fprintf(stderr
, "wiggle: no file given for --diff\n");
304 f
= load_file(argv
[optind
]);
305 if (f
.body
== NULL
) {
306 fprintf(stderr
, "wiggle: cannot load file '%s' - %s\n",
307 argv
[optind
], strerror(errno
));
310 chunks1
= chunks2
= split_patch(f
, &flist
[0], &flist
[1]);
311 if (!flist
[0].body
|| !flist
[1].body
) {
312 fprintf(stderr
, "wiggle: couldn't parse patch %s\n",
318 flist
[0] = load_file(argv
[optind
]);
319 if (flist
[0].body
== NULL
) {
320 fprintf(stderr
, "wiggle: cannot load file '%s' - %s\n",
321 argv
[optind
], strerror(errno
));
325 f
= load_file(argv
[optind
+1]);
326 if (f
.body
== NULL
) {
327 fprintf(stderr
, "wiggle: cannot load patch '%s' - %s\n",
328 argv
[optind
], strerror(errno
));
332 chunks2
= chunks3
= split_patch(f
, &flist
[2], &flist
[1]);
334 chunks2
= chunks3
= split_patch(f
, &flist
[1], &flist
[2]);
337 flist
[1] = load_file(argv
[optind
+1]);
338 if (flist
[1].body
== NULL
) {
339 fprintf(stderr
, "wiggle: cannot load file '%s' - %s\n",
340 argv
[optind
+1], strerror(errno
));
345 fprintf(stderr
, "wiggle: too many files given for --diff\n");
355 fl
[0] = split_stream(flist
[0], ByLine
, 0);
356 fl
[1] = split_stream(flist
[1], ByLine
, 0);
357 if (chunks2
&& ! chunks1
)
358 csl1
= pdiff(fl
[0], fl
[1], chunks2
);
360 csl1
= diff(fl
[0], fl
[1]);
363 printf("@@ -1,%d +1,%d @@\n", fl
[0].elcnt
, fl
[1].elcnt
);
365 while (a
<fl
[0].elcnt
|| b
< fl
[1].elcnt
) {
367 if (fl
[0].list
[a
].start
[0]) {
369 printword(stdout
, fl
[0].list
[a
]);
373 } else if (b
< csl1
->b
) {
374 if (fl
[1].list
[b
].start
[0]) {
376 printword(stdout
, fl
[1].list
[b
]);
381 if (fl
[0].list
[a
].start
[0] == '\0')
382 printsep(fl
[0].list
[a
], fl
[1].list
[b
]);
385 printword(stdout
, fl
[0].list
[a
]);
389 if (a
>= csl1
->a
+csl1
->len
)
395 int sol
= 1; /* start of line */
396 fl
[0] = split_stream(flist
[0], ByWord
, 0);
397 fl
[1] = split_stream(flist
[1], ByWord
, 0);
398 if (chunks2
&& !chunks1
)
399 csl1
= pdiff(fl
[0], fl
[1], chunks2
);
401 csl1
= diff(fl
[0], fl
[1]);
404 /* count lines in each file */
407 for (i
=0;i
<fl
[0].elcnt
;i
++)
408 if (ends_line(fl
[0].list
[i
]))
410 for (i
=0;i
<fl
[1].elcnt
;i
++)
411 if (ends_line(fl
[1].list
[i
]))
413 printf("@@ -1,%d +1,%d @@\n", l1
,l2
);
416 while (a
< fl
[0].elcnt
|| b
< fl
[1].elcnt
) {
421 /* If we remove a whole line, output +line
422 * else clear sol and retry */
424 for (a1
=a
; a1
<csl1
->a
;a1
++)
425 if (ends_line(fl
[0].list
[a1
])) {
431 for (; a
<csl1
->a
; a
++) {
432 printword(stdout
, fl
[0].list
[a
]);
433 if (ends_line(fl
[0].list
[a
])) {
443 if (sol
) printf("|");
444 printword(stdout
, fl
[0].list
[a
]);
445 sol
= ends_line(fl
[0].list
[a
]);
447 } while (a
< csl1
->a
);
448 printf("%s-->>>", sol
?"|":"");
451 } else if (b
< csl1
->b
) {
456 for (b1
=b
; b1
<csl1
->b
;b1
++)
457 if(ends_line(fl
[1].list
[b1
])) {
463 for(; b
<csl1
->b
; b
++) {
464 printword(stdout
, fl
[1].list
[b
]);
465 if(ends_line(fl
[1].list
[b
])) {
475 if (sol
) printf("|");
476 printword(stdout
, fl
[1].list
[b
]);
477 sol
= ends_line(fl
[1].list
[b
]);
479 } while (b
< csl1
->b
);
480 printf("%s++>>>",sol
?"|":"");
487 for (a1
=a
; a1
<csl1
->a
+csl1
->len
; a1
++)
488 if (ends_line(fl
[0].list
[a1
]))
491 if (fl
[0].list
[a
].start
[0]) {
493 for(; a
<csl1
->a
+csl1
->len
; a
++,b
++) {
494 printword(stdout
, fl
[0].list
[a
]);
495 if (ends_line(fl
[0].list
[a
])) {
501 printsep(fl
[0].list
[a
], fl
[1].list
[b
]);
508 printword(stdout
, fl
[0].list
[a
]);
509 if (ends_line(fl
[0].list
[a
]))
514 if (a
>= csl1
->a
+csl1
->len
)
522 /* merge three files, A B C, so changed between B and C get made to A
524 switch (argc
-optind
) {
526 fprintf(stderr
, "wiggle: no files given for --merge\n");
531 for (i
=0; i
< argc
-optind
; i
++) {
532 flist
[i
] = load_file(argv
[optind
+i
]);
533 if (flist
[i
].body
== NULL
) {
534 fprintf(stderr
, "wiggle: cannot load file '%s' - %s\n",
535 argv
[optind
+i
], strerror(errno
));
541 fprintf(stderr
, "wiggle: too many files given for --merge\n");
544 switch(argc
-optind
) {
545 case 1: /* a merge file */
547 if (!split_merge(f
, &flist
[0], &flist
[1], &flist
[2])) {
548 fprintf(stderr
,"wiggle: merge file %s looks bad.\n",
553 case 2: /* a file and a patch */
555 chunks2
= chunks3
= split_patch(f
, &flist
[1], &flist
[2]);
557 case 3: /* three separate files */
566 for (i
=0; i
<3; i
++) {
567 if (flist
[i
].body
==NULL
) {
568 fprintf(stderr
, "wiggle: file %d missing\n", i
);
574 replacename
= malloc(strlen(argv
[optind
])+ 20);
575 if (!replacename
) die();
576 orignew
= malloc(strlen(argv
[optind
])+20);
578 strcpy(replacename
, argv
[optind
]);
579 strcpy(orignew
, argv
[optind
]);
580 strcat(orignew
, ".porig");
581 if (open(orignew
, O_RDONLY
) >= 0 ||
583 fprintf(stderr
,"wiggle: %s already exists\n",
587 strcat(replacename
,"XXXXXX");
588 fd
= mkstemp(replacename
);
590 fprintf(stderr
,"wiggle: could not create temporary file for %s\n",
594 outfile
= fdopen(fd
, "w");
599 fl
[0] = split_stream(flist
[0], ByLine
, 0);
600 fl
[1] = split_stream(flist
[1], ByLine
, 0);
601 fl
[2] = split_stream(flist
[2], ByLine
, 0);
603 fl
[0] = split_stream(flist
[0], ByWord
, 0);
604 fl
[1] = split_stream(flist
[1], ByWord
, 0);
605 fl
[2] = split_stream(flist
[2], ByWord
, 0);
607 if (chunks2
&& !chunks1
)
608 csl1
= pdiff(fl
[0], fl
[1], chunks2
);
610 csl1
= diff(fl
[0], fl
[1]);
611 csl2
= diff(fl
[1], fl
[2]);
614 cleanlist(fl
[0],fl
[1],csl1
);
615 cleanlist(fl
[1],fl
[2],csl2
);
621 ci
= print_merge(outfile
, &fl
[0], &fl
[1], &fl
[2],
622 csl1
, csl2
, obj
=='w');
623 if (!quiet
&& ci
.conflicts
)
624 fprintf(stderr
, "%d unresolved conflict%s found\n", ci
.conflicts
, ci
.conflicts
==1?"":"s");
625 if (!quiet
&& ci
.ignored
)
626 fprintf(stderr
, "%d already-applied change%s ignored\n", ci
.ignored
, ci
.ignored
==1?"":"s");
627 exit_status
= (ci
.conflicts
> 0);
631 if (rename(argv
[optind
], orignew
) ==0 &&
632 rename(replacename
, argv
[optind
]) ==0)
635 fprintf(stderr
, "wiggle: failed to move new file into place.\n");