Fossil SCM
More improvements to the 3-way merge algorithm.
Commit
83566f24241a01bedbf89d8ca068c7c1b926b4f0
Parent
434830cc0012516…
1 file changed
+154
-157
+154
-157
| --- src/merge3.c | ||
| +++ src/merge3.c | ||
| @@ -32,44 +32,102 @@ | ||
| 32 | 32 | #else |
| 33 | 33 | #define DEBUG(X) |
| 34 | 34 | #define ISDEBUG 0 |
| 35 | 35 | #endif |
| 36 | 36 | |
| 37 | -/* | |
| 38 | -** Opcodes. | |
| 39 | -** | |
| 40 | -** Values are important here. The text_diff() function returns an array | |
| 41 | -** of triples of integers where within each triple, the 0 element is | |
| 42 | -** the number of lines to copy, the 1 element is the number of lines to | |
| 43 | -** delete and the 2 element is the number of lines to insert. The CPY, | |
| 44 | -** DEL, and INS opcodes must correspond to these indices. | |
| 45 | -*/ | |
| 46 | -#define CPY 0 | |
| 47 | -#define DEL 1 | |
| 48 | -#define INS 2 | |
| 49 | -#define END 3 | |
| 50 | -#define UNK 4 | |
| 51 | - | |
| 52 | -/* | |
| 53 | -** Compare a single line of text from pV1 and pV2. If the lines | |
| 37 | +/* The minimum of two integers */ | |
| 38 | +#define min(A,B) (A<B?A:B) | |
| 39 | + | |
| 40 | +/* | |
| 41 | +** Compare N lines of text from pV1 and pV2. If the lines | |
| 54 | 42 | ** are the same, return true. Return false if they are different. |
| 55 | 43 | ** |
| 56 | 44 | ** The cursor on both pV1 and pV2 is unchanged. |
| 57 | 45 | */ |
| 58 | -static int sameLine(Blob *pV1, Blob *pV2){ | |
| 46 | +static int sameLines(Blob *pV1, Blob *pV2, int N){ | |
| 59 | 47 | char *z1, *z2; |
| 60 | 48 | int i; |
| 61 | 49 | |
| 50 | + if( N==0 ) return 1; | |
| 62 | 51 | z1 = &blob_buffer(pV1)[blob_tell(pV1)]; |
| 63 | 52 | z2 = &blob_buffer(pV2)[blob_tell(pV2)]; |
| 64 | - for(i=0; z1[i]!='\n' && z1[i]==z2[i]; i++){} | |
| 65 | - return z2[i]=='\n' || (z2[i]=='\r' && z2[i+1]=='\n') | |
| 66 | - || (z1[i]=='\r' && z2[i]=='\n' && z1[i+1]=='\n'); | |
| 53 | + for(i=0; z1[i]==z2[i]; i++){ | |
| 54 | + if( z1[i]=='\n' ){ | |
| 55 | + N--; | |
| 56 | + if( N==0 ) return 1; | |
| 57 | + } | |
| 58 | + } | |
| 59 | + return 0; | |
| 60 | +} | |
| 61 | + | |
| 62 | +/* | |
| 63 | +** Look at the next edit triple in both aC1 and aC2. If they describe | |
| 64 | +** an identical edit, then return 1. If the edits are different, | |
| 65 | +** return 0. | |
| 66 | +*/ | |
| 67 | +static int sameEdit(int *aC1, int *aC2, Blob *pV1, Blob *pV2){ | |
| 68 | + if( aC1[0]!=aC2[0] ) return 0; | |
| 69 | + if( aC1[1]!=aC2[1] ) return 0; | |
| 70 | + if( aC1[2]!=aC2[2] ) return 0; | |
| 71 | + if( sameLines(pV1, pV2, aC1[2]) ) return 1; | |
| 72 | + return 0; | |
| 73 | +} | |
| 74 | + | |
| 75 | +/* | |
| 76 | +** The aC[] array contains triples of integers. Within each triple, the | |
| 77 | +** elements are: | |
| 78 | +** | |
| 79 | +** (0) The number of lines to copy | |
| 80 | +** (1) The number of lines to delete | |
| 81 | +** (2) The number of liens to insert | |
| 82 | +** | |
| 83 | +** Suppose we want to advance over sz lines of the pivot. This routine | |
| 84 | +** returns true if that advance would land us on a copy operation. It | |
| 85 | +** returns false if the advance would end on a delete. | |
| 86 | +*/ | |
| 87 | +static int ends_at_CPY(int *aC, int sz){ | |
| 88 | + while( sz>0 && (aC[0]>0 || aC[1]>0 || aC[2]>0) ){ | |
| 89 | + if( aC[0]>=sz ) return 1; | |
| 90 | + sz -= aC[0]; | |
| 91 | + if( aC[1]>sz ) return 0; | |
| 92 | + sz -= aC[1]; | |
| 93 | + aC += 3; | |
| 94 | + } | |
| 95 | + return 1; | |
| 96 | +} | |
| 97 | + | |
| 98 | +/* | |
| 99 | +** pSrc contains an edited file where aC[] describes the edit. Part of | |
| 100 | +** pSrc has already been output. This routine outputs additional lines | |
| 101 | +** of pSrc - lines that correspond to the next sz lines of the original | |
| 102 | +** unedited file. | |
| 103 | +** | |
| 104 | +** The aC[] array is updated and the new index into aC[] is returned. | |
| 105 | +*/ | |
| 106 | +static int output_one_side( | |
| 107 | + Blob *pOut, /* Write to this blob */ | |
| 108 | + Blob *pSrc, /* The edited file that is to be copied to pOut */ | |
| 109 | + int *aC, /* Array of integer triples describing the edit */ | |
| 110 | + int i, /* Index in aC[] of current location in pSrc */ | |
| 111 | + int sz /* Number of lines in unedited source to output */ | |
| 112 | +){ | |
| 113 | + while( sz>0 ){ | |
| 114 | + if( aC[i]==0 && aC[i+1]==0 && aC[i+2]==0 ) break; | |
| 115 | + if( aC[i]>sz ){ | |
| 116 | + blob_copy_lines(pOut, pSrc, sz); | |
| 117 | + aC[i] -= sz; | |
| 118 | + break; | |
| 119 | + } | |
| 120 | + blob_copy_lines(pOut, pSrc, aC[i]); | |
| 121 | + blob_copy_lines(pOut, pSrc, aC[i+2]); | |
| 122 | + sz -= aC[i] + aC[i+1]; | |
| 123 | + i += 3; | |
| 124 | + } | |
| 125 | + return i; | |
| 67 | 126 | } |
| 68 | 127 | |
| 69 | -/* The minimum of two integers */ | |
| 70 | -#define min(A,B) (A<B?A:B) | |
| 128 | + | |
| 71 | 129 | |
| 72 | 130 | /* |
| 73 | 131 | ** Do a three-way merge. Initialize pOut to contain the result. |
| 74 | 132 | ** |
| 75 | 133 | ** The merge is an edit against pV2. Both pV1 and pV2 have a |
| @@ -83,23 +141,17 @@ | ||
| 83 | 141 | */ |
| 84 | 142 | int blob_merge(Blob *pPivot, Blob *pV1, Blob *pV2, Blob *pOut){ |
| 85 | 143 | int *aC1; /* Changes from pPivot to pV1 */ |
| 86 | 144 | int *aC2; /* Changes from pPivot to pV2 */ |
| 87 | 145 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 88 | - int op1, op2; /* Opcode for aC1[] and aC2[] */ | |
| 89 | - int n1, n2; /* Counts for op1 and op2 */ | |
| 90 | - int mn; /* Minimum count of op1 and op2 */ | |
| 146 | + int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ | |
| 91 | 147 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 92 | 148 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 93 | 149 | static const char zBegin[] = ">>>>>>> BEGIN MERGE CONFLICT\n"; |
| 94 | 150 | static const char zMid[] = "============================\n"; |
| 95 | 151 | static const char zEnd[] = "<<<<<<< END MERGE CONFLICT\n"; |
| 96 | 152 | |
| 97 | -#if ISDEBUG | |
| 98 | - static const char *zOp[] = { "CPY", "DEL", "INS", "END", "UNK" }; | |
| 99 | -#endif | |
| 100 | - | |
| 101 | 153 | /* Compute the edits that occur from pPivot => pV1 and pPivot => pV2 */ |
| 102 | 154 | aC1 = text_diff(pPivot, pV1, 0, 0); |
| 103 | 155 | aC2 = text_diff(pPivot, pV2, 0, 0); |
| 104 | 156 | if( aC2==0 || aC2==0 ){ |
| 105 | 157 | free(aC1); |
| @@ -125,137 +177,82 @@ | ||
| 125 | 177 | for(i2=0; i2<limit2; i2+=3){ |
| 126 | 178 | printf("c2: %4d %4d %4d\n", aC2[i2], aC2[i2+1], aC2[i2+2]); |
| 127 | 179 | } |
| 128 | 180 | ) |
| 129 | 181 | |
| 130 | - op1 = op2 = UNK; | |
| 131 | - i1 = i2 = -1; | |
| 132 | - n1 = n2 = 0; | |
| 133 | - while(1){ | |
| 134 | - if( op1==UNK ){ | |
| 135 | - if( n1 ){ | |
| 136 | - op1 = i1 % 3; | |
| 137 | - }else{ | |
| 138 | - i1++; | |
| 139 | - while( i1<limit1 && aC1[i1]==0 ){ i1++; } | |
| 140 | - if( i1>=limit1 ){ | |
| 141 | - op1 = END; | |
| 142 | - }else{ | |
| 143 | - op1 = i1 % 3; | |
| 144 | - n1 = aC1[i1]; | |
| 145 | - } | |
| 146 | - } | |
| 147 | - } | |
| 148 | - if( op2==UNK ){ | |
| 149 | - if( n2 ){ | |
| 150 | - op2 = i2 % 3; | |
| 151 | - }else{ | |
| 152 | - i2++; | |
| 153 | - while( i2<limit2 && aC2[i2]==0 ){ i2++; } | |
| 154 | - if( i2>=limit2 ){ | |
| 155 | - op2 = END; | |
| 156 | - }else{ | |
| 157 | - op2 = i2 % 3; | |
| 158 | - n2 = aC2[i2]; | |
| 159 | - } | |
| 160 | - } | |
| 161 | - } | |
| 162 | - DEBUG( printf("op1=%s(%d) op2=%s(%d)\n", zOp[op1], n1, zOp[op2], n2); ) | |
| 163 | - if( op1==END ){ | |
| 164 | - if( op2==INS ){ | |
| 165 | - DEBUG( printf("INSERT %d FROM 2\n", n2); ) | |
| 166 | - blob_copy_lines(pOut, pV2, n2); | |
| 167 | - } | |
| 168 | - break; | |
| 169 | - }else if( op2==END ){ | |
| 170 | - if( op1==INS ){ | |
| 171 | - DEBUG( printf("INSERT %d FROM 1\n", n1); ) | |
| 172 | - blob_copy_lines(pOut, pV1, n1); | |
| 173 | - } | |
| 174 | - break; | |
| 175 | - }else if( op1==CPY && op2==CPY ){ | |
| 176 | - mn = min(n1,n2); | |
| 177 | - DEBUG( printf("COPY %d\n", mn); ) | |
| 178 | - blob_copy_lines(pOut, pPivot, mn); | |
| 179 | - blob_copy_lines(0, pV1, mn); | |
| 180 | - blob_copy_lines(0, pV2, mn); | |
| 181 | - n1 -= mn; | |
| 182 | - n2 -= mn; | |
| 183 | - op1 = op2 = UNK; | |
| 184 | - }else if( op1==DEL && op2==DEL ){ | |
| 185 | - mn = min(n1,n2); | |
| 186 | - DEBUG( printf("SKIP %d both\n", mn); ) | |
| 187 | - blob_copy_lines(0, pPivot, mn); | |
| 188 | - n1 -= mn; | |
| 189 | - n2 -= mn; | |
| 190 | - op1 = op2 = UNK; | |
| 191 | - }else if( op1==INS && op2==INS && sameLine(pV1, pV2) ){ | |
| 192 | - DEBUG( printf("DUPLICATE INSERT\n"); ) | |
| 193 | - blob_copy_lines(pOut, pV2, 1); | |
| 194 | - blob_copy_lines(0, pV1, 1); | |
| 195 | - n1--; | |
| 196 | - n2--; | |
| 197 | - op1 = op2 = UNK; | |
| 198 | - }else if( op1==CPY && op2==DEL ){ | |
| 199 | - mn = min(n1,n2); | |
| 200 | - DEBUG( printf("SKIP %d two\n", mn); ) | |
| 201 | - blob_copy_lines(0, pPivot, mn); | |
| 202 | - blob_copy_lines(0, pV1, mn); | |
| 203 | - n1 -= mn; | |
| 204 | - n2 -= mn; | |
| 205 | - op1 = op2 = UNK; | |
| 206 | - }else if( op2==CPY && op1==DEL ){ | |
| 207 | - mn = min(n1,n2); | |
| 208 | - DEBUG( printf("SKIP %d one\n", mn); ) | |
| 209 | - blob_copy_lines(0, pPivot, mn); | |
| 210 | - blob_copy_lines(0, pV2, mn); | |
| 211 | - n2 -= mn; | |
| 212 | - n1 -= mn; | |
| 213 | - op1 = op2 = UNK; | |
| 214 | - }else if( op1==CPY && op2==INS ){ | |
| 215 | - DEBUG( printf("INSERT %d two\n", n2); ) | |
| 216 | - blob_copy_lines(pOut, pV2, n2); | |
| 217 | - n2 = 0; | |
| 218 | - op2 = UNK; | |
| 219 | - }else if( op2==CPY && op1==INS ){ | |
| 220 | - DEBUG( printf("INSERT %d one\n", n1); ) | |
| 221 | - blob_copy_lines(pOut, pV1, n1); | |
| 222 | - n1 = 0; | |
| 223 | - op1 = UNK; | |
| 224 | - }else{ | |
| 225 | - int toSkip = 0; | |
| 226 | - nConflict++; | |
| 227 | - DEBUG( printf("CONFLICT\n"); ) | |
| 228 | - blob_appendf(pOut, zBegin); | |
| 229 | - if( op1==DEL ){ | |
| 230 | - toSkip = n1; | |
| 231 | - i1++; | |
| 232 | - if( aC1[i1] ){ | |
| 233 | - blob_copy_lines(pOut, pV1, aC1[i1]); | |
| 234 | - } | |
| 235 | - }else{ | |
| 236 | - blob_copy_lines(pOut, pV1, n1); | |
| 237 | - } | |
| 238 | - n1 = 0; | |
| 239 | - op1 = UNK; | |
| 240 | - blob_appendf(pOut, zMid); | |
| 241 | - if( op2==DEL ){ | |
| 242 | - blob_copy_lines(0, pPivot, n2); | |
| 243 | - i2++; | |
| 244 | - if( aC2[i2] ){ | |
| 245 | - blob_copy_lines(pOut, pV2, aC2[i2]); | |
| 246 | - } | |
| 247 | - }else{ | |
| 248 | - blob_copy_lines(pOut, pV2, n2); | |
| 249 | - } | |
| 250 | - if( toSkip ){ | |
| 251 | - blob_copy_lines(0, pPivot, toSkip); | |
| 252 | - } | |
| 253 | - n2 = 0; | |
| 254 | - op2 = UNK; | |
| 255 | - blob_appendf(pOut, zEnd); | |
| 256 | - } | |
| 182 | + i1 = i2 = 0; | |
| 183 | + while( i1<limit1 && i2<limit2 ){ | |
| 184 | + DEBUG( printf("%d: %2d %2d %2d %d: %2d %2d %2d\n", | |
| 185 | + i1/3, aC1[i1], aC1[i1+1], aC1[i1+2], | |
| 186 | + i2/3, aC2[i2], aC2[i2+1], aC2[i2+2]); ) | |
| 187 | + | |
| 188 | + if( aC1[i1]>0 && aC2[i2]>0 ){ | |
| 189 | + nCpy = min(aC1[i1], aC2[i2]); | |
| 190 | + DEBUG( printf("COPY %d\n", nCpy); ) | |
| 191 | + blob_copy_lines(pOut, pPivot, nCpy); | |
| 192 | + blob_copy_lines(0, pV1, nCpy); | |
| 193 | + blob_copy_lines(0, pV2, nCpy); | |
| 194 | + aC1[i1] -= nCpy; | |
| 195 | + aC2[i2] -= nCpy; | |
| 196 | + }else | |
| 197 | + if( aC1[i1] >= aC2[i2+1] && aC2[i2+1]+aC2[i2+2]>0 ){ | |
| 198 | + nDel = aC2[i2+1]; | |
| 199 | + nIns = aC2[i2+2]; | |
| 200 | + DEBUG( printf("EDIT -%d+%d left\n", nDel, nIns); ) | |
| 201 | + blob_copy_lines(0, pPivot, nDel); | |
| 202 | + blob_copy_lines(0, pV1, nDel); | |
| 203 | + blob_copy_lines(pOut, pV2, nIns); | |
| 204 | + aC1[i1] -= nDel; | |
| 205 | + i2 += 3; | |
| 206 | + }else | |
| 207 | + if( aC2[i2] >= aC1[i1+1] && aC1[i1+1]+aC1[i1+2]>0 ){ | |
| 208 | + nDel = aC1[i1+1]; | |
| 209 | + nIns = aC1[i1+2]; | |
| 210 | + DEBUG( printf("EDIT -%d+%d right\n", nDel, nIns); ) | |
| 211 | + blob_copy_lines(0, pPivot, nDel); | |
| 212 | + blob_copy_lines(0, pV2, nDel); | |
| 213 | + blob_copy_lines(pOut, pV1, nIns); | |
| 214 | + aC2[i2] -= nDel; | |
| 215 | + i1 += 3; | |
| 216 | + }else | |
| 217 | + if( sameEdit(&aC1[i1], &aC2[i2], pV1, pV2) ){ | |
| 218 | + assert( aC1[i1]==0 ); | |
| 219 | + nDel = aC1[i1+1]; | |
| 220 | + nIns = aC1[i1+2]; | |
| 221 | + DEBUG( printf("EDIT -%d+%d both\n", nDel, nIns); ) | |
| 222 | + blob_copy_lines(0, pPivot, nDel); | |
| 223 | + blob_copy_lines(pOut, pV1, nIns); | |
| 224 | + blob_copy_lines(0, pV2, nIns); | |
| 225 | + i1 += 3; | |
| 226 | + i2 += 3; | |
| 227 | + }else | |
| 228 | + { | |
| 229 | + int sz = 1; /* Size of the conflict in lines */ | |
| 230 | + nConflict++; | |
| 231 | + while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){ | |
| 232 | + sz++; | |
| 233 | + } | |
| 234 | + DEBUG( printf("CONFLICT %d\n", sz); ) | |
| 235 | + blob_appendf(pOut, zBegin); | |
| 236 | + i1 = output_one_side(pOut, pV1, aC1, i1, sz); | |
| 237 | + blob_appendf(pOut, zMid); | |
| 238 | + i2 = output_one_side(pOut, pV2, aC2, i2, sz); | |
| 239 | + blob_appendf(pOut, zEnd); | |
| 240 | + blob_copy_lines(0, pPivot, sz); | |
| 241 | + } | |
| 242 | + if( i1<limit1 && aC1[i1]==0 && aC1[i1+1]==0 && aC1[i1+2]==0 ) i1+=3; | |
| 243 | + if( i2<limit2 && aC2[i2]==0 && aC2[i2+1]==0 && aC2[i2+2]==0 ) i2+=3; | |
| 244 | + } | |
| 245 | + DEBUG( printf("%d: %2d %2d %2d %d: %2d %2d %2d\n", | |
| 246 | + i1/3, aC1[i1], aC1[i1+1], aC1[i1+2], | |
| 247 | + i2/3, aC2[i2], aC2[i2+1], aC2[i2+2]); ) | |
| 248 | + if( i1<limit1 && aC1[i1+2]>0 ){ | |
| 249 | + DEBUG( printf("INSERT +%d left\n", aC1[i1+2]); ) | |
| 250 | + blob_copy_lines(pOut, pV1, aC1[i1+2]); | |
| 251 | + }else if( i2<limit2 && aC2[i2+2]>0 ){ | |
| 252 | + DEBUG( printf("INSERT +%d right\n", aC2[i2+2]); ) | |
| 253 | + blob_copy_lines(pOut, pV2, aC2[i2+2]); | |
| 257 | 254 | } |
| 258 | 255 | |
| 259 | 256 | free(aC1); |
| 260 | 257 | free(aC2); |
| 261 | 258 | return nConflict; |
| 262 | 259 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -32,44 +32,102 @@ | |
| 32 | #else |
| 33 | #define DEBUG(X) |
| 34 | #define ISDEBUG 0 |
| 35 | #endif |
| 36 | |
| 37 | /* |
| 38 | ** Opcodes. |
| 39 | ** |
| 40 | ** Values are important here. The text_diff() function returns an array |
| 41 | ** of triples of integers where within each triple, the 0 element is |
| 42 | ** the number of lines to copy, the 1 element is the number of lines to |
| 43 | ** delete and the 2 element is the number of lines to insert. The CPY, |
| 44 | ** DEL, and INS opcodes must correspond to these indices. |
| 45 | */ |
| 46 | #define CPY 0 |
| 47 | #define DEL 1 |
| 48 | #define INS 2 |
| 49 | #define END 3 |
| 50 | #define UNK 4 |
| 51 | |
| 52 | /* |
| 53 | ** Compare a single line of text from pV1 and pV2. If the lines |
| 54 | ** are the same, return true. Return false if they are different. |
| 55 | ** |
| 56 | ** The cursor on both pV1 and pV2 is unchanged. |
| 57 | */ |
| 58 | static int sameLine(Blob *pV1, Blob *pV2){ |
| 59 | char *z1, *z2; |
| 60 | int i; |
| 61 | |
| 62 | z1 = &blob_buffer(pV1)[blob_tell(pV1)]; |
| 63 | z2 = &blob_buffer(pV2)[blob_tell(pV2)]; |
| 64 | for(i=0; z1[i]!='\n' && z1[i]==z2[i]; i++){} |
| 65 | return z2[i]=='\n' || (z2[i]=='\r' && z2[i+1]=='\n') |
| 66 | || (z1[i]=='\r' && z2[i]=='\n' && z1[i+1]=='\n'); |
| 67 | } |
| 68 | |
| 69 | /* The minimum of two integers */ |
| 70 | #define min(A,B) (A<B?A:B) |
| 71 | |
| 72 | /* |
| 73 | ** Do a three-way merge. Initialize pOut to contain the result. |
| 74 | ** |
| 75 | ** The merge is an edit against pV2. Both pV1 and pV2 have a |
| @@ -83,23 +141,17 @@ | |
| 83 | */ |
| 84 | int blob_merge(Blob *pPivot, Blob *pV1, Blob *pV2, Blob *pOut){ |
| 85 | int *aC1; /* Changes from pPivot to pV1 */ |
| 86 | int *aC2; /* Changes from pPivot to pV2 */ |
| 87 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 88 | int op1, op2; /* Opcode for aC1[] and aC2[] */ |
| 89 | int n1, n2; /* Counts for op1 and op2 */ |
| 90 | int mn; /* Minimum count of op1 and op2 */ |
| 91 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 92 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 93 | static const char zBegin[] = ">>>>>>> BEGIN MERGE CONFLICT\n"; |
| 94 | static const char zMid[] = "============================\n"; |
| 95 | static const char zEnd[] = "<<<<<<< END MERGE CONFLICT\n"; |
| 96 | |
| 97 | #if ISDEBUG |
| 98 | static const char *zOp[] = { "CPY", "DEL", "INS", "END", "UNK" }; |
| 99 | #endif |
| 100 | |
| 101 | /* Compute the edits that occur from pPivot => pV1 and pPivot => pV2 */ |
| 102 | aC1 = text_diff(pPivot, pV1, 0, 0); |
| 103 | aC2 = text_diff(pPivot, pV2, 0, 0); |
| 104 | if( aC2==0 || aC2==0 ){ |
| 105 | free(aC1); |
| @@ -125,137 +177,82 @@ | |
| 125 | for(i2=0; i2<limit2; i2+=3){ |
| 126 | printf("c2: %4d %4d %4d\n", aC2[i2], aC2[i2+1], aC2[i2+2]); |
| 127 | } |
| 128 | ) |
| 129 | |
| 130 | op1 = op2 = UNK; |
| 131 | i1 = i2 = -1; |
| 132 | n1 = n2 = 0; |
| 133 | while(1){ |
| 134 | if( op1==UNK ){ |
| 135 | if( n1 ){ |
| 136 | op1 = i1 % 3; |
| 137 | }else{ |
| 138 | i1++; |
| 139 | while( i1<limit1 && aC1[i1]==0 ){ i1++; } |
| 140 | if( i1>=limit1 ){ |
| 141 | op1 = END; |
| 142 | }else{ |
| 143 | op1 = i1 % 3; |
| 144 | n1 = aC1[i1]; |
| 145 | } |
| 146 | } |
| 147 | } |
| 148 | if( op2==UNK ){ |
| 149 | if( n2 ){ |
| 150 | op2 = i2 % 3; |
| 151 | }else{ |
| 152 | i2++; |
| 153 | while( i2<limit2 && aC2[i2]==0 ){ i2++; } |
| 154 | if( i2>=limit2 ){ |
| 155 | op2 = END; |
| 156 | }else{ |
| 157 | op2 = i2 % 3; |
| 158 | n2 = aC2[i2]; |
| 159 | } |
| 160 | } |
| 161 | } |
| 162 | DEBUG( printf("op1=%s(%d) op2=%s(%d)\n", zOp[op1], n1, zOp[op2], n2); ) |
| 163 | if( op1==END ){ |
| 164 | if( op2==INS ){ |
| 165 | DEBUG( printf("INSERT %d FROM 2\n", n2); ) |
| 166 | blob_copy_lines(pOut, pV2, n2); |
| 167 | } |
| 168 | break; |
| 169 | }else if( op2==END ){ |
| 170 | if( op1==INS ){ |
| 171 | DEBUG( printf("INSERT %d FROM 1\n", n1); ) |
| 172 | blob_copy_lines(pOut, pV1, n1); |
| 173 | } |
| 174 | break; |
| 175 | }else if( op1==CPY && op2==CPY ){ |
| 176 | mn = min(n1,n2); |
| 177 | DEBUG( printf("COPY %d\n", mn); ) |
| 178 | blob_copy_lines(pOut, pPivot, mn); |
| 179 | blob_copy_lines(0, pV1, mn); |
| 180 | blob_copy_lines(0, pV2, mn); |
| 181 | n1 -= mn; |
| 182 | n2 -= mn; |
| 183 | op1 = op2 = UNK; |
| 184 | }else if( op1==DEL && op2==DEL ){ |
| 185 | mn = min(n1,n2); |
| 186 | DEBUG( printf("SKIP %d both\n", mn); ) |
| 187 | blob_copy_lines(0, pPivot, mn); |
| 188 | n1 -= mn; |
| 189 | n2 -= mn; |
| 190 | op1 = op2 = UNK; |
| 191 | }else if( op1==INS && op2==INS && sameLine(pV1, pV2) ){ |
| 192 | DEBUG( printf("DUPLICATE INSERT\n"); ) |
| 193 | blob_copy_lines(pOut, pV2, 1); |
| 194 | blob_copy_lines(0, pV1, 1); |
| 195 | n1--; |
| 196 | n2--; |
| 197 | op1 = op2 = UNK; |
| 198 | }else if( op1==CPY && op2==DEL ){ |
| 199 | mn = min(n1,n2); |
| 200 | DEBUG( printf("SKIP %d two\n", mn); ) |
| 201 | blob_copy_lines(0, pPivot, mn); |
| 202 | blob_copy_lines(0, pV1, mn); |
| 203 | n1 -= mn; |
| 204 | n2 -= mn; |
| 205 | op1 = op2 = UNK; |
| 206 | }else if( op2==CPY && op1==DEL ){ |
| 207 | mn = min(n1,n2); |
| 208 | DEBUG( printf("SKIP %d one\n", mn); ) |
| 209 | blob_copy_lines(0, pPivot, mn); |
| 210 | blob_copy_lines(0, pV2, mn); |
| 211 | n2 -= mn; |
| 212 | n1 -= mn; |
| 213 | op1 = op2 = UNK; |
| 214 | }else if( op1==CPY && op2==INS ){ |
| 215 | DEBUG( printf("INSERT %d two\n", n2); ) |
| 216 | blob_copy_lines(pOut, pV2, n2); |
| 217 | n2 = 0; |
| 218 | op2 = UNK; |
| 219 | }else if( op2==CPY && op1==INS ){ |
| 220 | DEBUG( printf("INSERT %d one\n", n1); ) |
| 221 | blob_copy_lines(pOut, pV1, n1); |
| 222 | n1 = 0; |
| 223 | op1 = UNK; |
| 224 | }else{ |
| 225 | int toSkip = 0; |
| 226 | nConflict++; |
| 227 | DEBUG( printf("CONFLICT\n"); ) |
| 228 | blob_appendf(pOut, zBegin); |
| 229 | if( op1==DEL ){ |
| 230 | toSkip = n1; |
| 231 | i1++; |
| 232 | if( aC1[i1] ){ |
| 233 | blob_copy_lines(pOut, pV1, aC1[i1]); |
| 234 | } |
| 235 | }else{ |
| 236 | blob_copy_lines(pOut, pV1, n1); |
| 237 | } |
| 238 | n1 = 0; |
| 239 | op1 = UNK; |
| 240 | blob_appendf(pOut, zMid); |
| 241 | if( op2==DEL ){ |
| 242 | blob_copy_lines(0, pPivot, n2); |
| 243 | i2++; |
| 244 | if( aC2[i2] ){ |
| 245 | blob_copy_lines(pOut, pV2, aC2[i2]); |
| 246 | } |
| 247 | }else{ |
| 248 | blob_copy_lines(pOut, pV2, n2); |
| 249 | } |
| 250 | if( toSkip ){ |
| 251 | blob_copy_lines(0, pPivot, toSkip); |
| 252 | } |
| 253 | n2 = 0; |
| 254 | op2 = UNK; |
| 255 | blob_appendf(pOut, zEnd); |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | free(aC1); |
| 260 | free(aC2); |
| 261 | return nConflict; |
| 262 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -32,44 +32,102 @@ | |
| 32 | #else |
| 33 | #define DEBUG(X) |
| 34 | #define ISDEBUG 0 |
| 35 | #endif |
| 36 | |
| 37 | /* The minimum of two integers */ |
| 38 | #define min(A,B) (A<B?A:B) |
| 39 | |
| 40 | /* |
| 41 | ** Compare N lines of text from pV1 and pV2. If the lines |
| 42 | ** are the same, return true. Return false if they are different. |
| 43 | ** |
| 44 | ** The cursor on both pV1 and pV2 is unchanged. |
| 45 | */ |
| 46 | static int sameLines(Blob *pV1, Blob *pV2, int N){ |
| 47 | char *z1, *z2; |
| 48 | int i; |
| 49 | |
| 50 | if( N==0 ) return 1; |
| 51 | z1 = &blob_buffer(pV1)[blob_tell(pV1)]; |
| 52 | z2 = &blob_buffer(pV2)[blob_tell(pV2)]; |
| 53 | for(i=0; z1[i]==z2[i]; i++){ |
| 54 | if( z1[i]=='\n' ){ |
| 55 | N--; |
| 56 | if( N==0 ) return 1; |
| 57 | } |
| 58 | } |
| 59 | return 0; |
| 60 | } |
| 61 | |
| 62 | /* |
| 63 | ** Look at the next edit triple in both aC1 and aC2. If they describe |
| 64 | ** an identical edit, then return 1. If the edits are different, |
| 65 | ** return 0. |
| 66 | */ |
| 67 | static int sameEdit(int *aC1, int *aC2, Blob *pV1, Blob *pV2){ |
| 68 | if( aC1[0]!=aC2[0] ) return 0; |
| 69 | if( aC1[1]!=aC2[1] ) return 0; |
| 70 | if( aC1[2]!=aC2[2] ) return 0; |
| 71 | if( sameLines(pV1, pV2, aC1[2]) ) return 1; |
| 72 | return 0; |
| 73 | } |
| 74 | |
| 75 | /* |
| 76 | ** The aC[] array contains triples of integers. Within each triple, the |
| 77 | ** elements are: |
| 78 | ** |
| 79 | ** (0) The number of lines to copy |
| 80 | ** (1) The number of lines to delete |
| 81 | ** (2) The number of liens to insert |
| 82 | ** |
| 83 | ** Suppose we want to advance over sz lines of the pivot. This routine |
| 84 | ** returns true if that advance would land us on a copy operation. It |
| 85 | ** returns false if the advance would end on a delete. |
| 86 | */ |
| 87 | static int ends_at_CPY(int *aC, int sz){ |
| 88 | while( sz>0 && (aC[0]>0 || aC[1]>0 || aC[2]>0) ){ |
| 89 | if( aC[0]>=sz ) return 1; |
| 90 | sz -= aC[0]; |
| 91 | if( aC[1]>sz ) return 0; |
| 92 | sz -= aC[1]; |
| 93 | aC += 3; |
| 94 | } |
| 95 | return 1; |
| 96 | } |
| 97 | |
| 98 | /* |
| 99 | ** pSrc contains an edited file where aC[] describes the edit. Part of |
| 100 | ** pSrc has already been output. This routine outputs additional lines |
| 101 | ** of pSrc - lines that correspond to the next sz lines of the original |
| 102 | ** unedited file. |
| 103 | ** |
| 104 | ** The aC[] array is updated and the new index into aC[] is returned. |
| 105 | */ |
| 106 | static int output_one_side( |
| 107 | Blob *pOut, /* Write to this blob */ |
| 108 | Blob *pSrc, /* The edited file that is to be copied to pOut */ |
| 109 | int *aC, /* Array of integer triples describing the edit */ |
| 110 | int i, /* Index in aC[] of current location in pSrc */ |
| 111 | int sz /* Number of lines in unedited source to output */ |
| 112 | ){ |
| 113 | while( sz>0 ){ |
| 114 | if( aC[i]==0 && aC[i+1]==0 && aC[i+2]==0 ) break; |
| 115 | if( aC[i]>sz ){ |
| 116 | blob_copy_lines(pOut, pSrc, sz); |
| 117 | aC[i] -= sz; |
| 118 | break; |
| 119 | } |
| 120 | blob_copy_lines(pOut, pSrc, aC[i]); |
| 121 | blob_copy_lines(pOut, pSrc, aC[i+2]); |
| 122 | sz -= aC[i] + aC[i+1]; |
| 123 | i += 3; |
| 124 | } |
| 125 | return i; |
| 126 | } |
| 127 | |
| 128 | |
| 129 | |
| 130 | /* |
| 131 | ** Do a three-way merge. Initialize pOut to contain the result. |
| 132 | ** |
| 133 | ** The merge is an edit against pV2. Both pV1 and pV2 have a |
| @@ -83,23 +141,17 @@ | |
| 141 | */ |
| 142 | int blob_merge(Blob *pPivot, Blob *pV1, Blob *pV2, Blob *pOut){ |
| 143 | int *aC1; /* Changes from pPivot to pV1 */ |
| 144 | int *aC2; /* Changes from pPivot to pV2 */ |
| 145 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 146 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 147 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 148 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 149 | static const char zBegin[] = ">>>>>>> BEGIN MERGE CONFLICT\n"; |
| 150 | static const char zMid[] = "============================\n"; |
| 151 | static const char zEnd[] = "<<<<<<< END MERGE CONFLICT\n"; |
| 152 | |
| 153 | /* Compute the edits that occur from pPivot => pV1 and pPivot => pV2 */ |
| 154 | aC1 = text_diff(pPivot, pV1, 0, 0); |
| 155 | aC2 = text_diff(pPivot, pV2, 0, 0); |
| 156 | if( aC2==0 || aC2==0 ){ |
| 157 | free(aC1); |
| @@ -125,137 +177,82 @@ | |
| 177 | for(i2=0; i2<limit2; i2+=3){ |
| 178 | printf("c2: %4d %4d %4d\n", aC2[i2], aC2[i2+1], aC2[i2+2]); |
| 179 | } |
| 180 | ) |
| 181 | |
| 182 | i1 = i2 = 0; |
| 183 | while( i1<limit1 && i2<limit2 ){ |
| 184 | DEBUG( printf("%d: %2d %2d %2d %d: %2d %2d %2d\n", |
| 185 | i1/3, aC1[i1], aC1[i1+1], aC1[i1+2], |
| 186 | i2/3, aC2[i2], aC2[i2+1], aC2[i2+2]); ) |
| 187 | |
| 188 | if( aC1[i1]>0 && aC2[i2]>0 ){ |
| 189 | nCpy = min(aC1[i1], aC2[i2]); |
| 190 | DEBUG( printf("COPY %d\n", nCpy); ) |
| 191 | blob_copy_lines(pOut, pPivot, nCpy); |
| 192 | blob_copy_lines(0, pV1, nCpy); |
| 193 | blob_copy_lines(0, pV2, nCpy); |
| 194 | aC1[i1] -= nCpy; |
| 195 | aC2[i2] -= nCpy; |
| 196 | }else |
| 197 | if( aC1[i1] >= aC2[i2+1] && aC2[i2+1]+aC2[i2+2]>0 ){ |
| 198 | nDel = aC2[i2+1]; |
| 199 | nIns = aC2[i2+2]; |
| 200 | DEBUG( printf("EDIT -%d+%d left\n", nDel, nIns); ) |
| 201 | blob_copy_lines(0, pPivot, nDel); |
| 202 | blob_copy_lines(0, pV1, nDel); |
| 203 | blob_copy_lines(pOut, pV2, nIns); |
| 204 | aC1[i1] -= nDel; |
| 205 | i2 += 3; |
| 206 | }else |
| 207 | if( aC2[i2] >= aC1[i1+1] && aC1[i1+1]+aC1[i1+2]>0 ){ |
| 208 | nDel = aC1[i1+1]; |
| 209 | nIns = aC1[i1+2]; |
| 210 | DEBUG( printf("EDIT -%d+%d right\n", nDel, nIns); ) |
| 211 | blob_copy_lines(0, pPivot, nDel); |
| 212 | blob_copy_lines(0, pV2, nDel); |
| 213 | blob_copy_lines(pOut, pV1, nIns); |
| 214 | aC2[i2] -= nDel; |
| 215 | i1 += 3; |
| 216 | }else |
| 217 | if( sameEdit(&aC1[i1], &aC2[i2], pV1, pV2) ){ |
| 218 | assert( aC1[i1]==0 ); |
| 219 | nDel = aC1[i1+1]; |
| 220 | nIns = aC1[i1+2]; |
| 221 | DEBUG( printf("EDIT -%d+%d both\n", nDel, nIns); ) |
| 222 | blob_copy_lines(0, pPivot, nDel); |
| 223 | blob_copy_lines(pOut, pV1, nIns); |
| 224 | blob_copy_lines(0, pV2, nIns); |
| 225 | i1 += 3; |
| 226 | i2 += 3; |
| 227 | }else |
| 228 | { |
| 229 | int sz = 1; /* Size of the conflict in lines */ |
| 230 | nConflict++; |
| 231 | while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){ |
| 232 | sz++; |
| 233 | } |
| 234 | DEBUG( printf("CONFLICT %d\n", sz); ) |
| 235 | blob_appendf(pOut, zBegin); |
| 236 | i1 = output_one_side(pOut, pV1, aC1, i1, sz); |
| 237 | blob_appendf(pOut, zMid); |
| 238 | i2 = output_one_side(pOut, pV2, aC2, i2, sz); |
| 239 | blob_appendf(pOut, zEnd); |
| 240 | blob_copy_lines(0, pPivot, sz); |
| 241 | } |
| 242 | if( i1<limit1 && aC1[i1]==0 && aC1[i1+1]==0 && aC1[i1+2]==0 ) i1+=3; |
| 243 | if( i2<limit2 && aC2[i2]==0 && aC2[i2+1]==0 && aC2[i2+2]==0 ) i2+=3; |
| 244 | } |
| 245 | DEBUG( printf("%d: %2d %2d %2d %d: %2d %2d %2d\n", |
| 246 | i1/3, aC1[i1], aC1[i1+1], aC1[i1+2], |
| 247 | i2/3, aC2[i2], aC2[i2+1], aC2[i2+2]); ) |
| 248 | if( i1<limit1 && aC1[i1+2]>0 ){ |
| 249 | DEBUG( printf("INSERT +%d left\n", aC1[i1+2]); ) |
| 250 | blob_copy_lines(pOut, pV1, aC1[i1+2]); |
| 251 | }else if( i2<limit2 && aC2[i2+2]>0 ){ |
| 252 | DEBUG( printf("INSERT +%d right\n", aC2[i2+2]); ) |
| 253 | blob_copy_lines(pOut, pV2, aC2[i2+2]); |
| 254 | } |
| 255 | |
| 256 | free(aC1); |
| 257 | free(aC2); |
| 258 | return nConflict; |
| 259 |