Fossil

Check-in [9b7a6f80b2]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment: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.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 9b7a6f80b22bb5b88a7f5edcf38654688b710e22
User & Date: drh 2011-02-21 16:33:18.596
Context
2011-02-21
18:20
Factor the code that outputs line-numbered text into a separate subroutine, for clarity of presentation. check-in: a4d57c6b26 user: drh tags: trunk
16:33
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. check-in: 9b7a6f80b2 user: drh tags: trunk
14:21
Infrastructure changes toward adding graphical merging options. check-in: b9abb86798 user: drh tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/db.c.
1618
1619
1620
1621
1622
1623
1624

1625
1626
1627
1628
1629
1630
1631
  { "binary-glob",   0,               32, ""                    },
  { "clearsign",     0,                0, "off"                 },
  { "default-perms", 0,               16, "u"                   },
  { "diff-command",  0,               16, ""                    },
  { "dont-push",     0,                0, "off"                 },
  { "editor",        0,               16, ""                    },
  { "gdiff-command", 0,               16, "gdiff"               },

  { "ignore-glob",   0,               40, ""                    },
  { "http-port",     0,               16, "8080"                },
  { "localauth",     0,                0, "off"                 },
  { "manifest",      0,                0, "off"                 },
  { "max-upload",    0,               25, "250000"              },
  { "mtime-changes", 0,                0, "on"                  },
  { "pgp-command",   0,               32, "gpg --clearsign -o " },







>







1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
  { "binary-glob",   0,               32, ""                    },
  { "clearsign",     0,                0, "off"                 },
  { "default-perms", 0,               16, "u"                   },
  { "diff-command",  0,               16, ""                    },
  { "dont-push",     0,                0, "off"                 },
  { "editor",        0,               16, ""                    },
  { "gdiff-command", 0,               16, "gdiff"               },
  { "gmerge-command",0,               40, ""                    },
  { "ignore-glob",   0,               40, ""                    },
  { "http-port",     0,               16, "8080"                },
  { "localauth",     0,                0, "off"                 },
  { "manifest",      0,                0, "off"                 },
  { "max-upload",    0,               25, "250000"              },
  { "mtime-changes", 0,                0, "on"                  },
  { "pgp-command",   0,               32, "gpg --clearsign -o " },
1682
1683
1684
1685
1686
1687
1688





1689
1690
1691
1692
1693
1694
1695
**    dont-push        Prevent this repository from pushing from client to
**                     server.  Useful when setting up a private branch.
**
**    editor           Text editor command used for check-in comments.
**
**    gdiff-command    External command to run when performing a graphical
**                     diff. If undefined, text diff will be used.





**
**    http-port        The TCP/IP port number to use by the "server"
**                     and "ui" commands.  Default: 8080
**
**    ignore-glob      The VALUE is a comma-separated list of GLOB patterns
**                     specifying files that the "extra" command will ignore.
**                     Example:  *.o,*.obj,*.exe







>
>
>
>
>







1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
**    dont-push        Prevent this repository from pushing from client to
**                     server.  Useful when setting up a private branch.
**
**    editor           Text editor command used for check-in comments.
**
**    gdiff-command    External command to run when performing a graphical
**                     diff. If undefined, text diff will be used.
**
**    gmerge-command   A graphical merge conflict resolver command operating
**                     on four files.
**                     Ex:  kdiff3 %baseline %original %merge -o %output
**                     Ex:  xxdiff %original %baseline %merge -M %output
**
**    http-port        The TCP/IP port number to use by the "server"
**                     and "ui" commands.  Default: 8080
**
**    ignore-glob      The VALUE is a comma-separated list of GLOB patterns
**                     specifying files that the "extra" command will ignore.
**                     Example:  *.o,*.obj,*.exe
Changes to src/file.c.
118
119
120
121
122
123
124

























125
126
127
128
129
130
131
    rc = getStat(zFN);
    free(zFN);
  }else{
    rc = getStat(0);
  }
  return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
}


























/*
** Return the tail of a file pathname.  The tail is the last component
** of the path.  For example, the tail of "/a/b/c.d" is "c.d".
*/
const char *file_tail(const char *z){
  const char *zTail = z;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
    rc = getStat(zFN);
    free(zFN);
  }else{
    rc = getStat(0);
  }
  return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
}

/*
** Find an unused filename similar to zBase with zSuffix appended.
**
** Make the name relative to the working directory if relFlag is true.
**
** Space to hold the new filename is obtained form mprintf() and should
** be freed by the caller.
*/
char *file_newname(const char *zBase, const char *zSuffix, int relFlag){
  char *z = 0;
  int cnt = 0;
  z = mprintf("%s-%s", zBase, zSuffix);
  while( file_size(z)>=0 ){
    fossil_free(z);
    z = mprintf("%s-%s-%d", zBase, zSuffix, cnt++);
  }
  if( relFlag ){
    Blob x;
    file_relative_name(z, &x);
    fossil_free(z);
    z = blob_str(&x);
  }
  return z;
}

/*
** Return the tail of a file pathname.  The tail is the last component
** of the path.  For example, the tail of "/a/b/c.d" is "c.d".
*/
const char *file_tail(const char *z){
  const char *zTail = z;
Changes to src/merge3.c.
329
330
331
332
333
334
335




































336

337
338
339
340

341
342
343
344
345
346

347
348
349
350
351
352
353
354
355
356
357
358
359
360



































361
362
363
  blob_reset(&pivot);
  blob_reset(&v1);
  blob_reset(&v2);
  blob_reset(&merged);
}

/*




































** This routine is a wrapper around blob_merge() with enhancements:

**
**    (1) If the merge-command is defined, then use the external merging
**        program specified instead of the built-in blob-merge to do the
**        merging.  Panic if the external merger fails.

**
**    (2) If gmerge-command is defined and there are merge conflicts in
**        blob_merge() then invoke the external graphical merger to resolve
**        the conflicts.
**
** Otherwise, the interface and actions are the same as for blob_merge().

**
** The enhancements are planned - they are not yet implemented.
*/
int merge_3way(
  Blob *pPivot,       /* Common ancestor (older) */
  const char *zV1,    /* Name of file for version merging into (mine) */
  Blob *pV2,          /* Version merging from (yours) */
  Blob *pOut          /* Output written here */
){
  Blob v1;            /* Content of zV1 */
  int rc;             /* Return code of subroutines and this routine */

  blob_read_from_file(&v1, zV1);
  rc = blob_merge(pPivot, &v1, pV2, pOut);



































  blob_reset(&v1);
  return rc;
}







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>




>





|
>
|
<












>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386

387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
  blob_reset(&pivot);
  blob_reset(&v1);
  blob_reset(&v2);
  blob_reset(&merged);
}

/*
** aSubst is an array of string pairs.  The first element of each pair is
** a string that begins with %.  The second element is a replacement for that
** string.
**
** This routine makes a copy of zInput into memory obtained from malloc and
** performance all applicable substitutions on that string.
*/
char *string_subst(const char *zInput, int nSubst, const char **azSubst){
  Blob x;
  int i, j;
  blob_zero(&x);
  while( zInput[0] ){
    for(i=0; zInput[i] && zInput[i]!='%'; i++){}
    if( i>0 ){
      blob_append(&x, zInput, i);
      zInput += i;
    }
    if( zInput[i]==0 ) break;
    for(j=0; j<nSubst; j+=2){
      int n = strlen(azSubst[j]);
      if( memcmp(zInput, azSubst[j], n)==0 ){
        blob_append(&x, azSubst[j+1], -1);
        zInput += n;
        break;
      }
    }
    if( j>=nSubst ){
      blob_append(&x, "%", 1);
      zInput++;
    }
  }
  return blob_str(&x);
}


/*
** This routine is a wrapper around blob_merge() with the following
** enhancements:
**
**    (1) If the merge-command is defined, then use the external merging
**        program specified instead of the built-in blob-merge to do the
**        merging.  Panic if the external merger fails.
**        ** Not currently implemented **
**
**    (2) If gmerge-command is defined and there are merge conflicts in
**        blob_merge() then invoke the external graphical merger to resolve
**        the conflicts.
**
**    (3) If a merge conflict occurs and gmerge-command is not defined,
**        then write the pivot, original, and merge-in files to the
**        filesystem.

*/
int merge_3way(
  Blob *pPivot,       /* Common ancestor (older) */
  const char *zV1,    /* Name of file for version merging into (mine) */
  Blob *pV2,          /* Version merging from (yours) */
  Blob *pOut          /* Output written here */
){
  Blob v1;            /* Content of zV1 */
  int rc;             /* Return code of subroutines and this routine */

  blob_read_from_file(&v1, zV1);
  rc = blob_merge(pPivot, &v1, pV2, pOut);
  if( rc>0 ){
    char *zPivot;   /* Name of the pivot file */
    char *zOrig;    /* Name of the original content file */
    char *zOther;   /* Name of the merge file */
    const char *zGMerge;   /* Name of the gmerge command */

    zPivot = file_newname(zV1, "baseline", 1);
    blob_write_to_file(pPivot, zPivot);
    zOrig = file_newname(zV1, "original", 1);
    blob_write_to_file(&v1, zOrig);
    zOther = file_newname(zV1, "merge", 1);
    blob_write_to_file(pV2, zOther);
    zGMerge = db_get("gmerge-command", 0);
    if( zGMerge && zGMerge[0] ){
      char *zOut;     /* Temporary output file */
      char *zCmd;     /* Command to invoke */
      const char *azSubst[8];  /* Strings to be substituted */

      zOut = file_newname(zV1, "output", 1);
      azSubst[0] = "%baseline";  azSubst[1] = zPivot;
      azSubst[2] = "%original";  azSubst[3] = zOrig;
      azSubst[4] = "%merge";     azSubst[5] = zOther;
      azSubst[6] = "%output";    azSubst[7] = zOut;
      zCmd = string_subst(zGMerge, 8, azSubst);
      printf("%s\n", zCmd); fflush(stdout);
      fossil_system(zCmd);
      if( file_size(zOut)>=0 ) blob_read_from_file(pOut, zOut);
      unlink(zOut);
      fossil_free(zCmd);
      fossil_free(zOut);
    }
    fossil_free(zPivot);
    fossil_free(zOrig);
    fossil_free(zOther);
  }
  blob_reset(&v1);
  return rc;
}