Fossil SCM

When doing a merge, if a merge conflict occurs, a new section appears in the merge conflict markings that shows the suggested resolution. In addition, the "merge-info --tk" GUI is improved so that line numbers in the result column correspond with line numbers in the actual file.

drh 2024-12-05 20:12 trunk merge
Commit 1cf0d372fa69c7a9c93321d220b5ab3a43d68b33de186938b0bb72b98aba402e
5 files changed +49 -1 +135 -5 +15 -15 +148 -126 +158 -34
+49 -1
--- src/blob.c
+++ src/blob.c
@@ -665,11 +665,12 @@
665665
pBlob->nUsed = dehttpize(pBlob->aData);
666666
}
667667
668668
/*
669669
** Extract N bytes from blob pFrom and use it to initialize blob pTo.
670
-** Return the actual number of bytes extracted.
670
+** Return the actual number of bytes extracted. The cursor position
671
+** is advanced by the number of bytes extracted.
671672
**
672673
** After this call completes, pTo will be an ephemeral blob.
673674
*/
674675
int blob_extract(Blob *pFrom, int N, Blob *pTo){
675676
blob_is_init(pFrom);
@@ -687,10 +688,57 @@
687688
pTo->iCursor = 0;
688689
pTo->xRealloc = blobReallocStatic;
689690
pFrom->iCursor += N;
690691
return N;
691692
}
693
+
694
+/*
695
+** Extract N **lines** of text from blob pFrom beginning at the current
696
+** cursor position and use that text to initialize blob pTo. Unlike the
697
+** blob_extract() routine, the cursor position is unchanged.
698
+**
699
+** pTo is assumed to be uninitialized.
700
+**
701
+** After this call completes, pTo will be an ephemeral blob.
702
+*/
703
+int blob_extract_lines(Blob *pFrom, int N, Blob *pTo){
704
+ int i;
705
+ int mx;
706
+ int iStart;
707
+ int n;
708
+ const char *z;
709
+
710
+ blob_zero(pTo);
711
+ z = pFrom->aData;
712
+ i = pFrom->iCursor;
713
+ mx = pFrom->nUsed;
714
+ while( N>0 ){
715
+ while( i<mx && z[i]!='\n' ){ i++; }
716
+ if( i>=mx ) break;
717
+ i++;
718
+ N--;
719
+ }
720
+ iStart = pFrom->iCursor;
721
+ n = blob_extract(pFrom, i-pFrom->iCursor, pTo);
722
+ pFrom->iCursor = iStart;
723
+ return n;
724
+}
725
+
726
+/*
727
+** Return the number of lines of text in the blob. If the last
728
+** line is incomplete (if it does not have a \n at the end) then
729
+** it still counts.
730
+*/
731
+int blob_linecount(Blob *p){
732
+ int n = 0;
733
+ int i;
734
+ for(i=0; i<p->nUsed; i++){
735
+ if( p->aData[i]=='\n' ) n++;
736
+ }
737
+ if( p->nUsed>0 && p->aData[p->nUsed-1]!='\n' ) n++;
738
+ return n;
739
+}
692740
693741
/*
694742
** Rewind the cursor on a blob back to the beginning.
695743
*/
696744
void blob_rewind(Blob *p){
697745
--- src/blob.c
+++ src/blob.c
@@ -665,11 +665,12 @@
665 pBlob->nUsed = dehttpize(pBlob->aData);
666 }
667
668 /*
669 ** Extract N bytes from blob pFrom and use it to initialize blob pTo.
670 ** Return the actual number of bytes extracted.
 
671 **
672 ** After this call completes, pTo will be an ephemeral blob.
673 */
674 int blob_extract(Blob *pFrom, int N, Blob *pTo){
675 blob_is_init(pFrom);
@@ -687,10 +688,57 @@
687 pTo->iCursor = 0;
688 pTo->xRealloc = blobReallocStatic;
689 pFrom->iCursor += N;
690 return N;
691 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
692
693 /*
694 ** Rewind the cursor on a blob back to the beginning.
695 */
696 void blob_rewind(Blob *p){
697
--- src/blob.c
+++ src/blob.c
@@ -665,11 +665,12 @@
665 pBlob->nUsed = dehttpize(pBlob->aData);
666 }
667
668 /*
669 ** Extract N bytes from blob pFrom and use it to initialize blob pTo.
670 ** Return the actual number of bytes extracted. The cursor position
671 ** is advanced by the number of bytes extracted.
672 **
673 ** After this call completes, pTo will be an ephemeral blob.
674 */
675 int blob_extract(Blob *pFrom, int N, Blob *pTo){
676 blob_is_init(pFrom);
@@ -687,10 +688,57 @@
688 pTo->iCursor = 0;
689 pTo->xRealloc = blobReallocStatic;
690 pFrom->iCursor += N;
691 return N;
692 }
693
694 /*
695 ** Extract N **lines** of text from blob pFrom beginning at the current
696 ** cursor position and use that text to initialize blob pTo. Unlike the
697 ** blob_extract() routine, the cursor position is unchanged.
698 **
699 ** pTo is assumed to be uninitialized.
700 **
701 ** After this call completes, pTo will be an ephemeral blob.
702 */
703 int blob_extract_lines(Blob *pFrom, int N, Blob *pTo){
704 int i;
705 int mx;
706 int iStart;
707 int n;
708 const char *z;
709
710 blob_zero(pTo);
711 z = pFrom->aData;
712 i = pFrom->iCursor;
713 mx = pFrom->nUsed;
714 while( N>0 ){
715 while( i<mx && z[i]!='\n' ){ i++; }
716 if( i>=mx ) break;
717 i++;
718 N--;
719 }
720 iStart = pFrom->iCursor;
721 n = blob_extract(pFrom, i-pFrom->iCursor, pTo);
722 pFrom->iCursor = iStart;
723 return n;
724 }
725
726 /*
727 ** Return the number of lines of text in the blob. If the last
728 ** line is incomplete (if it does not have a \n at the end) then
729 ** it still counts.
730 */
731 int blob_linecount(Blob *p){
732 int n = 0;
733 int i;
734 for(i=0; i<p->nUsed; i++){
735 if( p->aData[i]=='\n' ) n++;
736 }
737 if( p->nUsed>0 && p->aData[p->nUsed-1]!='\n' ) n++;
738 return n;
739 }
740
741 /*
742 ** Rewind the cursor on a blob back to the beginning.
743 */
744 void blob_rewind(Blob *p){
745
+135 -5
--- src/diff.c
+++ src/diff.c
@@ -50,10 +50,11 @@
5050
#define DIFF_RAW 0x00040000 /* Raw triples - for debugging */
5151
#define DIFF_TCL 0x00080000 /* For the --tk option */
5252
#define DIFF_INCBINARY 0x00100000 /* The --diff-binary option */
5353
#define DIFF_SHOW_VERS 0x00200000 /* Show compared versions */
5454
#define DIFF_DARKMODE 0x00400000 /* Use dark mode for HTML */
55
+#define DIFF_BY_TOKEN 0x01000000 /* Split on tokens, not lines */
5556
5657
/*
5758
** Per file information that may influence output.
5859
*/
5960
#define DIFF_FILE_ADDED 0x40000000 /* Added or rename destination */
@@ -319,10 +320,113 @@
319320
320321
/* Return results */
321322
*pnLine = nLine;
322323
return a;
323324
}
325
+
326
+/*
327
+** Character classes for the purpose of tokenization.
328
+**
329
+** 1 - alphanumeric
330
+** 2 - whitespace
331
+** 3 - punctuation
332
+*/
333
+static char aTCharClass[256] = {
334
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
335
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
336
+ 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
337
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3,
338
+
339
+ 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
340
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3,
341
+ 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
342
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3,
343
+
344
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
345
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
346
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
347
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
348
+
349
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
350
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
351
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
352
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
353
+};
354
+
355
+/*
356
+** Count the number of tokens in the given string.
357
+*/
358
+static int count_tokens(const unsigned char *p, int n){
359
+ int nToken = 0;
360
+ int iPrev = 0;
361
+ int i;
362
+ for(i=0; i<n; i++){
363
+ char x = aTCharClass[p[i]];
364
+ if( x!=iPrev ){
365
+ iPrev = x;
366
+ nToken++;
367
+ }
368
+ }
369
+ return nToken;
370
+}
371
+
372
+/*
373
+** Return an array of DLine objects containing a pointer to the
374
+** start of each token and a hash of that token. The lower
375
+** bits of the hash store the length of each token.
376
+**
377
+** This is like break_into_lines() except that it works with tokens
378
+** instead of lines. A token is:
379
+**
380
+** * A contiguous sequence of alphanumeric characters.
381
+** * A contiguous sequence of whitespace
382
+** * A contiguous sequence of punctuation characters.
383
+**
384
+** Return 0 if the file is binary or contains a line that is
385
+** too long.
386
+*/
387
+static DLine *break_into_tokens(
388
+ const char *z,
389
+ int n,
390
+ int *pnToken,
391
+ u64 diffFlags
392
+){
393
+ int nToken, i, k;
394
+ u64 h, h2;
395
+ DLine *a;
396
+ unsigned char *p = (unsigned char*)z;
397
+
398
+ nToken = count_tokens(p, n);
399
+ a = fossil_malloc( sizeof(a[0])*(nToken+1) );
400
+ memset(a, 0, sizeof(a[0])*(nToken+1));
401
+ if( n==0 ){
402
+ *pnToken = 0;
403
+ return a;
404
+ }
405
+ i = 0;
406
+ while( n>0 ){
407
+ char x = aTCharClass[*p];
408
+ h = 0xcbf29ce484222325LL;
409
+ for(k=1; k<n && aTCharClass[p[k]]==x; k++){
410
+ h ^= p[k];
411
+ h *= 0x100000001b3LL;
412
+ }
413
+ a[i].z = (char*)p;
414
+ a[i].n = k;
415
+ a[i].h = h = ((h%281474976710597LL)<<LENGTH_MASK_SZ) | k;
416
+ h2 = h % nToken;
417
+ a[i].iNext = a[h2].iHash;
418
+ a[h2].iHash = i+1;
419
+ p += k; n -= k;
420
+ i++;
421
+ };
422
+ assert( i==nToken );
423
+
424
+ /* Return results */
425
+ *pnToken = nToken;
426
+ return a;
427
+}
324428
325429
/*
326430
** Return zero if two DLine elements are identical.
327431
*/
328432
static int compare_dline(const DLine *pA, const DLine *pB){
@@ -2997,14 +3101,21 @@
29973101
if( (pCfg->diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
29983102
c.xDiffer = compare_dline_ignore_allws;
29993103
}else{
30003104
c.xDiffer = compare_dline;
30013105
}
3002
- c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
3003
- &c.nFrom, pCfg->diffFlags);
3004
- c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
3005
- &c.nTo, pCfg->diffFlags);
3106
+ if( pCfg->diffFlags & DIFF_BY_TOKEN ){
3107
+ c.aFrom = break_into_tokens(blob_str(pA_Blob), blob_size(pA_Blob),
3108
+ &c.nFrom, pCfg->diffFlags);
3109
+ c.aTo = break_into_tokens(blob_str(pB_Blob), blob_size(pB_Blob),
3110
+ &c.nTo, pCfg->diffFlags);
3111
+ }else{
3112
+ c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
3113
+ &c.nFrom, pCfg->diffFlags);
3114
+ c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
3115
+ &c.nTo, pCfg->diffFlags);
3116
+ }
30063117
if( c.aFrom==0 || c.aTo==0 ){
30073118
fossil_free(c.aFrom);
30083119
fossil_free(c.aTo);
30093120
if( pOut ){
30103121
diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, pCfg->diffFlags);
@@ -3035,10 +3146,26 @@
30353146
}
30363147
}
30373148
if( (pCfg->diffFlags & DIFF_NOOPT)==0 ){
30383149
diff_optimize(&c);
30393150
}
3151
+ if( (pCfg->diffFlags & DIFF_BY_TOKEN)!=0 ){
3152
+ /* Convert token counts into byte counts. */
3153
+ int i;
3154
+ int iA = 0;
3155
+ int iB = 0;
3156
+ for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
3157
+ int k, sum;
3158
+ for(k=0, sum=0; k<c.aEdit[i]; k++) sum += c.aFrom[iA++].n;
3159
+ iB += c.aEdit[i];
3160
+ c.aEdit[i] = sum;
3161
+ for(k=0, sum=0; k<c.aEdit[i+1]; k++) sum += c.aFrom[iA++].n;
3162
+ c.aEdit[i+1] = sum;
3163
+ for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n;
3164
+ c.aEdit[i+2] = sum;
3165
+ }
3166
+ }
30403167
30413168
if( pOut ){
30423169
if( pCfg->diffFlags & DIFF_NUMSTAT ){
30433170
int nDel = 0, nIns = 0, i;
30443171
for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
@@ -3049,11 +3176,11 @@
30493176
g.diffCnt[2] += nDel;
30503177
if( nIns+nDel ){
30513178
g.diffCnt[0]++;
30523179
blob_appendf(pOut, "%10d %10d", nIns, nDel);
30533180
}
3054
- }else if( pCfg->diffFlags & DIFF_RAW ){
3181
+ }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
30553182
const int *R = c.aEdit;
30563183
unsigned int r;
30573184
for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
30583185
blob_appendf(pOut, " copy %6d delete %6d insert %6d\n",
30593186
R[r], R[r+1], R[r+2]);
@@ -3157,10 +3284,13 @@
31573284
31583285
/* Undocumented and unsupported flags used for development
31593286
** debugging and analysis: */
31603287
if( find_option("debug",0,0)!=0 ) diffFlags |= DIFF_DEBUG;
31613288
if( find_option("raw",0,0)!=0 ) diffFlags |= DIFF_RAW;
3289
+ if( find_option("bytoken",0,0)!=0 ){
3290
+ diffFlags = DIFF_RAW|DIFF_BY_TOKEN;
3291
+ }
31623292
}
31633293
if( (z = find_option("context","c",1))!=0 ){
31643294
char *zEnd;
31653295
f = (int)strtol(z, &zEnd, 10);
31663296
if( zEnd[0]==0 && errno!=ERANGE ){
31673297
--- src/diff.c
+++ src/diff.c
@@ -50,10 +50,11 @@
50 #define DIFF_RAW 0x00040000 /* Raw triples - for debugging */
51 #define DIFF_TCL 0x00080000 /* For the --tk option */
52 #define DIFF_INCBINARY 0x00100000 /* The --diff-binary option */
53 #define DIFF_SHOW_VERS 0x00200000 /* Show compared versions */
54 #define DIFF_DARKMODE 0x00400000 /* Use dark mode for HTML */
 
55
56 /*
57 ** Per file information that may influence output.
58 */
59 #define DIFF_FILE_ADDED 0x40000000 /* Added or rename destination */
@@ -319,10 +320,113 @@
319
320 /* Return results */
321 *pnLine = nLine;
322 return a;
323 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
325 /*
326 ** Return zero if two DLine elements are identical.
327 */
328 static int compare_dline(const DLine *pA, const DLine *pB){
@@ -2997,14 +3101,21 @@
2997 if( (pCfg->diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
2998 c.xDiffer = compare_dline_ignore_allws;
2999 }else{
3000 c.xDiffer = compare_dline;
3001 }
3002 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
3003 &c.nFrom, pCfg->diffFlags);
3004 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
3005 &c.nTo, pCfg->diffFlags);
 
 
 
 
 
 
 
3006 if( c.aFrom==0 || c.aTo==0 ){
3007 fossil_free(c.aFrom);
3008 fossil_free(c.aTo);
3009 if( pOut ){
3010 diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, pCfg->diffFlags);
@@ -3035,10 +3146,26 @@
3035 }
3036 }
3037 if( (pCfg->diffFlags & DIFF_NOOPT)==0 ){
3038 diff_optimize(&c);
3039 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3040
3041 if( pOut ){
3042 if( pCfg->diffFlags & DIFF_NUMSTAT ){
3043 int nDel = 0, nIns = 0, i;
3044 for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
@@ -3049,11 +3176,11 @@
3049 g.diffCnt[2] += nDel;
3050 if( nIns+nDel ){
3051 g.diffCnt[0]++;
3052 blob_appendf(pOut, "%10d %10d", nIns, nDel);
3053 }
3054 }else if( pCfg->diffFlags & DIFF_RAW ){
3055 const int *R = c.aEdit;
3056 unsigned int r;
3057 for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
3058 blob_appendf(pOut, " copy %6d delete %6d insert %6d\n",
3059 R[r], R[r+1], R[r+2]);
@@ -3157,10 +3284,13 @@
3157
3158 /* Undocumented and unsupported flags used for development
3159 ** debugging and analysis: */
3160 if( find_option("debug",0,0)!=0 ) diffFlags |= DIFF_DEBUG;
3161 if( find_option("raw",0,0)!=0 ) diffFlags |= DIFF_RAW;
 
 
 
3162 }
3163 if( (z = find_option("context","c",1))!=0 ){
3164 char *zEnd;
3165 f = (int)strtol(z, &zEnd, 10);
3166 if( zEnd[0]==0 && errno!=ERANGE ){
3167
--- src/diff.c
+++ src/diff.c
@@ -50,10 +50,11 @@
50 #define DIFF_RAW 0x00040000 /* Raw triples - for debugging */
51 #define DIFF_TCL 0x00080000 /* For the --tk option */
52 #define DIFF_INCBINARY 0x00100000 /* The --diff-binary option */
53 #define DIFF_SHOW_VERS 0x00200000 /* Show compared versions */
54 #define DIFF_DARKMODE 0x00400000 /* Use dark mode for HTML */
55 #define DIFF_BY_TOKEN 0x01000000 /* Split on tokens, not lines */
56
57 /*
58 ** Per file information that may influence output.
59 */
60 #define DIFF_FILE_ADDED 0x40000000 /* Added or rename destination */
@@ -319,10 +320,113 @@
320
321 /* Return results */
322 *pnLine = nLine;
323 return a;
324 }
325
326 /*
327 ** Character classes for the purpose of tokenization.
328 **
329 ** 1 - alphanumeric
330 ** 2 - whitespace
331 ** 3 - punctuation
332 */
333 static char aTCharClass[256] = {
334 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
335 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
336 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
337 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3,
338
339 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
340 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3,
341 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
342 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3,
343
344 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
345 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
346 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
347 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
348
349 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
350 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
351 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
352 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
353 };
354
355 /*
356 ** Count the number of tokens in the given string.
357 */
358 static int count_tokens(const unsigned char *p, int n){
359 int nToken = 0;
360 int iPrev = 0;
361 int i;
362 for(i=0; i<n; i++){
363 char x = aTCharClass[p[i]];
364 if( x!=iPrev ){
365 iPrev = x;
366 nToken++;
367 }
368 }
369 return nToken;
370 }
371
372 /*
373 ** Return an array of DLine objects containing a pointer to the
374 ** start of each token and a hash of that token. The lower
375 ** bits of the hash store the length of each token.
376 **
377 ** This is like break_into_lines() except that it works with tokens
378 ** instead of lines. A token is:
379 **
380 ** * A contiguous sequence of alphanumeric characters.
381 ** * A contiguous sequence of whitespace
382 ** * A contiguous sequence of punctuation characters.
383 **
384 ** Return 0 if the file is binary or contains a line that is
385 ** too long.
386 */
387 static DLine *break_into_tokens(
388 const char *z,
389 int n,
390 int *pnToken,
391 u64 diffFlags
392 ){
393 int nToken, i, k;
394 u64 h, h2;
395 DLine *a;
396 unsigned char *p = (unsigned char*)z;
397
398 nToken = count_tokens(p, n);
399 a = fossil_malloc( sizeof(a[0])*(nToken+1) );
400 memset(a, 0, sizeof(a[0])*(nToken+1));
401 if( n==0 ){
402 *pnToken = 0;
403 return a;
404 }
405 i = 0;
406 while( n>0 ){
407 char x = aTCharClass[*p];
408 h = 0xcbf29ce484222325LL;
409 for(k=1; k<n && aTCharClass[p[k]]==x; k++){
410 h ^= p[k];
411 h *= 0x100000001b3LL;
412 }
413 a[i].z = (char*)p;
414 a[i].n = k;
415 a[i].h = h = ((h%281474976710597LL)<<LENGTH_MASK_SZ) | k;
416 h2 = h % nToken;
417 a[i].iNext = a[h2].iHash;
418 a[h2].iHash = i+1;
419 p += k; n -= k;
420 i++;
421 };
422 assert( i==nToken );
423
424 /* Return results */
425 *pnToken = nToken;
426 return a;
427 }
428
429 /*
430 ** Return zero if two DLine elements are identical.
431 */
432 static int compare_dline(const DLine *pA, const DLine *pB){
@@ -2997,14 +3101,21 @@
3101 if( (pCfg->diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
3102 c.xDiffer = compare_dline_ignore_allws;
3103 }else{
3104 c.xDiffer = compare_dline;
3105 }
3106 if( pCfg->diffFlags & DIFF_BY_TOKEN ){
3107 c.aFrom = break_into_tokens(blob_str(pA_Blob), blob_size(pA_Blob),
3108 &c.nFrom, pCfg->diffFlags);
3109 c.aTo = break_into_tokens(blob_str(pB_Blob), blob_size(pB_Blob),
3110 &c.nTo, pCfg->diffFlags);
3111 }else{
3112 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
3113 &c.nFrom, pCfg->diffFlags);
3114 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
3115 &c.nTo, pCfg->diffFlags);
3116 }
3117 if( c.aFrom==0 || c.aTo==0 ){
3118 fossil_free(c.aFrom);
3119 fossil_free(c.aTo);
3120 if( pOut ){
3121 diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, pCfg->diffFlags);
@@ -3035,10 +3146,26 @@
3146 }
3147 }
3148 if( (pCfg->diffFlags & DIFF_NOOPT)==0 ){
3149 diff_optimize(&c);
3150 }
3151 if( (pCfg->diffFlags & DIFF_BY_TOKEN)!=0 ){
3152 /* Convert token counts into byte counts. */
3153 int i;
3154 int iA = 0;
3155 int iB = 0;
3156 for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
3157 int k, sum;
3158 for(k=0, sum=0; k<c.aEdit[i]; k++) sum += c.aFrom[iA++].n;
3159 iB += c.aEdit[i];
3160 c.aEdit[i] = sum;
3161 for(k=0, sum=0; k<c.aEdit[i+1]; k++) sum += c.aFrom[iA++].n;
3162 c.aEdit[i+1] = sum;
3163 for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n;
3164 c.aEdit[i+2] = sum;
3165 }
3166 }
3167
3168 if( pOut ){
3169 if( pCfg->diffFlags & DIFF_NUMSTAT ){
3170 int nDel = 0, nIns = 0, i;
3171 for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
@@ -3049,11 +3176,11 @@
3176 g.diffCnt[2] += nDel;
3177 if( nIns+nDel ){
3178 g.diffCnt[0]++;
3179 blob_appendf(pOut, "%10d %10d", nIns, nDel);
3180 }
3181 }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
3182 const int *R = c.aEdit;
3183 unsigned int r;
3184 for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
3185 blob_appendf(pOut, " copy %6d delete %6d insert %6d\n",
3186 R[r], R[r+1], R[r+2]);
@@ -3157,10 +3284,13 @@
3284
3285 /* Undocumented and unsupported flags used for development
3286 ** debugging and analysis: */
3287 if( find_option("debug",0,0)!=0 ) diffFlags |= DIFF_DEBUG;
3288 if( find_option("raw",0,0)!=0 ) diffFlags |= DIFF_RAW;
3289 if( find_option("bytoken",0,0)!=0 ){
3290 diffFlags = DIFF_RAW|DIFF_BY_TOKEN;
3291 }
3292 }
3293 if( (z = find_option("context","c",1))!=0 ){
3294 char *zEnd;
3295 f = (int)strtol(z, &zEnd, 10);
3296 if( zEnd[0]==0 && errno!=ERANGE ){
3297
+15 -15
--- src/merge.c
+++ src/merge.c
@@ -165,31 +165,31 @@
165165
rid = db_column_int(&q, 1);
166166
content_get(rid, &pivot);
167167
}
168168
mb.pPivot = &pivot;
169169
170
- /* Set up the merge-in as V1 */
170
+ /* Set up the merge-in as V2 */
171171
zFN = db_column_text(&q, 5);
172172
if( zFN==0 ){
173173
/* File deleted in the merged-in branch */
174
- mb.zV1 = "(deleted file)";
175
- blob_zero(&v1);
174
+ mb.zV2 = "(deleted file)";
175
+ blob_zero(&v2);
176176
}else{
177
- mb.zV1 = mprintf("%s (merge-in)", file_tail(zFN));
177
+ mb.zV2 = mprintf("%s (merge-in)", file_tail(zFN));
178178
rid = db_column_int(&q, 6);
179
- content_get(rid, &v1);
179
+ content_get(rid, &v2);
180180
}
181
- mb.pV1 = &v1;
181
+ mb.pV2 = &v2;
182182
183
- /* Set up the local content as V2 */
183
+ /* Set up the local content as V1 */
184184
zFN = db_column_text(&q, 2);
185185
if( zFN==0 ){
186186
/* File added by merge */
187
- mb.zV2 = "(no original)";
188
- blob_zero(&v2);
187
+ mb.zV1 = "(no original)";
188
+ blob_zero(&v1);
189189
}else{
190
- mb.zV2 = mprintf("%s (local)", file_tail(zFN));
190
+ mb.zV1 = mprintf("%s (local)", file_tail(zFN));
191191
rid = db_column_int(&q, 3);
192192
sz = db_column_int(&q, 4);
193193
if( rid==0 && sz>0 ){
194194
/* The origin file had been edited so we'll have to pull its
195195
** original content out of the undo buffer */
@@ -197,23 +197,23 @@
197197
db_prepare(&q2,
198198
"SELECT content FROM undo"
199199
" WHERE pathname=%Q AND octet_length(content)=%d",
200200
zFN, sz
201201
);
202
- blob_zero(&v2);
202
+ blob_zero(&v1);
203203
if( db_step(&q2)==SQLITE_ROW ){
204
- db_column_blob(&q, 0, &v2);
204
+ db_column_blob(&q, 0, &v1);
205205
}else{
206
- mb.zV2 = "(local content missing)";
206
+ mb.zV1 = "(local content missing)";
207207
}
208208
db_finalize(&q2);
209209
}else{
210210
/* The origin file was unchanged when the merge first occurred */
211
- content_get(rid, &v2);
211
+ content_get(rid, &v1);
212212
}
213213
}
214
- mb.pV2 = &v2;
214
+ mb.pV1 = &v1;
215215
216216
/* Set up the output */
217217
zFN = db_column_text(&q, 7);
218218
if( zFN==0 ){
219219
mb.zOut = "(Merge Result)";
220220
--- src/merge.c
+++ src/merge.c
@@ -165,31 +165,31 @@
165 rid = db_column_int(&q, 1);
166 content_get(rid, &pivot);
167 }
168 mb.pPivot = &pivot;
169
170 /* Set up the merge-in as V1 */
171 zFN = db_column_text(&q, 5);
172 if( zFN==0 ){
173 /* File deleted in the merged-in branch */
174 mb.zV1 = "(deleted file)";
175 blob_zero(&v1);
176 }else{
177 mb.zV1 = mprintf("%s (merge-in)", file_tail(zFN));
178 rid = db_column_int(&q, 6);
179 content_get(rid, &v1);
180 }
181 mb.pV1 = &v1;
182
183 /* Set up the local content as V2 */
184 zFN = db_column_text(&q, 2);
185 if( zFN==0 ){
186 /* File added by merge */
187 mb.zV2 = "(no original)";
188 blob_zero(&v2);
189 }else{
190 mb.zV2 = mprintf("%s (local)", file_tail(zFN));
191 rid = db_column_int(&q, 3);
192 sz = db_column_int(&q, 4);
193 if( rid==0 && sz>0 ){
194 /* The origin file had been edited so we'll have to pull its
195 ** original content out of the undo buffer */
@@ -197,23 +197,23 @@
197 db_prepare(&q2,
198 "SELECT content FROM undo"
199 " WHERE pathname=%Q AND octet_length(content)=%d",
200 zFN, sz
201 );
202 blob_zero(&v2);
203 if( db_step(&q2)==SQLITE_ROW ){
204 db_column_blob(&q, 0, &v2);
205 }else{
206 mb.zV2 = "(local content missing)";
207 }
208 db_finalize(&q2);
209 }else{
210 /* The origin file was unchanged when the merge first occurred */
211 content_get(rid, &v2);
212 }
213 }
214 mb.pV2 = &v2;
215
216 /* Set up the output */
217 zFN = db_column_text(&q, 7);
218 if( zFN==0 ){
219 mb.zOut = "(Merge Result)";
220
--- src/merge.c
+++ src/merge.c
@@ -165,31 +165,31 @@
165 rid = db_column_int(&q, 1);
166 content_get(rid, &pivot);
167 }
168 mb.pPivot = &pivot;
169
170 /* Set up the merge-in as V2 */
171 zFN = db_column_text(&q, 5);
172 if( zFN==0 ){
173 /* File deleted in the merged-in branch */
174 mb.zV2 = "(deleted file)";
175 blob_zero(&v2);
176 }else{
177 mb.zV2 = mprintf("%s (merge-in)", file_tail(zFN));
178 rid = db_column_int(&q, 6);
179 content_get(rid, &v2);
180 }
181 mb.pV2 = &v2;
182
183 /* Set up the local content as V1 */
184 zFN = db_column_text(&q, 2);
185 if( zFN==0 ){
186 /* File added by merge */
187 mb.zV1 = "(no original)";
188 blob_zero(&v1);
189 }else{
190 mb.zV1 = mprintf("%s (local)", file_tail(zFN));
191 rid = db_column_int(&q, 3);
192 sz = db_column_int(&q, 4);
193 if( rid==0 && sz>0 ){
194 /* The origin file had been edited so we'll have to pull its
195 ** original content out of the undo buffer */
@@ -197,23 +197,23 @@
197 db_prepare(&q2,
198 "SELECT content FROM undo"
199 " WHERE pathname=%Q AND octet_length(content)=%d",
200 zFN, sz
201 );
202 blob_zero(&v1);
203 if( db_step(&q2)==SQLITE_ROW ){
204 db_column_blob(&q, 0, &v1);
205 }else{
206 mb.zV1 = "(local content missing)";
207 }
208 db_finalize(&q2);
209 }else{
210 /* The origin file was unchanged when the merge first occurred */
211 content_get(rid, &v1);
212 }
213 }
214 mb.pV1 = &v1;
215
216 /* Set up the output */
217 zFN = db_column_text(&q, 7);
218 if( zFN==0 ){
219 mb.zOut = "(Merge Result)";
220
+148 -126
--- src/merge.tcl
+++ src/merge.tcl
@@ -100,11 +100,12 @@
100100
set cmd "$fossilcmd -c -1"
101101
} else {
102102
set cmd "$fossilcmd -c $ncontext"
103103
}
104104
if {[info exists current_file]} {
105
- append cmd " -tcl [list $current_file]"
105
+ regsub {^[A-Z]+ } $current_file {} fn
106
+ append cmd " -tcl [list $fn]"
106107
}
107108
if {[catch {
108109
set in [open $cmd r]
109110
fconfigure $in -encoding utf-8
110111
set mergetxt [read $in]
@@ -122,74 +123,80 @@
122123
set lnC 1
123124
set lnD 1
124125
foreach {A B C D} $mergetxt {
125126
set key1 [string index $A 0]
126127
if {$key1=="S"} {
127
- set N [string range $A 1 end]
128
- incr lnA $N
129
- incr lnB $N
130
- incr lnC $N
131
- incr lnD $N
132
- .lnA insert end ...\n hrln
133
- .txtA insert end [string repeat . 30]\n hrtxt
134
- .lnB insert end ...\n hrln
135
- .txtB insert end [string repeat . 30]\n hrtxt
136
- .lnC insert end ...\n hrln
137
- .txtC insert end [string repeat . 30]\n hrtxt
138
- .lnD insert end ...\n hrln
139
- .txtD insert end [string repeat . 30]\n hrtxt
128
+ scan [string range $A 1 end] "%d %d %d %d" nA nB nC nD
129
+ foreach x {A B C D} {
130
+ set N [set n$x]
131
+ incr ln$x $N
132
+ if {$N>0} {
133
+ .ln$x insert end ...\n hrln
134
+ .txt$x insert end [string repeat . 30]\n hrtxt
135
+ } else {
136
+ .ln$x insert end \n hrln
137
+ .txt$x insert end \n hrtxt
138
+ }
139
+ }
140140
continue
141141
}
142142
set key2 [string index $B 0]
143143
set key3 [string index $C 0]
144144
set key4 [string index $D 0]
145
- if {$key4=="X"} {set dtag rm} {set dtag -}
146145
if {$key1=="."} {
147146
.lnA insert end \n -
148
- .txtA insert end \n $dtag
147
+ .txtA insert end \n -
149148
} elseif {$key1=="N"} {
150149
.nameA config -text [string range $A 1 end]
151150
} else {
152151
.lnA insert end $lnA\n -
153152
incr lnA
154
- .txtA insert end [string range $A 1 end]\n $dtag
153
+ if {$key1=="X"} {
154
+ .txtA insert end [string range $A 1 end]\n rm
155
+ } else {
156
+ .txtA insert end [string range $A 1 end]\n -
157
+ }
155158
}
156159
if {$key2=="."} {
157160
.lnB insert end \n -
158
- .txtB insert end \n $dtag
161
+ .txtB insert end \n -
159162
} elseif {$key2=="N"} {
160163
.nameB config -text [string range $B 1 end]
161164
} else {
162165
.lnB insert end $lnB\n -
163166
incr lnB
164
- if {$key4=="2"} {set tag chng} {set tag $dtag}
167
+ if {$key4=="2"} {set tag chng} {set tag -}
165168
if {$key2=="1"} {
166169
.txtB insert end [string range $A 1 end]\n $tag
170
+ } elseif {$key2=="X"} {
171
+ .txtB insert end [string range $B 1 end]\n rm
167172
} else {
168173
.txtB insert end [string range $B 1 end]\n $tag
169174
}
170175
}
171176
if {$key3=="."} {
172177
.lnC insert end \n -
173
- .txtC insert end \n $dtag
174
- } elseif {$key3=="N"} {
178
+ .txtC insert end \n -
179
+ } elseif {$key3=="N"} {
175180
.nameC config -text [string range $C 1 end]
176181
} else {
177182
.lnC insert end $lnC\n -
178183
incr lnC
179
- if {$key4=="3"} {set tag add} {set tag $dtag}
184
+ if {$key4=="3"} {set tag add} {set tag -}
180185
if {$key3=="1"} {
181186
.txtC insert end [string range $A 1 end]\n $tag
182187
} elseif {$key3=="2"} {
183188
.txtC insert end [string range $B 1 end]\n chng
184
- } else {
189
+ } elseif {$key3=="X"} {
190
+ .txtC insert end [string range $C 1 end]\n rm
191
+ } else {
185192
.txtC insert end [string range $C 1 end]\n $tag
186193
}
187194
}
188
- if {$key4=="." || $key4=="X"} {
195
+ if {$key4=="."} {
189196
.lnD insert end \n -
190
- .txtD insert end \n $dtag
197
+ .txtD insert end \n -
191198
} elseif {$key4=="N"} {
192199
.nameD config -text [string range $D 1 end]
193200
} else {
194201
.lnD insert end $lnD\n -
195202
incr lnD
@@ -197,10 +204,12 @@
197204
.txtD insert end [string range $A 1 end]\n -
198205
} elseif {$key4=="2"} {
199206
.txtD insert end [string range $B 1 end]\n chng
200207
} elseif {$key4=="3"} {
201208
.txtD insert end [string range $C 1 end]\n add
209
+ } elseif {$key4=="X"} {
210
+ .txtD insert end [string range $D 1 end]\n rm
202211
} else {
203212
.txtD insert end [string range $D 1 end]\n -
204213
}
205214
}
206215
}
@@ -209,10 +218,21 @@
209218
if {$type ne "txt"} {
210219
$c config -width 6; # $widths($type)
211220
}
212221
$c config -state disabled
213222
}
223
+ set mx $lnA
224
+ if {$lnB>$mx} {set mx $lnB}
225
+ if {$lnC>$mx} {set mx $lnC}
226
+ if {$lnD>$mx} {set mx $lnD}
227
+ global lnWidth
228
+ set lnWidth [string length [format %d $mx]]
229
+ .lnA config -width $lnWidth
230
+ .lnB config -width $lnWidth
231
+ .lnC config -width $lnWidth
232
+ .lnD config -width $lnWidth
233
+ grid columnconfig . {0 2 4 6} -minsize $lnWidth
214234
}
215235
216236
proc viewDiff {idx} {
217237
.txtA yview $idx
218238
.txtA xview moveto 0
@@ -259,20 +279,10 @@
259279
proc disableSync {axis} {
260280
rename sync-$axis _sync-$axis
261281
interp alias {} sync-$axis {} noop
262282
}
263283
264
-proc sync-x {col first last} {
265
- disableSync x
266
- $col xview moveto [expr {$first*[xvis $col]/($last-$first)}]
267
- foreach side {A B C D} {
268
- set sb .sbx$side
269
- set xview [.txt$side xview]
270
- }
271
- enableSync x
272
-}
273
-
274284
proc sync-y {first last} {
275285
disableSync y
276286
foreach c [cols] {
277287
$c yview moveto $first
278288
}
@@ -327,108 +337,122 @@
327337
bind . <$key> "scroll-$axis $args; break"
328338
bind . <Shift-$key> continue
329339
}
330340
331341
frame .bb
342
+set useOptionMenu 1
332343
if {[info exists filelist]} {
333
- label .bb.filetag -text "File:"
334
- set current_file [lindex $filelist 1]
335
- trace add variable current_file write readMerge
336
- ::ttk::menubutton .bb.files -text $current_file
337
- if {[tk windowingsystem] eq "win32"} {
338
- ::ttk::style theme use winnative
339
- .bb.files configure -padding {20 1 10 2}
340
- }
341
- toplevel .wfiles
342
- wm withdraw .wfiles
343
- update idletasks
344
- wm transient .wfiles .
345
- wm overrideredirect .wfiles 1
346
- set ht [expr {[llength $filelist]/2}]
347
- if {$ht>$CFG(LB_HEIGHT)} {set ht $CFG(LB_HEIGHT)}
348
- listbox .wfiles.lb -width 0 -height $ht -activestyle none \
349
- -yscroll {.wfiles.sb set}
350
- set mx 1
351
- foreach {op fn} $filelist {
352
- set n [string length $fn]
353
- if {$n>$mx} {set mx $n}
354
- .wfiles.lb insert end [format "%-9s %s" $op $fn]
355
- }
356
- .bb.files config -width $mx
357
- ::ttk::scrollbar .wfiles.sb -command {.wfiles.lb yview}
358
- grid .wfiles.lb .wfiles.sb -sticky ns
359
- bind .bb.files <1> {
360
- set x [winfo rootx %W]
361
- set y [expr {[winfo rooty %W]+[winfo height %W]}]
362
- wm geometry .wfiles +$x+$y
363
- wm deiconify .wfiles
364
- focus .wfiles.lb
365
- }
366
- bind .wfiles <FocusOut> {wm withdraw .wfiles}
367
- bind .wfiles <Escape> {focus .}
368
- foreach evt {1 Return} {
369
- bind .wfiles.lb <$evt> {
370
- set ii [%W curselection]
371
- set ::current_file [lindex $::filelist [expr {$ii*2+1}]]
372
- .bb.files config -text $::current_file
373
- focus .
374
- break
375
- }
376
- }
377
- bind .wfiles.lb <Motion> {
378
- %W selection clear 0 end
379
- %W selection set @%x,%y
344
+ set current_file "[lindex $filelist 0] [lindex $filelist 1]"
345
+ if {[llength $filelist]>2} {
346
+ trace add variable current_file write readMerge
347
+
348
+ if {$tcl_platform(os)=="Darwin" || [llength $filelist]<30} {
349
+ set fnlist {}
350
+ foreach {op fn} $filelist {lappend fnlist "$op $fn"}
351
+ tk_optionMenu .bb.files current_file {*}$fnlist
352
+ } else {
353
+ set useOptionMenu 0
354
+ ::ttk::menubutton .bb.files -text $current_file
355
+ if {[tk windowingsystem] eq "win32"} {
356
+ ::ttk::style theme use winnative
357
+ .bb.files configure -padding {20 1 10 2}
358
+ }
359
+ toplevel .wfiles
360
+ wm withdraw .wfiles
361
+ update idletasks
362
+ wm transient .wfiles .
363
+ wm overrideredirect .wfiles 1
364
+ set ht [expr {[llength $filelist]/2}]
365
+ if {$ht>$CFG(LB_HEIGHT)} {set ht $CFG(LB_HEIGHT)}
366
+ listbox .wfiles.lb -width 0 -height $ht -activestyle none \
367
+ -yscroll {.wfiles.sb set}
368
+ set mx 1
369
+ foreach {op fn} $filelist {
370
+ set n [string length $fn]
371
+ if {$n>$mx} {set mx $n}
372
+ .wfiles.lb insert end "$op $fn"
373
+ }
374
+ .bb.files config -width $mx
375
+ ::ttk::scrollbar .wfiles.sb -command {.wfiles.lb yview}
376
+ grid .wfiles.lb .wfiles.sb -sticky ns
377
+ bind .bb.files <1> {
378
+ set x [winfo rootx %W]
379
+ set y [expr {[winfo rooty %W]+[winfo height %W]}]
380
+ wm geometry .wfiles +$x+$y
381
+ wm deiconify .wfiles
382
+ focus .wfiles.lb
383
+ }
384
+ bind .wfiles <FocusOut> {wm withdraw .wfiles}
385
+ bind .wfiles <Escape> {focus .}
386
+ foreach evt {1 Return} {
387
+ bind .wfiles.lb <$evt> {
388
+ set ii [%W curselection]
389
+ set ::current_file [%W get $ii]
390
+ .bb.files config -text $::current_file
391
+ focus .
392
+ break
393
+ }
394
+ }
395
+ bind .wfiles.lb <Motion> {
396
+ %W selection clear 0 end
397
+ %W selection set @%x,%y
398
+ }
399
+ }
380400
}
381401
}
382402
383403
label .bb.ctxtag -text "Context:"
384404
set context_choices {3 6 12 25 50 100 All}
385405
if {$ncontext<0} {set ncontext All}
386406
trace add variable ncontext write readMerge
387
-::ttk::menubutton .bb.ctx -text $ncontext
388
-if {[tk windowingsystem] eq "win32"} {
389
- ::ttk::style theme use winnative
390
- .bb.ctx configure -padding {20 1 10 2}
391
-}
392
-toplevel .wctx
393
-wm withdraw .wctx
394
-update idletasks
395
-wm transient .wctx .
396
-wm overrideredirect .wctx 1
397
-listbox .wctx.lb -width 0 -height 7 -activestyle none
398
-.wctx.lb insert end {*}$context_choices
399
-pack .wctx.lb
400
-bind .bb.ctx <1> {
401
- set x [winfo rootx %W]
402
- set y [expr {[winfo rooty %W]+[winfo height %W]}]
403
- wm geometry .wctx +$x+$y
404
- wm deiconify .wctx
405
- focus .wctx.lb
406
-}
407
-bind .wctx <FocusOut> {wm withdraw .wctx}
408
-bind .wctx <Escape> {focus .}
409
-foreach evt {1 Return} {
410
- bind .wctx.lb <$evt> {
411
- set ::ncontext [lindex $::context_choices [%W curselection]]
412
- .bb.ctx config -text $::ncontext
413
- focus .
414
- break
415
- }
416
-}
417
-bind .wctx.lb <Motion> {
418
- %W selection clear 0 end
419
- %W selection set @%x,%y
407
+if {$tcl_platform(os)=="Darwin" || $useOptionMenu} {
408
+ tk_optionMenu .bb.ctx ncontext {*}$context_choices
409
+} else {
410
+ ::ttk::menubutton .bb.ctx -text $ncontext
411
+ if {[tk windowingsystem] eq "win32"} {
412
+ ::ttk::style theme use winnative
413
+ .bb.ctx configure -padding {20 1 10 2}
414
+ }
415
+ toplevel .wctx
416
+ wm withdraw .wctx
417
+ update idletasks
418
+ wm transient .wctx .
419
+ wm overrideredirect .wctx 1
420
+ listbox .wctx.lb -width 0 -height 7 -activestyle none
421
+ .wctx.lb insert end {*}$context_choices
422
+ pack .wctx.lb
423
+ bind .bb.ctx <1> {
424
+ set x [winfo rootx %W]
425
+ set y [expr {[winfo rooty %W]+[winfo height %W]}]
426
+ wm geometry .wctx +$x+$y
427
+ wm deiconify .wctx
428
+ focus .wctx.lb
429
+ }
430
+ bind .wctx <FocusOut> {wm withdraw .wctx}
431
+ bind .wctx <Escape> {focus .}
432
+ foreach evt {1 Return} {
433
+ bind .wctx.lb <$evt> {
434
+ set ::ncontext [lindex $::context_choices [%W curselection]]
435
+ .bb.ctx config -text $::ncontext
436
+ focus .
437
+ break
438
+ }
439
+ }
440
+ bind .wctx.lb <Motion> {
441
+ %W selection clear 0 end
442
+ %W selection set @%x,%y
443
+ }
420444
}
421445
422446
foreach {side syncCol} {A .txtB B .txtA C .txtC D .txtD} {
423447
set ln .ln$side
424448
text $ln
425449
$ln tag config - -justify right
426450
427451
set txt .txt$side
428452
text $txt -width $CFG(WIDTH) -height $CFG(HEIGHT) -wrap none \
429
- -xscroll "sync-x $syncCol"
453
+ -xscroll ".sbx$side set"
430454
catch {$txt config -tabstyle wordprocessor} ;# Required for Tk>=8.5
431455
foreach tag {add rm chng} {
432456
$txt tag config $tag -background $CFG([string toupper $tag]_BG)
433457
$txt tag lower $tag
434458
}
@@ -540,22 +564,18 @@
540564
set ::search $w
541565
}
542566
::ttk::button .bb.quit -text {Quit} -command exit
543567
::ttk::button .bb.search -text {Search} -command searchOnOff
544568
pack .bb.quit -side left
545
-if {[info exists filelist]} {
546
- pack .bb.filetag .bb.files -side left
569
+if {[winfo exists .bb.files]} {
570
+ pack .bb.files -side left
547571
}
548572
pack .bb.ctxtag .bb.ctx -side left
549573
pack .bb.search -side left
550
-grid rowconfigure . 1 -weight 1
551
-set rn 0
552
-foreach {lnwid txtwid} [cols] {
553
- grid columnconfigure . $rn -weight 1 -uniform a
554
- grid columnconfigure . [expr {$rn+1}] -weight 1 -uniform b
555
- incr rn 2
556
-}
574
+grid rowconfigure . 1 -weight 1 -minsize [winfo reqheight .nameA]
575
+grid rowconfigure . 2 -weight 100
576
+readMerge
557577
grid .bb -row 0 -columnspan 8
558578
grid .nameA -row 1 -column 1 -sticky ew
559579
grid .nameB -row 1 -column 3 -sticky ew
560580
grid .nameC -row 1 -column 5 -sticky ew
561581
grid .nameD -row 1 -column 7 -sticky ew
@@ -563,9 +583,11 @@
563583
grid .sby -row 2 -column 8 -sticky ns
564584
grid .sbxA -row 3 -column 1 -sticky ew
565585
grid .sbxB -row 3 -column 3 -sticky ew
566586
grid .sbxC -row 3 -column 5 -sticky ew
567587
grid .sbxD -row 3 -column 7 -sticky ew
568
-readMerge
588
+grid columnconfigure . {0 2 4 6} \
589
+ -weight 1 -uniform a -minsize [winfo reqwidth .lnA]
590
+grid columnconfigure . {1 3 5 7} -weight 100 -uniform b
569591
570592
.spacer config -height [winfo height .sbxA]
571593
wm deiconify .
572594
--- src/merge.tcl
+++ src/merge.tcl
@@ -100,11 +100,12 @@
100 set cmd "$fossilcmd -c -1"
101 } else {
102 set cmd "$fossilcmd -c $ncontext"
103 }
104 if {[info exists current_file]} {
105 append cmd " -tcl [list $current_file]"
 
106 }
107 if {[catch {
108 set in [open $cmd r]
109 fconfigure $in -encoding utf-8
110 set mergetxt [read $in]
@@ -122,74 +123,80 @@
122 set lnC 1
123 set lnD 1
124 foreach {A B C D} $mergetxt {
125 set key1 [string index $A 0]
126 if {$key1=="S"} {
127 set N [string range $A 1 end]
128 incr lnA $N
129 incr lnB $N
130 incr lnC $N
131 incr lnD $N
132 .lnA insert end ...\n hrln
133 .txtA insert end [string repeat . 30]\n hrtxt
134 .lnB insert end ...\n hrln
135 .txtB insert end [string repeat . 30]\n hrtxt
136 .lnC insert end ...\n hrln
137 .txtC insert end [string repeat . 30]\n hrtxt
138 .lnD insert end ...\n hrln
139 .txtD insert end [string repeat . 30]\n hrtxt
140 continue
141 }
142 set key2 [string index $B 0]
143 set key3 [string index $C 0]
144 set key4 [string index $D 0]
145 if {$key4=="X"} {set dtag rm} {set dtag -}
146 if {$key1=="."} {
147 .lnA insert end \n -
148 .txtA insert end \n $dtag
149 } elseif {$key1=="N"} {
150 .nameA config -text [string range $A 1 end]
151 } else {
152 .lnA insert end $lnA\n -
153 incr lnA
154 .txtA insert end [string range $A 1 end]\n $dtag
 
 
 
 
155 }
156 if {$key2=="."} {
157 .lnB insert end \n -
158 .txtB insert end \n $dtag
159 } elseif {$key2=="N"} {
160 .nameB config -text [string range $B 1 end]
161 } else {
162 .lnB insert end $lnB\n -
163 incr lnB
164 if {$key4=="2"} {set tag chng} {set tag $dtag}
165 if {$key2=="1"} {
166 .txtB insert end [string range $A 1 end]\n $tag
 
 
167 } else {
168 .txtB insert end [string range $B 1 end]\n $tag
169 }
170 }
171 if {$key3=="."} {
172 .lnC insert end \n -
173 .txtC insert end \n $dtag
174 } elseif {$key3=="N"} {
175 .nameC config -text [string range $C 1 end]
176 } else {
177 .lnC insert end $lnC\n -
178 incr lnC
179 if {$key4=="3"} {set tag add} {set tag $dtag}
180 if {$key3=="1"} {
181 .txtC insert end [string range $A 1 end]\n $tag
182 } elseif {$key3=="2"} {
183 .txtC insert end [string range $B 1 end]\n chng
184 } else {
 
 
185 .txtC insert end [string range $C 1 end]\n $tag
186 }
187 }
188 if {$key4=="." || $key4=="X"} {
189 .lnD insert end \n -
190 .txtD insert end \n $dtag
191 } elseif {$key4=="N"} {
192 .nameD config -text [string range $D 1 end]
193 } else {
194 .lnD insert end $lnD\n -
195 incr lnD
@@ -197,10 +204,12 @@
197 .txtD insert end [string range $A 1 end]\n -
198 } elseif {$key4=="2"} {
199 .txtD insert end [string range $B 1 end]\n chng
200 } elseif {$key4=="3"} {
201 .txtD insert end [string range $C 1 end]\n add
 
 
202 } else {
203 .txtD insert end [string range $D 1 end]\n -
204 }
205 }
206 }
@@ -209,10 +218,21 @@
209 if {$type ne "txt"} {
210 $c config -width 6; # $widths($type)
211 }
212 $c config -state disabled
213 }
 
 
 
 
 
 
 
 
 
 
 
214 }
215
216 proc viewDiff {idx} {
217 .txtA yview $idx
218 .txtA xview moveto 0
@@ -259,20 +279,10 @@
259 proc disableSync {axis} {
260 rename sync-$axis _sync-$axis
261 interp alias {} sync-$axis {} noop
262 }
263
264 proc sync-x {col first last} {
265 disableSync x
266 $col xview moveto [expr {$first*[xvis $col]/($last-$first)}]
267 foreach side {A B C D} {
268 set sb .sbx$side
269 set xview [.txt$side xview]
270 }
271 enableSync x
272 }
273
274 proc sync-y {first last} {
275 disableSync y
276 foreach c [cols] {
277 $c yview moveto $first
278 }
@@ -327,108 +337,122 @@
327 bind . <$key> "scroll-$axis $args; break"
328 bind . <Shift-$key> continue
329 }
330
331 frame .bb
 
332 if {[info exists filelist]} {
333 label .bb.filetag -text "File:"
334 set current_file [lindex $filelist 1]
335 trace add variable current_file write readMerge
336 ::ttk::menubutton .bb.files -text $current_file
337 if {[tk windowingsystem] eq "win32"} {
338 ::ttk::style theme use winnative
339 .bb.files configure -padding {20 1 10 2}
340 }
341 toplevel .wfiles
342 wm withdraw .wfiles
343 update idletasks
344 wm transient .wfiles .
345 wm overrideredirect .wfiles 1
346 set ht [expr {[llength $filelist]/2}]
347 if {$ht>$CFG(LB_HEIGHT)} {set ht $CFG(LB_HEIGHT)}
348 listbox .wfiles.lb -width 0 -height $ht -activestyle none \
349 -yscroll {.wfiles.sb set}
350 set mx 1
351 foreach {op fn} $filelist {
352 set n [string length $fn]
353 if {$n>$mx} {set mx $n}
354 .wfiles.lb insert end [format "%-9s %s" $op $fn]
355 }
356 .bb.files config -width $mx
357 ::ttk::scrollbar .wfiles.sb -command {.wfiles.lb yview}
358 grid .wfiles.lb .wfiles.sb -sticky ns
359 bind .bb.files <1> {
360 set x [winfo rootx %W]
361 set y [expr {[winfo rooty %W]+[winfo height %W]}]
362 wm geometry .wfiles +$x+$y
363 wm deiconify .wfiles
364 focus .wfiles.lb
365 }
366 bind .wfiles <FocusOut> {wm withdraw .wfiles}
367 bind .wfiles <Escape> {focus .}
368 foreach evt {1 Return} {
369 bind .wfiles.lb <$evt> {
370 set ii [%W curselection]
371 set ::current_file [lindex $::filelist [expr {$ii*2+1}]]
372 .bb.files config -text $::current_file
373 focus .
374 break
375 }
376 }
377 bind .wfiles.lb <Motion> {
378 %W selection clear 0 end
379 %W selection set @%x,%y
 
 
 
 
 
 
 
 
 
380 }
381 }
382
383 label .bb.ctxtag -text "Context:"
384 set context_choices {3 6 12 25 50 100 All}
385 if {$ncontext<0} {set ncontext All}
386 trace add variable ncontext write readMerge
387 ::ttk::menubutton .bb.ctx -text $ncontext
388 if {[tk windowingsystem] eq "win32"} {
389 ::ttk::style theme use winnative
390 .bb.ctx configure -padding {20 1 10 2}
391 }
392 toplevel .wctx
393 wm withdraw .wctx
394 update idletasks
395 wm transient .wctx .
396 wm overrideredirect .wctx 1
397 listbox .wctx.lb -width 0 -height 7 -activestyle none
398 .wctx.lb insert end {*}$context_choices
399 pack .wctx.lb
400 bind .bb.ctx <1> {
401 set x [winfo rootx %W]
402 set y [expr {[winfo rooty %W]+[winfo height %W]}]
403 wm geometry .wctx +$x+$y
404 wm deiconify .wctx
405 focus .wctx.lb
406 }
407 bind .wctx <FocusOut> {wm withdraw .wctx}
408 bind .wctx <Escape> {focus .}
409 foreach evt {1 Return} {
410 bind .wctx.lb <$evt> {
411 set ::ncontext [lindex $::context_choices [%W curselection]]
412 .bb.ctx config -text $::ncontext
413 focus .
414 break
415 }
416 }
417 bind .wctx.lb <Motion> {
418 %W selection clear 0 end
419 %W selection set @%x,%y
 
 
 
 
420 }
421
422 foreach {side syncCol} {A .txtB B .txtA C .txtC D .txtD} {
423 set ln .ln$side
424 text $ln
425 $ln tag config - -justify right
426
427 set txt .txt$side
428 text $txt -width $CFG(WIDTH) -height $CFG(HEIGHT) -wrap none \
429 -xscroll "sync-x $syncCol"
430 catch {$txt config -tabstyle wordprocessor} ;# Required for Tk>=8.5
431 foreach tag {add rm chng} {
432 $txt tag config $tag -background $CFG([string toupper $tag]_BG)
433 $txt tag lower $tag
434 }
@@ -540,22 +564,18 @@
540 set ::search $w
541 }
542 ::ttk::button .bb.quit -text {Quit} -command exit
543 ::ttk::button .bb.search -text {Search} -command searchOnOff
544 pack .bb.quit -side left
545 if {[info exists filelist]} {
546 pack .bb.filetag .bb.files -side left
547 }
548 pack .bb.ctxtag .bb.ctx -side left
549 pack .bb.search -side left
550 grid rowconfigure . 1 -weight 1
551 set rn 0
552 foreach {lnwid txtwid} [cols] {
553 grid columnconfigure . $rn -weight 1 -uniform a
554 grid columnconfigure . [expr {$rn+1}] -weight 1 -uniform b
555 incr rn 2
556 }
557 grid .bb -row 0 -columnspan 8
558 grid .nameA -row 1 -column 1 -sticky ew
559 grid .nameB -row 1 -column 3 -sticky ew
560 grid .nameC -row 1 -column 5 -sticky ew
561 grid .nameD -row 1 -column 7 -sticky ew
@@ -563,9 +583,11 @@
563 grid .sby -row 2 -column 8 -sticky ns
564 grid .sbxA -row 3 -column 1 -sticky ew
565 grid .sbxB -row 3 -column 3 -sticky ew
566 grid .sbxC -row 3 -column 5 -sticky ew
567 grid .sbxD -row 3 -column 7 -sticky ew
568 readMerge
 
 
569
570 .spacer config -height [winfo height .sbxA]
571 wm deiconify .
572
--- src/merge.tcl
+++ src/merge.tcl
@@ -100,11 +100,12 @@
100 set cmd "$fossilcmd -c -1"
101 } else {
102 set cmd "$fossilcmd -c $ncontext"
103 }
104 if {[info exists current_file]} {
105 regsub {^[A-Z]+ } $current_file {} fn
106 append cmd " -tcl [list $fn]"
107 }
108 if {[catch {
109 set in [open $cmd r]
110 fconfigure $in -encoding utf-8
111 set mergetxt [read $in]
@@ -122,74 +123,80 @@
123 set lnC 1
124 set lnD 1
125 foreach {A B C D} $mergetxt {
126 set key1 [string index $A 0]
127 if {$key1=="S"} {
128 scan [string range $A 1 end] "%d %d %d %d" nA nB nC nD
129 foreach x {A B C D} {
130 set N [set n$x]
131 incr ln$x $N
132 if {$N>0} {
133 .ln$x insert end ...\n hrln
134 .txt$x insert end [string repeat . 30]\n hrtxt
135 } else {
136 .ln$x insert end \n hrln
137 .txt$x insert end \n hrtxt
138 }
139 }
 
140 continue
141 }
142 set key2 [string index $B 0]
143 set key3 [string index $C 0]
144 set key4 [string index $D 0]
 
145 if {$key1=="."} {
146 .lnA insert end \n -
147 .txtA insert end \n -
148 } elseif {$key1=="N"} {
149 .nameA config -text [string range $A 1 end]
150 } else {
151 .lnA insert end $lnA\n -
152 incr lnA
153 if {$key1=="X"} {
154 .txtA insert end [string range $A 1 end]\n rm
155 } else {
156 .txtA insert end [string range $A 1 end]\n -
157 }
158 }
159 if {$key2=="."} {
160 .lnB insert end \n -
161 .txtB insert end \n -
162 } elseif {$key2=="N"} {
163 .nameB config -text [string range $B 1 end]
164 } else {
165 .lnB insert end $lnB\n -
166 incr lnB
167 if {$key4=="2"} {set tag chng} {set tag -}
168 if {$key2=="1"} {
169 .txtB insert end [string range $A 1 end]\n $tag
170 } elseif {$key2=="X"} {
171 .txtB insert end [string range $B 1 end]\n rm
172 } else {
173 .txtB insert end [string range $B 1 end]\n $tag
174 }
175 }
176 if {$key3=="."} {
177 .lnC insert end \n -
178 .txtC insert end \n -
179 } elseif {$key3=="N"} {
180 .nameC config -text [string range $C 1 end]
181 } else {
182 .lnC insert end $lnC\n -
183 incr lnC
184 if {$key4=="3"} {set tag add} {set tag -}
185 if {$key3=="1"} {
186 .txtC insert end [string range $A 1 end]\n $tag
187 } elseif {$key3=="2"} {
188 .txtC insert end [string range $B 1 end]\n chng
189 } elseif {$key3=="X"} {
190 .txtC insert end [string range $C 1 end]\n rm
191 } else {
192 .txtC insert end [string range $C 1 end]\n $tag
193 }
194 }
195 if {$key4=="."} {
196 .lnD insert end \n -
197 .txtD insert end \n -
198 } elseif {$key4=="N"} {
199 .nameD config -text [string range $D 1 end]
200 } else {
201 .lnD insert end $lnD\n -
202 incr lnD
@@ -197,10 +204,12 @@
204 .txtD insert end [string range $A 1 end]\n -
205 } elseif {$key4=="2"} {
206 .txtD insert end [string range $B 1 end]\n chng
207 } elseif {$key4=="3"} {
208 .txtD insert end [string range $C 1 end]\n add
209 } elseif {$key4=="X"} {
210 .txtD insert end [string range $D 1 end]\n rm
211 } else {
212 .txtD insert end [string range $D 1 end]\n -
213 }
214 }
215 }
@@ -209,10 +218,21 @@
218 if {$type ne "txt"} {
219 $c config -width 6; # $widths($type)
220 }
221 $c config -state disabled
222 }
223 set mx $lnA
224 if {$lnB>$mx} {set mx $lnB}
225 if {$lnC>$mx} {set mx $lnC}
226 if {$lnD>$mx} {set mx $lnD}
227 global lnWidth
228 set lnWidth [string length [format %d $mx]]
229 .lnA config -width $lnWidth
230 .lnB config -width $lnWidth
231 .lnC config -width $lnWidth
232 .lnD config -width $lnWidth
233 grid columnconfig . {0 2 4 6} -minsize $lnWidth
234 }
235
236 proc viewDiff {idx} {
237 .txtA yview $idx
238 .txtA xview moveto 0
@@ -259,20 +279,10 @@
279 proc disableSync {axis} {
280 rename sync-$axis _sync-$axis
281 interp alias {} sync-$axis {} noop
282 }
283
 
 
 
 
 
 
 
 
 
 
284 proc sync-y {first last} {
285 disableSync y
286 foreach c [cols] {
287 $c yview moveto $first
288 }
@@ -327,108 +337,122 @@
337 bind . <$key> "scroll-$axis $args; break"
338 bind . <Shift-$key> continue
339 }
340
341 frame .bb
342 set useOptionMenu 1
343 if {[info exists filelist]} {
344 set current_file "[lindex $filelist 0] [lindex $filelist 1]"
345 if {[llength $filelist]>2} {
346 trace add variable current_file write readMerge
347
348 if {$tcl_platform(os)=="Darwin" || [llength $filelist]<30} {
349 set fnlist {}
350 foreach {op fn} $filelist {lappend fnlist "$op $fn"}
351 tk_optionMenu .bb.files current_file {*}$fnlist
352 } else {
353 set useOptionMenu 0
354 ::ttk::menubutton .bb.files -text $current_file
355 if {[tk windowingsystem] eq "win32"} {
356 ::ttk::style theme use winnative
357 .bb.files configure -padding {20 1 10 2}
358 }
359 toplevel .wfiles
360 wm withdraw .wfiles
361 update idletasks
362 wm transient .wfiles .
363 wm overrideredirect .wfiles 1
364 set ht [expr {[llength $filelist]/2}]
365 if {$ht>$CFG(LB_HEIGHT)} {set ht $CFG(LB_HEIGHT)}
366 listbox .wfiles.lb -width 0 -height $ht -activestyle none \
367 -yscroll {.wfiles.sb set}
368 set mx 1
369 foreach {op fn} $filelist {
370 set n [string length $fn]
371 if {$n>$mx} {set mx $n}
372 .wfiles.lb insert end "$op $fn"
373 }
374 .bb.files config -width $mx
375 ::ttk::scrollbar .wfiles.sb -command {.wfiles.lb yview}
376 grid .wfiles.lb .wfiles.sb -sticky ns
377 bind .bb.files <1> {
378 set x [winfo rootx %W]
379 set y [expr {[winfo rooty %W]+[winfo height %W]}]
380 wm geometry .wfiles +$x+$y
381 wm deiconify .wfiles
382 focus .wfiles.lb
383 }
384 bind .wfiles <FocusOut> {wm withdraw .wfiles}
385 bind .wfiles <Escape> {focus .}
386 foreach evt {1 Return} {
387 bind .wfiles.lb <$evt> {
388 set ii [%W curselection]
389 set ::current_file [%W get $ii]
390 .bb.files config -text $::current_file
391 focus .
392 break
393 }
394 }
395 bind .wfiles.lb <Motion> {
396 %W selection clear 0 end
397 %W selection set @%x,%y
398 }
399 }
400 }
401 }
402
403 label .bb.ctxtag -text "Context:"
404 set context_choices {3 6 12 25 50 100 All}
405 if {$ncontext<0} {set ncontext All}
406 trace add variable ncontext write readMerge
407 if {$tcl_platform(os)=="Darwin" || $useOptionMenu} {
408 tk_optionMenu .bb.ctx ncontext {*}$context_choices
409 } else {
410 ::ttk::menubutton .bb.ctx -text $ncontext
411 if {[tk windowingsystem] eq "win32"} {
412 ::ttk::style theme use winnative
413 .bb.ctx configure -padding {20 1 10 2}
414 }
415 toplevel .wctx
416 wm withdraw .wctx
417 update idletasks
418 wm transient .wctx .
419 wm overrideredirect .wctx 1
420 listbox .wctx.lb -width 0 -height 7 -activestyle none
421 .wctx.lb insert end {*}$context_choices
422 pack .wctx.lb
423 bind .bb.ctx <1> {
424 set x [winfo rootx %W]
425 set y [expr {[winfo rooty %W]+[winfo height %W]}]
426 wm geometry .wctx +$x+$y
427 wm deiconify .wctx
428 focus .wctx.lb
429 }
430 bind .wctx <FocusOut> {wm withdraw .wctx}
431 bind .wctx <Escape> {focus .}
432 foreach evt {1 Return} {
433 bind .wctx.lb <$evt> {
434 set ::ncontext [lindex $::context_choices [%W curselection]]
435 .bb.ctx config -text $::ncontext
436 focus .
437 break
438 }
439 }
440 bind .wctx.lb <Motion> {
441 %W selection clear 0 end
442 %W selection set @%x,%y
443 }
444 }
445
446 foreach {side syncCol} {A .txtB B .txtA C .txtC D .txtD} {
447 set ln .ln$side
448 text $ln
449 $ln tag config - -justify right
450
451 set txt .txt$side
452 text $txt -width $CFG(WIDTH) -height $CFG(HEIGHT) -wrap none \
453 -xscroll ".sbx$side set"
454 catch {$txt config -tabstyle wordprocessor} ;# Required for Tk>=8.5
455 foreach tag {add rm chng} {
456 $txt tag config $tag -background $CFG([string toupper $tag]_BG)
457 $txt tag lower $tag
458 }
@@ -540,22 +564,18 @@
564 set ::search $w
565 }
566 ::ttk::button .bb.quit -text {Quit} -command exit
567 ::ttk::button .bb.search -text {Search} -command searchOnOff
568 pack .bb.quit -side left
569 if {[winfo exists .bb.files]} {
570 pack .bb.files -side left
571 }
572 pack .bb.ctxtag .bb.ctx -side left
573 pack .bb.search -side left
574 grid rowconfigure . 1 -weight 1 -minsize [winfo reqheight .nameA]
575 grid rowconfigure . 2 -weight 100
576 readMerge
 
 
 
 
577 grid .bb -row 0 -columnspan 8
578 grid .nameA -row 1 -column 1 -sticky ew
579 grid .nameB -row 1 -column 3 -sticky ew
580 grid .nameC -row 1 -column 5 -sticky ew
581 grid .nameD -row 1 -column 7 -sticky ew
@@ -563,9 +583,11 @@
583 grid .sby -row 2 -column 8 -sticky ns
584 grid .sbxA -row 3 -column 1 -sticky ew
585 grid .sbxB -row 3 -column 3 -sticky ew
586 grid .sbxC -row 3 -column 5 -sticky ew
587 grid .sbxD -row 3 -column 7 -sticky ew
588 grid columnconfigure . {0 2 4 6} \
589 -weight 1 -uniform a -minsize [winfo reqwidth .lnA]
590 grid columnconfigure . {1 3 5 7} -weight 100 -uniform b
591
592 .spacer config -height [winfo height .sbxA]
593 wm deiconify .
594
+158 -34
--- src/merge3.c
+++ src/merge3.c
@@ -81,10 +81,11 @@
8181
** Text of boundary markers for merge conflicts.
8282
*/
8383
static const char *const mergeMarker[] = {
8484
/*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/
8585
"<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<",
86
+ "####### SUGGESTED CONFLICT RESOLUTION follows ###################",
8687
"||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||",
8788
"======= MERGED IN content follows ===============================",
8889
">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
8990
};
9091
@@ -150,12 +151,12 @@
150151
const char *zPivot; /* Label or name for the pivot */
151152
const char *zV1; /* Label or name for the V1 file */
152153
const char *zV2; /* Label or name for the V2 file */
153154
const char *zOut; /* Label or name for the output */
154155
Blob *pPivot; /* The common ancestor */
155
- Blob *pV1; /* First variant */
156
- Blob *pV2; /* Second variant */
156
+ Blob *pV1; /* First variant (local copy) */
157
+ Blob *pV2; /* Second variant (merged in) */
157158
Blob *pOut; /* Write merge results here */
158159
int useCrLf; /* Use CRLF line endings */
159160
int nContext; /* Size of unchanged line boundaries */
160161
unsigned int mxPivot; /* Number of lines in the pivot */
161162
unsigned int mxV1; /* Number of lines in V1 */
@@ -163,10 +164,11 @@
163164
unsigned int lnPivot; /* Lines read from pivot */
164165
unsigned int lnV1; /* Lines read from v1 */
165166
unsigned int lnV2; /* Lines read from v2 */
166167
unsigned int lnOut; /* Lines written to out */
167168
unsigned int nConflict; /* Number of conflicts seen */
169
+ u64 diffFlags; /* Flags for difference engine */
168170
};
169171
#endif /* INTERFACE */
170172
171173
172174
/************************* Generic MergeBuilder ******************************/
@@ -261,10 +263,100 @@
261263
p->xChngBoth = dbgChngBoth;
262264
p->xConflict = dbgConflict;
263265
p->xEnd = dbgStartEnd;
264266
p->xDestroy = dbgDestroy;
265267
}
268
+
269
+/************************* MergeBuilderToken ********************************/
270
+/* This version of MergeBuilder actually performs a merge on file that
271
+** are broken up into tokens instead of lines, and puts the result in pOut.
272
+*/
273
+static void tokenSame(MergeBuilder *p, unsigned int N){
274
+ blob_append(p->pOut, p->pPivot->aData+p->pPivot->iCursor, N);
275
+ p->pPivot->iCursor += N;
276
+ p->pV1->iCursor += N;
277
+ p->pV2->iCursor += N;
278
+}
279
+static void tokenChngV1(MergeBuilder *p, unsigned int nPivot, unsigned nV1){
280
+ blob_append(p->pOut, p->pV1->aData+p->pV1->iCursor, nV1);
281
+ p->pPivot->iCursor += nPivot;
282
+ p->pV1->iCursor += nV1;
283
+ p->pV2->iCursor += nPivot;
284
+}
285
+static void tokenChngV2(MergeBuilder *p, unsigned int nPivot, unsigned nV2){
286
+ blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV2);
287
+ p->pPivot->iCursor += nPivot;
288
+ p->pV1->iCursor += nPivot;
289
+ p->pV2->iCursor += nV2;
290
+}
291
+static void tokenChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned nV){
292
+ blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV);
293
+ p->pPivot->iCursor += nPivot;
294
+ p->pV1->iCursor += nV;
295
+ p->pV2->iCursor += nV;
296
+}
297
+static void tokenConflict(
298
+ MergeBuilder *p,
299
+ unsigned int nPivot,
300
+ unsigned int nV1,
301
+ unsigned int nV2
302
+){
303
+ /* For a token-merge conflict, use the text from the merge-in */
304
+ blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV2);
305
+ p->pPivot->iCursor += nPivot;
306
+ p->pV1->iCursor += nV1;
307
+ p->pV2->iCursor += nV2;
308
+}
309
+static void mergebuilder_init_token(MergeBuilder *p){
310
+ mergebuilder_init(p);
311
+ p->xSame = tokenSame;
312
+ p->xChngV1 = tokenChngV1;
313
+ p->xChngV2 = tokenChngV2;
314
+ p->xChngBoth = tokenChngBoth;
315
+ p->xConflict = tokenConflict;
316
+ p->diffFlags = DIFF_BY_TOKEN;
317
+}
318
+
319
+/*
320
+** Attempt to do a low-level merge on a conflict. The conflict is
321
+** described by the first four parameters, which are the same as the
322
+** arguments to the xConflict method of the MergeBuilder object.
323
+** This routine attempts to resolve the conflict by looking at
324
+** elements of the conflict region that are finer grain than complete
325
+** lines of text.
326
+**
327
+** The result is written into Blob pOut. pOut is initialized by this
328
+** routine.
329
+*/
330
+int merge_try_to_resolve_conflict(
331
+ MergeBuilder *pMB, /* MergeBuilder that encounter conflict */
332
+ unsigned int nPivot, /* Lines of conflict in the pivot */
333
+ unsigned int nV1, /* Lines of conflict in V1 */
334
+ unsigned int nV2, /* Lines of conflict in V2 */
335
+ Blob *pOut /* Write resolution text here */
336
+){
337
+ int nConflict;
338
+ MergeBuilder mb;
339
+ Blob pv, v1, v2;
340
+ mergebuilder_init_token(&mb);
341
+ blob_extract_lines(pMB->pPivot, nPivot, &pv);
342
+ blob_extract_lines(pMB->pV1, nV1, &v1);
343
+ blob_extract_lines(pMB->pV2, nV2, &v2);
344
+ blob_zero(pOut);
345
+ mb.pPivot = &pv;
346
+ mb.pV1 = &v1;
347
+ mb.pV2 = &v2;
348
+ mb.pOut = pOut;
349
+ nConflict = merge_three_blobs(&mb);
350
+ /* The pv, v1, and v2 blobs are all ephemeral and hence do not need
351
+ ** to be freed. */
352
+ /* mb has not allocated any resources, so we do not need to invoke
353
+ ** the xDestroy method. */
354
+ blob_add_final_newline(pOut);
355
+ return nConflict;
356
+}
357
+
266358
267359
/************************* MergeBuilderText **********************************/
268360
/* This version of MergeBuilder actually performs a merge on file and puts
269361
** the result in pOut
270362
*/
@@ -305,20 +397,31 @@
305397
MergeBuilder *p,
306398
unsigned int nPivot,
307399
unsigned int nV1,
308400
unsigned int nV2
309401
){
310
- append_merge_mark(p->pOut, 0, p->lnV1, p->useCrLf);
402
+ int nRes; /* Lines in the computed conflict resolution */
403
+ Blob res; /* Text of the conflict resolution */
404
+
405
+ merge_try_to_resolve_conflict(p, nPivot, nV1, nV2, &res);
406
+ nRes = blob_linecount(&res);
407
+
408
+ append_merge_mark(p->pOut, 0, p->lnV1+1, p->useCrLf);
311409
blob_copy_lines(p->pOut, p->pV1, nV1); p->lnV1 += nV1;
312410
313
- append_merge_mark(p->pOut, 1, p->lnPivot, p->useCrLf);
411
+ if( nRes>0 ){
412
+ append_merge_mark(p->pOut, 1, 0, p->useCrLf);
413
+ blob_copy_lines(p->pOut, &res, nRes);
414
+ }
415
+
416
+ append_merge_mark(p->pOut, 2, p->lnPivot+1, p->useCrLf);
314417
blob_copy_lines(p->pOut, p->pPivot, nPivot); p->lnPivot += nPivot;
315418
316
- append_merge_mark(p->pOut, 2, p->lnV2, p->useCrLf);
419
+ append_merge_mark(p->pOut, 3, p->lnV2+1, p->useCrLf);
317420
blob_copy_lines(p->pOut, p->pV2, nV2); p->lnV2 += nV2;
318421
319
- append_merge_mark(p->pOut, 3, -1, p->useCrLf);
422
+ append_merge_mark(p->pOut, 4, -1, p->useCrLf);
320423
}
321424
static void mergebuilder_init_text(MergeBuilder *p){
322425
mergebuilder_init(p);
323426
p->xStart = txtStart;
324427
p->xSame = txtSame;
@@ -338,13 +441,13 @@
338441
**
339442
** . This line is omitted.
340443
** N Name of the file.
341444
** T Literal text follows that should have a \n terminator.
342445
** R Literal text follows that needs a \r\n terminator.
343
-** X Merge conflict. (Column 4 only)
446
+** X Merge conflict.
344447
** Z Literal text without a line terminator.
345
-** S Skipped lines in all 4 files.
448
+** S Skipped lines. Followed by number of lines to skip.
346449
** 1 Text is a copy of token 1
347450
** 2 Use data from data-token 2
348451
** 3 Use data from data-token 3
349452
*/
350453
@@ -370,25 +473,24 @@
370473
}
371474
}
372475
}
373476
374477
/* Copy one line of text from pIn and append to pOut, encoded as TCL */
375
-static void tclLineOfText(Blob *pOut, Blob *pIn){
478
+static void tclLineOfText(Blob *pOut, Blob *pIn, char cType){
376479
int i, k;
377480
for(i=pIn->iCursor; i<pIn->nUsed && pIn->aData[i]!='\n'; i++){}
378481
if( i==pIn->nUsed ){
379
- blob_append(pOut, "\"Z", 2);
380482
k = i;
381483
}else if( i>pIn->iCursor && pIn->aData[i-1]=='\r' ){
382
- blob_append(pOut, "\"R", 2);
383484
k = i-1;
384485
i++;
385486
}else{
386
- blob_append(pOut, "\"T", 2);
387487
k = i;
388488
i++;
389489
}
490
+ blob_append_char(pOut, '"');
491
+ blob_append_char(pOut, cType);
390492
tclWriteQuotedText(pOut, pIn->aData+pIn->iCursor, k-pIn->iCursor);
391493
pIn->iCursor = i;
392494
blob_append_char(pOut, '"');
393495
}
394496
static void tclStart(MergeBuilder *p){
@@ -411,20 +513,21 @@
411513
int i = 0;
412514
int nSkip;
413515
414516
if( p->lnPivot>=2 || p->lnV1>2 || p->lnV2>2 ){
415517
while( i<N && i<p->nContext ){
416
- tclLineOfText(p->pOut, p->pPivot);
518
+ tclLineOfText(p->pOut, p->pPivot, 'T');
417519
blob_append(p->pOut, " 1 1 1\n", 7);
418520
i++;
419521
}
420522
nSkip = N - p->nContext*2;
421523
}else{
422524
nSkip = N - p->nContext;
423525
}
424526
if( nSkip>0 ){
425
- blob_appendf(p->pOut, "S%d . . .\n", nSkip);
527
+ blob_appendf(p->pOut, "\"S%d %d %d %d\" . . .\n",
528
+ nSkip, nSkip, nSkip, nSkip);
426529
blob_copy_lines(0, p->pPivot, nSkip);
427530
i += nSkip;
428531
}
429532
430533
p->lnPivot += N;
@@ -431,11 +534,11 @@
431534
p->lnV1 += N;
432535
p->lnV2 += N;
433536
434537
if( p->lnPivot<p->mxPivot || p->lnV1<p->mxV1 || p->lnV2<p->mxV2 ){
435538
while( i<N ){
436
- tclLineOfText(p->pOut, p->pPivot);
539
+ tclLineOfText(p->pOut, p->pPivot, 'T');
437540
blob_append(p->pOut, " 1 1 1\n", 7);
438541
i++;
439542
}
440543
}
441544
@@ -443,23 +546,23 @@
443546
blob_copy_lines(0, p->pV2, N);
444547
}
445548
static void tclChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){
446549
int i;
447550
for(i=0; i<nPivot && i<nV1; i++){
448
- tclLineOfText(p->pOut, p->pPivot);
551
+ tclLineOfText(p->pOut, p->pPivot, 'T');
449552
blob_append_char(p->pOut, ' ');
450
- tclLineOfText(p->pOut, p->pV1);
553
+ tclLineOfText(p->pOut, p->pV1, 'T');
451554
blob_append(p->pOut, " 1 2\n", 5);
452555
}
453556
while( i<nPivot ){
454
- tclLineOfText(p->pOut, p->pPivot);
557
+ tclLineOfText(p->pOut, p->pPivot, 'T');
455558
blob_append(p->pOut, " . 1 .\n", 7);
456559
i++;
457560
}
458561
while( i<nV1 ){
459562
blob_append(p->pOut, ". ", 2);
460
- tclLineOfText(p->pOut, p->pV1);
563
+ tclLineOfText(p->pOut, p->pV1, 'T');
461564
blob_append(p->pOut, " . 2\n", 5);
462565
i++;
463566
}
464567
p->lnPivot += nPivot;
465568
p->lnV1 += nV1;
@@ -467,23 +570,23 @@
467570
blob_copy_lines(0, p->pV2, nPivot);
468571
}
469572
static void tclChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){
470573
int i;
471574
for(i=0; i<nPivot && i<nV2; i++){
472
- tclLineOfText(p->pOut, p->pPivot);
575
+ tclLineOfText(p->pOut, p->pPivot, 'T');
473576
blob_append(p->pOut, " 1 ", 3);
474
- tclLineOfText(p->pOut, p->pV2);
577
+ tclLineOfText(p->pOut, p->pV2, 'T');
475578
blob_append(p->pOut, " 3\n", 3);
476579
}
477580
while( i<nPivot ){
478
- tclLineOfText(p->pOut, p->pPivot);
581
+ tclLineOfText(p->pOut, p->pPivot, 'T');
479582
blob_append(p->pOut, " 1 . .\n", 7);
480583
i++;
481584
}
482585
while( i<nV2 ){
483586
blob_append(p->pOut, ". . ", 4);
484
- tclLineOfText(p->pOut, p->pV2);
587
+ tclLineOfText(p->pOut, p->pV2, 'T');
485588
blob_append(p->pOut, " 3\n", 3);
486589
i++;
487590
}
488591
p->lnPivot += nPivot;
489592
p->lnV1 += nPivot;
@@ -491,23 +594,23 @@
491594
blob_copy_lines(0, p->pV1, nPivot);
492595
}
493596
static void tclChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){
494597
int i;
495598
for(i=0; i<nPivot && i<nV; i++){
496
- tclLineOfText(p->pOut, p->pPivot);
599
+ tclLineOfText(p->pOut, p->pPivot, 'T');
497600
blob_append_char(p->pOut, ' ');
498
- tclLineOfText(p->pOut, p->pV1);
601
+ tclLineOfText(p->pOut, p->pV1, 'T');
499602
blob_append(p->pOut, " 2 2\n", 5);
500603
}
501604
while( i<nPivot ){
502
- tclLineOfText(p->pOut, p->pPivot);
605
+ tclLineOfText(p->pOut, p->pPivot, 'T');
503606
blob_append(p->pOut, " . . .\n", 7);
504607
i++;
505608
}
506609
while( i<nV ){
507610
blob_append(p->pOut, ". ", 2);
508
- tclLineOfText(p->pOut, p->pV1);
611
+ tclLineOfText(p->pOut, p->pV1, 'T');
509612
blob_append(p->pOut, " 2 2\n", 5);
510613
i++;
511614
}
512615
p->lnPivot += nPivot;
513616
p->lnV1 += nV;
@@ -520,32 +623,51 @@
520623
unsigned int nV1,
521624
unsigned int nV2
522625
){
523626
int mx = nPivot;
524627
int i;
628
+ int nRes;
629
+ Blob res;
630
+
631
+ merge_try_to_resolve_conflict(p, nPivot, nV1, nV2, &res);
632
+ nRes = blob_linecount(&res);
525633
if( nV1>mx ) mx = nV1;
526634
if( nV2>mx ) mx = nV2;
635
+ if( nRes>mx ) mx = nRes;
636
+ if( nRes>0 ){
637
+ blob_appendf(p->pOut, "\"S0 0 0 %d\" . . .\n", nV2+2);
638
+ }
527639
for(i=0; i<mx; i++){
528640
if( i<nPivot ){
529
- tclLineOfText(p->pOut, p->pPivot);
641
+ tclLineOfText(p->pOut, p->pPivot, 'X');
530642
}else{
531643
blob_append_char(p->pOut, '.');
532644
}
533645
blob_append_char(p->pOut, ' ');
534646
if( i<nV1 ){
535
- tclLineOfText(p->pOut, p->pV1);
647
+ tclLineOfText(p->pOut, p->pV1, 'X');
536648
}else{
537649
blob_append_char(p->pOut, '.');
538650
}
539651
blob_append_char(p->pOut, ' ');
540652
if( i<nV2 ){
541
- tclLineOfText(p->pOut, p->pV2);
653
+ tclLineOfText(p->pOut, p->pV2, 'X');
542654
}else{
543655
blob_append_char(p->pOut, '.');
544656
}
545
- blob_append(p->pOut, " X\n", 3);
657
+ if( i<nRes ){
658
+ blob_append_char(p->pOut, ' ');
659
+ tclLineOfText(p->pOut, &res, 'X');
660
+ blob_append_char(p->pOut, '\n');
661
+ }else{
662
+ blob_append(p->pOut, " .\n", 3);
663
+ }
664
+ if( i==mx-1 ){
665
+ blob_appendf(p->pOut, "\"S0 0 0 %d\" . . .\n", nPivot+nV1+3);
666
+ }
546667
}
668
+ blob_reset(&res);
547669
p->lnPivot += nPivot;
548670
p->lnV1 += nV1;
549671
p->lnV2 += nV2;
550672
}
551673
void mergebuilder_init_tcl(MergeBuilder *p){
@@ -637,10 +759,11 @@
637759
** the second integer is the number of lines of text to omit from the
638760
** pivot, and the third integer is the number of lines of text that are
639761
** inserted. The edit array ends with a triple of 0,0,0.
640762
*/
641763
diff_config_init(&DCfg, 0);
764
+ DCfg.diffFlags = p->diffFlags;
642765
aC1 = text_diff(p->pPivot, p->pV1, 0, &DCfg);
643766
aC2 = text_diff(p->pPivot, p->pV2, 0, &DCfg);
644767
if( aC1==0 || aC2==0 ){
645768
free(aC1);
646769
free(aC2);
@@ -758,11 +881,12 @@
758881
const char *z = blob_buffer(p);
759882
int n = blob_size(p) - len + 1;
760883
assert( len==(int)strlen(mergeMarker[1]) );
761884
assert( len==(int)strlen(mergeMarker[2]) );
762885
assert( len==(int)strlen(mergeMarker[3]) );
763
- assert( count(mergeMarker)==4 );
886
+ assert( len==(int)strlen(mergeMarker[4]) );
887
+ assert( count(mergeMarker)==5 );
764888
for(i=0; i<n; ){
765889
for(j=0; j<4; j++){
766890
if( (memcmp(&z[i], mergeMarker[j], len)==0) ){
767891
return 1;
768892
}
@@ -1004,17 +1128,17 @@
10041128
10051129
#if INTERFACE
10061130
/*
10071131
** Flags to the 3-way merger
10081132
*/
1009
-#define MERGE_DRYRUN 0x0001
1133
+#define MERGE_DRYRUN 0x0001
10101134
/*
10111135
** The MERGE_KEEP_FILES flag specifies that merge_3way() should retain
10121136
** its temporary files on error. By default they are removed after the
10131137
** merge, regardless of success or failure.
10141138
*/
1015
-#define MERGE_KEEP_FILES 0x0002
1139
+#define MERGE_KEEP_FILES 0x0002
10161140
#endif
10171141
10181142
10191143
/*
10201144
** This routine is a wrapper around merge_three_blobs() with the following
10211145
--- src/merge3.c
+++ src/merge3.c
@@ -81,10 +81,11 @@
81 ** Text of boundary markers for merge conflicts.
82 */
83 static const char *const mergeMarker[] = {
84 /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/
85 "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<",
 
86 "||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||",
87 "======= MERGED IN content follows ===============================",
88 ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
89 };
90
@@ -150,12 +151,12 @@
150 const char *zPivot; /* Label or name for the pivot */
151 const char *zV1; /* Label or name for the V1 file */
152 const char *zV2; /* Label or name for the V2 file */
153 const char *zOut; /* Label or name for the output */
154 Blob *pPivot; /* The common ancestor */
155 Blob *pV1; /* First variant */
156 Blob *pV2; /* Second variant */
157 Blob *pOut; /* Write merge results here */
158 int useCrLf; /* Use CRLF line endings */
159 int nContext; /* Size of unchanged line boundaries */
160 unsigned int mxPivot; /* Number of lines in the pivot */
161 unsigned int mxV1; /* Number of lines in V1 */
@@ -163,10 +164,11 @@
163 unsigned int lnPivot; /* Lines read from pivot */
164 unsigned int lnV1; /* Lines read from v1 */
165 unsigned int lnV2; /* Lines read from v2 */
166 unsigned int lnOut; /* Lines written to out */
167 unsigned int nConflict; /* Number of conflicts seen */
 
168 };
169 #endif /* INTERFACE */
170
171
172 /************************* Generic MergeBuilder ******************************/
@@ -261,10 +263,100 @@
261 p->xChngBoth = dbgChngBoth;
262 p->xConflict = dbgConflict;
263 p->xEnd = dbgStartEnd;
264 p->xDestroy = dbgDestroy;
265 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
267 /************************* MergeBuilderText **********************************/
268 /* This version of MergeBuilder actually performs a merge on file and puts
269 ** the result in pOut
270 */
@@ -305,20 +397,31 @@
305 MergeBuilder *p,
306 unsigned int nPivot,
307 unsigned int nV1,
308 unsigned int nV2
309 ){
310 append_merge_mark(p->pOut, 0, p->lnV1, p->useCrLf);
 
 
 
 
 
 
311 blob_copy_lines(p->pOut, p->pV1, nV1); p->lnV1 += nV1;
312
313 append_merge_mark(p->pOut, 1, p->lnPivot, p->useCrLf);
 
 
 
 
 
314 blob_copy_lines(p->pOut, p->pPivot, nPivot); p->lnPivot += nPivot;
315
316 append_merge_mark(p->pOut, 2, p->lnV2, p->useCrLf);
317 blob_copy_lines(p->pOut, p->pV2, nV2); p->lnV2 += nV2;
318
319 append_merge_mark(p->pOut, 3, -1, p->useCrLf);
320 }
321 static void mergebuilder_init_text(MergeBuilder *p){
322 mergebuilder_init(p);
323 p->xStart = txtStart;
324 p->xSame = txtSame;
@@ -338,13 +441,13 @@
338 **
339 ** . This line is omitted.
340 ** N Name of the file.
341 ** T Literal text follows that should have a \n terminator.
342 ** R Literal text follows that needs a \r\n terminator.
343 ** X Merge conflict. (Column 4 only)
344 ** Z Literal text without a line terminator.
345 ** S Skipped lines in all 4 files.
346 ** 1 Text is a copy of token 1
347 ** 2 Use data from data-token 2
348 ** 3 Use data from data-token 3
349 */
350
@@ -370,25 +473,24 @@
370 }
371 }
372 }
373
374 /* Copy one line of text from pIn and append to pOut, encoded as TCL */
375 static void tclLineOfText(Blob *pOut, Blob *pIn){
376 int i, k;
377 for(i=pIn->iCursor; i<pIn->nUsed && pIn->aData[i]!='\n'; i++){}
378 if( i==pIn->nUsed ){
379 blob_append(pOut, "\"Z", 2);
380 k = i;
381 }else if( i>pIn->iCursor && pIn->aData[i-1]=='\r' ){
382 blob_append(pOut, "\"R", 2);
383 k = i-1;
384 i++;
385 }else{
386 blob_append(pOut, "\"T", 2);
387 k = i;
388 i++;
389 }
 
 
390 tclWriteQuotedText(pOut, pIn->aData+pIn->iCursor, k-pIn->iCursor);
391 pIn->iCursor = i;
392 blob_append_char(pOut, '"');
393 }
394 static void tclStart(MergeBuilder *p){
@@ -411,20 +513,21 @@
411 int i = 0;
412 int nSkip;
413
414 if( p->lnPivot>=2 || p->lnV1>2 || p->lnV2>2 ){
415 while( i<N && i<p->nContext ){
416 tclLineOfText(p->pOut, p->pPivot);
417 blob_append(p->pOut, " 1 1 1\n", 7);
418 i++;
419 }
420 nSkip = N - p->nContext*2;
421 }else{
422 nSkip = N - p->nContext;
423 }
424 if( nSkip>0 ){
425 blob_appendf(p->pOut, "S%d . . .\n", nSkip);
 
426 blob_copy_lines(0, p->pPivot, nSkip);
427 i += nSkip;
428 }
429
430 p->lnPivot += N;
@@ -431,11 +534,11 @@
431 p->lnV1 += N;
432 p->lnV2 += N;
433
434 if( p->lnPivot<p->mxPivot || p->lnV1<p->mxV1 || p->lnV2<p->mxV2 ){
435 while( i<N ){
436 tclLineOfText(p->pOut, p->pPivot);
437 blob_append(p->pOut, " 1 1 1\n", 7);
438 i++;
439 }
440 }
441
@@ -443,23 +546,23 @@
443 blob_copy_lines(0, p->pV2, N);
444 }
445 static void tclChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){
446 int i;
447 for(i=0; i<nPivot && i<nV1; i++){
448 tclLineOfText(p->pOut, p->pPivot);
449 blob_append_char(p->pOut, ' ');
450 tclLineOfText(p->pOut, p->pV1);
451 blob_append(p->pOut, " 1 2\n", 5);
452 }
453 while( i<nPivot ){
454 tclLineOfText(p->pOut, p->pPivot);
455 blob_append(p->pOut, " . 1 .\n", 7);
456 i++;
457 }
458 while( i<nV1 ){
459 blob_append(p->pOut, ". ", 2);
460 tclLineOfText(p->pOut, p->pV1);
461 blob_append(p->pOut, " . 2\n", 5);
462 i++;
463 }
464 p->lnPivot += nPivot;
465 p->lnV1 += nV1;
@@ -467,23 +570,23 @@
467 blob_copy_lines(0, p->pV2, nPivot);
468 }
469 static void tclChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){
470 int i;
471 for(i=0; i<nPivot && i<nV2; i++){
472 tclLineOfText(p->pOut, p->pPivot);
473 blob_append(p->pOut, " 1 ", 3);
474 tclLineOfText(p->pOut, p->pV2);
475 blob_append(p->pOut, " 3\n", 3);
476 }
477 while( i<nPivot ){
478 tclLineOfText(p->pOut, p->pPivot);
479 blob_append(p->pOut, " 1 . .\n", 7);
480 i++;
481 }
482 while( i<nV2 ){
483 blob_append(p->pOut, ". . ", 4);
484 tclLineOfText(p->pOut, p->pV2);
485 blob_append(p->pOut, " 3\n", 3);
486 i++;
487 }
488 p->lnPivot += nPivot;
489 p->lnV1 += nPivot;
@@ -491,23 +594,23 @@
491 blob_copy_lines(0, p->pV1, nPivot);
492 }
493 static void tclChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){
494 int i;
495 for(i=0; i<nPivot && i<nV; i++){
496 tclLineOfText(p->pOut, p->pPivot);
497 blob_append_char(p->pOut, ' ');
498 tclLineOfText(p->pOut, p->pV1);
499 blob_append(p->pOut, " 2 2\n", 5);
500 }
501 while( i<nPivot ){
502 tclLineOfText(p->pOut, p->pPivot);
503 blob_append(p->pOut, " . . .\n", 7);
504 i++;
505 }
506 while( i<nV ){
507 blob_append(p->pOut, ". ", 2);
508 tclLineOfText(p->pOut, p->pV1);
509 blob_append(p->pOut, " 2 2\n", 5);
510 i++;
511 }
512 p->lnPivot += nPivot;
513 p->lnV1 += nV;
@@ -520,32 +623,51 @@
520 unsigned int nV1,
521 unsigned int nV2
522 ){
523 int mx = nPivot;
524 int i;
 
 
 
 
 
525 if( nV1>mx ) mx = nV1;
526 if( nV2>mx ) mx = nV2;
 
 
 
 
527 for(i=0; i<mx; i++){
528 if( i<nPivot ){
529 tclLineOfText(p->pOut, p->pPivot);
530 }else{
531 blob_append_char(p->pOut, '.');
532 }
533 blob_append_char(p->pOut, ' ');
534 if( i<nV1 ){
535 tclLineOfText(p->pOut, p->pV1);
536 }else{
537 blob_append_char(p->pOut, '.');
538 }
539 blob_append_char(p->pOut, ' ');
540 if( i<nV2 ){
541 tclLineOfText(p->pOut, p->pV2);
542 }else{
543 blob_append_char(p->pOut, '.');
544 }
545 blob_append(p->pOut, " X\n", 3);
 
 
 
 
 
 
 
 
 
546 }
 
547 p->lnPivot += nPivot;
548 p->lnV1 += nV1;
549 p->lnV2 += nV2;
550 }
551 void mergebuilder_init_tcl(MergeBuilder *p){
@@ -637,10 +759,11 @@
637 ** the second integer is the number of lines of text to omit from the
638 ** pivot, and the third integer is the number of lines of text that are
639 ** inserted. The edit array ends with a triple of 0,0,0.
640 */
641 diff_config_init(&DCfg, 0);
 
642 aC1 = text_diff(p->pPivot, p->pV1, 0, &DCfg);
643 aC2 = text_diff(p->pPivot, p->pV2, 0, &DCfg);
644 if( aC1==0 || aC2==0 ){
645 free(aC1);
646 free(aC2);
@@ -758,11 +881,12 @@
758 const char *z = blob_buffer(p);
759 int n = blob_size(p) - len + 1;
760 assert( len==(int)strlen(mergeMarker[1]) );
761 assert( len==(int)strlen(mergeMarker[2]) );
762 assert( len==(int)strlen(mergeMarker[3]) );
763 assert( count(mergeMarker)==4 );
 
764 for(i=0; i<n; ){
765 for(j=0; j<4; j++){
766 if( (memcmp(&z[i], mergeMarker[j], len)==0) ){
767 return 1;
768 }
@@ -1004,17 +1128,17 @@
1004
1005 #if INTERFACE
1006 /*
1007 ** Flags to the 3-way merger
1008 */
1009 #define MERGE_DRYRUN 0x0001
1010 /*
1011 ** The MERGE_KEEP_FILES flag specifies that merge_3way() should retain
1012 ** its temporary files on error. By default they are removed after the
1013 ** merge, regardless of success or failure.
1014 */
1015 #define MERGE_KEEP_FILES 0x0002
1016 #endif
1017
1018
1019 /*
1020 ** This routine is a wrapper around merge_three_blobs() with the following
1021
--- src/merge3.c
+++ src/merge3.c
@@ -81,10 +81,11 @@
81 ** Text of boundary markers for merge conflicts.
82 */
83 static const char *const mergeMarker[] = {
84 /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/
85 "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<",
86 "####### SUGGESTED CONFLICT RESOLUTION follows ###################",
87 "||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||",
88 "======= MERGED IN content follows ===============================",
89 ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
90 };
91
@@ -150,12 +151,12 @@
151 const char *zPivot; /* Label or name for the pivot */
152 const char *zV1; /* Label or name for the V1 file */
153 const char *zV2; /* Label or name for the V2 file */
154 const char *zOut; /* Label or name for the output */
155 Blob *pPivot; /* The common ancestor */
156 Blob *pV1; /* First variant (local copy) */
157 Blob *pV2; /* Second variant (merged in) */
158 Blob *pOut; /* Write merge results here */
159 int useCrLf; /* Use CRLF line endings */
160 int nContext; /* Size of unchanged line boundaries */
161 unsigned int mxPivot; /* Number of lines in the pivot */
162 unsigned int mxV1; /* Number of lines in V1 */
@@ -163,10 +164,11 @@
164 unsigned int lnPivot; /* Lines read from pivot */
165 unsigned int lnV1; /* Lines read from v1 */
166 unsigned int lnV2; /* Lines read from v2 */
167 unsigned int lnOut; /* Lines written to out */
168 unsigned int nConflict; /* Number of conflicts seen */
169 u64 diffFlags; /* Flags for difference engine */
170 };
171 #endif /* INTERFACE */
172
173
174 /************************* Generic MergeBuilder ******************************/
@@ -261,10 +263,100 @@
263 p->xChngBoth = dbgChngBoth;
264 p->xConflict = dbgConflict;
265 p->xEnd = dbgStartEnd;
266 p->xDestroy = dbgDestroy;
267 }
268
269 /************************* MergeBuilderToken ********************************/
270 /* This version of MergeBuilder actually performs a merge on file that
271 ** are broken up into tokens instead of lines, and puts the result in pOut.
272 */
273 static void tokenSame(MergeBuilder *p, unsigned int N){
274 blob_append(p->pOut, p->pPivot->aData+p->pPivot->iCursor, N);
275 p->pPivot->iCursor += N;
276 p->pV1->iCursor += N;
277 p->pV2->iCursor += N;
278 }
279 static void tokenChngV1(MergeBuilder *p, unsigned int nPivot, unsigned nV1){
280 blob_append(p->pOut, p->pV1->aData+p->pV1->iCursor, nV1);
281 p->pPivot->iCursor += nPivot;
282 p->pV1->iCursor += nV1;
283 p->pV2->iCursor += nPivot;
284 }
285 static void tokenChngV2(MergeBuilder *p, unsigned int nPivot, unsigned nV2){
286 blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV2);
287 p->pPivot->iCursor += nPivot;
288 p->pV1->iCursor += nPivot;
289 p->pV2->iCursor += nV2;
290 }
291 static void tokenChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned nV){
292 blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV);
293 p->pPivot->iCursor += nPivot;
294 p->pV1->iCursor += nV;
295 p->pV2->iCursor += nV;
296 }
297 static void tokenConflict(
298 MergeBuilder *p,
299 unsigned int nPivot,
300 unsigned int nV1,
301 unsigned int nV2
302 ){
303 /* For a token-merge conflict, use the text from the merge-in */
304 blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV2);
305 p->pPivot->iCursor += nPivot;
306 p->pV1->iCursor += nV1;
307 p->pV2->iCursor += nV2;
308 }
309 static void mergebuilder_init_token(MergeBuilder *p){
310 mergebuilder_init(p);
311 p->xSame = tokenSame;
312 p->xChngV1 = tokenChngV1;
313 p->xChngV2 = tokenChngV2;
314 p->xChngBoth = tokenChngBoth;
315 p->xConflict = tokenConflict;
316 p->diffFlags = DIFF_BY_TOKEN;
317 }
318
319 /*
320 ** Attempt to do a low-level merge on a conflict. The conflict is
321 ** described by the first four parameters, which are the same as the
322 ** arguments to the xConflict method of the MergeBuilder object.
323 ** This routine attempts to resolve the conflict by looking at
324 ** elements of the conflict region that are finer grain than complete
325 ** lines of text.
326 **
327 ** The result is written into Blob pOut. pOut is initialized by this
328 ** routine.
329 */
330 int merge_try_to_resolve_conflict(
331 MergeBuilder *pMB, /* MergeBuilder that encounter conflict */
332 unsigned int nPivot, /* Lines of conflict in the pivot */
333 unsigned int nV1, /* Lines of conflict in V1 */
334 unsigned int nV2, /* Lines of conflict in V2 */
335 Blob *pOut /* Write resolution text here */
336 ){
337 int nConflict;
338 MergeBuilder mb;
339 Blob pv, v1, v2;
340 mergebuilder_init_token(&mb);
341 blob_extract_lines(pMB->pPivot, nPivot, &pv);
342 blob_extract_lines(pMB->pV1, nV1, &v1);
343 blob_extract_lines(pMB->pV2, nV2, &v2);
344 blob_zero(pOut);
345 mb.pPivot = &pv;
346 mb.pV1 = &v1;
347 mb.pV2 = &v2;
348 mb.pOut = pOut;
349 nConflict = merge_three_blobs(&mb);
350 /* The pv, v1, and v2 blobs are all ephemeral and hence do not need
351 ** to be freed. */
352 /* mb has not allocated any resources, so we do not need to invoke
353 ** the xDestroy method. */
354 blob_add_final_newline(pOut);
355 return nConflict;
356 }
357
358
359 /************************* MergeBuilderText **********************************/
360 /* This version of MergeBuilder actually performs a merge on file and puts
361 ** the result in pOut
362 */
@@ -305,20 +397,31 @@
397 MergeBuilder *p,
398 unsigned int nPivot,
399 unsigned int nV1,
400 unsigned int nV2
401 ){
402 int nRes; /* Lines in the computed conflict resolution */
403 Blob res; /* Text of the conflict resolution */
404
405 merge_try_to_resolve_conflict(p, nPivot, nV1, nV2, &res);
406 nRes = blob_linecount(&res);
407
408 append_merge_mark(p->pOut, 0, p->lnV1+1, p->useCrLf);
409 blob_copy_lines(p->pOut, p->pV1, nV1); p->lnV1 += nV1;
410
411 if( nRes>0 ){
412 append_merge_mark(p->pOut, 1, 0, p->useCrLf);
413 blob_copy_lines(p->pOut, &res, nRes);
414 }
415
416 append_merge_mark(p->pOut, 2, p->lnPivot+1, p->useCrLf);
417 blob_copy_lines(p->pOut, p->pPivot, nPivot); p->lnPivot += nPivot;
418
419 append_merge_mark(p->pOut, 3, p->lnV2+1, p->useCrLf);
420 blob_copy_lines(p->pOut, p->pV2, nV2); p->lnV2 += nV2;
421
422 append_merge_mark(p->pOut, 4, -1, p->useCrLf);
423 }
424 static void mergebuilder_init_text(MergeBuilder *p){
425 mergebuilder_init(p);
426 p->xStart = txtStart;
427 p->xSame = txtSame;
@@ -338,13 +441,13 @@
441 **
442 ** . This line is omitted.
443 ** N Name of the file.
444 ** T Literal text follows that should have a \n terminator.
445 ** R Literal text follows that needs a \r\n terminator.
446 ** X Merge conflict.
447 ** Z Literal text without a line terminator.
448 ** S Skipped lines. Followed by number of lines to skip.
449 ** 1 Text is a copy of token 1
450 ** 2 Use data from data-token 2
451 ** 3 Use data from data-token 3
452 */
453
@@ -370,25 +473,24 @@
473 }
474 }
475 }
476
477 /* Copy one line of text from pIn and append to pOut, encoded as TCL */
478 static void tclLineOfText(Blob *pOut, Blob *pIn, char cType){
479 int i, k;
480 for(i=pIn->iCursor; i<pIn->nUsed && pIn->aData[i]!='\n'; i++){}
481 if( i==pIn->nUsed ){
 
482 k = i;
483 }else if( i>pIn->iCursor && pIn->aData[i-1]=='\r' ){
 
484 k = i-1;
485 i++;
486 }else{
 
487 k = i;
488 i++;
489 }
490 blob_append_char(pOut, '"');
491 blob_append_char(pOut, cType);
492 tclWriteQuotedText(pOut, pIn->aData+pIn->iCursor, k-pIn->iCursor);
493 pIn->iCursor = i;
494 blob_append_char(pOut, '"');
495 }
496 static void tclStart(MergeBuilder *p){
@@ -411,20 +513,21 @@
513 int i = 0;
514 int nSkip;
515
516 if( p->lnPivot>=2 || p->lnV1>2 || p->lnV2>2 ){
517 while( i<N && i<p->nContext ){
518 tclLineOfText(p->pOut, p->pPivot, 'T');
519 blob_append(p->pOut, " 1 1 1\n", 7);
520 i++;
521 }
522 nSkip = N - p->nContext*2;
523 }else{
524 nSkip = N - p->nContext;
525 }
526 if( nSkip>0 ){
527 blob_appendf(p->pOut, "\"S%d %d %d %d\" . . .\n",
528 nSkip, nSkip, nSkip, nSkip);
529 blob_copy_lines(0, p->pPivot, nSkip);
530 i += nSkip;
531 }
532
533 p->lnPivot += N;
@@ -431,11 +534,11 @@
534 p->lnV1 += N;
535 p->lnV2 += N;
536
537 if( p->lnPivot<p->mxPivot || p->lnV1<p->mxV1 || p->lnV2<p->mxV2 ){
538 while( i<N ){
539 tclLineOfText(p->pOut, p->pPivot, 'T');
540 blob_append(p->pOut, " 1 1 1\n", 7);
541 i++;
542 }
543 }
544
@@ -443,23 +546,23 @@
546 blob_copy_lines(0, p->pV2, N);
547 }
548 static void tclChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){
549 int i;
550 for(i=0; i<nPivot && i<nV1; i++){
551 tclLineOfText(p->pOut, p->pPivot, 'T');
552 blob_append_char(p->pOut, ' ');
553 tclLineOfText(p->pOut, p->pV1, 'T');
554 blob_append(p->pOut, " 1 2\n", 5);
555 }
556 while( i<nPivot ){
557 tclLineOfText(p->pOut, p->pPivot, 'T');
558 blob_append(p->pOut, " . 1 .\n", 7);
559 i++;
560 }
561 while( i<nV1 ){
562 blob_append(p->pOut, ". ", 2);
563 tclLineOfText(p->pOut, p->pV1, 'T');
564 blob_append(p->pOut, " . 2\n", 5);
565 i++;
566 }
567 p->lnPivot += nPivot;
568 p->lnV1 += nV1;
@@ -467,23 +570,23 @@
570 blob_copy_lines(0, p->pV2, nPivot);
571 }
572 static void tclChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){
573 int i;
574 for(i=0; i<nPivot && i<nV2; i++){
575 tclLineOfText(p->pOut, p->pPivot, 'T');
576 blob_append(p->pOut, " 1 ", 3);
577 tclLineOfText(p->pOut, p->pV2, 'T');
578 blob_append(p->pOut, " 3\n", 3);
579 }
580 while( i<nPivot ){
581 tclLineOfText(p->pOut, p->pPivot, 'T');
582 blob_append(p->pOut, " 1 . .\n", 7);
583 i++;
584 }
585 while( i<nV2 ){
586 blob_append(p->pOut, ". . ", 4);
587 tclLineOfText(p->pOut, p->pV2, 'T');
588 blob_append(p->pOut, " 3\n", 3);
589 i++;
590 }
591 p->lnPivot += nPivot;
592 p->lnV1 += nPivot;
@@ -491,23 +594,23 @@
594 blob_copy_lines(0, p->pV1, nPivot);
595 }
596 static void tclChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){
597 int i;
598 for(i=0; i<nPivot && i<nV; i++){
599 tclLineOfText(p->pOut, p->pPivot, 'T');
600 blob_append_char(p->pOut, ' ');
601 tclLineOfText(p->pOut, p->pV1, 'T');
602 blob_append(p->pOut, " 2 2\n", 5);
603 }
604 while( i<nPivot ){
605 tclLineOfText(p->pOut, p->pPivot, 'T');
606 blob_append(p->pOut, " . . .\n", 7);
607 i++;
608 }
609 while( i<nV ){
610 blob_append(p->pOut, ". ", 2);
611 tclLineOfText(p->pOut, p->pV1, 'T');
612 blob_append(p->pOut, " 2 2\n", 5);
613 i++;
614 }
615 p->lnPivot += nPivot;
616 p->lnV1 += nV;
@@ -520,32 +623,51 @@
623 unsigned int nV1,
624 unsigned int nV2
625 ){
626 int mx = nPivot;
627 int i;
628 int nRes;
629 Blob res;
630
631 merge_try_to_resolve_conflict(p, nPivot, nV1, nV2, &res);
632 nRes = blob_linecount(&res);
633 if( nV1>mx ) mx = nV1;
634 if( nV2>mx ) mx = nV2;
635 if( nRes>mx ) mx = nRes;
636 if( nRes>0 ){
637 blob_appendf(p->pOut, "\"S0 0 0 %d\" . . .\n", nV2+2);
638 }
639 for(i=0; i<mx; i++){
640 if( i<nPivot ){
641 tclLineOfText(p->pOut, p->pPivot, 'X');
642 }else{
643 blob_append_char(p->pOut, '.');
644 }
645 blob_append_char(p->pOut, ' ');
646 if( i<nV1 ){
647 tclLineOfText(p->pOut, p->pV1, 'X');
648 }else{
649 blob_append_char(p->pOut, '.');
650 }
651 blob_append_char(p->pOut, ' ');
652 if( i<nV2 ){
653 tclLineOfText(p->pOut, p->pV2, 'X');
654 }else{
655 blob_append_char(p->pOut, '.');
656 }
657 if( i<nRes ){
658 blob_append_char(p->pOut, ' ');
659 tclLineOfText(p->pOut, &res, 'X');
660 blob_append_char(p->pOut, '\n');
661 }else{
662 blob_append(p->pOut, " .\n", 3);
663 }
664 if( i==mx-1 ){
665 blob_appendf(p->pOut, "\"S0 0 0 %d\" . . .\n", nPivot+nV1+3);
666 }
667 }
668 blob_reset(&res);
669 p->lnPivot += nPivot;
670 p->lnV1 += nV1;
671 p->lnV2 += nV2;
672 }
673 void mergebuilder_init_tcl(MergeBuilder *p){
@@ -637,10 +759,11 @@
759 ** the second integer is the number of lines of text to omit from the
760 ** pivot, and the third integer is the number of lines of text that are
761 ** inserted. The edit array ends with a triple of 0,0,0.
762 */
763 diff_config_init(&DCfg, 0);
764 DCfg.diffFlags = p->diffFlags;
765 aC1 = text_diff(p->pPivot, p->pV1, 0, &DCfg);
766 aC2 = text_diff(p->pPivot, p->pV2, 0, &DCfg);
767 if( aC1==0 || aC2==0 ){
768 free(aC1);
769 free(aC2);
@@ -758,11 +881,12 @@
881 const char *z = blob_buffer(p);
882 int n = blob_size(p) - len + 1;
883 assert( len==(int)strlen(mergeMarker[1]) );
884 assert( len==(int)strlen(mergeMarker[2]) );
885 assert( len==(int)strlen(mergeMarker[3]) );
886 assert( len==(int)strlen(mergeMarker[4]) );
887 assert( count(mergeMarker)==5 );
888 for(i=0; i<n; ){
889 for(j=0; j<4; j++){
890 if( (memcmp(&z[i], mergeMarker[j], len)==0) ){
891 return 1;
892 }
@@ -1004,17 +1128,17 @@
1128
1129 #if INTERFACE
1130 /*
1131 ** Flags to the 3-way merger
1132 */
1133 #define MERGE_DRYRUN 0x0001
1134 /*
1135 ** The MERGE_KEEP_FILES flag specifies that merge_3way() should retain
1136 ** its temporary files on error. By default they are removed after the
1137 ** merge, regardless of success or failure.
1138 */
1139 #define MERGE_KEEP_FILES 0x0002
1140 #endif
1141
1142
1143 /*
1144 ** This routine is a wrapper around merge_three_blobs() with the following
1145

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button