Fossil SCM

fossil-scm / src / merge3.c
Source Blame History 1232 lines
dbda8d6… drh 1 /*
c19f34c… drh 2 ** Copyright (c) 2007 D. Richard Hipp
dbda8d6… drh 3 **
dbda8d6… drh 4 ** This program is free software; you can redistribute it and/or
c06edd2… drh 5 ** modify it under the terms of the Simplified BSD License (also
c06edd2… drh 6 ** known as the "2-Clause License" or "FreeBSD License".)
c06edd2… drh 7
dbda8d6… drh 8 ** This program is distributed in the hope that it will be useful,
c06edd2… drh 9 ** but without any warranty; without even the implied warranty of
c06edd2… drh 10 ** merchantability or fitness for a particular purpose.
dbda8d6… drh 11 **
dbda8d6… drh 12 ** Author contact information:
dbda8d6… drh 13 ** [email protected]
dbda8d6… drh 14 ** http://www.hwaci.com/drh/
dbda8d6… drh 15 **
dbda8d6… drh 16 *******************************************************************************
dbda8d6… drh 17 **
dbda8d6… drh 18 ** This module implements a 3-way merge
dbda8d6… drh 19 */
dbda8d6… drh 20 #include "config.h"
dbda8d6… drh 21 #include "merge3.h"
dbda8d6… drh 22
434830c… drh 23 #if 0
d0305b3… aku 24 #define DEBUG(X) X
e45d478… drh 25 #define ISDEBUG 1
d0305b3… aku 26 #else
d0305b3… aku 27 #define DEBUG(X)
e45d478… drh 28 #define ISDEBUG 0
e45d478… drh 29 #endif
e45d478… drh 30
83566f2… drh 31 /* The minimum of two integers */
c738a1a… mistachkin 32 #ifndef min
c738a1a… mistachkin 33 # define min(A,B) (A<B?A:B)
c738a1a… mistachkin 34 #endif
83566f2… drh 35
83566f2… drh 36 /*
83566f2… drh 37 ** Compare N lines of text from pV1 and pV2. If the lines
3e89b0c… drh 38 ** are the same, return true. Return false if one or more of the N
3e89b0c… drh 39 ** lines are different.
83566f2… drh 40 **
3e89b0c… drh 41 ** The cursors on both pV1 and pV2 is unchanged by this comparison.
83566f2… drh 42 */
83566f2… drh 43 static int sameLines(Blob *pV1, Blob *pV2, int N){
d0305b3… aku 44 char *z1, *z2;
d0305b3… aku 45 int i;
e2122bd… drh 46 char c;
d0305b3… aku 47
83566f2… drh 48 if( N==0 ) return 1;
d0305b3… aku 49 z1 = &blob_buffer(pV1)[blob_tell(pV1)];
d0305b3… aku 50 z2 = &blob_buffer(pV2)[blob_tell(pV2)];
e2122bd… drh 51 for(i=0; (c=z1[i])==z2[i]; i++){
e2122bd… drh 52 if( c=='\n' || c==0 ){
83566f2… drh 53 N--;
e2122bd… drh 54 if( N==0 || c==0 ) return 1;
83566f2… drh 55 }
83566f2… drh 56 }
83566f2… drh 57 return 0;
83566f2… drh 58 }
83566f2… drh 59
83566f2… drh 60 /*
3e89b0c… drh 61 ** Look at the next edit triple in both aC1 and aC2. (An "edit triple" is
3e89b0c… drh 62 ** three integers describing the number of copies, deletes, and inserts in
3e89b0c… drh 63 ** moving from the original to the edited copy of the file.) If the three
233463c… jan.nijtmans 64 ** integers of the edit triples describe an identical edit, then return 1.
3e89b0c… drh 65 ** If the edits are different, return 0.
83566f2… drh 66 */
3e89b0c… drh 67 static int sameEdit(
3e89b0c… drh 68 int *aC1, /* Array of edit integers for file 1 */
3e89b0c… drh 69 int *aC2, /* Array of edit integers for file 2 */
3e89b0c… drh 70 Blob *pV1, /* Text of file 1 */
3e89b0c… drh 71 Blob *pV2 /* Text of file 2 */
3e89b0c… drh 72 ){
83566f2… drh 73 if( aC1[0]!=aC2[0] ) return 0;
83566f2… drh 74 if( aC1[1]!=aC2[1] ) return 0;
83566f2… drh 75 if( aC1[2]!=aC2[2] ) return 0;
83566f2… drh 76 if( sameLines(pV1, pV2, aC1[2]) ) return 1;
83566f2… drh 77 return 0;
83566f2… drh 78 }
3e89b0c… drh 79
3e89b0c… drh 80 /*
88ff264… drh 81 ** Text of boundary markers for merge conflicts.
88ff264… drh 82 */
88ff264… drh 83 static const char *const mergeMarker[] = {
88ff264… drh 84 /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/
14f44e9… drh 85 "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<",
1cf0d37… drh 86 "####### SUGGESTED CONFLICT RESOLUTION follows ###################",
14f44e9… drh 87 "||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||",
14f44e9… drh 88 "======= MERGED IN content follows ===============================",
14f44e9… drh 89 ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
88ff264… drh 90 };
88ff264… drh 91
88ff264… drh 92 /*
88ff264… drh 93 ** Return true if the input blob contains any CR/LF pairs on the first
88ff264… drh 94 ** ten lines. This should be enough to detect files that use mainly CR/LF
88ff264… drh 95 ** line endings without causing a performance impact for LF only files.
88ff264… drh 96 */
88ff264… drh 97 int contains_crlf(Blob *p){
88ff264… drh 98 int i;
88ff264… drh 99 int j = 0;
88ff264… drh 100 const int maxL = 10; /* Max lines to check */
88ff264… drh 101 const char *z = blob_buffer(p);
88ff264… drh 102 int n = blob_size(p)+1;
88ff264… drh 103 for(i=1; i<n; ){
88ff264… drh 104 if( z[i-1]=='\r' && z[i]=='\n' ) return 1;
88ff264… drh 105 while( i<n && z[i]!='\n' ){ i++; }
88ff264… drh 106 j++;
88ff264… drh 107 if( j>maxL ) return 0;
88ff264… drh 108 }
88ff264… drh 109 return 0;
88ff264… drh 110 }
88ff264… drh 111
88ff264… drh 112 /*
88ff264… drh 113 ** Ensure that the text in pBlob ends with a new line.
88ff264… drh 114 ** If useCrLf is true adds "\r\n" otherwise '\n'.
88ff264… drh 115 */
88ff264… drh 116 void ensure_line_end(Blob *pBlob, int useCrLf){
88ff264… drh 117 if( pBlob->nUsed<=0 ) return;
88ff264… drh 118 if( pBlob->aData[pBlob->nUsed-1]!='\n' ){
88ff264… drh 119 if( useCrLf ) blob_append_char(pBlob, '\r');
88ff264… drh 120 blob_append_char(pBlob, '\n');
88ff264… drh 121 }
88ff264… drh 122 }
88ff264… drh 123
88ff264… drh 124 /*
14f44e9… drh 125 ** Write out one of the four merge-marks.
14f44e9… drh 126 */
14f44e9… drh 127 void append_merge_mark(Blob *pOut, int iMark, int ln, int useCrLf){
14f44e9… drh 128 ensure_line_end(pOut, useCrLf);
14f44e9… drh 129 blob_append(pOut, mergeMarker[iMark], -1);
14f44e9… drh 130 if( ln>0 ) blob_appendf(pOut, " (line %d)", ln);
14f44e9… drh 131 ensure_line_end(pOut, useCrLf);
14f44e9… drh 132 }
14f44e9… drh 133
aa82965… drh 134 #if INTERFACE
aa82965… drh 135 /*
aa82965… drh 136 ** This is an abstract class for constructing a merge.
aa82965… drh 137 ** Subclasses of this object format the merge output in different ways.
aa82965… drh 138 **
aa82965… drh 139 ** To subclass, create an instance of the MergeBuilder object and fill
aa82965… drh 140 ** in appropriate method implementations.
aa82965… drh 141 */
aa82965… drh 142 struct MergeBuilder {
aa82965… drh 143 void (*xStart)(MergeBuilder*);
aa82965… drh 144 void (*xSame)(MergeBuilder*, unsigned int);
aa82965… drh 145 void (*xChngV1)(MergeBuilder*, unsigned int, unsigned int);
aa82965… drh 146 void (*xChngV2)(MergeBuilder*, unsigned int, unsigned int);
aa82965… drh 147 void (*xChngBoth)(MergeBuilder*, unsigned int, unsigned int);
aa82965… drh 148 void (*xConflict)(MergeBuilder*, unsigned int, unsigned int, unsigned int);
aa82965… drh 149 void (*xEnd)(MergeBuilder*);
aa82965… drh 150 void (*xDestroy)(MergeBuilder*);
aa82965… drh 151 const char *zPivot; /* Label or name for the pivot */
aa82965… drh 152 const char *zV1; /* Label or name for the V1 file */
aa82965… drh 153 const char *zV2; /* Label or name for the V2 file */
aa82965… drh 154 const char *zOut; /* Label or name for the output */
aa82965… drh 155 Blob *pPivot; /* The common ancestor */
1cf0d37… drh 156 Blob *pV1; /* First variant (local copy) */
1cf0d37… drh 157 Blob *pV2; /* Second variant (merged in) */
aa82965… drh 158 Blob *pOut; /* Write merge results here */
aa82965… drh 159 int useCrLf; /* Use CRLF line endings */
aa82965… drh 160 int nContext; /* Size of unchanged line boundaries */
aa82965… drh 161 unsigned int mxPivot; /* Number of lines in the pivot */
aa82965… drh 162 unsigned int mxV1; /* Number of lines in V1 */
aa82965… drh 163 unsigned int mxV2; /* Number of lines in V2 */
aa82965… drh 164 unsigned int lnPivot; /* Lines read from pivot */
aa82965… drh 165 unsigned int lnV1; /* Lines read from v1 */
aa82965… drh 166 unsigned int lnV2; /* Lines read from v2 */
aa82965… drh 167 unsigned int lnOut; /* Lines written to out */
aa82965… drh 168 unsigned int nConflict; /* Number of conflicts seen */
1cf0d37… drh 169 u64 diffFlags; /* Flags for difference engine */
aa82965… drh 170 };
aa82965… drh 171 #endif /* INTERFACE */
aa82965… drh 172
aa82965… drh 173
aa82965… drh 174 /************************* Generic MergeBuilder ******************************/
aa82965… drh 175 /* These are generic methods for MergeBuilder. They just output debugging
aa82965… drh 176 ** information. But some of them are useful as base methods for other useful
aa82965… drh 177 ** implementations of MergeBuilder.
aa82965… drh 178 */
aa82965… drh 179
e2bdc10… danield 180 /* xStart() and xEnd() are called to generate header and footer information
aa82965… drh 181 ** in the output. This is a no-op in the generic implementation.
aa82965… drh 182 */
aa82965… drh 183 static void dbgStartEnd(MergeBuilder *p){ (void)p; }
aa82965… drh 184
aa82965… drh 185 /* The next N lines of PIVOT are unchanged in both V1 and V2
aa82965… drh 186 */
aa82965… drh 187 static void dbgSame(MergeBuilder *p, unsigned int N){
aa82965… drh 188 blob_appendf(p->pOut,
aa82965… drh 189 "COPY %u from BASELINE(%u..%u) or V1(%u..%u) or V2(%u..%u)\n",
aa82965… drh 190 N, p->lnPivot+1, p->lnPivot+N, p->lnV1+1, p->lnV1+N,
aa82965… drh 191 p->lnV2+1, p->lnV2+N);
aa82965… drh 192 p->lnPivot += N;
aa82965… drh 193 p->lnV1 += N;
aa82965… drh 194 p->lnV2 += N;
aa82965… drh 195 }
aa82965… drh 196
aa82965… drh 197 /* The next nPivot lines of the PIVOT are changed into nV1 lines by V1
aa82965… drh 198 */
aa82965… drh 199 static void dbgChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){
aa82965… drh 200 blob_appendf(p->pOut, "COPY %u from V1(%u..%u)\n",
aa82965… drh 201 nV1, p->lnV1+1, p->lnV1+nV1);
aa82965… drh 202 p->lnPivot += nPivot;
aa82965… drh 203 p->lnV2 += nPivot;
aa82965… drh 204 p->lnV1 += nV1;
aa82965… drh 205 }
aa82965… drh 206
aa82965… drh 207 /* The next nPivot lines of the PIVOT are changed into nV2 lines by V2
aa82965… drh 208 */
aa82965… drh 209 static void dbgChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){
aa82965… drh 210 blob_appendf(p->pOut, "COPY %u lines FROM V2(%u..%u)\n",
aa82965… drh 211 nV2, p->lnV2+1, p->lnV2+nV2);
aa82965… drh 212 p->lnPivot += nPivot;
aa82965… drh 213 p->lnV1 += nPivot;
aa82965… drh 214 p->lnV2 += nV2;
aa82965… drh 215 }
aa82965… drh 216
aa82965… drh 217 /* The next nPivot lines of the PIVOT are changed into nV lines from V1 and
aa82965… drh 218 ** V2, which should be the same. In other words, the same change is found
aa82965… drh 219 ** in both V1 and V2.
aa82965… drh 220 */
aa82965… drh 221 static void dbgChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){
aa82965… drh 222 blob_appendf(p->pOut, "COPY %u lines from V1(%u..%u) or V2(%u..%u)\n",
aa82965… drh 223 nV, p->lnV1+1, p->lnV1+nV, p->lnV2+1, p->lnV2+nV);
aa82965… drh 224 p->lnPivot += nPivot;
aa82965… drh 225 p->lnV1 += nV;
aa82965… drh 226 p->lnV2 += nV;
aa82965… drh 227 }
aa82965… drh 228
aa82965… drh 229 /* V1 and V2 have different and overlapping changes. The next nPivot lines
aa82965… drh 230 ** of the PIVOT are converted into nV1 lines of V1 and nV2 lines of V2.
aa82965… drh 231 */
aa82965… drh 232 static void dbgConflict(
aa82965… drh 233 MergeBuilder *p,
aa82965… drh 234 unsigned int nPivot,
aa82965… drh 235 unsigned int nV1,
aa82965… drh 236 unsigned int nV2
aa82965… drh 237 ){
aa82965… drh 238 blob_appendf(p->pOut,
aa82965… drh 239 "CONFLICT %u,%u,%u BASELINE(%u..%u) versus V1(%u..%u) versus V2(%u..%u)\n",
aa82965… drh 240 nPivot, nV1, nV2,
aa82965… drh 241 p->lnPivot+1, p->lnPivot+nPivot,
aa82965… drh 242 p->lnV1+1, p->lnV1+nV1,
aa82965… drh 243 p->lnV2+1, p->lnV2+nV2);
aa82965… drh 244 p->lnV1 += nV1;
aa82965… drh 245 p->lnPivot += nPivot;
aa82965… drh 246 p->lnV2 += nV2;
aa82965… drh 247 }
aa82965… drh 248
aa82965… drh 249 /* Generic destructor for the MergeBuilder object
aa82965… drh 250 */
aa82965… drh 251 static void dbgDestroy(MergeBuilder *p){
aa82965… drh 252 memset(p, 0, sizeof(*p));
aa82965… drh 253 }
aa82965… drh 254
aa82965… drh 255 /* Generic initializer for a MergeBuilder object
aa82965… drh 256 */
aa82965… drh 257 static void mergebuilder_init(MergeBuilder *p){
aa82965… drh 258 memset(p, 0, sizeof(*p));
aa82965… drh 259 p->xStart = dbgStartEnd;
aa82965… drh 260 p->xSame = dbgSame;
aa82965… drh 261 p->xChngV1 = dbgChngV1;
aa82965… drh 262 p->xChngV2 = dbgChngV2;
aa82965… drh 263 p->xChngBoth = dbgChngBoth;
aa82965… drh 264 p->xConflict = dbgConflict;
aa82965… drh 265 p->xEnd = dbgStartEnd;
aa82965… drh 266 p->xDestroy = dbgDestroy;
aa82965… drh 267 }
aa82965… drh 268
1cf0d37… drh 269 /************************* MergeBuilderToken ********************************/
1cf0d37… drh 270 /* This version of MergeBuilder actually performs a merge on file that
1cf0d37… drh 271 ** are broken up into tokens instead of lines, and puts the result in pOut.
1cf0d37… drh 272 */
1cf0d37… drh 273 static void tokenSame(MergeBuilder *p, unsigned int N){
1cf0d37… drh 274 blob_append(p->pOut, p->pPivot->aData+p->pPivot->iCursor, N);
1cf0d37… drh 275 p->pPivot->iCursor += N;
1cf0d37… drh 276 p->pV1->iCursor += N;
1cf0d37… drh 277 p->pV2->iCursor += N;
1cf0d37… drh 278 }
1cf0d37… drh 279 static void tokenChngV1(MergeBuilder *p, unsigned int nPivot, unsigned nV1){
1cf0d37… drh 280 blob_append(p->pOut, p->pV1->aData+p->pV1->iCursor, nV1);
1cf0d37… drh 281 p->pPivot->iCursor += nPivot;
1cf0d37… drh 282 p->pV1->iCursor += nV1;
1cf0d37… drh 283 p->pV2->iCursor += nPivot;
1cf0d37… drh 284 }
1cf0d37… drh 285 static void tokenChngV2(MergeBuilder *p, unsigned int nPivot, unsigned nV2){
1cf0d37… drh 286 blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV2);
1cf0d37… drh 287 p->pPivot->iCursor += nPivot;
1cf0d37… drh 288 p->pV1->iCursor += nPivot;
1cf0d37… drh 289 p->pV2->iCursor += nV2;
1cf0d37… drh 290 }
1cf0d37… drh 291 static void tokenChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned nV){
1cf0d37… drh 292 blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV);
1cf0d37… drh 293 p->pPivot->iCursor += nPivot;
1cf0d37… drh 294 p->pV1->iCursor += nV;
1cf0d37… drh 295 p->pV2->iCursor += nV;
1cf0d37… drh 296 }
1cf0d37… drh 297 static void tokenConflict(
1cf0d37… drh 298 MergeBuilder *p,
1cf0d37… drh 299 unsigned int nPivot,
1cf0d37… drh 300 unsigned int nV1,
1cf0d37… drh 301 unsigned int nV2
1cf0d37… drh 302 ){
1cf0d37… drh 303 /* For a token-merge conflict, use the text from the merge-in */
1cf0d37… drh 304 blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV2);
1cf0d37… drh 305 p->pPivot->iCursor += nPivot;
1cf0d37… drh 306 p->pV1->iCursor += nV1;
1cf0d37… drh 307 p->pV2->iCursor += nV2;
1cf0d37… drh 308 }
1cf0d37… drh 309 static void mergebuilder_init_token(MergeBuilder *p){
1cf0d37… drh 310 mergebuilder_init(p);
1cf0d37… drh 311 p->xSame = tokenSame;
1cf0d37… drh 312 p->xChngV1 = tokenChngV1;
1cf0d37… drh 313 p->xChngV2 = tokenChngV2;
1cf0d37… drh 314 p->xChngBoth = tokenChngBoth;
1cf0d37… drh 315 p->xConflict = tokenConflict;
1cf0d37… drh 316 p->diffFlags = DIFF_BY_TOKEN;
1cf0d37… drh 317 }
1cf0d37… drh 318
1cf0d37… drh 319 /*
1cf0d37… drh 320 ** Attempt to do a low-level merge on a conflict. The conflict is
1cf0d37… drh 321 ** described by the first four parameters, which are the same as the
1cf0d37… drh 322 ** arguments to the xConflict method of the MergeBuilder object.
1cf0d37… drh 323 ** This routine attempts to resolve the conflict by looking at
1cf0d37… drh 324 ** elements of the conflict region that are finer grain than complete
1cf0d37… drh 325 ** lines of text.
1cf0d37… drh 326 **
1cf0d37… drh 327 ** The result is written into Blob pOut. pOut is initialized by this
1cf0d37… drh 328 ** routine.
1cf0d37… drh 329 */
1cf0d37… drh 330 int merge_try_to_resolve_conflict(
1cf0d37… drh 331 MergeBuilder *pMB, /* MergeBuilder that encounter conflict */
1cf0d37… drh 332 unsigned int nPivot, /* Lines of conflict in the pivot */
1cf0d37… drh 333 unsigned int nV1, /* Lines of conflict in V1 */
1cf0d37… drh 334 unsigned int nV2, /* Lines of conflict in V2 */
1cf0d37… drh 335 Blob *pOut /* Write resolution text here */
1cf0d37… drh 336 ){
1cf0d37… drh 337 int nConflict;
1cf0d37… drh 338 MergeBuilder mb;
1cf0d37… drh 339 Blob pv, v1, v2;
1cf0d37… drh 340 mergebuilder_init_token(&mb);
1cf0d37… drh 341 blob_extract_lines(pMB->pPivot, nPivot, &pv);
1cf0d37… drh 342 blob_extract_lines(pMB->pV1, nV1, &v1);
1cf0d37… drh 343 blob_extract_lines(pMB->pV2, nV2, &v2);
1cf0d37… drh 344 blob_zero(pOut);
337617b… drh 345 blob_materialize(&pv);
337617b… drh 346 blob_materialize(&v1);
337617b… drh 347 blob_materialize(&v2);
1cf0d37… drh 348 mb.pPivot = &pv;
1cf0d37… drh 349 mb.pV1 = &v1;
1cf0d37… drh 350 mb.pV2 = &v2;
1cf0d37… drh 351 mb.pOut = pOut;
1cf0d37… drh 352 nConflict = merge_three_blobs(&mb);
337617b… drh 353 blob_reset(&pv);
337617b… drh 354 blob_reset(&v1);
337617b… drh 355 blob_reset(&v2);
1cf0d37… drh 356 /* mb has not allocated any resources, so we do not need to invoke
1cf0d37… drh 357 ** the xDestroy method. */
1cf0d37… drh 358 blob_add_final_newline(pOut);
1cf0d37… drh 359 return nConflict;
1cf0d37… drh 360 }
1cf0d37… drh 361
1cf0d37… drh 362
aa82965… drh 363 /************************* MergeBuilderText **********************************/
aa82965… drh 364 /* This version of MergeBuilder actually performs a merge on file and puts
aa82965… drh 365 ** the result in pOut
aa82965… drh 366 */
aa82965… drh 367 static void txtStart(MergeBuilder *p){
aa82965… drh 368 /* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM),
aa82965… drh 369 ** keep it in the output. This should be secure enough not to cause
aa82965… drh 370 ** unintended changes to the merged file and consistent with what
aa82965… drh 371 ** users are using in their source files.
aa82965… drh 372 */
aa82965… drh 373 if( starts_with_utf8_bom(p->pV1, 0) && starts_with_utf8_bom(p->pV2, 0) ){
aa82965… drh 374 blob_append(p->pOut, (char*)get_utf8_bom(0), -1);
aa82965… drh 375 }
aa82965… drh 376 if( contains_crlf(p->pV1) && contains_crlf(p->pV2) ){
aa82965… drh 377 p->useCrLf = 1;
aa82965… drh 378 }
aa82965… drh 379 }
aa82965… drh 380 static void txtSame(MergeBuilder *p, unsigned int N){
aa82965… drh 381 blob_copy_lines(p->pOut, p->pPivot, N); p->lnPivot += N;
aa82965… drh 382 blob_copy_lines(0, p->pV1, N); p->lnV1 += N;
aa82965… drh 383 blob_copy_lines(0, p->pV2, N); p->lnV2 += N;
aa82965… drh 384 }
aa82965… drh 385 static void txtChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){
aa82965… drh 386 blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot;
aa82965… drh 387 blob_copy_lines(0, p->pV2, nPivot); p->lnV2 += nPivot;
aa82965… drh 388 blob_copy_lines(p->pOut, p->pV1, nV1); p->lnV1 += nV1;
aa82965… drh 389 }
aa82965… drh 390 static void txtChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){
aa82965… drh 391 blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot;
aa82965… drh 392 blob_copy_lines(0, p->pV1, nPivot); p->lnV1 += nPivot;
aa82965… drh 393 blob_copy_lines(p->pOut, p->pV2, nV2); p->lnV2 += nV2;
aa82965… drh 394 }
aa82965… drh 395 static void txtChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){
aa82965… drh 396 blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot;
58c74fc… drh 397 blob_copy_lines(0, p->pV1, nV); p->lnV1 += nV;
aa82965… drh 398 blob_copy_lines(p->pOut, p->pV2, nV); p->lnV2 += nV;
aa82965… drh 399 }
aa82965… drh 400 static void txtConflict(
aa82965… drh 401 MergeBuilder *p,
aa82965… drh 402 unsigned int nPivot,
aa82965… drh 403 unsigned int nV1,
aa82965… drh 404 unsigned int nV2
aa82965… drh 405 ){
1cf0d37… drh 406 int nRes; /* Lines in the computed conflict resolution */
1cf0d37… drh 407 Blob res; /* Text of the conflict resolution */
1cf0d37… drh 408
1cf0d37… drh 409 merge_try_to_resolve_conflict(p, nPivot, nV1, nV2, &res);
1cf0d37… drh 410 nRes = blob_linecount(&res);
1cf0d37… drh 411
1cf0d37… drh 412 append_merge_mark(p->pOut, 0, p->lnV1+1, p->useCrLf);
aa82965… drh 413 blob_copy_lines(p->pOut, p->pV1, nV1); p->lnV1 += nV1;
aa82965… drh 414
1cf0d37… drh 415 if( nRes>0 ){
1cf0d37… drh 416 append_merge_mark(p->pOut, 1, 0, p->useCrLf);
1cf0d37… drh 417 blob_copy_lines(p->pOut, &res, nRes);
1cf0d37… drh 418 }
337617b… drh 419 blob_reset(&res);
1cf0d37… drh 420
1cf0d37… drh 421 append_merge_mark(p->pOut, 2, p->lnPivot+1, p->useCrLf);
aa82965… drh 422 blob_copy_lines(p->pOut, p->pPivot, nPivot); p->lnPivot += nPivot;
aa82965… drh 423
1cf0d37… drh 424 append_merge_mark(p->pOut, 3, p->lnV2+1, p->useCrLf);
aa82965… drh 425 blob_copy_lines(p->pOut, p->pV2, nV2); p->lnV2 += nV2;
aa82965… drh 426
1cf0d37… drh 427 append_merge_mark(p->pOut, 4, -1, p->useCrLf);
aa82965… drh 428 }
aa82965… drh 429 static void mergebuilder_init_text(MergeBuilder *p){
aa82965… drh 430 mergebuilder_init(p);
aa82965… drh 431 p->xStart = txtStart;
aa82965… drh 432 p->xSame = txtSame;
aa82965… drh 433 p->xChngV1 = txtChngV1;
aa82965… drh 434 p->xChngV2 = txtChngV2;
aa82965… drh 435 p->xChngBoth = txtChngBoth;
aa82965… drh 436 p->xConflict = txtConflict;
aa82965… drh 437 }
aa82965… drh 438
aa82965… drh 439 /************************* MergeBuilderTcl **********************************/
aa82965… drh 440 /* Generate merge output formatted for reading by a TCL script.
aa82965… drh 441 **
aa82965… drh 442 ** The output consists of lines of text, each with 4 tokens. The tokens
aa82965… drh 443 ** represent the content for one line from baseline, v1, v2, and output
aa82965… drh 444 ** respectively. The first character of each token provides auxiliary
aa82965… drh 445 ** information:
aa82965… drh 446 **
aa82965… drh 447 ** . This line is omitted.
aa82965… drh 448 ** N Name of the file.
aa82965… drh 449 ** T Literal text follows that should have a \n terminator.
aa82965… drh 450 ** R Literal text follows that needs a \r\n terminator.
1cf0d37… drh 451 ** X Merge conflict.
aa82965… drh 452 ** Z Literal text without a line terminator.
1cf0d37… drh 453 ** S Skipped lines. Followed by number of lines to skip.
aa82965… drh 454 ** 1 Text is a copy of token 1
aa82965… drh 455 ** 2 Use data from data-token 2
aa82965… drh 456 ** 3 Use data from data-token 3
aa82965… drh 457 */
aa82965… drh 458
aa82965… drh 459 /* Write text that goes into the interior of a double-quoted string in TCL */
aa82965… drh 460 static void tclWriteQuotedText(Blob *pOut, const char *zIn, int nIn){
aa82965… drh 461 int j;
aa82965… drh 462 for(j=0; j<nIn; j++){
aa82965… drh 463 char c = zIn[j];
aa82965… drh 464 if( c=='\\' ){
aa82965… drh 465 blob_append(pOut, "\\\\", 2);
aa82965… drh 466 }else if( c=='"' ){
aa82965… drh 467 blob_append(pOut, "\\\"", 2);
aa82965… drh 468 }else if( c<' ' || c>0x7e ){
aa82965… drh 469 char z[5];
aa82965… drh 470 z[0] = '\\';
aa82965… drh 471 z[1] = "01234567"[(c>>6)&0x3];
aa82965… drh 472 z[2] = "01234567"[(c>>3)&0x7];
aa82965… drh 473 z[3] = "01234567"[c&0x7];
aa82965… drh 474 z[4] = 0;
aa82965… drh 475 blob_append(pOut, z, 4);
aa82965… drh 476 }else{
aa82965… drh 477 blob_append_char(pOut, c);
aa82965… drh 478 }
aa82965… drh 479 }
aa82965… drh 480 }
aa82965… drh 481
aa82965… drh 482 /* Copy one line of text from pIn and append to pOut, encoded as TCL */
1cf0d37… drh 483 static void tclLineOfText(Blob *pOut, Blob *pIn, char cType){
aa82965… drh 484 int i, k;
aa82965… drh 485 for(i=pIn->iCursor; i<pIn->nUsed && pIn->aData[i]!='\n'; i++){}
aa82965… drh 486 if( i==pIn->nUsed ){
aa82965… drh 487 k = i;
aa82965… drh 488 }else if( i>pIn->iCursor && pIn->aData[i-1]=='\r' ){
aa82965… drh 489 k = i-1;
aa82965… drh 490 i++;
aa82965… drh 491 }else{
aa82965… drh 492 k = i;
aa82965… drh 493 i++;
aa82965… drh 494 }
1cf0d37… drh 495 blob_append_char(pOut, '"');
1cf0d37… drh 496 blob_append_char(pOut, cType);
aa82965… drh 497 tclWriteQuotedText(pOut, pIn->aData+pIn->iCursor, k-pIn->iCursor);
aa82965… drh 498 pIn->iCursor = i;
aa82965… drh 499 blob_append_char(pOut, '"');
aa82965… drh 500 }
aa82965… drh 501 static void tclStart(MergeBuilder *p){
aa82965… drh 502 Blob *pOut = p->pOut;
aa82965… drh 503 blob_append(pOut, "\"N", 2);
aa82965… drh 504 tclWriteQuotedText(pOut, p->zPivot, (int)strlen(p->zPivot));
aa82965… drh 505 blob_append(pOut, "\" \"N", 4);
aa82965… drh 506 tclWriteQuotedText(pOut, p->zV1, (int)strlen(p->zV1));
aa82965… drh 507 blob_append(pOut, "\" \"N", 4);
aa82965… drh 508 tclWriteQuotedText(pOut, p->zV2, (int)strlen(p->zV2));
aa82965… drh 509 blob_append(pOut, "\" \"N", 4);
aa82965… drh 510 if( p->zOut ){
aa82965… drh 511 tclWriteQuotedText(pOut, p->zOut, (int)strlen(p->zOut));
aa82965… drh 512 }else{
aa82965… drh 513 blob_append(pOut, "(Merge Result)", -1);
aa82965… drh 514 }
aa82965… drh 515 blob_append(pOut, "\"\n", 2);
aa82965… drh 516 }
aa82965… drh 517 static void tclSame(MergeBuilder *p, unsigned int N){
aa82965… drh 518 int i = 0;
aa82965… drh 519 int nSkip;
aa82965… drh 520
aa82965… drh 521 if( p->lnPivot>=2 || p->lnV1>2 || p->lnV2>2 ){
aa82965… drh 522 while( i<N && i<p->nContext ){
1cf0d37… drh 523 tclLineOfText(p->pOut, p->pPivot, 'T');
aa82965… drh 524 blob_append(p->pOut, " 1 1 1\n", 7);
aa82965… drh 525 i++;
aa82965… drh 526 }
aa82965… drh 527 nSkip = N - p->nContext*2;
aa82965… drh 528 }else{
aa82965… drh 529 nSkip = N - p->nContext;
aa82965… drh 530 }
aa82965… drh 531 if( nSkip>0 ){
1cf0d37… drh 532 blob_appendf(p->pOut, "\"S%d %d %d %d\" . . .\n",
1cf0d37… drh 533 nSkip, nSkip, nSkip, nSkip);
aa82965… drh 534 blob_copy_lines(0, p->pPivot, nSkip);
aa82965… drh 535 i += nSkip;
aa82965… drh 536 }
aa82965… drh 537
aa82965… drh 538 p->lnPivot += N;
aa82965… drh 539 p->lnV1 += N;
aa82965… drh 540 p->lnV2 += N;
aa82965… drh 541
aa82965… drh 542 if( p->lnPivot<p->mxPivot || p->lnV1<p->mxV1 || p->lnV2<p->mxV2 ){
aa82965… drh 543 while( i<N ){
1cf0d37… drh 544 tclLineOfText(p->pOut, p->pPivot, 'T');
aa82965… drh 545 blob_append(p->pOut, " 1 1 1\n", 7);
aa82965… drh 546 i++;
aa82965… drh 547 }
aa82965… drh 548 }
aa82965… drh 549
aa82965… drh 550 blob_copy_lines(0, p->pV1, N);
aa82965… drh 551 blob_copy_lines(0, p->pV2, N);
aa82965… drh 552 }
aa82965… drh 553 static void tclChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){
aa82965… drh 554 int i;
aa82965… drh 555 for(i=0; i<nPivot && i<nV1; i++){
1cf0d37… drh 556 tclLineOfText(p->pOut, p->pPivot, 'T');
aa82965… drh 557 blob_append_char(p->pOut, ' ');
1cf0d37… drh 558 tclLineOfText(p->pOut, p->pV1, 'T');
aa82965… drh 559 blob_append(p->pOut, " 1 2\n", 5);
aa82965… drh 560 }
aa82965… drh 561 while( i<nPivot ){
1cf0d37… drh 562 tclLineOfText(p->pOut, p->pPivot, 'T');
aa82965… drh 563 blob_append(p->pOut, " . 1 .\n", 7);
aa82965… drh 564 i++;
aa82965… drh 565 }
aa82965… drh 566 while( i<nV1 ){
aa82965… drh 567 blob_append(p->pOut, ". ", 2);
1cf0d37… drh 568 tclLineOfText(p->pOut, p->pV1, 'T');
aa82965… drh 569 blob_append(p->pOut, " . 2\n", 5);
aa82965… drh 570 i++;
aa82965… drh 571 }
aa82965… drh 572 p->lnPivot += nPivot;
aa82965… drh 573 p->lnV1 += nV1;
aa82965… drh 574 p->lnV2 += nPivot;
aa82965… drh 575 blob_copy_lines(0, p->pV2, nPivot);
aa82965… drh 576 }
aa82965… drh 577 static void tclChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){
aa82965… drh 578 int i;
aa82965… drh 579 for(i=0; i<nPivot && i<nV2; i++){
1cf0d37… drh 580 tclLineOfText(p->pOut, p->pPivot, 'T');
aa82965… drh 581 blob_append(p->pOut, " 1 ", 3);
1cf0d37… drh 582 tclLineOfText(p->pOut, p->pV2, 'T');
aa82965… drh 583 blob_append(p->pOut, " 3\n", 3);
aa82965… drh 584 }
aa82965… drh 585 while( i<nPivot ){
1cf0d37… drh 586 tclLineOfText(p->pOut, p->pPivot, 'T');
aa82965… drh 587 blob_append(p->pOut, " 1 . .\n", 7);
aa82965… drh 588 i++;
aa82965… drh 589 }
aa82965… drh 590 while( i<nV2 ){
aa82965… drh 591 blob_append(p->pOut, ". . ", 4);
1cf0d37… drh 592 tclLineOfText(p->pOut, p->pV2, 'T');
aa82965… drh 593 blob_append(p->pOut, " 3\n", 3);
aa82965… drh 594 i++;
aa82965… drh 595 }
aa82965… drh 596 p->lnPivot += nPivot;
aa82965… drh 597 p->lnV1 += nPivot;
aa82965… drh 598 p->lnV2 += nV2;
aa82965… drh 599 blob_copy_lines(0, p->pV1, nPivot);
aa82965… drh 600 }
aa82965… drh 601 static void tclChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){
aa82965… drh 602 int i;
aa82965… drh 603 for(i=0; i<nPivot && i<nV; i++){
1cf0d37… drh 604 tclLineOfText(p->pOut, p->pPivot, 'T');
aa82965… drh 605 blob_append_char(p->pOut, ' ');
1cf0d37… drh 606 tclLineOfText(p->pOut, p->pV1, 'T');
aa82965… drh 607 blob_append(p->pOut, " 2 2\n", 5);
aa82965… drh 608 }
aa82965… drh 609 while( i<nPivot ){
1cf0d37… drh 610 tclLineOfText(p->pOut, p->pPivot, 'T');
aa82965… drh 611 blob_append(p->pOut, " . . .\n", 7);
aa82965… drh 612 i++;
aa82965… drh 613 }
aa82965… drh 614 while( i<nV ){
aa82965… drh 615 blob_append(p->pOut, ". ", 2);
1cf0d37… drh 616 tclLineOfText(p->pOut, p->pV1, 'T');
aa82965… drh 617 blob_append(p->pOut, " 2 2\n", 5);
aa82965… drh 618 i++;
aa82965… drh 619 }
aa82965… drh 620 p->lnPivot += nPivot;
aa82965… drh 621 p->lnV1 += nV;
aa82965… drh 622 p->lnV2 += nV;
aa82965… drh 623 blob_copy_lines(0, p->pV2, nV);
aa82965… drh 624 }
aa82965… drh 625 static void tclConflict(
aa82965… drh 626 MergeBuilder *p,
aa82965… drh 627 unsigned int nPivot,
aa82965… drh 628 unsigned int nV1,
aa82965… drh 629 unsigned int nV2
aa82965… drh 630 ){
aa82965… drh 631 int mx = nPivot;
aa82965… drh 632 int i;
1cf0d37… drh 633 int nRes;
1cf0d37… drh 634 Blob res;
1cf0d37… drh 635
1cf0d37… drh 636 merge_try_to_resolve_conflict(p, nPivot, nV1, nV2, &res);
1cf0d37… drh 637 nRes = blob_linecount(&res);
aa82965… drh 638 if( nV1>mx ) mx = nV1;
aa82965… drh 639 if( nV2>mx ) mx = nV2;
1cf0d37… drh 640 if( nRes>mx ) mx = nRes;
1cf0d37… drh 641 if( nRes>0 ){
1cf0d37… drh 642 blob_appendf(p->pOut, "\"S0 0 0 %d\" . . .\n", nV2+2);
1cf0d37… drh 643 }
aa82965… drh 644 for(i=0; i<mx; i++){
aa82965… drh 645 if( i<nPivot ){
1cf0d37… drh 646 tclLineOfText(p->pOut, p->pPivot, 'X');
aa82965… drh 647 }else{
aa82965… drh 648 blob_append_char(p->pOut, '.');
aa82965… drh 649 }
aa82965… drh 650 blob_append_char(p->pOut, ' ');
aa82965… drh 651 if( i<nV1 ){
1cf0d37… drh 652 tclLineOfText(p->pOut, p->pV1, 'X');
aa82965… drh 653 }else{
aa82965… drh 654 blob_append_char(p->pOut, '.');
aa82965… drh 655 }
aa82965… drh 656 blob_append_char(p->pOut, ' ');
aa82965… drh 657 if( i<nV2 ){
1cf0d37… drh 658 tclLineOfText(p->pOut, p->pV2, 'X');
aa82965… drh 659 }else{
aa82965… drh 660 blob_append_char(p->pOut, '.');
aa82965… drh 661 }
1cf0d37… drh 662 if( i<nRes ){
1cf0d37… drh 663 blob_append_char(p->pOut, ' ');
1cf0d37… drh 664 tclLineOfText(p->pOut, &res, 'X');
1cf0d37… drh 665 blob_append_char(p->pOut, '\n');
1cf0d37… drh 666 }else{
1cf0d37… drh 667 blob_append(p->pOut, " .\n", 3);
1cf0d37… drh 668 }
1cf0d37… drh 669 if( i==mx-1 ){
1cf0d37… drh 670 blob_appendf(p->pOut, "\"S0 0 0 %d\" . . .\n", nPivot+nV1+3);
1cf0d37… drh 671 }
aa82965… drh 672 }
1cf0d37… drh 673 blob_reset(&res);
aa82965… drh 674 p->lnPivot += nPivot;
aa82965… drh 675 p->lnV1 += nV1;
aa82965… drh 676 p->lnV2 += nV2;
aa82965… drh 677 }
aa82965… drh 678 void mergebuilder_init_tcl(MergeBuilder *p){
aa82965… drh 679 mergebuilder_init(p);
aa82965… drh 680 p->xStart = tclStart;
aa82965… drh 681 p->xSame = tclSame;
aa82965… drh 682 p->xChngV1 = tclChngV1;
aa82965… drh 683 p->xChngV2 = tclChngV2;
aa82965… drh 684 p->xChngBoth = tclChngBoth;
aa82965… drh 685 p->xConflict = tclConflict;
aa82965… drh 686 }
aa82965… drh 687 /*****************************************************************************/
aa82965… drh 688
aa82965… drh 689 /*
aa82965… drh 690 ** The aC[] array contains triples of integers. Within each triple, the
aa82965… drh 691 ** elements are:
aa82965… drh 692 **
aa82965… drh 693 ** (0) The number of lines to copy
aa82965… drh 694 ** (1) The number of lines to delete
aa82965… drh 695 ** (2) The number of liens to insert
aa82965… drh 696 **
aa82965… drh 697 ** Suppose we want to advance over sz lines of the original file. This routine
aa82965… drh 698 ** returns true if that advance would land us on a copy operation. It
aa82965… drh 699 ** returns false if the advance would end on a delete.
aa82965… drh 700 */
aa82965… drh 701 static int ends_with_copy(int *aC, int sz){
aa82965… drh 702 while( sz>0 && (aC[0]>0 || aC[1]>0 || aC[2]>0) ){
aa82965… drh 703 if( aC[0]>=sz ) return 1;
aa82965… drh 704 sz -= aC[0];
aa82965… drh 705 if( aC[1]>sz ) return 0;
aa82965… drh 706 sz -= aC[1];
aa82965… drh 707 aC += 3;
aa82965… drh 708 }
aa82965… drh 709 return 1;
aa82965… drh 710 }
aa82965… drh 711
aa82965… drh 712 /*
aa82965… drh 713 ** aC[] is an "edit triple" for changes from A to B. Advance through
aa82965… drh 714 ** this triple to determine the number of lines to bypass on B in order
aa82965… drh 715 ** to match an advance of sz lines on A.
aa82965… drh 716 */
aa82965… drh 717 static int skip_conflict(
aa82965… drh 718 int *aC, /* Array of integer triples describing the edit */
aa82965… drh 719 int i, /* Index in aC[] of current location */
aa82965… drh 720 int sz, /* Lines of A that have been skipped */
e2bdc10… danield 721 unsigned int *pLn /* OUT: Lines of B to skip to keep alignment with A */
aa82965… drh 722 ){
aa82965… drh 723 *pLn = 0;
aa82965… drh 724 while( sz>0 ){
aa82965… drh 725 if( aC[i]==0 && aC[i+1]==0 && aC[i+2]==0 ) break;
aa82965… drh 726 if( aC[i]>=sz ){
aa82965… drh 727 aC[i] -= sz;
aa82965… drh 728 *pLn += sz;
aa82965… drh 729 break;
aa82965… drh 730 }
aa82965… drh 731 *pLn += aC[i];
aa82965… drh 732 *pLn += aC[i+2];
aa82965… drh 733 sz -= aC[i] + aC[i+1];
aa82965… drh 734 i += 3;
aa82965… drh 735 }
aa82965… drh 736 return i;
aa82965… drh 737 }
aa82965… drh 738
14f44e9… drh 739 /*
dbda8d6… drh 740 ** Do a three-way merge. Initialize pOut to contain the result.
d0305b3… aku 741 **
d0305b3… aku 742 ** The merge is an edit against pV2. Both pV1 and pV2 have a
d0305b3… aku 743 ** common origin at pPivot. Apply the changes of pPivot ==> pV1
d0305b3… aku 744 ** to pV2.
d0305b3… aku 745 **
d0305b3… aku 746 ** The return is 0 upon complete success. If any input file is binary,
d0305b3… aku 747 ** -1 is returned and pOut is unmodified. If there are merge
233463c… jan.nijtmans 748 ** conflicts, the merge proceeds as best as it can and the number
d0305b3… aku 749 ** of conflicts is returns
d0305b3… aku 750 */
aa82965… drh 751 int merge_three_blobs(MergeBuilder *p){
e45d478… drh 752 int *aC1; /* Changes from pPivot to pV1 */
e45d478… drh 753 int *aC2; /* Changes from pPivot to pV2 */
e45d478… drh 754 int i1, i2; /* Index into aC1[] and aC2[] */
83566f2… drh 755 int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */
e45d478… drh 756 int limit1, limit2; /* Sizes of aC1[] and aC2[] */
e45d478… drh 757 int nConflict = 0; /* Number of merge conflicts seen so far */
1347a1d… drh 758 DiffConfig DCfg;
3e89b0c… drh 759
3e89b0c… drh 760 /* Compute the edits that occur from pPivot => pV1 (into aC1)
3e89b0c… drh 761 ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is
3e89b0c… drh 762 ** an array of integer triples. Within each triple, the first integer
3e89b0c… drh 763 ** is the number of lines of text to copy directly from the pivot,
3e89b0c… drh 764 ** the second integer is the number of lines of text to omit from the
3e89b0c… drh 765 ** pivot, and the third integer is the number of lines of text that are
3e89b0c… drh 766 ** inserted. The edit array ends with a triple of 0,0,0.
3e89b0c… drh 767 */
1347a1d… drh 768 diff_config_init(&DCfg, 0);
1cf0d37… drh 769 DCfg.diffFlags = p->diffFlags;
aa82965… drh 770 aC1 = text_diff(p->pPivot, p->pV1, 0, &DCfg);
aa82965… drh 771 aC2 = text_diff(p->pPivot, p->pV2, 0, &DCfg);
3e89b0c… drh 772 if( aC1==0 || aC2==0 ){
d0305b3… aku 773 free(aC1);
d0305b3… aku 774 free(aC2);
d0305b3… aku 775 return -1;
d0305b3… aku 776 }
e45d478… drh 777
aa82965… drh 778 blob_rewind(p->pV1); /* Rewind inputs: Needed to reconstruct output */
aa82965… drh 779 blob_rewind(p->pV2);
aa82965… drh 780 blob_rewind(p->pPivot);
8112298… drh 781
8112298… drh 782 /* Determine the length of the aC1[] and aC2[] change vectors */
aa82965… drh 783 p->mxPivot = 0;
aa82965… drh 784 p->mxV1 = 0;
aa82965… drh 785 for(i1=0; aC1[i1] || aC1[i1+1] || aC1[i1+2]; i1+=3){
aa82965… drh 786 p->mxPivot += aC1[i1] + aC1[i1+1];
aa82965… drh 787 p->mxV1 += aC1[i1] + aC1[i1+2];
aa82965… drh 788 }
8112298… drh 789 limit1 = i1;
aa82965… drh 790 p->mxV2 = 0;
aa82965… drh 791 for(i2=0; aC2[i2] || aC2[i2+1] || aC2[i2+2]; i2+=3){
aa82965… drh 792 p->mxV2 += aC2[i2] + aC2[i2+2];
aa82965… drh 793 }
8112298… drh 794 limit2 = i2;
8112298… drh 795
aa82965… drh 796 /* Output header text and do any other required initialization */
aa82965… drh 797 p->xStart(p);
3e89b0c… drh 798
3e89b0c… drh 799 /* Loop over the two edit vectors and use them to compute merged text
3e89b0c… drh 800 ** which is written into pOut. i1 and i2 are multiples of 3 which are
3e89b0c… drh 801 ** indices into aC1[] and aC2[] to the edit triple currently being
3e89b0c… drh 802 ** processed
3e89b0c… drh 803 */
8112298… drh 804 i1 = i2 = 0;
8112298… drh 805 while( i1<limit1 && i2<limit2 ){
8112298… drh 806 if( aC1[i1]>0 && aC2[i2]>0 ){
3e89b0c… drh 807 /* Output text that is unchanged in both V1 and V2 */
8112298… drh 808 nCpy = min(aC1[i1], aC2[i2]);
aa82965… drh 809 p->xSame(p, nCpy);
8112298… drh 810 aC1[i1] -= nCpy;
8112298… drh 811 aC2[i2] -= nCpy;
8112298… drh 812 }else
8112298… drh 813 if( aC1[i1] >= aC2[i2+1] && aC1[i1]>0 && aC2[i2+1]+aC2[i2+2]>0 ){
3e89b0c… drh 814 /* Output edits to V2 that occurs within unchanged regions of V1 */
8112298… drh 815 nDel = aC2[i2+1];
8112298… drh 816 nIns = aC2[i2+2];
aa82965… drh 817 p->xChngV2(p, nDel, nIns);
8112298… drh 818 aC1[i1] -= nDel;
8112298… drh 819 i2 += 3;
8112298… drh 820 }else
8112298… drh 821 if( aC2[i2] >= aC1[i1+1] && aC2[i2]>0 && aC1[i1+1]+aC1[i1+2]>0 ){
3e89b0c… drh 822 /* Output edits to V1 that occur within unchanged regions of V2 */
8112298… drh 823 nDel = aC1[i1+1];
8112298… drh 824 nIns = aC1[i1+2];
aa82965… drh 825 p->xChngV1(p, nDel, nIns);
8112298… drh 826 aC2[i2] -= nDel;
8112298… drh 827 i1 += 3;
8112298… drh 828 }else
aa82965… drh 829 if( sameEdit(&aC1[i1], &aC2[i2], p->pV1, p->pV2) ){
3e89b0c… drh 830 /* Output edits that are identical in both V1 and V2. */
8112298… drh 831 assert( aC1[i1]==0 );
8112298… drh 832 nDel = aC1[i1+1];
8112298… drh 833 nIns = aC1[i1+2];
aa82965… drh 834 p->xChngBoth(p, nDel, nIns);
8112298… drh 835 i1 += 3;
8112298… drh 836 i2 += 3;
8112298… drh 837 }else
8112298… drh 838 {
3e89b0c… drh 839 /* We have found a region where different edits to V1 and V2 overlap.
3e89b0c… drh 840 ** This is a merge conflict. Find the size of the conflict, then
d397f1c… andybradford 841 ** output both possible edits separated by distinctive marks.
3e89b0c… drh 842 */
aa82965… drh 843 unsigned int sz = 1; /* Size of the conflict in the pivot, in lines */
aa82965… drh 844 unsigned int nV1, nV2; /* Size of conflict in V1 and V2, in lines */
8112298… drh 845 nConflict++;
aa82965… drh 846 while( !ends_with_copy(&aC1[i1], sz) || !ends_with_copy(&aC2[i2], sz) ){
8112298… drh 847 sz++;
8112298… drh 848 }
aa82965… drh 849 i1 = skip_conflict(aC1, i1, sz, &nV1);
aa82965… drh 850 i2 = skip_conflict(aC2, i2, sz, &nV2);
aa82965… drh 851 p->xConflict(p, sz, nV1, nV2);
aa82965… drh 852 }
aa82965… drh 853
3e89b0c… drh 854 /* If we are finished with an edit triple, advance to the next
3e89b0c… drh 855 ** triple.
3e89b0c… drh 856 */
8112298… drh 857 if( i1<limit1 && aC1[i1]==0 && aC1[i1+1]==0 && aC1[i1+2]==0 ) i1+=3;
8112298… drh 858 if( i2<limit2 && aC2[i2]==0 && aC2[i2+1]==0 && aC2[i2+2]==0 ) i2+=3;
8112298… drh 859 }
3e89b0c… drh 860
3e89b0c… drh 861 /* When one of the two edit vectors reaches its end, there might still
3e89b0c… drh 862 ** be an insert in the other edit vector. Output this remaining
3e89b0c… drh 863 ** insert.
3e89b0c… drh 864 */
8112298… drh 865 if( i1<limit1 && aC1[i1+2]>0 ){
aa82965… drh 866 p->xChngV1(p, 0, aC1[i1+2]);
8112298… drh 867 }else if( i2<limit2 && aC2[i2+2]>0 ){
aa82965… drh 868 p->xChngV2(p, 0, aC2[i2+2]);
8112298… drh 869 }
aa82965… drh 870
aa82965… drh 871 /* Output footer text */
aa82965… drh 872 p->xEnd(p);
d0305b3… aku 873
d0305b3… aku 874 free(aC1);
d0305b3… aku 875 free(aC2);
d0305b3… aku 876 return nConflict;
d0305b3… aku 877 }
d0305b3… aku 878
d0305b3… aku 879 /*
7d34d17… drh 880 ** Return true if the input string contains a merge marker on a line by
7d34d17… drh 881 ** itself.
7d34d17… drh 882 */
7d34d17… drh 883 int contains_merge_marker(Blob *p){
7d34d17… drh 884 int i, j;
7d34d17… drh 885 int len = (int)strlen(mergeMarker[0]);
7d34d17… drh 886 const char *z = blob_buffer(p);
7d34d17… drh 887 int n = blob_size(p) - len + 1;
7d34d17… drh 888 assert( len==(int)strlen(mergeMarker[1]) );
7d34d17… drh 889 assert( len==(int)strlen(mergeMarker[2]) );
7d34d17… drh 890 assert( len==(int)strlen(mergeMarker[3]) );
1cf0d37… drh 891 assert( len==(int)strlen(mergeMarker[4]) );
1cf0d37… drh 892 assert( count(mergeMarker)==5 );
7d34d17… drh 893 for(i=0; i<n; ){
7d34d17… drh 894 for(j=0; j<4; j++){
14f44e9… drh 895 if( (memcmp(&z[i], mergeMarker[j], len)==0) ){
14f44e9… drh 896 return 1;
14f44e9… drh 897 }
7d34d17… drh 898 }
7d34d17… drh 899 while( i<n && z[i]!='\n' ){ i++; }
88ff264… drh 900 while( i<n && (z[i]=='\n' || z[i]=='\r') ){ i++; }
7d34d17… drh 901 }
7d34d17… drh 902 return 0;
7d34d17… drh 903 }
7d34d17… drh 904
7d34d17… drh 905 /*
7d34d17… drh 906 ** Return true if the named file contains an unresolved merge marker line.
7d34d17… drh 907 */
7d34d17… drh 908 int file_contains_merge_marker(const char *zFullpath){
7d34d17… drh 909 Blob file;
7d34d17… drh 910 int rc;
1772357… drh 911 blob_read_from_file(&file, zFullpath, ExtFILE);
7d34d17… drh 912 rc = contains_merge_marker(&file);
7d34d17… drh 913 blob_reset(&file);
7d34d17… drh 914 return rc;
7d34d17… drh 915 }
7d34d17… drh 916
7d34d17… drh 917 /*
aa82965… drh 918 ** Show merge output in a Tcl/Tk window, in response to the --tk option
aa82965… drh 919 ** to the "merge" or "3-way-merge" command.
aa82965… drh 920 **
aa82965… drh 921 ** If fossil has direct access to a Tcl interpreter (either loaded
aa82965… drh 922 ** dynamically through stubs or linked in statically), we can use it
aa82965… drh 923 ** directly. Otherwise:
aa82965… drh 924 ** (1) Write the Tcl/Tk script used for rendering into a temp file.
aa82965… drh 925 ** (2) Invoke "tclsh" on the temp file using fossil_system().
aa82965… drh 926 ** (3) Delete the temp file.
aa82965… drh 927 */
aa82965… drh 928 void merge_tk(const char *zSubCmd, int firstArg){
aa82965… drh 929 int i;
aa82965… drh 930 Blob script;
aa82965… drh 931 const char *zTempFile = 0;
aa82965… drh 932 char *zCmd;
aa82965… drh 933 const char *zTclsh;
aa82965… drh 934 const char *zCnt;
aa82965… drh 935 int bDarkMode = find_option("dark",0,0)!=0;
aa82965… drh 936 int nContext;
aa82965… drh 937 zCnt = find_option("context", "c", 1);
aa82965… drh 938 if( zCnt==0 ){
aa82965… drh 939 nContext = 6;
aa82965… drh 940 }else{
aa82965… drh 941 nContext = atoi(zCnt);
aa82965… drh 942 if( nContext<0 ) nContext = 0xfffffff;
aa82965… drh 943 }
aa82965… drh 944 blob_zero(&script);
d6fa91f… drh 945 blob_appendf(&script, "set ncontext %d\n", nContext);
d6fa91f… drh 946 blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl",
d6fa91f… drh 947 g.nameOfExe, zSubCmd);
aa82965… drh 948 find_option("tcl",0,0);
aa82965… drh 949 find_option("debug",0,0);
aa82965… drh 950 zTclsh = find_option("tclsh",0,1);
aa82965… drh 951 if( zTclsh==0 ){
aa82965… drh 952 zTclsh = db_get("tclsh",0);
aa82965… drh 953 }
aa82965… drh 954 /* The undocumented --script FILENAME option causes the Tk script to
aa82965… drh 955 ** be written into the FILENAME instead of being run. This is used
aa82965… drh 956 ** for testing and debugging. */
aa82965… drh 957 zTempFile = find_option("script",0,1);
aa82965… drh 958 verify_all_options();
aa82965… drh 959
aa82965… drh 960 if( (g.argc - firstArg)!=3 ){
aa82965… drh 961 fossil_fatal("Requires 3 filename arguments");
aa82965… drh 962 }
aa82965… drh 963
aa82965… drh 964 for(i=firstArg; i<g.argc; i++){
aa82965… drh 965 const char *z = g.argv[i];
aa82965… drh 966 if( sqlite3_strglob("*}*",z) ){
aa82965… drh 967 blob_appendf(&script, " {%/}", z);
aa82965… drh 968 }else{
aa82965… drh 969 int j;
aa82965… drh 970 blob_append(&script, " ", 1);
aa82965… drh 971 for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]);
aa82965… drh 972 }
aa82965… drh 973 }
aa82965… drh 974 blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode);
aa82965… drh 975 blob_appendf(&script, "%s", builtin_file("merge.tcl", 0));
aa82965… drh 976 if( zTempFile ){
aa82965… drh 977 blob_write_to_file(&script, zTempFile);
aa82965… drh 978 fossil_print("To see the merge, run: %s \"%s\"\n", zTclsh, zTempFile);
aa82965… drh 979 }else{
aa82965… drh 980 #if defined(FOSSIL_ENABLE_TCL)
aa82965… drh 981 Th_FossilInit(TH_INIT_DEFAULT);
aa82965… drh 982 if( evaluateTclWithEvents(g.interp, &g.tcl, blob_str(&script),
aa82965… drh 983 blob_size(&script), 1, 1, 0)==TCL_OK ){
aa82965… drh 984 blob_reset(&script);
aa82965… drh 985 return;
aa82965… drh 986 }
aa82965… drh 987 /*
aa82965… drh 988 * If evaluation of the Tcl script fails, the reason may be that Tk
aa82965… drh 989 * could not be found by the loaded Tcl, or that Tcl cannot be loaded
aa82965… drh 990 * dynamically (e.g. x64 Tcl with x86 Fossil). Therefore, fallback
aa82965… drh 991 * to using the external "tclsh", if available.
aa82965… drh 992 */
aa82965… drh 993 #endif
aa82965… drh 994 zTempFile = write_blob_to_temp_file(&script);
aa82965… drh 995 zCmd = mprintf("%$ %$", zTclsh, zTempFile);
aa82965… drh 996 fossil_system(zCmd);
aa82965… drh 997 file_delete(zTempFile);
aa82965… drh 998 fossil_free(zCmd);
aa82965… drh 999 }
aa82965… drh 1000 blob_reset(&script);
aa82965… drh 1001 }
aa82965… drh 1002
aa82965… drh 1003
aa82965… drh 1004 /*
26eef7f… rberteig 1005 ** COMMAND: 3-way-merge*
e5c634b… drh 1006 **
aa82965… drh 1007 ** Usage: %fossil 3-way-merge BASELINE V1 V2 [MERGED]
e5c634b… drh 1008 **
e5c634b… drh 1009 ** Inputs are files BASELINE, V1, and V2. The file MERGED is generated
aa82965… drh 1010 ** as output. If no MERGED file is specified, output is sent to
aa82965… drh 1011 ** stdout.
e5c634b… drh 1012 **
e5c634b… drh 1013 ** BASELINE is a common ancestor of two files V1 and V2 that have diverging
e5c634b… drh 1014 ** edits. The generated output file MERGED is the combination of all
e5c634b… drh 1015 ** changes in both V1 and V2.
e5c634b… drh 1016 **
e5c634b… drh 1017 ** This command has no effect on the Fossil repository. It is a utility
e5c634b… drh 1018 ** command made available for the convenience of users. This command can
e5c634b… drh 1019 ** be used, for example, to help import changes from an upstream project.
60c2fee… drh 1020 **
e5c634b… drh 1021 ** Suppose an upstream project has a file named "Xup.c" which is imported
e5c634b… drh 1022 ** with modifications to the local project as "Xlocal.c". Suppose further
e5c634b… drh 1023 ** that the "Xbase.c" is an exact copy of the last imported "Xup.c".
e5c634b… drh 1024 ** Then to import the latest "Xup.c" while preserving all the local changes:
d0305b3… aku 1025 **
e5c634b… drh 1026 ** fossil 3-way-merge Xbase.c Xlocal.c Xup.c Xlocal.c
e5c634b… drh 1027 ** cp Xup.c Xbase.c
e5c634b… drh 1028 ** # Verify that everything still works
e5c634b… drh 1029 ** fossil commit
45f3516… jan.nijtmans 1030 **
d0305b3… aku 1031 */
aa82965… drh 1032 void merge_3way_cmd(void){
aa82965… drh 1033 MergeBuilder s;
9e17c68… drh 1034 int nConflict;
aa82965… drh 1035 Blob pivot, v1, v2, out;
aa82965… drh 1036 int noWarn = 0;
aa82965… drh 1037 const char *zCnt;
aa82965… drh 1038
aa82965… drh 1039 if( find_option("tk", 0, 0)!=0 ){
aa82965… drh 1040 merge_tk("3-way-merge", 2);
aa82965… drh 1041 return;
aa82965… drh 1042 }
aa82965… drh 1043 mergebuilder_init_text(&s);
aa82965… drh 1044 if( find_option("debug", 0, 0) ){
aa82965… drh 1045 mergebuilder_init(&s);
aa82965… drh 1046 }
aa82965… drh 1047 if( find_option("tcl", 0, 0) ){
aa82965… drh 1048 mergebuilder_init_tcl(&s);
aa82965… drh 1049 noWarn = 1;
aa82965… drh 1050 }
aa82965… drh 1051 zCnt = find_option("context", "c", 1);
aa82965… drh 1052 if( zCnt ){
aa82965… drh 1053 s.nContext = atoi(zCnt);
aa82965… drh 1054 if( s.nContext<0 ) s.nContext = 0xfffffff;
aa82965… drh 1055 }else{
aa82965… drh 1056 s.nContext = 6;
aa82965… drh 1057 }
aa82965… drh 1058 blob_zero(&pivot); s.pPivot = &pivot;
aa82965… drh 1059 blob_zero(&v1); s.pV1 = &v1;
aa82965… drh 1060 blob_zero(&v2); s.pV2 = &v2;
aa82965… drh 1061 blob_zero(&out); s.pOut = &out;
4e18dba… jan.nijtmans 1062
74ac0c9… drh 1063 /* We should be done with options.. */
74ac0c9… drh 1064 verify_all_options();
74ac0c9… drh 1065
aa82965… drh 1066 if( g.argc!=6 && g.argc!=5 ){
aa82965… drh 1067 usage("[OPTIONS] PIVOT V1 V2 [MERGED]");
2c42b21… andygoth 1068 }
aa82965… drh 1069 s.zPivot = file_tail(g.argv[2]);
aa82965… drh 1070 s.zV1 = file_tail(g.argv[3]);
aa82965… drh 1071 s.zV2 = file_tail(g.argv[4]);
aa82965… drh 1072 if( blob_read_from_file(s.pPivot, g.argv[2], ExtFILE)<0 ){
2c42b21… andygoth 1073 fossil_fatal("cannot read %s", g.argv[2]);
2c42b21… andygoth 1074 }
aa82965… drh 1075 if( blob_read_from_file(s.pV1, g.argv[3], ExtFILE)<0 ){
2c42b21… andygoth 1076 fossil_fatal("cannot read %s", g.argv[3]);
2c42b21… andygoth 1077 }
aa82965… drh 1078 if( blob_read_from_file(s.pV2, g.argv[4], ExtFILE)<0 ){
2c42b21… andygoth 1079 fossil_fatal("cannot read %s", g.argv[4]);
2c42b21… andygoth 1080 }
aa82965… drh 1081 nConflict = merge_three_blobs(&s);
aa82965… drh 1082 if( g.argc==6 ){
aa82965… drh 1083 s.zOut = file_tail(g.argv[5]);
aa82965… drh 1084 blob_write_to_file(s.pOut, g.argv[5]);
aa82965… drh 1085 }else{
aa82965… drh 1086 s.zOut = "(Merge Result)";
aa82965… drh 1087 blob_write_to_file(s.pOut, "-");
2c42b21… andygoth 1088 }
aa82965… drh 1089 s.xDestroy(&s);
dbda8d6… drh 1090 blob_reset(&pivot);
dbda8d6… drh 1091 blob_reset(&v1);
dbda8d6… drh 1092 blob_reset(&v2);
aa82965… drh 1093 blob_reset(&out);
aa82965… drh 1094 if( nConflict>0 && !noWarn ){
aa82965… drh 1095 fossil_warning("WARNING: %d merge conflicts", nConflict);
aa82965… drh 1096 }
9b7a6f8… drh 1097 }
9b7a6f8… drh 1098
9b7a6f8… drh 1099 /*
9b7a6f8… drh 1100 ** aSubst is an array of string pairs. The first element of each pair is
9b7a6f8… drh 1101 ** a string that begins with %. The second element is a replacement for that
9b7a6f8… drh 1102 ** string.
9b7a6f8… drh 1103 **
9b7a6f8… drh 1104 ** This routine makes a copy of zInput into memory obtained from malloc and
9b7a6f8… drh 1105 ** performance all applicable substitutions on that string.
9b7a6f8… drh 1106 */
9b7a6f8… drh 1107 char *string_subst(const char *zInput, int nSubst, const char **azSubst){
9b7a6f8… drh 1108 Blob x;
9b7a6f8… drh 1109 int i, j;
9b7a6f8… drh 1110 blob_zero(&x);
9b7a6f8… drh 1111 while( zInput[0] ){
9b7a6f8… drh 1112 for(i=0; zInput[i] && zInput[i]!='%'; i++){}
9b7a6f8… drh 1113 if( i>0 ){
9b7a6f8… drh 1114 blob_append(&x, zInput, i);
9b7a6f8… drh 1115 zInput += i;
9b7a6f8… drh 1116 }
60f0986… drh 1117 if( zInput[0]==0 ) break;
9b7a6f8… drh 1118 for(j=0; j<nSubst; j+=2){
9b7a6f8… drh 1119 int n = strlen(azSubst[j]);
60f0986… drh 1120 if( strncmp(zInput, azSubst[j], n)==0 ){
9b7a6f8… drh 1121 blob_append(&x, azSubst[j+1], -1);
9b7a6f8… drh 1122 zInput += n;
9b7a6f8… drh 1123 break;
9b7a6f8… drh 1124 }
9b7a6f8… drh 1125 }
9b7a6f8… drh 1126 if( j>=nSubst ){
9b7a6f8… drh 1127 blob_append(&x, "%", 1);
9b7a6f8… drh 1128 zInput++;
9b7a6f8… drh 1129 }
9b7a6f8… drh 1130 }
9b7a6f8… drh 1131 return blob_str(&x);
b9abb86… drh 1132 }
b9abb86… drh 1133
cd2c0e4… drh 1134 #if INTERFACE
cd2c0e4… drh 1135 /*
cd2c0e4… drh 1136 ** Flags to the 3-way merger
cd2c0e4… drh 1137 */
1cf0d37… drh 1138 #define MERGE_DRYRUN 0x0001
d20ead1… drh 1139 /*
d20ead1… drh 1140 ** The MERGE_KEEP_FILES flag specifies that merge_3way() should retain
d20ead1… drh 1141 ** its temporary files on error. By default they are removed after the
d20ead1… drh 1142 ** merge, regardless of success or failure.
d20ead1… drh 1143 */
1cf0d37… drh 1144 #define MERGE_KEEP_FILES 0x0002
cd2c0e4… drh 1145 #endif
cd2c0e4… drh 1146
9b7a6f8… drh 1147
b9abb86… drh 1148 /*
aa82965… drh 1149 ** This routine is a wrapper around merge_three_blobs() with the following
9b7a6f8… drh 1150 ** enhancements:
b9abb86… drh 1151 **
b9abb86… drh 1152 ** (1) If the merge-command is defined, then use the external merging
b9abb86… drh 1153 ** program specified instead of the built-in blob-merge to do the
b9abb86… drh 1154 ** merging. Panic if the external merger fails.
9b7a6f8… drh 1155 ** ** Not currently implemented **
b9abb86… drh 1156 **
b9abb86… drh 1157 ** (2) If gmerge-command is defined and there are merge conflicts in
aa82965… drh 1158 ** merge_three_blobs() then invoke the external graphical merger
aa82965… drh 1159 ** to resolve the conflicts.
b9abb86… drh 1160 **
9b7a6f8… drh 1161 ** (3) If a merge conflict occurs and gmerge-command is not defined,
9b7a6f8… drh 1162 ** then write the pivot, original, and merge-in files to the
9b7a6f8… drh 1163 ** filesystem.
b9abb86… drh 1164 */
b9abb86… drh 1165 int merge_3way(
b9abb86… drh 1166 Blob *pPivot, /* Common ancestor (older) */
b9abb86… drh 1167 const char *zV1, /* Name of file for version merging into (mine) */
b9abb86… drh 1168 Blob *pV2, /* Version merging from (yours) */
cd2c0e4… drh 1169 Blob *pOut, /* Output written here */
cd2c0e4… drh 1170 unsigned mergeFlags /* Flags that control operation */
cd2c0e4… drh 1171 ){
aa82965… drh 1172 Blob v1; /* Content of zV1 */
aa82965… drh 1173 int rc; /* Return code of subroutines and this routine */
d20ead1… drh 1174 const char *zGMerge; /* Name of the gmerge command */
aa82965… drh 1175 MergeBuilder s; /* The merge state */
233463c… jan.nijtmans 1176
aa82965… drh 1177 mergebuilder_init_text(&s);
aa82965… drh 1178 s.pPivot = pPivot;
aa82965… drh 1179 s.pV1 = &v1;
aa82965… drh 1180 s.pV2 = pV2;
aa82965… drh 1181 blob_zero(pOut);
aa82965… drh 1182 s.pOut = pOut;
aa82965… drh 1183 blob_read_from_file(s.pV1, zV1, ExtFILE);
aa82965… drh 1184 rc = merge_three_blobs(&s);
d20ead1… drh 1185 zGMerge = rc<=0 ? 0 : db_get("gmerge-command", 0);
d20ead1… drh 1186 if( (mergeFlags & MERGE_DRYRUN)==0
d20ead1… drh 1187 && ((zGMerge!=0 && zGMerge[0]!=0)
d20ead1… drh 1188 || (rc!=0 && (mergeFlags & MERGE_KEEP_FILES)!=0)) ){
233463c… jan.nijtmans 1189 char *zPivot; /* Name of the pivot file */
233463c… jan.nijtmans 1190 char *zOrig; /* Name of the original content file */
233463c… jan.nijtmans 1191 char *zOther; /* Name of the merge file */
233463c… jan.nijtmans 1192
233463c… jan.nijtmans 1193 zPivot = file_newname(zV1, "baseline", 1);
aa82965… drh 1194 blob_write_to_file(s.pPivot, zPivot);
233463c… jan.nijtmans 1195 zOrig = file_newname(zV1, "original", 1);
aa82965… drh 1196 blob_write_to_file(s.pV1, zOrig);
233463c… jan.nijtmans 1197 zOther = file_newname(zV1, "merge", 1);
aa82965… drh 1198 blob_write_to_file(s.pV2, zOther);
233463c… jan.nijtmans 1199 if( rc>0 ){
233463c… jan.nijtmans 1200 if( zGMerge && zGMerge[0] ){
233463c… jan.nijtmans 1201 char *zOut; /* Temporary output file */
233463c… jan.nijtmans 1202 char *zCmd; /* Command to invoke */
233463c… jan.nijtmans 1203 const char *azSubst[8]; /* Strings to be substituted */
233463c… jan.nijtmans 1204 zOut = file_newname(zV1, "output", 1);
233463c… jan.nijtmans 1205 azSubst[0] = "%baseline"; azSubst[1] = zPivot;
233463c… jan.nijtmans 1206 azSubst[2] = "%original"; azSubst[3] = zOrig;
233463c… jan.nijtmans 1207 azSubst[4] = "%merge"; azSubst[5] = zOther;
233463c… jan.nijtmans 1208 azSubst[6] = "%output"; azSubst[7] = zOut;
233463c… jan.nijtmans 1209 zCmd = string_subst(zGMerge, 8, azSubst);
233463c… jan.nijtmans 1210 printf("%s\n", zCmd); fflush(stdout);
233463c… jan.nijtmans 1211 fossil_system(zCmd);
1772357… drh 1212 if( file_size(zOut, RepoFILE)>=0 ){
1772357… drh 1213 blob_read_from_file(pOut, zOut, ExtFILE);
233463c… jan.nijtmans 1214 file_delete(zOut);
233463c… jan.nijtmans 1215 }
233463c… jan.nijtmans 1216 fossil_free(zCmd);
233463c… jan.nijtmans 1217 fossil_free(zOut);
233463c… jan.nijtmans 1218 }
233463c… jan.nijtmans 1219 }
d20ead1… drh 1220 if( (mergeFlags & MERGE_KEEP_FILES)==0 ){
d20ead1… drh 1221 file_delete(zPivot);
d20ead1… drh 1222 file_delete(zOrig);
d20ead1… drh 1223 file_delete(zOther);
d20ead1… drh 1224 }
9b7a6f8… drh 1225 fossil_free(zPivot);
9b7a6f8… drh 1226 fossil_free(zOrig);
9b7a6f8… drh 1227 fossil_free(zOther);
9b7a6f8… drh 1228 }
337617b… drh 1229 blob_reset(&v1);
aa82965… drh 1230 s.xDestroy(&s);
b9abb86… drh 1231 return rc;
dbda8d6… drh 1232 }

Keyboard Shortcuts

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