Fossil SCM
Improvements to handling of line endings and BOM marks when doing a 3-way merge.
Commit
88ff2642d3bf0adf1d3f6e43d45c9603d7ef442ea3e034a9765f1743de5adc3d
Parent
f741baa6be99434…
2 files changed
+63
-6
+63
-6
+63
-6
| --- src/merge3.c | ||
| +++ src/merge3.c | ||
| @@ -138,16 +138,47 @@ | ||
| 138 | 138 | /* |
| 139 | 139 | ** Text of boundary markers for merge conflicts. |
| 140 | 140 | */ |
| 141 | 141 | static const char *const mergeMarker[] = { |
| 142 | 142 | /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/ |
| 143 | - "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n", | |
| 144 | - "||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||\n", | |
| 145 | - "======= MERGED IN content follows ==================================\n", | |
| 146 | - ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" | |
| 143 | + "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<", | |
| 144 | + "||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||", | |
| 145 | + "======= MERGED IN content follows ==================================", | |
| 146 | + ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" | |
| 147 | 147 | }; |
| 148 | 148 | |
| 149 | +/* | |
| 150 | +** Return true if the input blob contains any CR/LF pairs on the first | |
| 151 | +** ten lines. This should be enough to detect files that use mainly CR/LF | |
| 152 | +** line endings without causing a performance impact for LF only files. | |
| 153 | +*/ | |
| 154 | +int contains_crlf(Blob *p){ | |
| 155 | + int i; | |
| 156 | + int j = 0; | |
| 157 | + const int maxL = 10; /* Max lines to check */ | |
| 158 | + const char *z = blob_buffer(p); | |
| 159 | + int n = blob_size(p)+1; | |
| 160 | + for(i=1; i<n; ){ | |
| 161 | + if( z[i-1]=='\r' && z[i]=='\n' ) return 1; | |
| 162 | + while( i<n && z[i]!='\n' ){ i++; } | |
| 163 | + j++; | |
| 164 | + if( j>maxL ) return 0; | |
| 165 | + } | |
| 166 | + return 0; | |
| 167 | +} | |
| 168 | + | |
| 169 | +/* | |
| 170 | +** Ensure that the text in pBlob ends with a new line. | |
| 171 | +** If useCrLf is true adds "\r\n" otherwise '\n'. | |
| 172 | +*/ | |
| 173 | +void ensure_line_end(Blob *pBlob, int useCrLf){ | |
| 174 | + if( pBlob->nUsed<=0 ) return; | |
| 175 | + if( pBlob->aData[pBlob->nUsed-1]!='\n' ){ | |
| 176 | + if( useCrLf ) blob_append_char(pBlob, '\r'); | |
| 177 | + blob_append_char(pBlob, '\n'); | |
| 178 | + } | |
| 179 | +} | |
| 149 | 180 | |
| 150 | 181 | /* |
| 151 | 182 | ** Do a three-way merge. Initialize pOut to contain the result. |
| 152 | 183 | ** |
| 153 | 184 | ** The merge is an edit against pV2. Both pV1 and pV2 have a |
| @@ -166,10 +197,28 @@ | ||
| 166 | 197 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 167 | 198 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 168 | 199 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 169 | 200 | |
| 170 | 201 | blob_zero(pOut); /* Merge results stored in pOut */ |
| 202 | + | |
| 203 | + /* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM), | |
| 204 | + ** keep it in the output. This should be secure enough not to cause | |
| 205 | + ** unintended changes to the merged file and consistent with what | |
| 206 | + ** users are using in their source files. | |
| 207 | + */ | |
| 208 | + if( starts_with_utf8_bom(pV1, 0) && starts_with_utf8_bom(pV2, 0) ){ | |
| 209 | + blob_append(pOut, (char*)get_utf8_bom(0), -1); | |
| 210 | + } | |
| 211 | + | |
| 212 | + /* Check once to see if both pV1 and pV2 contains CR/LF endings. | |
| 213 | + ** If true, CR/LF pair will be used later to append the | |
| 214 | + ** boundary markers for merge conflicts. | |
| 215 | + */ | |
| 216 | + int useCrLf = 0; | |
| 217 | + if( contains_crlf(pV1) && contains_crlf(pV2) ){ | |
| 218 | + useCrLf = 1; | |
| 219 | + } | |
| 171 | 220 | |
| 172 | 221 | /* Compute the edits that occur from pPivot => pV1 (into aC1) |
| 173 | 222 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is |
| 174 | 223 | ** an array of integer triples. Within each triple, the first integer |
| 175 | 224 | ** is the number of lines of text to copy directly from the pivot, |
| @@ -269,16 +318,23 @@ | ||
| 269 | 318 | while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){ |
| 270 | 319 | sz++; |
| 271 | 320 | } |
| 272 | 321 | DEBUG( printf("CONFLICT %d\n", sz); ) |
| 273 | 322 | blob_append(pOut, mergeMarker[0], -1); |
| 323 | + ensure_line_end(pOut, useCrLf); | |
| 274 | 324 | i1 = output_one_side(pOut, pV1, aC1, i1, sz); |
| 325 | + ensure_line_end(pOut, useCrLf); | |
| 275 | 326 | blob_append(pOut, mergeMarker[1], -1); |
| 327 | + ensure_line_end(pOut, useCrLf); | |
| 276 | 328 | blob_copy_lines(pOut, pPivot, sz); |
| 329 | + ensure_line_end(pOut, useCrLf); | |
| 277 | 330 | blob_append(pOut, mergeMarker[2], -1); |
| 331 | + ensure_line_end(pOut, useCrLf); | |
| 278 | 332 | i2 = output_one_side(pOut, pV2, aC2, i2, sz); |
| 333 | + ensure_line_end(pOut, useCrLf); | |
| 279 | 334 | blob_append(pOut, mergeMarker[3], -1); |
| 335 | + ensure_line_end(pOut, useCrLf); | |
| 280 | 336 | } |
| 281 | 337 | |
| 282 | 338 | /* If we are finished with an edit triple, advance to the next |
| 283 | 339 | ** triple. |
| 284 | 340 | */ |
| @@ -319,14 +375,15 @@ | ||
| 319 | 375 | assert( len==(int)strlen(mergeMarker[2]) ); |
| 320 | 376 | assert( len==(int)strlen(mergeMarker[3]) ); |
| 321 | 377 | assert( count(mergeMarker)==4 ); |
| 322 | 378 | for(i=0; i<n; ){ |
| 323 | 379 | for(j=0; j<4; j++){ |
| 324 | - if( memcmp(&z[i], mergeMarker[j], len)==0 ) return 1; | |
| 380 | + if( (memcmp(&z[i], mergeMarker[j], len)==0) | |
| 381 | + && (i+1==n || z[i+len]=='\n' || z[i+len]=='\r') ) return 1; | |
| 325 | 382 | } |
| 326 | 383 | while( i<n && z[i]!='\n' ){ i++; } |
| 327 | - while( i<n && z[i]=='\n' ){ i++; } | |
| 384 | + while( i<n && (z[i]=='\n' || z[i]=='\r') ){ i++; } | |
| 328 | 385 | } |
| 329 | 386 | return 0; |
| 330 | 387 | } |
| 331 | 388 | |
| 332 | 389 | /* |
| 333 | 390 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -138,16 +138,47 @@ | |
| 138 | /* |
| 139 | ** Text of boundary markers for merge conflicts. |
| 140 | */ |
| 141 | static const char *const mergeMarker[] = { |
| 142 | /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/ |
| 143 | "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n", |
| 144 | "||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||\n", |
| 145 | "======= MERGED IN content follows ==================================\n", |
| 146 | ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" |
| 147 | }; |
| 148 | |
| 149 | |
| 150 | /* |
| 151 | ** Do a three-way merge. Initialize pOut to contain the result. |
| 152 | ** |
| 153 | ** The merge is an edit against pV2. Both pV1 and pV2 have a |
| @@ -166,10 +197,28 @@ | |
| 166 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 167 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 168 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 169 | |
| 170 | blob_zero(pOut); /* Merge results stored in pOut */ |
| 171 | |
| 172 | /* Compute the edits that occur from pPivot => pV1 (into aC1) |
| 173 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is |
| 174 | ** an array of integer triples. Within each triple, the first integer |
| 175 | ** is the number of lines of text to copy directly from the pivot, |
| @@ -269,16 +318,23 @@ | |
| 269 | while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){ |
| 270 | sz++; |
| 271 | } |
| 272 | DEBUG( printf("CONFLICT %d\n", sz); ) |
| 273 | blob_append(pOut, mergeMarker[0], -1); |
| 274 | i1 = output_one_side(pOut, pV1, aC1, i1, sz); |
| 275 | blob_append(pOut, mergeMarker[1], -1); |
| 276 | blob_copy_lines(pOut, pPivot, sz); |
| 277 | blob_append(pOut, mergeMarker[2], -1); |
| 278 | i2 = output_one_side(pOut, pV2, aC2, i2, sz); |
| 279 | blob_append(pOut, mergeMarker[3], -1); |
| 280 | } |
| 281 | |
| 282 | /* If we are finished with an edit triple, advance to the next |
| 283 | ** triple. |
| 284 | */ |
| @@ -319,14 +375,15 @@ | |
| 319 | assert( len==(int)strlen(mergeMarker[2]) ); |
| 320 | assert( len==(int)strlen(mergeMarker[3]) ); |
| 321 | assert( count(mergeMarker)==4 ); |
| 322 | for(i=0; i<n; ){ |
| 323 | for(j=0; j<4; j++){ |
| 324 | if( memcmp(&z[i], mergeMarker[j], len)==0 ) return 1; |
| 325 | } |
| 326 | while( i<n && z[i]!='\n' ){ i++; } |
| 327 | while( i<n && z[i]=='\n' ){ i++; } |
| 328 | } |
| 329 | return 0; |
| 330 | } |
| 331 | |
| 332 | /* |
| 333 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -138,16 +138,47 @@ | |
| 138 | /* |
| 139 | ** Text of boundary markers for merge conflicts. |
| 140 | */ |
| 141 | static const char *const mergeMarker[] = { |
| 142 | /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/ |
| 143 | "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<", |
| 144 | "||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||", |
| 145 | "======= MERGED IN content follows ==================================", |
| 146 | ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" |
| 147 | }; |
| 148 | |
| 149 | /* |
| 150 | ** Return true if the input blob contains any CR/LF pairs on the first |
| 151 | ** ten lines. This should be enough to detect files that use mainly CR/LF |
| 152 | ** line endings without causing a performance impact for LF only files. |
| 153 | */ |
| 154 | int contains_crlf(Blob *p){ |
| 155 | int i; |
| 156 | int j = 0; |
| 157 | const int maxL = 10; /* Max lines to check */ |
| 158 | const char *z = blob_buffer(p); |
| 159 | int n = blob_size(p)+1; |
| 160 | for(i=1; i<n; ){ |
| 161 | if( z[i-1]=='\r' && z[i]=='\n' ) return 1; |
| 162 | while( i<n && z[i]!='\n' ){ i++; } |
| 163 | j++; |
| 164 | if( j>maxL ) return 0; |
| 165 | } |
| 166 | return 0; |
| 167 | } |
| 168 | |
| 169 | /* |
| 170 | ** Ensure that the text in pBlob ends with a new line. |
| 171 | ** If useCrLf is true adds "\r\n" otherwise '\n'. |
| 172 | */ |
| 173 | void ensure_line_end(Blob *pBlob, int useCrLf){ |
| 174 | if( pBlob->nUsed<=0 ) return; |
| 175 | if( pBlob->aData[pBlob->nUsed-1]!='\n' ){ |
| 176 | if( useCrLf ) blob_append_char(pBlob, '\r'); |
| 177 | blob_append_char(pBlob, '\n'); |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | /* |
| 182 | ** Do a three-way merge. Initialize pOut to contain the result. |
| 183 | ** |
| 184 | ** The merge is an edit against pV2. Both pV1 and pV2 have a |
| @@ -166,10 +197,28 @@ | |
| 197 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 198 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 199 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 200 | |
| 201 | blob_zero(pOut); /* Merge results stored in pOut */ |
| 202 | |
| 203 | /* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM), |
| 204 | ** keep it in the output. This should be secure enough not to cause |
| 205 | ** unintended changes to the merged file and consistent with what |
| 206 | ** users are using in their source files. |
| 207 | */ |
| 208 | if( starts_with_utf8_bom(pV1, 0) && starts_with_utf8_bom(pV2, 0) ){ |
| 209 | blob_append(pOut, (char*)get_utf8_bom(0), -1); |
| 210 | } |
| 211 | |
| 212 | /* Check once to see if both pV1 and pV2 contains CR/LF endings. |
| 213 | ** If true, CR/LF pair will be used later to append the |
| 214 | ** boundary markers for merge conflicts. |
| 215 | */ |
| 216 | int useCrLf = 0; |
| 217 | if( contains_crlf(pV1) && contains_crlf(pV2) ){ |
| 218 | useCrLf = 1; |
| 219 | } |
| 220 | |
| 221 | /* Compute the edits that occur from pPivot => pV1 (into aC1) |
| 222 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is |
| 223 | ** an array of integer triples. Within each triple, the first integer |
| 224 | ** is the number of lines of text to copy directly from the pivot, |
| @@ -269,16 +318,23 @@ | |
| 318 | while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){ |
| 319 | sz++; |
| 320 | } |
| 321 | DEBUG( printf("CONFLICT %d\n", sz); ) |
| 322 | blob_append(pOut, mergeMarker[0], -1); |
| 323 | ensure_line_end(pOut, useCrLf); |
| 324 | i1 = output_one_side(pOut, pV1, aC1, i1, sz); |
| 325 | ensure_line_end(pOut, useCrLf); |
| 326 | blob_append(pOut, mergeMarker[1], -1); |
| 327 | ensure_line_end(pOut, useCrLf); |
| 328 | blob_copy_lines(pOut, pPivot, sz); |
| 329 | ensure_line_end(pOut, useCrLf); |
| 330 | blob_append(pOut, mergeMarker[2], -1); |
| 331 | ensure_line_end(pOut, useCrLf); |
| 332 | i2 = output_one_side(pOut, pV2, aC2, i2, sz); |
| 333 | ensure_line_end(pOut, useCrLf); |
| 334 | blob_append(pOut, mergeMarker[3], -1); |
| 335 | ensure_line_end(pOut, useCrLf); |
| 336 | } |
| 337 | |
| 338 | /* If we are finished with an edit triple, advance to the next |
| 339 | ** triple. |
| 340 | */ |
| @@ -319,14 +375,15 @@ | |
| 375 | assert( len==(int)strlen(mergeMarker[2]) ); |
| 376 | assert( len==(int)strlen(mergeMarker[3]) ); |
| 377 | assert( count(mergeMarker)==4 ); |
| 378 | for(i=0; i<n; ){ |
| 379 | for(j=0; j<4; j++){ |
| 380 | if( (memcmp(&z[i], mergeMarker[j], len)==0) |
| 381 | && (i+1==n || z[i+len]=='\n' || z[i+len]=='\r') ) return 1; |
| 382 | } |
| 383 | while( i<n && z[i]!='\n' ){ i++; } |
| 384 | while( i<n && (z[i]=='\n' || z[i]=='\r') ){ i++; } |
| 385 | } |
| 386 | return 0; |
| 387 | } |
| 388 | |
| 389 | /* |
| 390 |
+63
-6
| --- src/merge3.c | ||
| +++ src/merge3.c | ||
| @@ -138,16 +138,47 @@ | ||
| 138 | 138 | /* |
| 139 | 139 | ** Text of boundary markers for merge conflicts. |
| 140 | 140 | */ |
| 141 | 141 | static const char *const mergeMarker[] = { |
| 142 | 142 | /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/ |
| 143 | - "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n", | |
| 144 | - "||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||\n", | |
| 145 | - "======= MERGED IN content follows ==================================\n", | |
| 146 | - ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" | |
| 143 | + "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<", | |
| 144 | + "||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||", | |
| 145 | + "======= MERGED IN content follows ==================================", | |
| 146 | + ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" | |
| 147 | 147 | }; |
| 148 | 148 | |
| 149 | +/* | |
| 150 | +** Return true if the input blob contains any CR/LF pairs on the first | |
| 151 | +** ten lines. This should be enough to detect files that use mainly CR/LF | |
| 152 | +** line endings without causing a performance impact for LF only files. | |
| 153 | +*/ | |
| 154 | +int contains_crlf(Blob *p){ | |
| 155 | + int i; | |
| 156 | + int j = 0; | |
| 157 | + const int maxL = 10; /* Max lines to check */ | |
| 158 | + const char *z = blob_buffer(p); | |
| 159 | + int n = blob_size(p)+1; | |
| 160 | + for(i=1; i<n; ){ | |
| 161 | + if( z[i-1]=='\r' && z[i]=='\n' ) return 1; | |
| 162 | + while( i<n && z[i]!='\n' ){ i++; } | |
| 163 | + j++; | |
| 164 | + if( j>maxL ) return 0; | |
| 165 | + } | |
| 166 | + return 0; | |
| 167 | +} | |
| 168 | + | |
| 169 | +/* | |
| 170 | +** Ensure that the text in pBlob ends with a new line. | |
| 171 | +** If useCrLf is true adds "\r\n" otherwise '\n'. | |
| 172 | +*/ | |
| 173 | +void ensure_line_end(Blob *pBlob, int useCrLf){ | |
| 174 | + if( pBlob->nUsed<=0 ) return; | |
| 175 | + if( pBlob->aData[pBlob->nUsed-1]!='\n' ){ | |
| 176 | + if( useCrLf ) blob_append_char(pBlob, '\r'); | |
| 177 | + blob_append_char(pBlob, '\n'); | |
| 178 | + } | |
| 179 | +} | |
| 149 | 180 | |
| 150 | 181 | /* |
| 151 | 182 | ** Do a three-way merge. Initialize pOut to contain the result. |
| 152 | 183 | ** |
| 153 | 184 | ** The merge is an edit against pV2. Both pV1 and pV2 have a |
| @@ -166,10 +197,28 @@ | ||
| 166 | 197 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 167 | 198 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 168 | 199 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 169 | 200 | |
| 170 | 201 | blob_zero(pOut); /* Merge results stored in pOut */ |
| 202 | + | |
| 203 | + /* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM), | |
| 204 | + ** keep it in the output. This should be secure enough not to cause | |
| 205 | + ** unintended changes to the merged file and consistent with what | |
| 206 | + ** users are using in their source files. | |
| 207 | + */ | |
| 208 | + if( starts_with_utf8_bom(pV1, 0) && starts_with_utf8_bom(pV2, 0) ){ | |
| 209 | + blob_append(pOut, (char*)get_utf8_bom(0), -1); | |
| 210 | + } | |
| 211 | + | |
| 212 | + /* Check once to see if both pV1 and pV2 contains CR/LF endings. | |
| 213 | + ** If true, CR/LF pair will be used later to append the | |
| 214 | + ** boundary markers for merge conflicts. | |
| 215 | + */ | |
| 216 | + int useCrLf = 0; | |
| 217 | + if( contains_crlf(pV1) && contains_crlf(pV2) ){ | |
| 218 | + useCrLf = 1; | |
| 219 | + } | |
| 171 | 220 | |
| 172 | 221 | /* Compute the edits that occur from pPivot => pV1 (into aC1) |
| 173 | 222 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is |
| 174 | 223 | ** an array of integer triples. Within each triple, the first integer |
| 175 | 224 | ** is the number of lines of text to copy directly from the pivot, |
| @@ -269,16 +318,23 @@ | ||
| 269 | 318 | while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){ |
| 270 | 319 | sz++; |
| 271 | 320 | } |
| 272 | 321 | DEBUG( printf("CONFLICT %d\n", sz); ) |
| 273 | 322 | blob_append(pOut, mergeMarker[0], -1); |
| 323 | + ensure_line_end(pOut, useCrLf); | |
| 274 | 324 | i1 = output_one_side(pOut, pV1, aC1, i1, sz); |
| 325 | + ensure_line_end(pOut, useCrLf); | |
| 275 | 326 | blob_append(pOut, mergeMarker[1], -1); |
| 327 | + ensure_line_end(pOut, useCrLf); | |
| 276 | 328 | blob_copy_lines(pOut, pPivot, sz); |
| 329 | + ensure_line_end(pOut, useCrLf); | |
| 277 | 330 | blob_append(pOut, mergeMarker[2], -1); |
| 331 | + ensure_line_end(pOut, useCrLf); | |
| 278 | 332 | i2 = output_one_side(pOut, pV2, aC2, i2, sz); |
| 333 | + ensure_line_end(pOut, useCrLf); | |
| 279 | 334 | blob_append(pOut, mergeMarker[3], -1); |
| 335 | + ensure_line_end(pOut, useCrLf); | |
| 280 | 336 | } |
| 281 | 337 | |
| 282 | 338 | /* If we are finished with an edit triple, advance to the next |
| 283 | 339 | ** triple. |
| 284 | 340 | */ |
| @@ -319,14 +375,15 @@ | ||
| 319 | 375 | assert( len==(int)strlen(mergeMarker[2]) ); |
| 320 | 376 | assert( len==(int)strlen(mergeMarker[3]) ); |
| 321 | 377 | assert( count(mergeMarker)==4 ); |
| 322 | 378 | for(i=0; i<n; ){ |
| 323 | 379 | for(j=0; j<4; j++){ |
| 324 | - if( memcmp(&z[i], mergeMarker[j], len)==0 ) return 1; | |
| 380 | + if( (memcmp(&z[i], mergeMarker[j], len)==0) | |
| 381 | + && (i+1==n || z[i+len]=='\n' || z[i+len]=='\r') ) return 1; | |
| 325 | 382 | } |
| 326 | 383 | while( i<n && z[i]!='\n' ){ i++; } |
| 327 | - while( i<n && z[i]=='\n' ){ i++; } | |
| 384 | + while( i<n && (z[i]=='\n' || z[i]=='\r') ){ i++; } | |
| 328 | 385 | } |
| 329 | 386 | return 0; |
| 330 | 387 | } |
| 331 | 388 | |
| 332 | 389 | /* |
| 333 | 390 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -138,16 +138,47 @@ | |
| 138 | /* |
| 139 | ** Text of boundary markers for merge conflicts. |
| 140 | */ |
| 141 | static const char *const mergeMarker[] = { |
| 142 | /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/ |
| 143 | "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n", |
| 144 | "||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||\n", |
| 145 | "======= MERGED IN content follows ==================================\n", |
| 146 | ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" |
| 147 | }; |
| 148 | |
| 149 | |
| 150 | /* |
| 151 | ** Do a three-way merge. Initialize pOut to contain the result. |
| 152 | ** |
| 153 | ** The merge is an edit against pV2. Both pV1 and pV2 have a |
| @@ -166,10 +197,28 @@ | |
| 166 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 167 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 168 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 169 | |
| 170 | blob_zero(pOut); /* Merge results stored in pOut */ |
| 171 | |
| 172 | /* Compute the edits that occur from pPivot => pV1 (into aC1) |
| 173 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is |
| 174 | ** an array of integer triples. Within each triple, the first integer |
| 175 | ** is the number of lines of text to copy directly from the pivot, |
| @@ -269,16 +318,23 @@ | |
| 269 | while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){ |
| 270 | sz++; |
| 271 | } |
| 272 | DEBUG( printf("CONFLICT %d\n", sz); ) |
| 273 | blob_append(pOut, mergeMarker[0], -1); |
| 274 | i1 = output_one_side(pOut, pV1, aC1, i1, sz); |
| 275 | blob_append(pOut, mergeMarker[1], -1); |
| 276 | blob_copy_lines(pOut, pPivot, sz); |
| 277 | blob_append(pOut, mergeMarker[2], -1); |
| 278 | i2 = output_one_side(pOut, pV2, aC2, i2, sz); |
| 279 | blob_append(pOut, mergeMarker[3], -1); |
| 280 | } |
| 281 | |
| 282 | /* If we are finished with an edit triple, advance to the next |
| 283 | ** triple. |
| 284 | */ |
| @@ -319,14 +375,15 @@ | |
| 319 | assert( len==(int)strlen(mergeMarker[2]) ); |
| 320 | assert( len==(int)strlen(mergeMarker[3]) ); |
| 321 | assert( count(mergeMarker)==4 ); |
| 322 | for(i=0; i<n; ){ |
| 323 | for(j=0; j<4; j++){ |
| 324 | if( memcmp(&z[i], mergeMarker[j], len)==0 ) return 1; |
| 325 | } |
| 326 | while( i<n && z[i]!='\n' ){ i++; } |
| 327 | while( i<n && z[i]=='\n' ){ i++; } |
| 328 | } |
| 329 | return 0; |
| 330 | } |
| 331 | |
| 332 | /* |
| 333 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -138,16 +138,47 @@ | |
| 138 | /* |
| 139 | ** Text of boundary markers for merge conflicts. |
| 140 | */ |
| 141 | static const char *const mergeMarker[] = { |
| 142 | /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/ |
| 143 | "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<", |
| 144 | "||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||", |
| 145 | "======= MERGED IN content follows ==================================", |
| 146 | ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" |
| 147 | }; |
| 148 | |
| 149 | /* |
| 150 | ** Return true if the input blob contains any CR/LF pairs on the first |
| 151 | ** ten lines. This should be enough to detect files that use mainly CR/LF |
| 152 | ** line endings without causing a performance impact for LF only files. |
| 153 | */ |
| 154 | int contains_crlf(Blob *p){ |
| 155 | int i; |
| 156 | int j = 0; |
| 157 | const int maxL = 10; /* Max lines to check */ |
| 158 | const char *z = blob_buffer(p); |
| 159 | int n = blob_size(p)+1; |
| 160 | for(i=1; i<n; ){ |
| 161 | if( z[i-1]=='\r' && z[i]=='\n' ) return 1; |
| 162 | while( i<n && z[i]!='\n' ){ i++; } |
| 163 | j++; |
| 164 | if( j>maxL ) return 0; |
| 165 | } |
| 166 | return 0; |
| 167 | } |
| 168 | |
| 169 | /* |
| 170 | ** Ensure that the text in pBlob ends with a new line. |
| 171 | ** If useCrLf is true adds "\r\n" otherwise '\n'. |
| 172 | */ |
| 173 | void ensure_line_end(Blob *pBlob, int useCrLf){ |
| 174 | if( pBlob->nUsed<=0 ) return; |
| 175 | if( pBlob->aData[pBlob->nUsed-1]!='\n' ){ |
| 176 | if( useCrLf ) blob_append_char(pBlob, '\r'); |
| 177 | blob_append_char(pBlob, '\n'); |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | /* |
| 182 | ** Do a three-way merge. Initialize pOut to contain the result. |
| 183 | ** |
| 184 | ** The merge is an edit against pV2. Both pV1 and pV2 have a |
| @@ -166,10 +197,28 @@ | |
| 197 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 198 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 199 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 200 | |
| 201 | blob_zero(pOut); /* Merge results stored in pOut */ |
| 202 | |
| 203 | /* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM), |
| 204 | ** keep it in the output. This should be secure enough not to cause |
| 205 | ** unintended changes to the merged file and consistent with what |
| 206 | ** users are using in their source files. |
| 207 | */ |
| 208 | if( starts_with_utf8_bom(pV1, 0) && starts_with_utf8_bom(pV2, 0) ){ |
| 209 | blob_append(pOut, (char*)get_utf8_bom(0), -1); |
| 210 | } |
| 211 | |
| 212 | /* Check once to see if both pV1 and pV2 contains CR/LF endings. |
| 213 | ** If true, CR/LF pair will be used later to append the |
| 214 | ** boundary markers for merge conflicts. |
| 215 | */ |
| 216 | int useCrLf = 0; |
| 217 | if( contains_crlf(pV1) && contains_crlf(pV2) ){ |
| 218 | useCrLf = 1; |
| 219 | } |
| 220 | |
| 221 | /* Compute the edits that occur from pPivot => pV1 (into aC1) |
| 222 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is |
| 223 | ** an array of integer triples. Within each triple, the first integer |
| 224 | ** is the number of lines of text to copy directly from the pivot, |
| @@ -269,16 +318,23 @@ | |
| 318 | while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){ |
| 319 | sz++; |
| 320 | } |
| 321 | DEBUG( printf("CONFLICT %d\n", sz); ) |
| 322 | blob_append(pOut, mergeMarker[0], -1); |
| 323 | ensure_line_end(pOut, useCrLf); |
| 324 | i1 = output_one_side(pOut, pV1, aC1, i1, sz); |
| 325 | ensure_line_end(pOut, useCrLf); |
| 326 | blob_append(pOut, mergeMarker[1], -1); |
| 327 | ensure_line_end(pOut, useCrLf); |
| 328 | blob_copy_lines(pOut, pPivot, sz); |
| 329 | ensure_line_end(pOut, useCrLf); |
| 330 | blob_append(pOut, mergeMarker[2], -1); |
| 331 | ensure_line_end(pOut, useCrLf); |
| 332 | i2 = output_one_side(pOut, pV2, aC2, i2, sz); |
| 333 | ensure_line_end(pOut, useCrLf); |
| 334 | blob_append(pOut, mergeMarker[3], -1); |
| 335 | ensure_line_end(pOut, useCrLf); |
| 336 | } |
| 337 | |
| 338 | /* If we are finished with an edit triple, advance to the next |
| 339 | ** triple. |
| 340 | */ |
| @@ -319,14 +375,15 @@ | |
| 375 | assert( len==(int)strlen(mergeMarker[2]) ); |
| 376 | assert( len==(int)strlen(mergeMarker[3]) ); |
| 377 | assert( count(mergeMarker)==4 ); |
| 378 | for(i=0; i<n; ){ |
| 379 | for(j=0; j<4; j++){ |
| 380 | if( (memcmp(&z[i], mergeMarker[j], len)==0) |
| 381 | && (i+1==n || z[i+len]=='\n' || z[i+len]=='\r') ) return 1; |
| 382 | } |
| 383 | while( i<n && z[i]!='\n' ){ i++; } |
| 384 | while( i<n && (z[i]=='\n' || z[i]=='\r') ){ i++; } |
| 385 | } |
| 386 | return 0; |
| 387 | } |
| 388 | |
| 389 | /* |
| 390 |