Fossil SCM

Add the ability to use a graphical merging tool to resolve merge conflicts. Even without a configured graphical tool, leave files behind (VCS droppings) that contain the baseline, original, and merged files.

drh 2011-02-21 16:33 trunk
Commit 9b7a6f80b22bb5b88a7f5edcf38654688b710e22
3 files changed +6 +25 +77 -4
+6
--- src/db.c
+++ src/db.c
@@ -1620,10 +1620,11 @@
16201620
{ "default-perms", 0, 16, "u" },
16211621
{ "diff-command", 0, 16, "" },
16221622
{ "dont-push", 0, 0, "off" },
16231623
{ "editor", 0, 16, "" },
16241624
{ "gdiff-command", 0, 16, "gdiff" },
1625
+ { "gmerge-command",0, 40, "" },
16251626
{ "ignore-glob", 0, 40, "" },
16261627
{ "http-port", 0, 16, "8080" },
16271628
{ "localauth", 0, 0, "off" },
16281629
{ "manifest", 0, 0, "off" },
16291630
{ "max-upload", 0, 25, "250000" },
@@ -1684,10 +1685,15 @@
16841685
**
16851686
** editor Text editor command used for check-in comments.
16861687
**
16871688
** gdiff-command External command to run when performing a graphical
16881689
** diff. If undefined, text diff will be used.
1690
+**
1691
+** gmerge-command A graphical merge conflict resolver command operating
1692
+** on four files.
1693
+** Ex: kdiff3 %baseline %original %merge -o %output
1694
+** Ex: xxdiff %original %baseline %merge -M %output
16891695
**
16901696
** http-port The TCP/IP port number to use by the "server"
16911697
** and "ui" commands. Default: 8080
16921698
**
16931699
** ignore-glob The VALUE is a comma-separated list of GLOB patterns
16941700
--- src/db.c
+++ src/db.c
@@ -1620,10 +1620,11 @@
1620 { "default-perms", 0, 16, "u" },
1621 { "diff-command", 0, 16, "" },
1622 { "dont-push", 0, 0, "off" },
1623 { "editor", 0, 16, "" },
1624 { "gdiff-command", 0, 16, "gdiff" },
 
1625 { "ignore-glob", 0, 40, "" },
1626 { "http-port", 0, 16, "8080" },
1627 { "localauth", 0, 0, "off" },
1628 { "manifest", 0, 0, "off" },
1629 { "max-upload", 0, 25, "250000" },
@@ -1684,10 +1685,15 @@
1684 **
1685 ** editor Text editor command used for check-in comments.
1686 **
1687 ** gdiff-command External command to run when performing a graphical
1688 ** diff. If undefined, text diff will be used.
 
 
 
 
 
1689 **
1690 ** http-port The TCP/IP port number to use by the "server"
1691 ** and "ui" commands. Default: 8080
1692 **
1693 ** ignore-glob The VALUE is a comma-separated list of GLOB patterns
1694
--- src/db.c
+++ src/db.c
@@ -1620,10 +1620,11 @@
1620 { "default-perms", 0, 16, "u" },
1621 { "diff-command", 0, 16, "" },
1622 { "dont-push", 0, 0, "off" },
1623 { "editor", 0, 16, "" },
1624 { "gdiff-command", 0, 16, "gdiff" },
1625 { "gmerge-command",0, 40, "" },
1626 { "ignore-glob", 0, 40, "" },
1627 { "http-port", 0, 16, "8080" },
1628 { "localauth", 0, 0, "off" },
1629 { "manifest", 0, 0, "off" },
1630 { "max-upload", 0, 25, "250000" },
@@ -1684,10 +1685,15 @@
1685 **
1686 ** editor Text editor command used for check-in comments.
1687 **
1688 ** gdiff-command External command to run when performing a graphical
1689 ** diff. If undefined, text diff will be used.
1690 **
1691 ** gmerge-command A graphical merge conflict resolver command operating
1692 ** on four files.
1693 ** Ex: kdiff3 %baseline %original %merge -o %output
1694 ** Ex: xxdiff %original %baseline %merge -M %output
1695 **
1696 ** http-port The TCP/IP port number to use by the "server"
1697 ** and "ui" commands. Default: 8080
1698 **
1699 ** ignore-glob The VALUE is a comma-separated list of GLOB patterns
1700
+25
--- src/file.c
+++ src/file.c
@@ -120,10 +120,35 @@
120120
}else{
121121
rc = getStat(0);
122122
}
123123
return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
124124
}
125
+
126
+/*
127
+** Find an unused filename similar to zBase with zSuffix appended.
128
+**
129
+** Make the name relative to the working directory if relFlag is true.
130
+**
131
+** Space to hold the new filename is obtained form mprintf() and should
132
+** be freed by the caller.
133
+*/
134
+char *file_newname(const char *zBase, const char *zSuffix, int relFlag){
135
+ char *z = 0;
136
+ int cnt = 0;
137
+ z = mprintf("%s-%s", zBase, zSuffix);
138
+ while( file_size(z)>=0 ){
139
+ fossil_free(z);
140
+ z = mprintf("%s-%s-%d", zBase, zSuffix, cnt++);
141
+ }
142
+ if( relFlag ){
143
+ Blob x;
144
+ file_relative_name(z, &x);
145
+ fossil_free(z);
146
+ z = blob_str(&x);
147
+ }
148
+ return z;
149
+}
125150
126151
/*
127152
** Return the tail of a file pathname. The tail is the last component
128153
** of the path. For example, the tail of "/a/b/c.d" is "c.d".
129154
*/
130155
--- src/file.c
+++ src/file.c
@@ -120,10 +120,35 @@
120 }else{
121 rc = getStat(0);
122 }
123 return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
124 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
126 /*
127 ** Return the tail of a file pathname. The tail is the last component
128 ** of the path. For example, the tail of "/a/b/c.d" is "c.d".
129 */
130
--- src/file.c
+++ src/file.c
@@ -120,10 +120,35 @@
120 }else{
121 rc = getStat(0);
122 }
123 return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
124 }
125
126 /*
127 ** Find an unused filename similar to zBase with zSuffix appended.
128 **
129 ** Make the name relative to the working directory if relFlag is true.
130 **
131 ** Space to hold the new filename is obtained form mprintf() and should
132 ** be freed by the caller.
133 */
134 char *file_newname(const char *zBase, const char *zSuffix, int relFlag){
135 char *z = 0;
136 int cnt = 0;
137 z = mprintf("%s-%s", zBase, zSuffix);
138 while( file_size(z)>=0 ){
139 fossil_free(z);
140 z = mprintf("%s-%s-%d", zBase, zSuffix, cnt++);
141 }
142 if( relFlag ){
143 Blob x;
144 file_relative_name(z, &x);
145 fossil_free(z);
146 z = blob_str(&x);
147 }
148 return z;
149 }
150
151 /*
152 ** Return the tail of a file pathname. The tail is the last component
153 ** of the path. For example, the tail of "/a/b/c.d" is "c.d".
154 */
155
+77 -4
--- src/merge3.c
+++ src/merge3.c
@@ -331,23 +331,61 @@
331331
blob_reset(&v2);
332332
blob_reset(&merged);
333333
}
334334
335335
/*
336
-** This routine is a wrapper around blob_merge() with enhancements:
336
+** aSubst is an array of string pairs. The first element of each pair is
337
+** a string that begins with %. The second element is a replacement for that
338
+** string.
339
+**
340
+** This routine makes a copy of zInput into memory obtained from malloc and
341
+** performance all applicable substitutions on that string.
342
+*/
343
+char *string_subst(const char *zInput, int nSubst, const char **azSubst){
344
+ Blob x;
345
+ int i, j;
346
+ blob_zero(&x);
347
+ while( zInput[0] ){
348
+ for(i=0; zInput[i] && zInput[i]!='%'; i++){}
349
+ if( i>0 ){
350
+ blob_append(&x, zInput, i);
351
+ zInput += i;
352
+ }
353
+ if( zInput[i]==0 ) break;
354
+ for(j=0; j<nSubst; j+=2){
355
+ int n = strlen(azSubst[j]);
356
+ if( memcmp(zInput, azSubst[j], n)==0 ){
357
+ blob_append(&x, azSubst[j+1], -1);
358
+ zInput += n;
359
+ break;
360
+ }
361
+ }
362
+ if( j>=nSubst ){
363
+ blob_append(&x, "%", 1);
364
+ zInput++;
365
+ }
366
+ }
367
+ return blob_str(&x);
368
+}
369
+
370
+
371
+/*
372
+** This routine is a wrapper around blob_merge() with the following
373
+** enhancements:
337374
**
338375
** (1) If the merge-command is defined, then use the external merging
339376
** program specified instead of the built-in blob-merge to do the
340377
** merging. Panic if the external merger fails.
378
+** ** Not currently implemented **
341379
**
342380
** (2) If gmerge-command is defined and there are merge conflicts in
343381
** blob_merge() then invoke the external graphical merger to resolve
344382
** the conflicts.
345383
**
346
-** Otherwise, the interface and actions are the same as for blob_merge().
347
-**
348
-** The enhancements are planned - they are not yet implemented.
384
+** (3) If a merge conflict occurs and gmerge-command is not defined,
385
+** then write the pivot, original, and merge-in files to the
386
+** filesystem.
349387
*/
350388
int merge_3way(
351389
Blob *pPivot, /* Common ancestor (older) */
352390
const char *zV1, /* Name of file for version merging into (mine) */
353391
Blob *pV2, /* Version merging from (yours) */
@@ -356,8 +394,43 @@
356394
Blob v1; /* Content of zV1 */
357395
int rc; /* Return code of subroutines and this routine */
358396
359397
blob_read_from_file(&v1, zV1);
360398
rc = blob_merge(pPivot, &v1, pV2, pOut);
399
+ if( rc>0 ){
400
+ char *zPivot; /* Name of the pivot file */
401
+ char *zOrig; /* Name of the original content file */
402
+ char *zOther; /* Name of the merge file */
403
+ const char *zGMerge; /* Name of the gmerge command */
404
+
405
+ zPivot = file_newname(zV1, "baseline", 1);
406
+ blob_write_to_file(pPivot, zPivot);
407
+ zOrig = file_newname(zV1, "original", 1);
408
+ blob_write_to_file(&v1, zOrig);
409
+ zOther = file_newname(zV1, "merge", 1);
410
+ blob_write_to_file(pV2, zOther);
411
+ zGMerge = db_get("gmerge-command", 0);
412
+ if( zGMerge && zGMerge[0] ){
413
+ char *zOut; /* Temporary output file */
414
+ char *zCmd; /* Command to invoke */
415
+ const char *azSubst[8]; /* Strings to be substituted */
416
+
417
+ zOut = file_newname(zV1, "output", 1);
418
+ azSubst[0] = "%baseline"; azSubst[1] = zPivot;
419
+ azSubst[2] = "%original"; azSubst[3] = zOrig;
420
+ azSubst[4] = "%merge"; azSubst[5] = zOther;
421
+ azSubst[6] = "%output"; azSubst[7] = zOut;
422
+ zCmd = string_subst(zGMerge, 8, azSubst);
423
+ printf("%s\n", zCmd); fflush(stdout);
424
+ fossil_system(zCmd);
425
+ if( file_size(zOut)>=0 ) blob_read_from_file(pOut, zOut);
426
+ unlink(zOut);
427
+ fossil_free(zCmd);
428
+ fossil_free(zOut);
429
+ }
430
+ fossil_free(zPivot);
431
+ fossil_free(zOrig);
432
+ fossil_free(zOther);
433
+ }
361434
blob_reset(&v1);
362435
return rc;
363436
}
364437
--- src/merge3.c
+++ src/merge3.c
@@ -331,23 +331,61 @@
331 blob_reset(&v2);
332 blob_reset(&merged);
333 }
334
335 /*
336 ** This routine is a wrapper around blob_merge() with enhancements:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337 **
338 ** (1) If the merge-command is defined, then use the external merging
339 ** program specified instead of the built-in blob-merge to do the
340 ** merging. Panic if the external merger fails.
 
341 **
342 ** (2) If gmerge-command is defined and there are merge conflicts in
343 ** blob_merge() then invoke the external graphical merger to resolve
344 ** the conflicts.
345 **
346 ** Otherwise, the interface and actions are the same as for blob_merge().
347 **
348 ** The enhancements are planned - they are not yet implemented.
349 */
350 int merge_3way(
351 Blob *pPivot, /* Common ancestor (older) */
352 const char *zV1, /* Name of file for version merging into (mine) */
353 Blob *pV2, /* Version merging from (yours) */
@@ -356,8 +394,43 @@
356 Blob v1; /* Content of zV1 */
357 int rc; /* Return code of subroutines and this routine */
358
359 blob_read_from_file(&v1, zV1);
360 rc = blob_merge(pPivot, &v1, pV2, pOut);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361 blob_reset(&v1);
362 return rc;
363 }
364
--- src/merge3.c
+++ src/merge3.c
@@ -331,23 +331,61 @@
331 blob_reset(&v2);
332 blob_reset(&merged);
333 }
334
335 /*
336 ** aSubst is an array of string pairs. The first element of each pair is
337 ** a string that begins with %. The second element is a replacement for that
338 ** string.
339 **
340 ** This routine makes a copy of zInput into memory obtained from malloc and
341 ** performance all applicable substitutions on that string.
342 */
343 char *string_subst(const char *zInput, int nSubst, const char **azSubst){
344 Blob x;
345 int i, j;
346 blob_zero(&x);
347 while( zInput[0] ){
348 for(i=0; zInput[i] && zInput[i]!='%'; i++){}
349 if( i>0 ){
350 blob_append(&x, zInput, i);
351 zInput += i;
352 }
353 if( zInput[i]==0 ) break;
354 for(j=0; j<nSubst; j+=2){
355 int n = strlen(azSubst[j]);
356 if( memcmp(zInput, azSubst[j], n)==0 ){
357 blob_append(&x, azSubst[j+1], -1);
358 zInput += n;
359 break;
360 }
361 }
362 if( j>=nSubst ){
363 blob_append(&x, "%", 1);
364 zInput++;
365 }
366 }
367 return blob_str(&x);
368 }
369
370
371 /*
372 ** This routine is a wrapper around blob_merge() with the following
373 ** enhancements:
374 **
375 ** (1) If the merge-command is defined, then use the external merging
376 ** program specified instead of the built-in blob-merge to do the
377 ** merging. Panic if the external merger fails.
378 ** ** Not currently implemented **
379 **
380 ** (2) If gmerge-command is defined and there are merge conflicts in
381 ** blob_merge() then invoke the external graphical merger to resolve
382 ** the conflicts.
383 **
384 ** (3) If a merge conflict occurs and gmerge-command is not defined,
385 ** then write the pivot, original, and merge-in files to the
386 ** filesystem.
387 */
388 int merge_3way(
389 Blob *pPivot, /* Common ancestor (older) */
390 const char *zV1, /* Name of file for version merging into (mine) */
391 Blob *pV2, /* Version merging from (yours) */
@@ -356,8 +394,43 @@
394 Blob v1; /* Content of zV1 */
395 int rc; /* Return code of subroutines and this routine */
396
397 blob_read_from_file(&v1, zV1);
398 rc = blob_merge(pPivot, &v1, pV2, pOut);
399 if( rc>0 ){
400 char *zPivot; /* Name of the pivot file */
401 char *zOrig; /* Name of the original content file */
402 char *zOther; /* Name of the merge file */
403 const char *zGMerge; /* Name of the gmerge command */
404
405 zPivot = file_newname(zV1, "baseline", 1);
406 blob_write_to_file(pPivot, zPivot);
407 zOrig = file_newname(zV1, "original", 1);
408 blob_write_to_file(&v1, zOrig);
409 zOther = file_newname(zV1, "merge", 1);
410 blob_write_to_file(pV2, zOther);
411 zGMerge = db_get("gmerge-command", 0);
412 if( zGMerge && zGMerge[0] ){
413 char *zOut; /* Temporary output file */
414 char *zCmd; /* Command to invoke */
415 const char *azSubst[8]; /* Strings to be substituted */
416
417 zOut = file_newname(zV1, "output", 1);
418 azSubst[0] = "%baseline"; azSubst[1] = zPivot;
419 azSubst[2] = "%original"; azSubst[3] = zOrig;
420 azSubst[4] = "%merge"; azSubst[5] = zOther;
421 azSubst[6] = "%output"; azSubst[7] = zOut;
422 zCmd = string_subst(zGMerge, 8, azSubst);
423 printf("%s\n", zCmd); fflush(stdout);
424 fossil_system(zCmd);
425 if( file_size(zOut)>=0 ) blob_read_from_file(pOut, zOut);
426 unlink(zOut);
427 fossil_free(zCmd);
428 fossil_free(zOut);
429 }
430 fossil_free(zPivot);
431 fossil_free(zOrig);
432 fossil_free(zOther);
433 }
434 blob_reset(&v1);
435 return rc;
436 }
437

Keyboard Shortcuts

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