Fossil SCM
Improve merge conflict markings.
Commit
e45d478f0c37add20a1dad418706b42745b1911e
Parent
f6790b7c3cc237f…
1 file changed
+151
-96
+151
-96
| --- src/merge3.c | ||
| +++ src/merge3.c | ||
| @@ -24,18 +24,26 @@ | ||
| 24 | 24 | ** This module implements a 3-way merge |
| 25 | 25 | */ |
| 26 | 26 | #include "config.h" |
| 27 | 27 | #include "merge3.h" |
| 28 | 28 | |
| 29 | -#if 0 | |
| 29 | +#if 1 | |
| 30 | 30 | #define DEBUG(X) X |
| 31 | +#define ISDEBUG 1 | |
| 31 | 32 | #else |
| 32 | 33 | #define DEBUG(X) |
| 34 | +#define ISDEBUG 0 | |
| 33 | 35 | #endif |
| 34 | 36 | |
| 35 | 37 | /* |
| 36 | -** Opcodes | |
| 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. | |
| 37 | 45 | */ |
| 38 | 46 | #define CPY 0 |
| 39 | 47 | #define DEL 1 |
| 40 | 48 | #define INS 2 |
| 41 | 49 | #define END 3 |
| @@ -56,10 +64,13 @@ | ||
| 56 | 64 | for(i=0; z1[i]!='\n' && z1[i]==z2[i]; i++){} |
| 57 | 65 | return z2[i]=='\n' || (z2[i]=='\r' && z2[i+1]=='\n') |
| 58 | 66 | || (z1[i]=='\r' && z2[i]=='\n' && z1[i+1]=='\n'); |
| 59 | 67 | } |
| 60 | 68 | |
| 69 | +/* The minimum of two integers */ | |
| 70 | +#define min(A,B) (A<B?A:B) | |
| 71 | + | |
| 61 | 72 | /* |
| 62 | 73 | ** Do a three-way merge. Initialize pOut to contain the result. |
| 63 | 74 | ** |
| 64 | 75 | ** The merge is an edit against pV2. Both pV1 and pV2 have a |
| 65 | 76 | ** common origin at pPivot. Apply the changes of pPivot ==> pV1 |
| @@ -69,33 +80,41 @@ | ||
| 69 | 80 | ** -1 is returned and pOut is unmodified. If there are merge |
| 70 | 81 | ** conflicts, the merge proceeds as best as it can and the number |
| 71 | 82 | ** of conflicts is returns |
| 72 | 83 | */ |
| 73 | 84 | int blob_merge(Blob *pPivot, Blob *pV1, Blob *pV2, Blob *pOut){ |
| 74 | - int *aC1; /* Changes from pPivot to pV1 */ | |
| 75 | - int *aC2; /* Changes from pPivot to pV2 */ | |
| 76 | - int i1, i2; | |
| 77 | - int op1, op2; | |
| 78 | - int limit1, limit2; | |
| 79 | - int inConflict = 0; | |
| 80 | - int nConflict = 0; | |
| 81 | - static const char zBegin[] = ">>>>>>>> BEGIN MERGE CONFLICT <<<<<<<<\n"; | |
| 82 | - static const char zEnd[] = ">>>>>>>>> END MERGE CONFLICT <<<<<<<<<\n"; | |
| 83 | - | |
| 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 */ | |
| 84 | 102 | aC1 = text_diff(pPivot, pV1, 0, 0); |
| 85 | 103 | aC2 = text_diff(pPivot, pV2, 0, 0); |
| 86 | - | |
| 87 | 104 | if( aC2==0 || aC2==0 ){ |
| 88 | 105 | free(aC1); |
| 89 | 106 | free(aC2); |
| 90 | 107 | return -1; |
| 91 | 108 | } |
| 92 | - blob_zero(pOut); | |
| 93 | - blob_rewind(pV1); | |
| 109 | + | |
| 110 | + blob_zero(pOut); /* Merge results stored in pOut */ | |
| 111 | + blob_rewind(pV1); /* Rewind inputs: Needed to reconstruct output */ | |
| 94 | 112 | blob_rewind(pV2); |
| 95 | 113 | blob_rewind(pPivot); |
| 96 | 114 | |
| 115 | + /* Determine the length of the aC1[] and aC2[] change vectors */ | |
| 97 | 116 | for(i1=0; aC1[i1] || aC1[i1+1] || aC1[i1+2]; i1+=3){} |
| 98 | 117 | limit1 = i1; |
| 99 | 118 | for(i2=0; aC2[i2] || aC2[i2+1] || aC2[i2+2]; i2+=3){} |
| 100 | 119 | limit2 = i2; |
| 101 | 120 | |
| @@ -107,100 +126,136 @@ | ||
| 107 | 126 | printf("c2: %4d %4d %4d\n", aC2[i2], aC2[i2+1], aC2[i2+2]); |
| 108 | 127 | } |
| 109 | 128 | ) |
| 110 | 129 | |
| 111 | 130 | op1 = op2 = UNK; |
| 112 | - i1 = i2 = 0; | |
| 113 | - while( i1<limit1 && aC1[i1]==0 ){ i1++; } | |
| 114 | - while( i2<limit2 && aC2[i2]==0 ){ i2++; } | |
| 115 | - | |
| 131 | + i1 = i2 = -1; | |
| 132 | + n1 = n2 = 0; | |
| 116 | 133 | while(1){ |
| 117 | 134 | if( op1==UNK ){ |
| 118 | - if( i1>=limit1 ){ | |
| 119 | - op1 = END; | |
| 120 | - DEBUG( printf("get op1=END\n"); ) | |
| 135 | + if( n1 ){ | |
| 136 | + op1 = i1 % 3; | |
| 121 | 137 | }else{ |
| 122 | - op1 = i1 % 3; | |
| 123 | - aC1[i1]--; | |
| 124 | - DEBUG( printf("get op1=%d from %d (%d->%d)\n", | |
| 125 | - op1,i1,aC1[i1]+1,aC1[i1]); ) | |
| 138 | + i1++; | |
| 126 | 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 | + } | |
| 127 | 146 | } |
| 128 | 147 | } |
| 129 | 148 | if( op2==UNK ){ |
| 130 | - if( i2>=limit2 ){ | |
| 131 | - op2 = END; | |
| 132 | - DEBUG( printf("get op2=END\n"); ) | |
| 133 | - }else{ | |
| 134 | - op2 = i2 % 3; | |
| 135 | - aC2[i2]--; | |
| 136 | - DEBUG( printf("get op2=%d from %d (%d->%d)\n", | |
| 137 | - op2,i2,aC2[i2]+1,aC2[i2]); ) | |
| 138 | - while( i2<limit2 && aC2[i2]==0 ){ i2++; } | |
| 139 | - } | |
| 140 | - } | |
| 141 | - DEBUG( printf("op1=%d op2=%d\n", op1, op2); ) | |
| 142 | - if( op1==END ){ | |
| 143 | - if( op2==INS ){ | |
| 144 | - blob_copy_lines(pOut, pV2, 1); | |
| 145 | - op2 = UNK; | |
| 146 | - }else{ | |
| 147 | - break; | |
| 148 | - } | |
| 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; | |
| 149 | 169 | }else if( op2==END ){ |
| 150 | 170 | if( op1==INS ){ |
| 151 | - blob_copy_lines(pOut, pV1, 1); | |
| 152 | - op1 = UNK; | |
| 153 | - }else{ | |
| 154 | - break; | |
| 155 | - } | |
| 156 | - }else if( op1==INS && op2==INS ){ | |
| 157 | - if( !inConflict && sameLine(pV1, pV2) ){ | |
| 158 | - blob_copy_lines(pOut, pV2, 1); | |
| 159 | - blob_copy_lines(0, pV1, 0); | |
| 160 | - op1 = UNK; | |
| 161 | - op2 = UNK; | |
| 162 | - }else{ | |
| 163 | - if( !inConflict ){ | |
| 164 | - inConflict = 1; | |
| 165 | - nConflict++; | |
| 166 | - blob_appendf(pOut, zBegin); | |
| 167 | - } | |
| 168 | - blob_copy_lines(pOut, pV1, 1); | |
| 169 | - op1 = UNK; | |
| 170 | - } | |
| 171 | - }else if( op1==INS ){ | |
| 172 | - blob_copy_lines(pOut, pV1, 1); | |
| 173 | - op1 = UNK; | |
| 174 | - }else if( op2==INS ){ | |
| 175 | - blob_copy_lines(pOut, pV2, 1); | |
| 176 | - op2 = UNK; | |
| 177 | - }else{ | |
| 178 | - if( inConflict ){ | |
| 179 | - inConflict = 0; | |
| 180 | - blob_appendf(pOut, zEnd); | |
| 181 | - } | |
| 182 | - if( op1==DEL || op2==DEL ){ | |
| 183 | - blob_copy_lines(0, pPivot, 1); | |
| 184 | - if( op2==CPY ){ | |
| 185 | - blob_copy_lines(0, pV2, 1); | |
| 186 | - } | |
| 187 | - if( op1==CPY ){ | |
| 188 | - blob_copy_lines(0, pV1, 1); | |
| 189 | - } | |
| 190 | - }else{ | |
| 191 | - assert( op1==CPY && op2==CPY ); | |
| 192 | - blob_copy_lines(pOut, pPivot, 1); | |
| 193 | - blob_copy_lines(0, pV1, 1); | |
| 194 | - blob_copy_lines(0, pV2, 1); | |
| 195 | - } | |
| 196 | - op1 = UNK; | |
| 197 | - op2 = UNK; | |
| 198 | - } | |
| 199 | - } | |
| 200 | - if( inConflict ){ | |
| 201 | - blob_appendf(pOut, zEnd); | |
| 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 | + } | |
| 202 | 257 | } |
| 203 | 258 | |
| 204 | 259 | free(aC1); |
| 205 | 260 | free(aC2); |
| 206 | 261 | return nConflict; |
| 207 | 262 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -24,18 +24,26 @@ | |
| 24 | ** This module implements a 3-way merge |
| 25 | */ |
| 26 | #include "config.h" |
| 27 | #include "merge3.h" |
| 28 | |
| 29 | #if 0 |
| 30 | #define DEBUG(X) X |
| 31 | #else |
| 32 | #define DEBUG(X) |
| 33 | #endif |
| 34 | |
| 35 | /* |
| 36 | ** Opcodes |
| 37 | */ |
| 38 | #define CPY 0 |
| 39 | #define DEL 1 |
| 40 | #define INS 2 |
| 41 | #define END 3 |
| @@ -56,10 +64,13 @@ | |
| 56 | for(i=0; z1[i]!='\n' && z1[i]==z2[i]; i++){} |
| 57 | return z2[i]=='\n' || (z2[i]=='\r' && z2[i+1]=='\n') |
| 58 | || (z1[i]=='\r' && z2[i]=='\n' && z1[i+1]=='\n'); |
| 59 | } |
| 60 | |
| 61 | /* |
| 62 | ** Do a three-way merge. Initialize pOut to contain the result. |
| 63 | ** |
| 64 | ** The merge is an edit against pV2. Both pV1 and pV2 have a |
| 65 | ** common origin at pPivot. Apply the changes of pPivot ==> pV1 |
| @@ -69,33 +80,41 @@ | |
| 69 | ** -1 is returned and pOut is unmodified. If there are merge |
| 70 | ** conflicts, the merge proceeds as best as it can and the number |
| 71 | ** of conflicts is returns |
| 72 | */ |
| 73 | int blob_merge(Blob *pPivot, Blob *pV1, Blob *pV2, Blob *pOut){ |
| 74 | int *aC1; /* Changes from pPivot to pV1 */ |
| 75 | int *aC2; /* Changes from pPivot to pV2 */ |
| 76 | int i1, i2; |
| 77 | int op1, op2; |
| 78 | int limit1, limit2; |
| 79 | int inConflict = 0; |
| 80 | int nConflict = 0; |
| 81 | static const char zBegin[] = ">>>>>>>> BEGIN MERGE CONFLICT <<<<<<<<\n"; |
| 82 | static const char zEnd[] = ">>>>>>>>> END MERGE CONFLICT <<<<<<<<<\n"; |
| 83 | |
| 84 | aC1 = text_diff(pPivot, pV1, 0, 0); |
| 85 | aC2 = text_diff(pPivot, pV2, 0, 0); |
| 86 | |
| 87 | if( aC2==0 || aC2==0 ){ |
| 88 | free(aC1); |
| 89 | free(aC2); |
| 90 | return -1; |
| 91 | } |
| 92 | blob_zero(pOut); |
| 93 | blob_rewind(pV1); |
| 94 | blob_rewind(pV2); |
| 95 | blob_rewind(pPivot); |
| 96 | |
| 97 | for(i1=0; aC1[i1] || aC1[i1+1] || aC1[i1+2]; i1+=3){} |
| 98 | limit1 = i1; |
| 99 | for(i2=0; aC2[i2] || aC2[i2+1] || aC2[i2+2]; i2+=3){} |
| 100 | limit2 = i2; |
| 101 | |
| @@ -107,100 +126,136 @@ | |
| 107 | printf("c2: %4d %4d %4d\n", aC2[i2], aC2[i2+1], aC2[i2+2]); |
| 108 | } |
| 109 | ) |
| 110 | |
| 111 | op1 = op2 = UNK; |
| 112 | i1 = i2 = 0; |
| 113 | while( i1<limit1 && aC1[i1]==0 ){ i1++; } |
| 114 | while( i2<limit2 && aC2[i2]==0 ){ i2++; } |
| 115 | |
| 116 | while(1){ |
| 117 | if( op1==UNK ){ |
| 118 | if( i1>=limit1 ){ |
| 119 | op1 = END; |
| 120 | DEBUG( printf("get op1=END\n"); ) |
| 121 | }else{ |
| 122 | op1 = i1 % 3; |
| 123 | aC1[i1]--; |
| 124 | DEBUG( printf("get op1=%d from %d (%d->%d)\n", |
| 125 | op1,i1,aC1[i1]+1,aC1[i1]); ) |
| 126 | while( i1<limit1 && aC1[i1]==0 ){ i1++; } |
| 127 | } |
| 128 | } |
| 129 | if( op2==UNK ){ |
| 130 | if( i2>=limit2 ){ |
| 131 | op2 = END; |
| 132 | DEBUG( printf("get op2=END\n"); ) |
| 133 | }else{ |
| 134 | op2 = i2 % 3; |
| 135 | aC2[i2]--; |
| 136 | DEBUG( printf("get op2=%d from %d (%d->%d)\n", |
| 137 | op2,i2,aC2[i2]+1,aC2[i2]); ) |
| 138 | while( i2<limit2 && aC2[i2]==0 ){ i2++; } |
| 139 | } |
| 140 | } |
| 141 | DEBUG( printf("op1=%d op2=%d\n", op1, op2); ) |
| 142 | if( op1==END ){ |
| 143 | if( op2==INS ){ |
| 144 | blob_copy_lines(pOut, pV2, 1); |
| 145 | op2 = UNK; |
| 146 | }else{ |
| 147 | break; |
| 148 | } |
| 149 | }else if( op2==END ){ |
| 150 | if( op1==INS ){ |
| 151 | blob_copy_lines(pOut, pV1, 1); |
| 152 | op1 = UNK; |
| 153 | }else{ |
| 154 | break; |
| 155 | } |
| 156 | }else if( op1==INS && op2==INS ){ |
| 157 | if( !inConflict && sameLine(pV1, pV2) ){ |
| 158 | blob_copy_lines(pOut, pV2, 1); |
| 159 | blob_copy_lines(0, pV1, 0); |
| 160 | op1 = UNK; |
| 161 | op2 = UNK; |
| 162 | }else{ |
| 163 | if( !inConflict ){ |
| 164 | inConflict = 1; |
| 165 | nConflict++; |
| 166 | blob_appendf(pOut, zBegin); |
| 167 | } |
| 168 | blob_copy_lines(pOut, pV1, 1); |
| 169 | op1 = UNK; |
| 170 | } |
| 171 | }else if( op1==INS ){ |
| 172 | blob_copy_lines(pOut, pV1, 1); |
| 173 | op1 = UNK; |
| 174 | }else if( op2==INS ){ |
| 175 | blob_copy_lines(pOut, pV2, 1); |
| 176 | op2 = UNK; |
| 177 | }else{ |
| 178 | if( inConflict ){ |
| 179 | inConflict = 0; |
| 180 | blob_appendf(pOut, zEnd); |
| 181 | } |
| 182 | if( op1==DEL || op2==DEL ){ |
| 183 | blob_copy_lines(0, pPivot, 1); |
| 184 | if( op2==CPY ){ |
| 185 | blob_copy_lines(0, pV2, 1); |
| 186 | } |
| 187 | if( op1==CPY ){ |
| 188 | blob_copy_lines(0, pV1, 1); |
| 189 | } |
| 190 | }else{ |
| 191 | assert( op1==CPY && op2==CPY ); |
| 192 | blob_copy_lines(pOut, pPivot, 1); |
| 193 | blob_copy_lines(0, pV1, 1); |
| 194 | blob_copy_lines(0, pV2, 1); |
| 195 | } |
| 196 | op1 = UNK; |
| 197 | op2 = UNK; |
| 198 | } |
| 199 | } |
| 200 | if( inConflict ){ |
| 201 | blob_appendf(pOut, zEnd); |
| 202 | } |
| 203 | |
| 204 | free(aC1); |
| 205 | free(aC2); |
| 206 | return nConflict; |
| 207 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -24,18 +24,26 @@ | |
| 24 | ** This module implements a 3-way merge |
| 25 | */ |
| 26 | #include "config.h" |
| 27 | #include "merge3.h" |
| 28 | |
| 29 | #if 1 |
| 30 | #define DEBUG(X) X |
| 31 | #define ISDEBUG 1 |
| 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 |
| @@ -56,10 +64,13 @@ | |
| 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 |
| 76 | ** common origin at pPivot. Apply the changes of pPivot ==> pV1 |
| @@ -69,33 +80,41 @@ | |
| 80 | ** -1 is returned and pOut is unmodified. If there are merge |
| 81 | ** conflicts, the merge proceeds as best as it can and the number |
| 82 | ** of conflicts is returns |
| 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); |
| 106 | free(aC2); |
| 107 | return -1; |
| 108 | } |
| 109 | |
| 110 | blob_zero(pOut); /* Merge results stored in pOut */ |
| 111 | blob_rewind(pV1); /* Rewind inputs: Needed to reconstruct output */ |
| 112 | blob_rewind(pV2); |
| 113 | blob_rewind(pPivot); |
| 114 | |
| 115 | /* Determine the length of the aC1[] and aC2[] change vectors */ |
| 116 | for(i1=0; aC1[i1] || aC1[i1+1] || aC1[i1+2]; i1+=3){} |
| 117 | limit1 = i1; |
| 118 | for(i2=0; aC2[i2] || aC2[i2+1] || aC2[i2+2]; i2+=3){} |
| 119 | limit2 = i2; |
| 120 | |
| @@ -107,100 +126,136 @@ | |
| 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 |