Fossil SCM

Improve the merge command's ability to handle various scenarios involving renames.

joel 2016-05-06 19:49 UTC trunk
Commit 423029b153bed49bb48cf9f26f24dfba321eac6e
+200 -130
--- src/merge.c
+++ src/merge.c
@@ -128,10 +128,46 @@
128128
}
129129
}
130130
db_finalize(&q);
131131
return fForkSeen;
132132
}
133
+
134
+/*
135
+** Add an entry to the FV table for all files renamed between
136
+** version N and the version specified by vid.
137
+*/
138
+static void add_renames(
139
+ const char *zFnCol, /* The FV column for the filename in vid */
140
+ int vid, /* The desired version's RID */
141
+ int nid, /* Version N's RID */
142
+ int revOk, /* Ok to move backwards (child->parent) if true */
143
+ const char *zDebug /* Generate trace output if not NULL */
144
+){
145
+ int nChng; /* Number of file name changes */
146
+ int *aChng; /* An array of file name changes */
147
+ int i; /* Loop counter */
148
+ find_filename_changes(nid, vid, revOk, &nChng, &aChng, zDebug);
149
+ if( nChng==0 ) return;
150
+ for(i=0; i<nChng; i++){
151
+ char *zN, *zV;
152
+ zN = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2]);
153
+ zV = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2+1]);
154
+ db_multi_exec(
155
+ "INSERT OR IGNORE INTO fv(%s,fnn) VALUES(%Q,%Q)",
156
+ zFnCol /*safe-for-%s*/, zV, zN
157
+ );
158
+ if( db_changes()==0 ){
159
+ db_multi_exec(
160
+ "UPDATE fv SET %s=%Q WHERE fnn=%Q",
161
+ zFnCol /*safe-for-%s*/, zV, zN
162
+ );
163
+ }
164
+ free(zN);
165
+ free(zV);
166
+ }
167
+ free(aChng);
168
+}
133169
134170
/*
135171
** COMMAND: merge
136172
**
137173
** Usage: %fossil merge ?OPTIONS? ?VERSION?
@@ -178,10 +214,11 @@
178214
*/
179215
void merge_cmd(void){
180216
int vid; /* Current version "V" */
181217
int mid; /* Version we are merging from "M" */
182218
int pid; /* The pivot version - most recent common ancestor P */
219
+ int nid = 0; /* The name pivot version "N" */
183220
int verboseFlag; /* True if the -v|--verbose option is present */
184221
int integrateFlag; /* True if the --integrate option is present */
185222
int pickFlag; /* True if the --cherrypick option is present */
186223
int backoutFlag; /* True if the --backout option is present */
187224
int dryRunFlag; /* True if the --dry-run or -n option is present */
@@ -188,13 +225,10 @@
188225
int forceFlag; /* True if the --force or -f option is present */
189226
int forceMissingFlag; /* True if the --force-missing option is present */
190227
const char *zBinGlob; /* The value of --binary */
191228
const char *zPivot; /* The value of --baseline */
192229
int debugFlag; /* True if --debug is present */
193
- int nChng; /* Number of file name changes */
194
- int *aChng; /* An array of file name changes */
195
- int i; /* Loop counter */
196230
int nConflict = 0; /* Number of conflicts seen */
197231
int nOverwrite = 0; /* Number of unmanaged files overwritten */
198232
Stmt q;
199233
200234
@@ -201,10 +235,11 @@
201235
/* Notation:
202236
**
203237
** V The current checkout
204238
** M The version being merged in
205239
** P The "pivot" - the most recent common ancestor of V and M.
240
+ ** N The "name pivot" - for detecting renames
206241
*/
207242
208243
undo_capture_command_line();
209244
verboseFlag = find_option("verbose","v",0)!=0;
210245
forceMissingFlag = find_option("force-missing",0,0)!=0;
@@ -291,11 +326,12 @@
291326
fossil_fatal("not a version: %s", zPivot);
292327
}
293328
if( pickFlag ){
294329
fossil_fatal("incompatible options: --cherrypick & --baseline");
295330
}
296
- }else if( pickFlag || backoutFlag ){
331
+ }
332
+ if( pickFlag || backoutFlag ){
297333
if( integrateFlag ){
298334
fossil_fatal("incompatible options: --integrate & --cherrypick or --backout");
299335
}
300336
pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid);
301337
if( pid<=0 ){
@@ -307,21 +343,25 @@
307343
db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0");
308344
while( db_step(&q)==SQLITE_ROW ){
309345
pivot_set_secondary(db_column_int(&q,0));
310346
}
311347
db_finalize(&q);
312
- pid = pivot_find();
313
- if( pid<=0 ){
314
- fossil_fatal("cannot find a common ancestor between the current "
315
- "checkout and %s", g.argv[2]);
348
+ if( !zPivot ){
349
+ pid = pivot_find(0);
350
+ if( pid<=0 ){
351
+ fossil_fatal("cannot find a common ancestor between the current "
352
+ "checkout and %s", g.argv[2]);
353
+ }
316354
}
355
+ nid = pivot_find(1);
317356
}
318357
if( backoutFlag ){
319358
int t = pid;
320359
pid = mid;
321360
mid = t;
322361
}
362
+ if( nid==0 ) nid = pid;
323363
if( !is_a_version(pid) ){
324364
fossil_fatal("not a version: record #%d", pid);
325365
}
326366
if( !forceFlag && mid==pid ){
327367
fossil_print("Merge skipped because it is a no-op. "
@@ -345,10 +385,12 @@
345385
if( load_vfile_from_rid(pid) && !forceMissingFlag ){
346386
fossil_fatal("missing content, unable to merge");
347387
}
348388
if( debugFlag ){
349389
char *z;
390
+ z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nid);
391
+ fossil_print("N=%d %z\n", nid, z);
350392
z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pid);
351393
fossil_print("P=%d %z\n", pid, z);
352394
z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
353395
fossil_print("M=%d %z\n", mid, z);
354396
z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
@@ -361,115 +403,102 @@
361403
** in the current checkout, the pivot, and the version being merged.
362404
*/
363405
db_multi_exec(
364406
"DROP TABLE IF EXISTS fv;"
365407
"CREATE TEMP TABLE fv("
366
- " fn TEXT PRIMARY KEY %s," /* The filename */
367
- " idv INTEGER," /* VFILE entry for current version */
368
- " idp INTEGER," /* VFILE entry for the pivot */
369
- " idm INTEGER," /* VFILE entry for version merging in */
408
+ " fn TEXT UNIQUE %s," /* The filename */
409
+ " idv INTEGER DEFAULT 0," /* VFILE entry for current version */
410
+ " idp INTEGER DEFAULT 0," /* VFILE entry for the pivot */
411
+ " idm INTEGER DEFAULT 0," /* VFILE entry for version merging in */
370412
" chnged BOOLEAN," /* True if current version has been edited */
371
- " ridv INTEGER," /* Record ID for current version */
372
- " ridp INTEGER," /* Record ID for pivot */
373
- " ridm INTEGER," /* Record ID for merge */
413
+ " ridv INTEGER DEFAULT 0," /* Record ID for current version */
414
+ " ridp INTEGER DEFAULT 0," /* Record ID for pivot */
415
+ " ridm INTEGER DEFAULT 0," /* Record ID for merge */
374416
" isexe BOOLEAN," /* Execute permission enabled */
375
- " fnp TEXT %s," /* The filename in the pivot */
376
- " fnm TEXT %s," /* the filename in the merged version */
417
+ " fnp TEXT UNIQUE %s," /* The filename in the pivot */
418
+ " fnm TEXT UNIQUE %s," /* The filename in the merged version */
419
+ " fnn TEXT UNIQUE %s," /* The filename in the name pivot */
377420
" islinkv BOOLEAN," /* True if current version is a symlink */
378421
" islinkm BOOLEAN" /* True if merged version in is a symlink */
379422
");",
380
- filename_collation(), filename_collation(), filename_collation()
423
+ filename_collation(), filename_collation(), filename_collation(),
424
+ filename_collation()
381425
);
382426
383
- /* Add files found in V
427
+ /*
428
+ ** Compute name changes from N to V, P, and M
429
+ */
430
+ add_renames("fn", vid, nid, 0, debugFlag ? "N->V" : 0);
431
+ add_renames("fnp", pid, nid, 0, debugFlag ? "N->P" : 0);
432
+ add_renames("fnm", mid, nid, backoutFlag, debugFlag ? "N->M" : 0);
433
+
434
+ /*
435
+ ** Add files found in V
384436
*/
385437
db_multi_exec(
386
- "INSERT OR IGNORE"
387
- " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
388
- " SELECT pathname, pathname, pathname, id, 0, 0, rid, 0, 0, isexe, chnged "
389
- " FROM vfile WHERE vid=%d",
438
+ "UPDATE OR IGNORE fv SET fn=coalesce(fnp,fnn) WHERE fn IS NULL;"
439
+ "REPLACE INTO fv(fn,fnp,fnm,fnn,idv,ridv,islinkv,isexe,chnged)"
440
+ " SELECT pathname, fnp, fnm, fnn, id, rid, islink, vf.isexe, vf.chnged"
441
+ " FROM vfile vf"
442
+ " LEFT JOIN fv ON fn=coalesce(origname,pathname)"
443
+ " AND rid>0 AND vf.chnged NOT IN (3,5)"
444
+ " WHERE vid=%d;",
390445
vid
391446
);
392447
393448
/*
394
- ** Compute name changes from P->V
395
- */
396
- find_filename_changes(pid, vid, 0, &nChng, &aChng, debugFlag ? "P->V" : 0);
397
- if( nChng ){
398
- for(i=0; i<nChng; i++){
399
- char *z;
400
- z = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2]);
401
- db_multi_exec(
402
- "UPDATE fv SET fnp=%Q, fnm=%Q"
403
- " WHERE fn=(SELECT name FROM filename WHERE fnid=%d)",
404
- z, z, aChng[i*2+1]
405
- );
406
- free(z);
407
- }
408
- fossil_free(aChng);
409
- db_multi_exec("UPDATE fv SET fnm=fnp WHERE fnp!=fn");
410
- }
411
-
412
- /* Add files found in P but not in V
413
- */
414
- db_multi_exec(
415
- "INSERT OR IGNORE"
416
- " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
417
- " SELECT pathname, pathname, pathname, 0, 0, 0, 0, 0, 0, isexe, 0 "
418
- " FROM vfile"
419
- " WHERE vid=%d AND pathname %s NOT IN (SELECT fnp FROM fv)",
420
- pid, filename_collation()
421
- );
422
-
423
- /*
424
- ** Compute name changes from P->M
425
- */
426
- find_filename_changes(pid, mid, 0, &nChng, &aChng, debugFlag ? "P->M" : 0);
427
- if( nChng ){
428
- if( nChng>4 ) db_multi_exec("CREATE INDEX fv_fnp ON fv(fnp)");
429
- for(i=0; i<nChng; i++){
430
- db_multi_exec(
431
- "UPDATE fv SET fnm=(SELECT name FROM filename WHERE fnid=%d)"
432
- " WHERE fnp=(SELECT name FROM filename WHERE fnid=%d)",
433
- aChng[i*2+1], aChng[i*2]
434
- );
435
- }
436
- fossil_free(aChng);
437
- }
438
-
439
- /* Add files found in M but not in P or V.
440
- */
441
- db_multi_exec(
442
- "INSERT OR IGNORE"
443
- " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
444
- " SELECT pathname, pathname, pathname, 0, 0, 0, 0, 0, 0, isexe, 0 "
445
- " FROM vfile"
446
- " WHERE vid=%d"
447
- " AND pathname %s NOT IN (SELECT fnp FROM fv UNION SELECT fnm FROM fv)",
448
- mid, filename_collation()
449
- );
450
-
451
- /*
452
- ** Compute the file version ids for P and M.
453
- */
449
+ ** Add files found in P
450
+ */
451
+ db_multi_exec(
452
+ "UPDATE OR IGNORE fv SET fnp=coalesce(fnn,"
453
+ " (SELECT coalesce(origname,pathname) FROM vfile WHERE id=idv))"
454
+ " WHERE fnp IS NULL;"
455
+ "INSERT OR IGNORE INTO fv(fnp)"
456
+ " SELECT coalesce(origname,pathname) FROM vfile WHERE vid=%d;",
457
+ pid
458
+ );
459
+
460
+ /*
461
+ ** Add files found in M
462
+ */
463
+ db_multi_exec(
464
+ "UPDATE OR IGNORE fv SET fnm=fnp WHERE fnm IS NULL;"
465
+ "INSERT OR IGNORE INTO fv(fnm)"
466
+ " SELECT pathname FROM vfile WHERE vid=%d;",
467
+ mid
468
+ );
469
+
470
+ /*
471
+ ** Compute the file version ids for P and M
472
+ */
473
+ if( pid==vid ){
474
+ db_multi_exec(
475
+ "UPDATE fv SET idp=idv, ridp=ridv WHERE ridv>0 AND chnged NOT IN (3,5)"
476
+ );
477
+ }else{
478
+ db_multi_exec(
479
+ "UPDATE fv SET"
480
+ " idp=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnp=pathname),0),"
481
+ " ridp=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnp=pathname),0)",
482
+ pid, pid
483
+ );
484
+ }
454485
db_multi_exec(
455486
"UPDATE fv SET"
456
- " idp=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnp=pathname),0),"
457
- " ridp=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnp=pathname),0),"
458487
" idm=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnm=pathname),0),"
459488
" ridm=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnm=pathname),0),"
460
- " islinkv=coalesce((SELECT islink FROM vfile"
489
+ " islinkm=coalesce((SELECT islink FROM vfile"
461490
" WHERE vid=%d AND fnm=pathname),0),"
462
- " islinkm=coalesce((SELECT islink FROM vfile"
463
- " WHERE vid=%d AND fnm=pathname),0)",
464
- pid, pid, mid, mid, vid, mid
491
+ " isexe=coalesce(isexe,"
492
+ " (SELECT isexe FROM vfile WHERE vid=%d AND fnm=pathname))",
493
+ mid, mid, mid, mid
465494
);
466495
467496
if( debugFlag ){
468497
db_prepare(&q,
469498
"SELECT rowid, fn, fnp, fnm, chnged, ridv, ridp, ridm, "
470
- " isexe, islinkv, islinkm FROM fv"
499
+ " isexe, islinkv, islinkm, fnn FROM fv"
471500
);
472501
while( db_step(&q)==SQLITE_ROW ){
473502
fossil_print("%3d: ridv=%-4d ridp=%-4d ridm=%-4d chnged=%d isexe=%d "
474503
" islinkv=%d islinkm=%d\n",
475504
db_column_int(&q, 0),
@@ -481,10 +510,11 @@
481510
db_column_int(&q, 9),
482511
db_column_int(&q, 10));
483512
fossil_print(" fn = [%s]\n", db_column_text(&q, 1));
484513
fossil_print(" fnp = [%s]\n", db_column_text(&q, 2));
485514
fossil_print(" fnm = [%s]\n", db_column_text(&q, 3));
515
+ fossil_print(" fnn = [%s]\n", db_column_text(&q, 11));
486516
}
487517
db_finalize(&q);
488518
}
489519
490520
/*
@@ -500,46 +530,10 @@
500530
char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm);
501531
fossil_warning("WARNING: no common ancestor for %s", zName);
502532
free(zName);
503533
db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm);
504534
}
505
- db_finalize(&q);
506
-
507
- /*
508
- ** Add to V files that are not in V or P but are in M
509
- */
510
- db_prepare(&q,
511
- "SELECT idm, rowid, fnm FROM fv AS x"
512
- " WHERE idp=0 AND idv=0 AND idm>0"
513
- );
514
- while( db_step(&q)==SQLITE_ROW ){
515
- int idm = db_column_int(&q, 0);
516
- int rowid = db_column_int(&q, 1);
517
- int idv;
518
- const char *zName;
519
- char *zFullName;
520
- db_multi_exec(
521
- "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,isexe,islink,pathname)"
522
- " SELECT %d,%d,0,rid,mrid,isexe,islink,pathname FROM vfile WHERE id=%d",
523
- vid, integrateFlag?5:3, idm
524
- );
525
- idv = db_last_insert_rowid();
526
- db_multi_exec("UPDATE fv SET idv=%d WHERE rowid=%d", idv, rowid);
527
- zName = db_column_text(&q, 2);
528
- zFullName = mprintf("%s%s", g.zLocalRoot, zName);
529
- if( file_wd_isfile_or_link(zFullName) ){
530
- fossil_print("ADDED %s (overwrites an unmanaged file)\n", zName);
531
- nOverwrite++;
532
- }else{
533
- fossil_print("ADDED %s\n", zName);
534
- }
535
- fossil_free(zFullName);
536
- if( !dryRunFlag ){
537
- undo_save(zName);
538
- vfile_to_disk(0, idm, 0, 0);
539
- }
540
- }
541535
db_finalize(&q);
542536
543537
/*
544538
** Find files that have changed from P->M but not P->V.
545539
** Copy the M content over into V.
@@ -661,45 +655,121 @@
661655
free(zFullPath);
662656
}
663657
}
664658
db_finalize(&q);
665659
660
+ /* For certain sets of renames (e.g. A -> B and B -> A), a file that is
661
+ ** being renamed must first be moved to a temporary location to avoid
662
+ ** being overwritten by another rename operation. A row is added to the
663
+ ** TMPRN table for each of these temporary renames.
664
+ */
665
+ db_multi_exec(
666
+ "DROP TABLE IF EXISTS tmprn;"
667
+ "CREATE TEMP TABLE tmprn(fn UNIQUE, tmpfn);"
668
+ );
669
+
666670
/*
667671
** Rename files that have taken a rename on P->M but which keep the same
668672
** name on P->V. If a file is renamed on P->V only or on both P->V and
669673
** P->M then we retain the V name of the file.
670674
*/
671675
db_prepare(&q,
672
- "SELECT idv, fnp, fnm FROM fv"
676
+ "SELECT idv, fnp, fnm, isexe FROM fv"
673677
" WHERE idv>0 AND idp>0 AND idm>0 AND fnp=fn AND fnm!=fnp"
674678
);
675679
while( db_step(&q)==SQLITE_ROW ){
676680
int idv = db_column_int(&q, 0);
677681
const char *zOldName = db_column_text(&q, 1);
678682
const char *zNewName = db_column_text(&q, 2);
683
+ int isExe = db_column_int(&q, 3);
679684
fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
680685
if( !dryRunFlag ) undo_save(zOldName);
681686
if( !dryRunFlag ) undo_save(zNewName);
682687
db_multi_exec(
688
+ "UPDATE vfile SET pathname=NULL, origname=pathname"
689
+ " WHERE vid=%d AND pathname=%Q;"
683690
"UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)"
684
- " WHERE id=%d AND vid=%d", zNewName, idv, vid
691
+ " WHERE id=%d;",
692
+ vid, zNewName, zNewName, idv
685693
);
686694
if( !dryRunFlag ){
687
- char *zFullOldPath = mprintf("%s%s", g.zLocalRoot, zOldName);
688
- char *zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
695
+ char *zFullOldPath, *zFullNewPath;
696
+ zFullOldPath = db_text(0,"SELECT tmpfn FROM tmprn WHERE fn=%Q", zOldName);
697
+ if( !zFullOldPath ){
698
+ zFullOldPath = mprintf("%s%s", g.zLocalRoot, zOldName);
699
+ }
700
+ zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
701
+ if( file_wd_size(zFullNewPath)>=0 ){
702
+ char zTmpPath[300];
703
+ file_tempname(sizeof(zTmpPath), zTmpPath);
704
+ db_multi_exec("INSERT INTO tmprn(fn,tmpfn) VALUES(%Q,%Q)",
705
+ zNewName, zTmpPath);
706
+ if( file_wd_islink(zFullNewPath) ){
707
+ symlink_copy(zFullNewPath, zTmpPath);
708
+ }else{
709
+ file_copy(zFullNewPath, zTmpPath);
710
+ }
711
+ }
689712
if( file_wd_islink(zFullOldPath) ){
690713
symlink_copy(zFullOldPath, zFullNewPath);
691714
}else{
692715
file_copy(zFullOldPath, zFullNewPath);
693716
}
717
+ file_wd_setexe(zFullNewPath, isExe);
694718
file_delete(zFullOldPath);
695719
free(zFullNewPath);
696720
free(zFullOldPath);
697721
}
698722
}
699723
db_finalize(&q);
700724
725
+ /* A file that has been deleted and replaced by a renamed file will have a
726
+ ** NULL pathname. Change it to something that makes the output of "status"
727
+ ** and similar commands make sense for such files and that will (most likely)
728
+ ** not be an actual existing pathname.
729
+ */
730
+ db_multi_exec(
731
+ "UPDATE vfile SET pathname=origname || ' (overwritten by rename)'"
732
+ " WHERE pathname IS NULL"
733
+ );
734
+
735
+ /*
736
+ ** Add to V files that are not in V or P but are in M
737
+ */
738
+ db_prepare(&q,
739
+ "SELECT idm, rowid, fnm FROM fv"
740
+ " WHERE idp=0 AND idv=0 AND idm>0"
741
+ );
742
+ while( db_step(&q)==SQLITE_ROW ){
743
+ int idm = db_column_int(&q, 0);
744
+ int rowid = db_column_int(&q, 1);
745
+ int idv;
746
+ const char *zName;
747
+ char *zFullName;
748
+ db_multi_exec(
749
+ "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,isexe,islink,pathname)"
750
+ " SELECT %d,%d,0,rid,mrid,isexe,islink,pathname FROM vfile WHERE id=%d",
751
+ vid, integrateFlag?5:3, idm
752
+ );
753
+ idv = db_last_insert_rowid();
754
+ db_multi_exec("UPDATE fv SET idv=%d WHERE rowid=%d", idv, rowid);
755
+ zName = db_column_text(&q, 2);
756
+ zFullName = mprintf("%s%s", g.zLocalRoot, zName);
757
+ if( file_wd_isfile_or_link(zFullName)
758
+ && !db_exists("SELECT 1 FROM fv WHERE fn=%Q", zName) ){
759
+ fossil_print("ADDED %s (overwrites an unmanaged file)\n", zName);
760
+ nOverwrite++;
761
+ }else{
762
+ fossil_print("ADDED %s\n", zName);
763
+ }
764
+ fossil_free(zFullName);
765
+ if( !dryRunFlag ){
766
+ undo_save(zName);
767
+ vfile_to_disk(0, idm, 0, 0);
768
+ }
769
+ }
770
+ db_finalize(&q);
701771
702772
/* Report on conflicts
703773
*/
704774
if( nConflict ){
705775
fossil_warning("WARNING: %d merge conflicts", nConflict);
706776
--- src/merge.c
+++ src/merge.c
@@ -128,10 +128,46 @@
128 }
129 }
130 db_finalize(&q);
131 return fForkSeen;
132 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
134 /*
135 ** COMMAND: merge
136 **
137 ** Usage: %fossil merge ?OPTIONS? ?VERSION?
@@ -178,10 +214,11 @@
178 */
179 void merge_cmd(void){
180 int vid; /* Current version "V" */
181 int mid; /* Version we are merging from "M" */
182 int pid; /* The pivot version - most recent common ancestor P */
 
183 int verboseFlag; /* True if the -v|--verbose option is present */
184 int integrateFlag; /* True if the --integrate option is present */
185 int pickFlag; /* True if the --cherrypick option is present */
186 int backoutFlag; /* True if the --backout option is present */
187 int dryRunFlag; /* True if the --dry-run or -n option is present */
@@ -188,13 +225,10 @@
188 int forceFlag; /* True if the --force or -f option is present */
189 int forceMissingFlag; /* True if the --force-missing option is present */
190 const char *zBinGlob; /* The value of --binary */
191 const char *zPivot; /* The value of --baseline */
192 int debugFlag; /* True if --debug is present */
193 int nChng; /* Number of file name changes */
194 int *aChng; /* An array of file name changes */
195 int i; /* Loop counter */
196 int nConflict = 0; /* Number of conflicts seen */
197 int nOverwrite = 0; /* Number of unmanaged files overwritten */
198 Stmt q;
199
200
@@ -201,10 +235,11 @@
201 /* Notation:
202 **
203 ** V The current checkout
204 ** M The version being merged in
205 ** P The "pivot" - the most recent common ancestor of V and M.
 
206 */
207
208 undo_capture_command_line();
209 verboseFlag = find_option("verbose","v",0)!=0;
210 forceMissingFlag = find_option("force-missing",0,0)!=0;
@@ -291,11 +326,12 @@
291 fossil_fatal("not a version: %s", zPivot);
292 }
293 if( pickFlag ){
294 fossil_fatal("incompatible options: --cherrypick & --baseline");
295 }
296 }else if( pickFlag || backoutFlag ){
 
297 if( integrateFlag ){
298 fossil_fatal("incompatible options: --integrate & --cherrypick or --backout");
299 }
300 pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid);
301 if( pid<=0 ){
@@ -307,21 +343,25 @@
307 db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0");
308 while( db_step(&q)==SQLITE_ROW ){
309 pivot_set_secondary(db_column_int(&q,0));
310 }
311 db_finalize(&q);
312 pid = pivot_find();
313 if( pid<=0 ){
314 fossil_fatal("cannot find a common ancestor between the current "
315 "checkout and %s", g.argv[2]);
 
 
316 }
 
317 }
318 if( backoutFlag ){
319 int t = pid;
320 pid = mid;
321 mid = t;
322 }
 
323 if( !is_a_version(pid) ){
324 fossil_fatal("not a version: record #%d", pid);
325 }
326 if( !forceFlag && mid==pid ){
327 fossil_print("Merge skipped because it is a no-op. "
@@ -345,10 +385,12 @@
345 if( load_vfile_from_rid(pid) && !forceMissingFlag ){
346 fossil_fatal("missing content, unable to merge");
347 }
348 if( debugFlag ){
349 char *z;
 
 
350 z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pid);
351 fossil_print("P=%d %z\n", pid, z);
352 z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
353 fossil_print("M=%d %z\n", mid, z);
354 z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
@@ -361,115 +403,102 @@
361 ** in the current checkout, the pivot, and the version being merged.
362 */
363 db_multi_exec(
364 "DROP TABLE IF EXISTS fv;"
365 "CREATE TEMP TABLE fv("
366 " fn TEXT PRIMARY KEY %s," /* The filename */
367 " idv INTEGER," /* VFILE entry for current version */
368 " idp INTEGER," /* VFILE entry for the pivot */
369 " idm INTEGER," /* VFILE entry for version merging in */
370 " chnged BOOLEAN," /* True if current version has been edited */
371 " ridv INTEGER," /* Record ID for current version */
372 " ridp INTEGER," /* Record ID for pivot */
373 " ridm INTEGER," /* Record ID for merge */
374 " isexe BOOLEAN," /* Execute permission enabled */
375 " fnp TEXT %s," /* The filename in the pivot */
376 " fnm TEXT %s," /* the filename in the merged version */
 
377 " islinkv BOOLEAN," /* True if current version is a symlink */
378 " islinkm BOOLEAN" /* True if merged version in is a symlink */
379 ");",
380 filename_collation(), filename_collation(), filename_collation()
 
381 );
382
383 /* Add files found in V
 
 
 
 
 
 
 
 
384 */
385 db_multi_exec(
386 "INSERT OR IGNORE"
387 " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
388 " SELECT pathname, pathname, pathname, id, 0, 0, rid, 0, 0, isexe, chnged "
389 " FROM vfile WHERE vid=%d",
 
 
 
390 vid
391 );
392
393 /*
394 ** Compute name changes from P->V
395 */
396 find_filename_changes(pid, vid, 0, &nChng, &aChng, debugFlag ? "P->V" : 0);
397 if( nChng ){
398 for(i=0; i<nChng; i++){
399 char *z;
400 z = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2]);
401 db_multi_exec(
402 "UPDATE fv SET fnp=%Q, fnm=%Q"
403 " WHERE fn=(SELECT name FROM filename WHERE fnid=%d)",
404 z, z, aChng[i*2+1]
405 );
406 free(z);
407 }
408 fossil_free(aChng);
409 db_multi_exec("UPDATE fv SET fnm=fnp WHERE fnp!=fn");
410 }
411
412 /* Add files found in P but not in V
413 */
414 db_multi_exec(
415 "INSERT OR IGNORE"
416 " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
417 " SELECT pathname, pathname, pathname, 0, 0, 0, 0, 0, 0, isexe, 0 "
418 " FROM vfile"
419 " WHERE vid=%d AND pathname %s NOT IN (SELECT fnp FROM fv)",
420 pid, filename_collation()
421 );
422
423 /*
424 ** Compute name changes from P->M
425 */
426 find_filename_changes(pid, mid, 0, &nChng, &aChng, debugFlag ? "P->M" : 0);
427 if( nChng ){
428 if( nChng>4 ) db_multi_exec("CREATE INDEX fv_fnp ON fv(fnp)");
429 for(i=0; i<nChng; i++){
430 db_multi_exec(
431 "UPDATE fv SET fnm=(SELECT name FROM filename WHERE fnid=%d)"
432 " WHERE fnp=(SELECT name FROM filename WHERE fnid=%d)",
433 aChng[i*2+1], aChng[i*2]
434 );
435 }
436 fossil_free(aChng);
437 }
438
439 /* Add files found in M but not in P or V.
440 */
441 db_multi_exec(
442 "INSERT OR IGNORE"
443 " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
444 " SELECT pathname, pathname, pathname, 0, 0, 0, 0, 0, 0, isexe, 0 "
445 " FROM vfile"
446 " WHERE vid=%d"
447 " AND pathname %s NOT IN (SELECT fnp FROM fv UNION SELECT fnm FROM fv)",
448 mid, filename_collation()
449 );
450
451 /*
452 ** Compute the file version ids for P and M.
453 */
454 db_multi_exec(
455 "UPDATE fv SET"
456 " idp=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnp=pathname),0),"
457 " ridp=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnp=pathname),0),"
458 " idm=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnm=pathname),0),"
459 " ridm=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnm=pathname),0),"
460 " islinkv=coalesce((SELECT islink FROM vfile"
461 " WHERE vid=%d AND fnm=pathname),0),"
462 " islinkm=coalesce((SELECT islink FROM vfile"
463 " WHERE vid=%d AND fnm=pathname),0)",
464 pid, pid, mid, mid, vid, mid
465 );
466
467 if( debugFlag ){
468 db_prepare(&q,
469 "SELECT rowid, fn, fnp, fnm, chnged, ridv, ridp, ridm, "
470 " isexe, islinkv, islinkm FROM fv"
471 );
472 while( db_step(&q)==SQLITE_ROW ){
473 fossil_print("%3d: ridv=%-4d ridp=%-4d ridm=%-4d chnged=%d isexe=%d "
474 " islinkv=%d islinkm=%d\n",
475 db_column_int(&q, 0),
@@ -481,10 +510,11 @@
481 db_column_int(&q, 9),
482 db_column_int(&q, 10));
483 fossil_print(" fn = [%s]\n", db_column_text(&q, 1));
484 fossil_print(" fnp = [%s]\n", db_column_text(&q, 2));
485 fossil_print(" fnm = [%s]\n", db_column_text(&q, 3));
 
486 }
487 db_finalize(&q);
488 }
489
490 /*
@@ -500,46 +530,10 @@
500 char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm);
501 fossil_warning("WARNING: no common ancestor for %s", zName);
502 free(zName);
503 db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm);
504 }
505 db_finalize(&q);
506
507 /*
508 ** Add to V files that are not in V or P but are in M
509 */
510 db_prepare(&q,
511 "SELECT idm, rowid, fnm FROM fv AS x"
512 " WHERE idp=0 AND idv=0 AND idm>0"
513 );
514 while( db_step(&q)==SQLITE_ROW ){
515 int idm = db_column_int(&q, 0);
516 int rowid = db_column_int(&q, 1);
517 int idv;
518 const char *zName;
519 char *zFullName;
520 db_multi_exec(
521 "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,isexe,islink,pathname)"
522 " SELECT %d,%d,0,rid,mrid,isexe,islink,pathname FROM vfile WHERE id=%d",
523 vid, integrateFlag?5:3, idm
524 );
525 idv = db_last_insert_rowid();
526 db_multi_exec("UPDATE fv SET idv=%d WHERE rowid=%d", idv, rowid);
527 zName = db_column_text(&q, 2);
528 zFullName = mprintf("%s%s", g.zLocalRoot, zName);
529 if( file_wd_isfile_or_link(zFullName) ){
530 fossil_print("ADDED %s (overwrites an unmanaged file)\n", zName);
531 nOverwrite++;
532 }else{
533 fossil_print("ADDED %s\n", zName);
534 }
535 fossil_free(zFullName);
536 if( !dryRunFlag ){
537 undo_save(zName);
538 vfile_to_disk(0, idm, 0, 0);
539 }
540 }
541 db_finalize(&q);
542
543 /*
544 ** Find files that have changed from P->M but not P->V.
545 ** Copy the M content over into V.
@@ -661,45 +655,121 @@
661 free(zFullPath);
662 }
663 }
664 db_finalize(&q);
665
 
 
 
 
 
 
 
 
 
 
666 /*
667 ** Rename files that have taken a rename on P->M but which keep the same
668 ** name on P->V. If a file is renamed on P->V only or on both P->V and
669 ** P->M then we retain the V name of the file.
670 */
671 db_prepare(&q,
672 "SELECT idv, fnp, fnm FROM fv"
673 " WHERE idv>0 AND idp>0 AND idm>0 AND fnp=fn AND fnm!=fnp"
674 );
675 while( db_step(&q)==SQLITE_ROW ){
676 int idv = db_column_int(&q, 0);
677 const char *zOldName = db_column_text(&q, 1);
678 const char *zNewName = db_column_text(&q, 2);
 
679 fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
680 if( !dryRunFlag ) undo_save(zOldName);
681 if( !dryRunFlag ) undo_save(zNewName);
682 db_multi_exec(
 
 
683 "UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)"
684 " WHERE id=%d AND vid=%d", zNewName, idv, vid
 
685 );
686 if( !dryRunFlag ){
687 char *zFullOldPath = mprintf("%s%s", g.zLocalRoot, zOldName);
688 char *zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
689 if( file_wd_islink(zFullOldPath) ){
690 symlink_copy(zFullOldPath, zFullNewPath);
691 }else{
692 file_copy(zFullOldPath, zFullNewPath);
693 }
 
694 file_delete(zFullOldPath);
695 free(zFullNewPath);
696 free(zFullOldPath);
697 }
698 }
699 db_finalize(&q);
700
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
701
702 /* Report on conflicts
703 */
704 if( nConflict ){
705 fossil_warning("WARNING: %d merge conflicts", nConflict);
706
--- src/merge.c
+++ src/merge.c
@@ -128,10 +128,46 @@
128 }
129 }
130 db_finalize(&q);
131 return fForkSeen;
132 }
133
134 /*
135 ** Add an entry to the FV table for all files renamed between
136 ** version N and the version specified by vid.
137 */
138 static void add_renames(
139 const char *zFnCol, /* The FV column for the filename in vid */
140 int vid, /* The desired version's RID */
141 int nid, /* Version N's RID */
142 int revOk, /* Ok to move backwards (child->parent) if true */
143 const char *zDebug /* Generate trace output if not NULL */
144 ){
145 int nChng; /* Number of file name changes */
146 int *aChng; /* An array of file name changes */
147 int i; /* Loop counter */
148 find_filename_changes(nid, vid, revOk, &nChng, &aChng, zDebug);
149 if( nChng==0 ) return;
150 for(i=0; i<nChng; i++){
151 char *zN, *zV;
152 zN = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2]);
153 zV = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2+1]);
154 db_multi_exec(
155 "INSERT OR IGNORE INTO fv(%s,fnn) VALUES(%Q,%Q)",
156 zFnCol /*safe-for-%s*/, zV, zN
157 );
158 if( db_changes()==0 ){
159 db_multi_exec(
160 "UPDATE fv SET %s=%Q WHERE fnn=%Q",
161 zFnCol /*safe-for-%s*/, zV, zN
162 );
163 }
164 free(zN);
165 free(zV);
166 }
167 free(aChng);
168 }
169
170 /*
171 ** COMMAND: merge
172 **
173 ** Usage: %fossil merge ?OPTIONS? ?VERSION?
@@ -178,10 +214,11 @@
214 */
215 void merge_cmd(void){
216 int vid; /* Current version "V" */
217 int mid; /* Version we are merging from "M" */
218 int pid; /* The pivot version - most recent common ancestor P */
219 int nid = 0; /* The name pivot version "N" */
220 int verboseFlag; /* True if the -v|--verbose option is present */
221 int integrateFlag; /* True if the --integrate option is present */
222 int pickFlag; /* True if the --cherrypick option is present */
223 int backoutFlag; /* True if the --backout option is present */
224 int dryRunFlag; /* True if the --dry-run or -n option is present */
@@ -188,13 +225,10 @@
225 int forceFlag; /* True if the --force or -f option is present */
226 int forceMissingFlag; /* True if the --force-missing option is present */
227 const char *zBinGlob; /* The value of --binary */
228 const char *zPivot; /* The value of --baseline */
229 int debugFlag; /* True if --debug is present */
 
 
 
230 int nConflict = 0; /* Number of conflicts seen */
231 int nOverwrite = 0; /* Number of unmanaged files overwritten */
232 Stmt q;
233
234
@@ -201,10 +235,11 @@
235 /* Notation:
236 **
237 ** V The current checkout
238 ** M The version being merged in
239 ** P The "pivot" - the most recent common ancestor of V and M.
240 ** N The "name pivot" - for detecting renames
241 */
242
243 undo_capture_command_line();
244 verboseFlag = find_option("verbose","v",0)!=0;
245 forceMissingFlag = find_option("force-missing",0,0)!=0;
@@ -291,11 +326,12 @@
326 fossil_fatal("not a version: %s", zPivot);
327 }
328 if( pickFlag ){
329 fossil_fatal("incompatible options: --cherrypick & --baseline");
330 }
331 }
332 if( pickFlag || backoutFlag ){
333 if( integrateFlag ){
334 fossil_fatal("incompatible options: --integrate & --cherrypick or --backout");
335 }
336 pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid);
337 if( pid<=0 ){
@@ -307,21 +343,25 @@
343 db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0");
344 while( db_step(&q)==SQLITE_ROW ){
345 pivot_set_secondary(db_column_int(&q,0));
346 }
347 db_finalize(&q);
348 if( !zPivot ){
349 pid = pivot_find(0);
350 if( pid<=0 ){
351 fossil_fatal("cannot find a common ancestor between the current "
352 "checkout and %s", g.argv[2]);
353 }
354 }
355 nid = pivot_find(1);
356 }
357 if( backoutFlag ){
358 int t = pid;
359 pid = mid;
360 mid = t;
361 }
362 if( nid==0 ) nid = pid;
363 if( !is_a_version(pid) ){
364 fossil_fatal("not a version: record #%d", pid);
365 }
366 if( !forceFlag && mid==pid ){
367 fossil_print("Merge skipped because it is a no-op. "
@@ -345,10 +385,12 @@
385 if( load_vfile_from_rid(pid) && !forceMissingFlag ){
386 fossil_fatal("missing content, unable to merge");
387 }
388 if( debugFlag ){
389 char *z;
390 z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nid);
391 fossil_print("N=%d %z\n", nid, z);
392 z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pid);
393 fossil_print("P=%d %z\n", pid, z);
394 z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
395 fossil_print("M=%d %z\n", mid, z);
396 z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
@@ -361,115 +403,102 @@
403 ** in the current checkout, the pivot, and the version being merged.
404 */
405 db_multi_exec(
406 "DROP TABLE IF EXISTS fv;"
407 "CREATE TEMP TABLE fv("
408 " fn TEXT UNIQUE %s," /* The filename */
409 " idv INTEGER DEFAULT 0," /* VFILE entry for current version */
410 " idp INTEGER DEFAULT 0," /* VFILE entry for the pivot */
411 " idm INTEGER DEFAULT 0," /* VFILE entry for version merging in */
412 " chnged BOOLEAN," /* True if current version has been edited */
413 " ridv INTEGER DEFAULT 0," /* Record ID for current version */
414 " ridp INTEGER DEFAULT 0," /* Record ID for pivot */
415 " ridm INTEGER DEFAULT 0," /* Record ID for merge */
416 " isexe BOOLEAN," /* Execute permission enabled */
417 " fnp TEXT UNIQUE %s," /* The filename in the pivot */
418 " fnm TEXT UNIQUE %s," /* The filename in the merged version */
419 " fnn TEXT UNIQUE %s," /* The filename in the name pivot */
420 " islinkv BOOLEAN," /* True if current version is a symlink */
421 " islinkm BOOLEAN" /* True if merged version in is a symlink */
422 ");",
423 filename_collation(), filename_collation(), filename_collation(),
424 filename_collation()
425 );
426
427 /*
428 ** Compute name changes from N to V, P, and M
429 */
430 add_renames("fn", vid, nid, 0, debugFlag ? "N->V" : 0);
431 add_renames("fnp", pid, nid, 0, debugFlag ? "N->P" : 0);
432 add_renames("fnm", mid, nid, backoutFlag, debugFlag ? "N->M" : 0);
433
434 /*
435 ** Add files found in V
436 */
437 db_multi_exec(
438 "UPDATE OR IGNORE fv SET fn=coalesce(fnp,fnn) WHERE fn IS NULL;"
439 "REPLACE INTO fv(fn,fnp,fnm,fnn,idv,ridv,islinkv,isexe,chnged)"
440 " SELECT pathname, fnp, fnm, fnn, id, rid, islink, vf.isexe, vf.chnged"
441 " FROM vfile vf"
442 " LEFT JOIN fv ON fn=coalesce(origname,pathname)"
443 " AND rid>0 AND vf.chnged NOT IN (3,5)"
444 " WHERE vid=%d;",
445 vid
446 );
447
448 /*
449 ** Add files found in P
450 */
451 db_multi_exec(
452 "UPDATE OR IGNORE fv SET fnp=coalesce(fnn,"
453 " (SELECT coalesce(origname,pathname) FROM vfile WHERE id=idv))"
454 " WHERE fnp IS NULL;"
455 "INSERT OR IGNORE INTO fv(fnp)"
456 " SELECT coalesce(origname,pathname) FROM vfile WHERE vid=%d;",
457 pid
458 );
459
460 /*
461 ** Add files found in M
462 */
463 db_multi_exec(
464 "UPDATE OR IGNORE fv SET fnm=fnp WHERE fnm IS NULL;"
465 "INSERT OR IGNORE INTO fv(fnm)"
466 " SELECT pathname FROM vfile WHERE vid=%d;",
467 mid
468 );
469
470 /*
471 ** Compute the file version ids for P and M
472 */
473 if( pid==vid ){
474 db_multi_exec(
475 "UPDATE fv SET idp=idv, ridp=ridv WHERE ridv>0 AND chnged NOT IN (3,5)"
476 );
477 }else{
478 db_multi_exec(
479 "UPDATE fv SET"
480 " idp=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnp=pathname),0),"
481 " ridp=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnp=pathname),0)",
482 pid, pid
483 );
484 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
485 db_multi_exec(
486 "UPDATE fv SET"
 
 
487 " idm=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnm=pathname),0),"
488 " ridm=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnm=pathname),0),"
489 " islinkm=coalesce((SELECT islink FROM vfile"
490 " WHERE vid=%d AND fnm=pathname),0),"
491 " isexe=coalesce(isexe,"
492 " (SELECT isexe FROM vfile WHERE vid=%d AND fnm=pathname))",
493 mid, mid, mid, mid
494 );
495
496 if( debugFlag ){
497 db_prepare(&q,
498 "SELECT rowid, fn, fnp, fnm, chnged, ridv, ridp, ridm, "
499 " isexe, islinkv, islinkm, fnn FROM fv"
500 );
501 while( db_step(&q)==SQLITE_ROW ){
502 fossil_print("%3d: ridv=%-4d ridp=%-4d ridm=%-4d chnged=%d isexe=%d "
503 " islinkv=%d islinkm=%d\n",
504 db_column_int(&q, 0),
@@ -481,10 +510,11 @@
510 db_column_int(&q, 9),
511 db_column_int(&q, 10));
512 fossil_print(" fn = [%s]\n", db_column_text(&q, 1));
513 fossil_print(" fnp = [%s]\n", db_column_text(&q, 2));
514 fossil_print(" fnm = [%s]\n", db_column_text(&q, 3));
515 fossil_print(" fnn = [%s]\n", db_column_text(&q, 11));
516 }
517 db_finalize(&q);
518 }
519
520 /*
@@ -500,46 +530,10 @@
530 char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm);
531 fossil_warning("WARNING: no common ancestor for %s", zName);
532 free(zName);
533 db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm);
534 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
535 db_finalize(&q);
536
537 /*
538 ** Find files that have changed from P->M but not P->V.
539 ** Copy the M content over into V.
@@ -661,45 +655,121 @@
655 free(zFullPath);
656 }
657 }
658 db_finalize(&q);
659
660 /* For certain sets of renames (e.g. A -> B and B -> A), a file that is
661 ** being renamed must first be moved to a temporary location to avoid
662 ** being overwritten by another rename operation. A row is added to the
663 ** TMPRN table for each of these temporary renames.
664 */
665 db_multi_exec(
666 "DROP TABLE IF EXISTS tmprn;"
667 "CREATE TEMP TABLE tmprn(fn UNIQUE, tmpfn);"
668 );
669
670 /*
671 ** Rename files that have taken a rename on P->M but which keep the same
672 ** name on P->V. If a file is renamed on P->V only or on both P->V and
673 ** P->M then we retain the V name of the file.
674 */
675 db_prepare(&q,
676 "SELECT idv, fnp, fnm, isexe FROM fv"
677 " WHERE idv>0 AND idp>0 AND idm>0 AND fnp=fn AND fnm!=fnp"
678 );
679 while( db_step(&q)==SQLITE_ROW ){
680 int idv = db_column_int(&q, 0);
681 const char *zOldName = db_column_text(&q, 1);
682 const char *zNewName = db_column_text(&q, 2);
683 int isExe = db_column_int(&q, 3);
684 fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
685 if( !dryRunFlag ) undo_save(zOldName);
686 if( !dryRunFlag ) undo_save(zNewName);
687 db_multi_exec(
688 "UPDATE vfile SET pathname=NULL, origname=pathname"
689 " WHERE vid=%d AND pathname=%Q;"
690 "UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)"
691 " WHERE id=%d;",
692 vid, zNewName, zNewName, idv
693 );
694 if( !dryRunFlag ){
695 char *zFullOldPath, *zFullNewPath;
696 zFullOldPath = db_text(0,"SELECT tmpfn FROM tmprn WHERE fn=%Q", zOldName);
697 if( !zFullOldPath ){
698 zFullOldPath = mprintf("%s%s", g.zLocalRoot, zOldName);
699 }
700 zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
701 if( file_wd_size(zFullNewPath)>=0 ){
702 char zTmpPath[300];
703 file_tempname(sizeof(zTmpPath), zTmpPath);
704 db_multi_exec("INSERT INTO tmprn(fn,tmpfn) VALUES(%Q,%Q)",
705 zNewName, zTmpPath);
706 if( file_wd_islink(zFullNewPath) ){
707 symlink_copy(zFullNewPath, zTmpPath);
708 }else{
709 file_copy(zFullNewPath, zTmpPath);
710 }
711 }
712 if( file_wd_islink(zFullOldPath) ){
713 symlink_copy(zFullOldPath, zFullNewPath);
714 }else{
715 file_copy(zFullOldPath, zFullNewPath);
716 }
717 file_wd_setexe(zFullNewPath, isExe);
718 file_delete(zFullOldPath);
719 free(zFullNewPath);
720 free(zFullOldPath);
721 }
722 }
723 db_finalize(&q);
724
725 /* A file that has been deleted and replaced by a renamed file will have a
726 ** NULL pathname. Change it to something that makes the output of "status"
727 ** and similar commands make sense for such files and that will (most likely)
728 ** not be an actual existing pathname.
729 */
730 db_multi_exec(
731 "UPDATE vfile SET pathname=origname || ' (overwritten by rename)'"
732 " WHERE pathname IS NULL"
733 );
734
735 /*
736 ** Add to V files that are not in V or P but are in M
737 */
738 db_prepare(&q,
739 "SELECT idm, rowid, fnm FROM fv"
740 " WHERE idp=0 AND idv=0 AND idm>0"
741 );
742 while( db_step(&q)==SQLITE_ROW ){
743 int idm = db_column_int(&q, 0);
744 int rowid = db_column_int(&q, 1);
745 int idv;
746 const char *zName;
747 char *zFullName;
748 db_multi_exec(
749 "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,isexe,islink,pathname)"
750 " SELECT %d,%d,0,rid,mrid,isexe,islink,pathname FROM vfile WHERE id=%d",
751 vid, integrateFlag?5:3, idm
752 );
753 idv = db_last_insert_rowid();
754 db_multi_exec("UPDATE fv SET idv=%d WHERE rowid=%d", idv, rowid);
755 zName = db_column_text(&q, 2);
756 zFullName = mprintf("%s%s", g.zLocalRoot, zName);
757 if( file_wd_isfile_or_link(zFullName)
758 && !db_exists("SELECT 1 FROM fv WHERE fn=%Q", zName) ){
759 fossil_print("ADDED %s (overwrites an unmanaged file)\n", zName);
760 nOverwrite++;
761 }else{
762 fossil_print("ADDED %s\n", zName);
763 }
764 fossil_free(zFullName);
765 if( !dryRunFlag ){
766 undo_save(zName);
767 vfile_to_disk(0, idm, 0, 0);
768 }
769 }
770 db_finalize(&q);
771
772 /* Report on conflicts
773 */
774 if( nConflict ){
775 fossil_warning("WARNING: %d merge conflicts", nConflict);
776
+8 -4
--- src/pivot.c
+++ src/pivot.c
@@ -73,12 +73,14 @@
7373
7474
/*
7575
** Find the most recent common ancestor of the primary and one of
7676
** the secondaries. Return its rid. Return 0 if no common ancestor
7777
** can be found.
78
+**
79
+** If ignoreMerges is true, follow only "primary" parent links.
7880
*/
79
-int pivot_find(void){
81
+int pivot_find(int ignoreMerges){
8082
Stmt q1, q2, u1, i1;
8183
int rid = 0;
8284
8385
/* aqueue must contain at least one primary and one other. Otherwise
8486
** we abort early
@@ -102,11 +104,12 @@
102104
db_prepare(&q2,
103105
"SELECT 1 FROM aqueue A, plink, aqueue B"
104106
" WHERE plink.pid=:rid"
105107
" AND plink.cid=B.rid"
106108
" AND A.rid=:rid"
107
- " AND A.src!=B.src"
109
+ " AND A.src!=B.src %s",
110
+ ignoreMerges ? "AND plink.isprim" : ""
108111
);
109112
110113
/* Mark the :rid record has having been checked. It is not the
111114
** common ancestor.
112115
*/
@@ -122,11 +125,12 @@
122125
" coalesce((SELECT mtime FROM plink X WHERE X.cid=plink.pid), 0.0),"
123126
" 1,"
124127
" aqueue.src "
125128
" FROM plink, aqueue"
126129
" WHERE plink.cid=:rid"
127
- " AND aqueue.rid=:rid"
130
+ " AND aqueue.rid=:rid %s",
131
+ ignoreMerges ? "AND plink.isprim" : ""
128132
);
129133
130134
while( db_step(&q1)==SQLITE_ROW ){
131135
rid = db_column_int(&q1, 0);
132136
db_reset(&q1);
@@ -161,10 +165,10 @@
161165
db_must_be_within_tree();
162166
pivot_set_primary(name_to_rid(g.argv[2]));
163167
for(i=3; i<g.argc; i++){
164168
pivot_set_secondary(name_to_rid(g.argv[i]));
165169
}
166
- rid = pivot_find();
170
+ rid = pivot_find(0);
167171
printf("pivot=%s\n",
168172
db_text("?","SELECT uuid FROM blob WHERE rid=%d",rid)
169173
);
170174
}
171175
--- src/pivot.c
+++ src/pivot.c
@@ -73,12 +73,14 @@
73
74 /*
75 ** Find the most recent common ancestor of the primary and one of
76 ** the secondaries. Return its rid. Return 0 if no common ancestor
77 ** can be found.
 
 
78 */
79 int pivot_find(void){
80 Stmt q1, q2, u1, i1;
81 int rid = 0;
82
83 /* aqueue must contain at least one primary and one other. Otherwise
84 ** we abort early
@@ -102,11 +104,12 @@
102 db_prepare(&q2,
103 "SELECT 1 FROM aqueue A, plink, aqueue B"
104 " WHERE plink.pid=:rid"
105 " AND plink.cid=B.rid"
106 " AND A.rid=:rid"
107 " AND A.src!=B.src"
 
108 );
109
110 /* Mark the :rid record has having been checked. It is not the
111 ** common ancestor.
112 */
@@ -122,11 +125,12 @@
122 " coalesce((SELECT mtime FROM plink X WHERE X.cid=plink.pid), 0.0),"
123 " 1,"
124 " aqueue.src "
125 " FROM plink, aqueue"
126 " WHERE plink.cid=:rid"
127 " AND aqueue.rid=:rid"
 
128 );
129
130 while( db_step(&q1)==SQLITE_ROW ){
131 rid = db_column_int(&q1, 0);
132 db_reset(&q1);
@@ -161,10 +165,10 @@
161 db_must_be_within_tree();
162 pivot_set_primary(name_to_rid(g.argv[2]));
163 for(i=3; i<g.argc; i++){
164 pivot_set_secondary(name_to_rid(g.argv[i]));
165 }
166 rid = pivot_find();
167 printf("pivot=%s\n",
168 db_text("?","SELECT uuid FROM blob WHERE rid=%d",rid)
169 );
170 }
171
--- src/pivot.c
+++ src/pivot.c
@@ -73,12 +73,14 @@
73
74 /*
75 ** Find the most recent common ancestor of the primary and one of
76 ** the secondaries. Return its rid. Return 0 if no common ancestor
77 ** can be found.
78 **
79 ** If ignoreMerges is true, follow only "primary" parent links.
80 */
81 int pivot_find(int ignoreMerges){
82 Stmt q1, q2, u1, i1;
83 int rid = 0;
84
85 /* aqueue must contain at least one primary and one other. Otherwise
86 ** we abort early
@@ -102,11 +104,12 @@
104 db_prepare(&q2,
105 "SELECT 1 FROM aqueue A, plink, aqueue B"
106 " WHERE plink.pid=:rid"
107 " AND plink.cid=B.rid"
108 " AND A.rid=:rid"
109 " AND A.src!=B.src %s",
110 ignoreMerges ? "AND plink.isprim" : ""
111 );
112
113 /* Mark the :rid record has having been checked. It is not the
114 ** common ancestor.
115 */
@@ -122,11 +125,12 @@
125 " coalesce((SELECT mtime FROM plink X WHERE X.cid=plink.pid), 0.0),"
126 " 1,"
127 " aqueue.src "
128 " FROM plink, aqueue"
129 " WHERE plink.cid=:rid"
130 " AND aqueue.rid=:rid %s",
131 ignoreMerges ? "AND plink.isprim" : ""
132 );
133
134 while( db_step(&q1)==SQLITE_ROW ){
135 rid = db_column_int(&q1, 0);
136 db_reset(&q1);
@@ -161,10 +165,10 @@
165 db_must_be_within_tree();
166 pivot_set_primary(name_to_rid(g.argv[2]));
167 for(i=3; i<g.argc; i++){
168 pivot_set_secondary(name_to_rid(g.argv[i]));
169 }
170 rid = pivot_find(0);
171 printf("pivot=%s\n",
172 db_text("?","SELECT uuid FROM blob WHERE rid=%d",rid)
173 );
174 }
175
--- test/merge6.test
+++ test/merge6.test
@@ -62,10 +62,10 @@
6262
fossil ls
6363
6464
test merge_multi-4 {[normalize_result] eq {f1
6565
f2
6666
f3
67
-f4}} knownBug
67
+f4}}
6868
6969
###############################################################################
7070
7171
test_cleanup
7272
--- test/merge6.test
+++ test/merge6.test
@@ -62,10 +62,10 @@
62 fossil ls
63
64 test merge_multi-4 {[normalize_result] eq {f1
65 f2
66 f3
67 f4}} knownBug
68
69 ###############################################################################
70
71 test_cleanup
72
--- test/merge6.test
+++ test/merge6.test
@@ -62,10 +62,10 @@
62 fossil ls
63
64 test merge_multi-4 {[normalize_result] eq {f1
65 f2
66 f3
67 f4}}
68
69 ###############################################################################
70
71 test_cleanup
72
--- test/merge_renames.test
+++ test/merge_renames.test
@@ -1,9 +1,14 @@
11
#
22
# Tests for merging with renames
33
#
44
#
5
+
6
+proc commit_id {version} {
7
+ regexp -line {^artifact:\s+(\S+)} [fossil whatis $version] - id
8
+ return $id
9
+}
510
611
require_no_open_checkout
712
813
######################################
914
# Test 1 #
@@ -31,12 +36,11 @@
3136
3237
write_file f1 "line6"
3338
fossil commit -m "c4"
3439
3540
fossil update pivot
36
-fossil mv f1 f2
37
-file rename -force f1 f2
41
+fossil mv --hard f1 f2
3842
fossil commit -b rename -m "c5"
3943
4044
fossil merge trunk
4145
fossil commit -m "trunk merged"
4246
@@ -44,26 +48,11 @@
4448
write_file f3 "someline"
4549
fossil add f3
4650
fossil commit -b branch2 -m "newbranch"
4751
4852
fossil merge trunk
49
-puts $RESULT
50
-
51
-set deletes 0
52
-foreach {status filename} $RESULT {
53
- if {$status=="DELETE"} {
54
- set deletes [expr $deletes + 1]
55
- }
56
-}
57
-
58
-if {$deletes!=0} {
59
- # failed
60
- protOut "Error, the merge should not delete any file"
61
- test merge_renames-1 0
62
-} else {
63
- test merge_renames-1 1
64
-}
53
+test_status_list merge_renames-1 $RESULT {UPDATE f1}
6554
6655
######################################
6756
# Test 2 #
6857
# Reported: Ticket [74413366fe5067] #
6958
######################################
@@ -77,12 +66,11 @@
7766
7867
write_file f2 "line2"
7968
fossil add f2
8069
fossil commit -m "newfile"
8170
82
-fossil mv f2 f2new
83
-file rename -force f2 f2new
71
+fossil mv --hard f2 f2new
8472
fossil commit -m "rename"
8573
8674
fossil update pivot
8775
write_file f1 "line3"
8876
fossil commit -b branch -m "change"
@@ -91,27 +79,11 @@
9179
fossil commit -m "trunk merged"
9280
9381
fossil update trunk
9482
9583
fossil merge branch
96
-puts $RESULT
97
-
98
-# Not a nice way to check, but I don't know more tcl now
99
-set deletes 0
100
-foreach {status filename} $RESULT {
101
- if {$status=="DELETE"} {
102
- set deletes [expr $deletes + 1]
103
- }
104
-}
105
-
106
-if {$deletes!=0} {
107
- # failed
108
- protOut "Error, the merge should not delete any file"
109
- test merge_renames-2 0
110
-} else {
111
- test merge_renames-2 1
112
-}
84
+test_status_list merge_renames-2 $RESULT {UPDATE f1}
11385
11486
######################################
11587
# Test 3 #
11688
# Reported: Ticket [30b28cf351] #
11789
######################################
@@ -125,12 +97,11 @@
12597
12698
write_file f2 "line2"
12799
fossil add f2
128100
fossil commit -m "newfile"
129101
130
-fossil mv f2 f2new
131
-file rename -force f2 f2new
102
+fossil mv --hard f2 f2new
132103
fossil commit -m "rename"
133104
134105
fossil update pivot
135106
write_file f1 "line3"
136107
fossil commit -b branch -m "change"
@@ -139,34 +110,33 @@
139110
fossil commit -m "trunk merged"
140111
141112
fossil update trunk
142113
143114
fossil merge branch
144
-puts $RESULT
145
-
146
-# Not a nice way to check, but I don't know more tcl now
147
-set deletes 0
148
-foreach {status filename} $RESULT {
149
- if {$status=="DELETE"} {
150
- set deletes [expr $deletes + 1]
151
- }
152
-}
153
-
154
-if {$deletes!=0} {
155
- # failed
156
- protOut "Error, the merge should not delete any file"
157
- test merge_renames-3 0
158
-} else {
159
- test merge_renames-3 1
160
-}
115
+test_status_list merge_renames-3 $RESULT {UPDATE f1}
161116
162117
######################################
163118
# Test 4 #
164119
# Reported: Ticket [67176c3aa4] #
165120
######################################
166121
167
-# TO BE WRITTEN.
122
+test_setup
123
+
124
+write_file f1 "f1"
125
+fossil add f1
126
+fossil commit -m "add f1"
127
+
128
+write_file f1 "f1.1"
129
+fossil commit --branch b -m "change f1"
130
+
131
+fossil update trunk
132
+fossil mv --hard f1 f2
133
+fossil commit -m "f1 -> f2"
134
+
135
+fossil merge b
136
+test_status_list merge_renames-4-1 $RESULT {UPDATE f2}
137
+test_file_contents merge_renames-4-2 f2 "f1.1"
168138
169139
######################################
170140
# Test 5 #
171141
# Handle Rename/Add via Merge #
172142
######################################
@@ -180,25 +150,190 @@
180150
write_file f3 "f3 line"
181151
fossil add f3
182152
fossil commit -m "branch file" -b branch_for_f3
183153
184154
fossil update trunk
185
-fossil mv f1 f2
186
-file rename -force f1 f2
155
+fossil mv --hard f1 f2
187156
write_file f1 "new f1 line"
188157
fossil add f1
189158
fossil commit -m "rename and add file with old name"
190159
191160
fossil update branch_for_f3
192161
fossil merge trunk
162
+test_status_list merge_renames-5-1 $RESULT {
163
+ RENAME f1 -> f2
164
+ ADDED f1
165
+}
166
+
193167
fossil commit -m "trunk merged, should have 3 files"
194168
195169
fossil ls
196170
197
-test merge_renames-5 {[normalize_result] eq {f1
171
+test merge_renames-5-2 {[normalize_result] eq {f1
198172
f2
199
-f3}} knownBug
173
+f3}}
174
+
175
+#####################################
176
+# Test 6 #
177
+# Merging a branch multiple times #
178
+#####################################
179
+
180
+test_setup
181
+
182
+write_file f1 "f1"
183
+fossil add f1
184
+fossil commit -m "add f1"
185
+
186
+fossil mv --hard f1 f2
187
+fossil commit -b b -m "f1 -> f2"
188
+
189
+fossil update trunk
190
+write_file f3 "f3"
191
+write_file f4 "f4"
192
+fossil add f3 f4
193
+fossil ci -m "add f3, f4"
194
+
195
+fossil mv --hard f3 f3-old
196
+fossil mv --hard f4 f3
197
+fossil mv --hard f3-old f4
198
+fossil ci -m "swap f3 and f4"
199
+
200
+write_file f1 "f1.1"
201
+fossil commit -m "edit f1"
202
+
203
+fossil update b
204
+fossil merge trunk
205
+fossil commit -m "merge trunk"
206
+
207
+fossil update trunk
208
+write_file f1 "f1.2"
209
+write_file f3 "f3.1"
210
+write_file f4 "f4.1"
211
+fossil commit -m "edit f1, f4"
212
+
213
+fossil update b
214
+fossil merge trunk
215
+test_status_list merge_renames-6-1 $RESULT {
216
+ UPDATE f2
217
+ UPDATE f3
218
+ UPDATE f4
219
+}
220
+test_file_contents merge_renames-6-2 f2 "f1.2"
221
+test_file_contents merge_renames-6-3 f3 "f3.1"
222
+test_file_contents merge_renames-6-4 f4 "f4.1"
223
+
224
+########################################################################
225
+# Test 7 #
226
+# Merging with an uncommitted rename of a file that has been renamed #
227
+# in the merged branch and adding a new file with the original name #
228
+########################################################################
229
+
230
+test_setup
231
+
232
+write_file f1 "f1"
233
+fossil add f1
234
+fossil commit -m "add f1"
235
+
236
+fossil mv --hard f1 f2
237
+write_file f2 "f2"
238
+fossil commit -b b -m "f1 -> f2, edit f2"
239
+
240
+fossil update trunk
241
+fossil mv --hard f1 f3
242
+write_file f1 "f1.1"
243
+fossil add f1
244
+fossil merge b
245
+test_status_list merge_renames-7-1 $RESULT {UPDATE f3}
246
+test_file_contents merge_renames-7-2 f1 "f1.1"
247
+test_file_contents merge_renames-7-3 f3 "f2"
248
+
249
+######################################################
250
+# Test 8 #
251
+# Merging two branches that both add the same file #
252
+######################################################
253
+
254
+test_setup
255
+
256
+write_file f1 "f1"
257
+fossil commit -m "add f1"
258
+
259
+fossil update trunk
260
+write_file f2 "f2.1"
261
+fossil add f2
262
+fossil commit -b b1 -m "add f2"
263
+
264
+fossil update trunk
265
+write_file f2 "f2.2"
266
+fossil add f2
267
+fossil commit -b b2 -m "add f2"
268
+
269
+fossil update trunk
270
+fossil merge b1
271
+fossil merge b2
272
+test_status_list merge_renames-8-1 $RESULT {
273
+ WARNING: no common ancestor for f2
274
+}
275
+
276
+fossil revert
277
+fossil merge --integrate b1
278
+fossil merge b2
279
+test_status_list merge_renames-8-2 $RESULT {
280
+ WARNING: no common ancestor for f2
281
+}
282
+
283
+#############################################
284
+# Test 9 #
285
+# Merging a delete/rename/add combination #
286
+#############################################
287
+
288
+test_setup
289
+
290
+write_file f1 "f1"
291
+write_file f2 "f2"
292
+fossil add f1 f2
293
+fossil commit -m "add files"
294
+
295
+fossil rm --hard f2
296
+fossil commit -b b -m "delete f2"
297
+
298
+fossil mv --hard f1 f2
299
+fossil commit -m "f1 -> f2"
300
+
301
+write_file f1 "f1.1"
302
+fossil add f1
303
+fossil commit -m "add new f1"
304
+
305
+fossil update trunk
306
+fossil merge b
307
+set expectedMerge {
308
+ DELETE f2
309
+ RENAME f1 -> f2
310
+ ADDED f1
311
+}
312
+test_status_list merge_renames-9-1 $RESULT $expectedMerge
313
+fossil changes
314
+test_status_list merge_renames-9-2 $RESULT "
315
+ MERGED_WITH [commit_id b]
316
+ ADDED_BY_MERGE f1
317
+ RENAMED f2
318
+ DELETED f2 (overwritten by rename)
319
+"
320
+test_file_contents merge_renames-9-3 f1 "f1.1"
321
+test_file_contents merge_renames-9-4 f2 "f1"
322
+
323
+# Undo and ensure a dry run merge results in no changes
324
+fossil undo
325
+test_status_list merge_renames-9-5 $RESULT {
326
+ UNDO f1
327
+ UNDO f2
328
+}
329
+fossil merge -n b
330
+test_status_list merge_renames-9-6 $RESULT "
331
+ $expectedMerge
332
+ REMINDER: this was a dry run - no files were actually changed.
333
+"
334
+test merge_renames-9-7 {[fossil changes] eq ""}
200335
201336
######################################
202337
#
203338
# Tests for troubles not specifically linked with renames but that I'd like to
204339
# write:
205340
--- test/merge_renames.test
+++ test/merge_renames.test
@@ -1,9 +1,14 @@
1 #
2 # Tests for merging with renames
3 #
4 #
 
 
 
 
 
5
6 require_no_open_checkout
7
8 ######################################
9 # Test 1 #
@@ -31,12 +36,11 @@
31
32 write_file f1 "line6"
33 fossil commit -m "c4"
34
35 fossil update pivot
36 fossil mv f1 f2
37 file rename -force f1 f2
38 fossil commit -b rename -m "c5"
39
40 fossil merge trunk
41 fossil commit -m "trunk merged"
42
@@ -44,26 +48,11 @@
44 write_file f3 "someline"
45 fossil add f3
46 fossil commit -b branch2 -m "newbranch"
47
48 fossil merge trunk
49 puts $RESULT
50
51 set deletes 0
52 foreach {status filename} $RESULT {
53 if {$status=="DELETE"} {
54 set deletes [expr $deletes + 1]
55 }
56 }
57
58 if {$deletes!=0} {
59 # failed
60 protOut "Error, the merge should not delete any file"
61 test merge_renames-1 0
62 } else {
63 test merge_renames-1 1
64 }
65
66 ######################################
67 # Test 2 #
68 # Reported: Ticket [74413366fe5067] #
69 ######################################
@@ -77,12 +66,11 @@
77
78 write_file f2 "line2"
79 fossil add f2
80 fossil commit -m "newfile"
81
82 fossil mv f2 f2new
83 file rename -force f2 f2new
84 fossil commit -m "rename"
85
86 fossil update pivot
87 write_file f1 "line3"
88 fossil commit -b branch -m "change"
@@ -91,27 +79,11 @@
91 fossil commit -m "trunk merged"
92
93 fossil update trunk
94
95 fossil merge branch
96 puts $RESULT
97
98 # Not a nice way to check, but I don't know more tcl now
99 set deletes 0
100 foreach {status filename} $RESULT {
101 if {$status=="DELETE"} {
102 set deletes [expr $deletes + 1]
103 }
104 }
105
106 if {$deletes!=0} {
107 # failed
108 protOut "Error, the merge should not delete any file"
109 test merge_renames-2 0
110 } else {
111 test merge_renames-2 1
112 }
113
114 ######################################
115 # Test 3 #
116 # Reported: Ticket [30b28cf351] #
117 ######################################
@@ -125,12 +97,11 @@
125
126 write_file f2 "line2"
127 fossil add f2
128 fossil commit -m "newfile"
129
130 fossil mv f2 f2new
131 file rename -force f2 f2new
132 fossil commit -m "rename"
133
134 fossil update pivot
135 write_file f1 "line3"
136 fossil commit -b branch -m "change"
@@ -139,34 +110,33 @@
139 fossil commit -m "trunk merged"
140
141 fossil update trunk
142
143 fossil merge branch
144 puts $RESULT
145
146 # Not a nice way to check, but I don't know more tcl now
147 set deletes 0
148 foreach {status filename} $RESULT {
149 if {$status=="DELETE"} {
150 set deletes [expr $deletes + 1]
151 }
152 }
153
154 if {$deletes!=0} {
155 # failed
156 protOut "Error, the merge should not delete any file"
157 test merge_renames-3 0
158 } else {
159 test merge_renames-3 1
160 }
161
162 ######################################
163 # Test 4 #
164 # Reported: Ticket [67176c3aa4] #
165 ######################################
166
167 # TO BE WRITTEN.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
169 ######################################
170 # Test 5 #
171 # Handle Rename/Add via Merge #
172 ######################################
@@ -180,25 +150,190 @@
180 write_file f3 "f3 line"
181 fossil add f3
182 fossil commit -m "branch file" -b branch_for_f3
183
184 fossil update trunk
185 fossil mv f1 f2
186 file rename -force f1 f2
187 write_file f1 "new f1 line"
188 fossil add f1
189 fossil commit -m "rename and add file with old name"
190
191 fossil update branch_for_f3
192 fossil merge trunk
 
 
 
 
 
193 fossil commit -m "trunk merged, should have 3 files"
194
195 fossil ls
196
197 test merge_renames-5 {[normalize_result] eq {f1
198 f2
199 f3}} knownBug
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
201 ######################################
202 #
203 # Tests for troubles not specifically linked with renames but that I'd like to
204 # write:
205
--- test/merge_renames.test
+++ test/merge_renames.test
@@ -1,9 +1,14 @@
1 #
2 # Tests for merging with renames
3 #
4 #
5
6 proc commit_id {version} {
7 regexp -line {^artifact:\s+(\S+)} [fossil whatis $version] - id
8 return $id
9 }
10
11 require_no_open_checkout
12
13 ######################################
14 # Test 1 #
@@ -31,12 +36,11 @@
36
37 write_file f1 "line6"
38 fossil commit -m "c4"
39
40 fossil update pivot
41 fossil mv --hard f1 f2
 
42 fossil commit -b rename -m "c5"
43
44 fossil merge trunk
45 fossil commit -m "trunk merged"
46
@@ -44,26 +48,11 @@
48 write_file f3 "someline"
49 fossil add f3
50 fossil commit -b branch2 -m "newbranch"
51
52 fossil merge trunk
53 test_status_list merge_renames-1 $RESULT {UPDATE f1}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
55 ######################################
56 # Test 2 #
57 # Reported: Ticket [74413366fe5067] #
58 ######################################
@@ -77,12 +66,11 @@
66
67 write_file f2 "line2"
68 fossil add f2
69 fossil commit -m "newfile"
70
71 fossil mv --hard f2 f2new
 
72 fossil commit -m "rename"
73
74 fossil update pivot
75 write_file f1 "line3"
76 fossil commit -b branch -m "change"
@@ -91,27 +79,11 @@
79 fossil commit -m "trunk merged"
80
81 fossil update trunk
82
83 fossil merge branch
84 test_status_list merge_renames-2 $RESULT {UPDATE f1}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
86 ######################################
87 # Test 3 #
88 # Reported: Ticket [30b28cf351] #
89 ######################################
@@ -125,12 +97,11 @@
97
98 write_file f2 "line2"
99 fossil add f2
100 fossil commit -m "newfile"
101
102 fossil mv --hard f2 f2new
 
103 fossil commit -m "rename"
104
105 fossil update pivot
106 write_file f1 "line3"
107 fossil commit -b branch -m "change"
@@ -139,34 +110,33 @@
110 fossil commit -m "trunk merged"
111
112 fossil update trunk
113
114 fossil merge branch
115 test_status_list merge_renames-3 $RESULT {UPDATE f1}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
117 ######################################
118 # Test 4 #
119 # Reported: Ticket [67176c3aa4] #
120 ######################################
121
122 test_setup
123
124 write_file f1 "f1"
125 fossil add f1
126 fossil commit -m "add f1"
127
128 write_file f1 "f1.1"
129 fossil commit --branch b -m "change f1"
130
131 fossil update trunk
132 fossil mv --hard f1 f2
133 fossil commit -m "f1 -> f2"
134
135 fossil merge b
136 test_status_list merge_renames-4-1 $RESULT {UPDATE f2}
137 test_file_contents merge_renames-4-2 f2 "f1.1"
138
139 ######################################
140 # Test 5 #
141 # Handle Rename/Add via Merge #
142 ######################################
@@ -180,25 +150,190 @@
150 write_file f3 "f3 line"
151 fossil add f3
152 fossil commit -m "branch file" -b branch_for_f3
153
154 fossil update trunk
155 fossil mv --hard f1 f2
 
156 write_file f1 "new f1 line"
157 fossil add f1
158 fossil commit -m "rename and add file with old name"
159
160 fossil update branch_for_f3
161 fossil merge trunk
162 test_status_list merge_renames-5-1 $RESULT {
163 RENAME f1 -> f2
164 ADDED f1
165 }
166
167 fossil commit -m "trunk merged, should have 3 files"
168
169 fossil ls
170
171 test merge_renames-5-2 {[normalize_result] eq {f1
172 f2
173 f3}}
174
175 #####################################
176 # Test 6 #
177 # Merging a branch multiple times #
178 #####################################
179
180 test_setup
181
182 write_file f1 "f1"
183 fossil add f1
184 fossil commit -m "add f1"
185
186 fossil mv --hard f1 f2
187 fossil commit -b b -m "f1 -> f2"
188
189 fossil update trunk
190 write_file f3 "f3"
191 write_file f4 "f4"
192 fossil add f3 f4
193 fossil ci -m "add f3, f4"
194
195 fossil mv --hard f3 f3-old
196 fossil mv --hard f4 f3
197 fossil mv --hard f3-old f4
198 fossil ci -m "swap f3 and f4"
199
200 write_file f1 "f1.1"
201 fossil commit -m "edit f1"
202
203 fossil update b
204 fossil merge trunk
205 fossil commit -m "merge trunk"
206
207 fossil update trunk
208 write_file f1 "f1.2"
209 write_file f3 "f3.1"
210 write_file f4 "f4.1"
211 fossil commit -m "edit f1, f4"
212
213 fossil update b
214 fossil merge trunk
215 test_status_list merge_renames-6-1 $RESULT {
216 UPDATE f2
217 UPDATE f3
218 UPDATE f4
219 }
220 test_file_contents merge_renames-6-2 f2 "f1.2"
221 test_file_contents merge_renames-6-3 f3 "f3.1"
222 test_file_contents merge_renames-6-4 f4 "f4.1"
223
224 ########################################################################
225 # Test 7 #
226 # Merging with an uncommitted rename of a file that has been renamed #
227 # in the merged branch and adding a new file with the original name #
228 ########################################################################
229
230 test_setup
231
232 write_file f1 "f1"
233 fossil add f1
234 fossil commit -m "add f1"
235
236 fossil mv --hard f1 f2
237 write_file f2 "f2"
238 fossil commit -b b -m "f1 -> f2, edit f2"
239
240 fossil update trunk
241 fossil mv --hard f1 f3
242 write_file f1 "f1.1"
243 fossil add f1
244 fossil merge b
245 test_status_list merge_renames-7-1 $RESULT {UPDATE f3}
246 test_file_contents merge_renames-7-2 f1 "f1.1"
247 test_file_contents merge_renames-7-3 f3 "f2"
248
249 ######################################################
250 # Test 8 #
251 # Merging two branches that both add the same file #
252 ######################################################
253
254 test_setup
255
256 write_file f1 "f1"
257 fossil commit -m "add f1"
258
259 fossil update trunk
260 write_file f2 "f2.1"
261 fossil add f2
262 fossil commit -b b1 -m "add f2"
263
264 fossil update trunk
265 write_file f2 "f2.2"
266 fossil add f2
267 fossil commit -b b2 -m "add f2"
268
269 fossil update trunk
270 fossil merge b1
271 fossil merge b2
272 test_status_list merge_renames-8-1 $RESULT {
273 WARNING: no common ancestor for f2
274 }
275
276 fossil revert
277 fossil merge --integrate b1
278 fossil merge b2
279 test_status_list merge_renames-8-2 $RESULT {
280 WARNING: no common ancestor for f2
281 }
282
283 #############################################
284 # Test 9 #
285 # Merging a delete/rename/add combination #
286 #############################################
287
288 test_setup
289
290 write_file f1 "f1"
291 write_file f2 "f2"
292 fossil add f1 f2
293 fossil commit -m "add files"
294
295 fossil rm --hard f2
296 fossil commit -b b -m "delete f2"
297
298 fossil mv --hard f1 f2
299 fossil commit -m "f1 -> f2"
300
301 write_file f1 "f1.1"
302 fossil add f1
303 fossil commit -m "add new f1"
304
305 fossil update trunk
306 fossil merge b
307 set expectedMerge {
308 DELETE f2
309 RENAME f1 -> f2
310 ADDED f1
311 }
312 test_status_list merge_renames-9-1 $RESULT $expectedMerge
313 fossil changes
314 test_status_list merge_renames-9-2 $RESULT "
315 MERGED_WITH [commit_id b]
316 ADDED_BY_MERGE f1
317 RENAMED f2
318 DELETED f2 (overwritten by rename)
319 "
320 test_file_contents merge_renames-9-3 f1 "f1.1"
321 test_file_contents merge_renames-9-4 f2 "f1"
322
323 # Undo and ensure a dry run merge results in no changes
324 fossil undo
325 test_status_list merge_renames-9-5 $RESULT {
326 UNDO f1
327 UNDO f2
328 }
329 fossil merge -n b
330 test_status_list merge_renames-9-6 $RESULT "
331 $expectedMerge
332 REMINDER: this was a dry run - no files were actually changed.
333 "
334 test merge_renames-9-7 {[fossil changes] eq ""}
335
336 ######################################
337 #
338 # Tests for troubles not specifically linked with renames but that I'd like to
339 # write:
340
--- test/tester.tcl
+++ test/tester.tcl
@@ -390,10 +390,29 @@
390390
protOut " Expected:\n [join $expected "\n "]" 1
391391
protOut " Got:\n [join $result "\n "]" 1
392392
test $name 0 $constraints
393393
}
394394
}
395
+
396
+# Perform a test on the contents of a file
397
+#
398
+proc test_file_contents {name path expected {constraints ""}} {
399
+ if {[file exists $path]} {
400
+ set result [read_file $path]
401
+ set passed [expr {$result eq $expected}]
402
+ if {!$passed} {
403
+ set expectedLines [split $expected "\n"]
404
+ set resultLines [split $result "\n"]
405
+ protOut " Expected:\n [join $expectedLines "\n "]" 1
406
+ protOut " Got:\n [join $resultLines "\n "]" 1
407
+ }
408
+ } else {
409
+ set passed 0
410
+ protOut " File does not exist: $path" 1
411
+ }
412
+ test $name $passed $constraints
413
+}
395414
396415
# Append all arguments into a single value and then returns it.
397416
#
398417
proc appendArgs {args} {
399418
eval append result $args
400419
--- test/tester.tcl
+++ test/tester.tcl
@@ -390,10 +390,29 @@
390 protOut " Expected:\n [join $expected "\n "]" 1
391 protOut " Got:\n [join $result "\n "]" 1
392 test $name 0 $constraints
393 }
394 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
395
396 # Append all arguments into a single value and then returns it.
397 #
398 proc appendArgs {args} {
399 eval append result $args
400
--- test/tester.tcl
+++ test/tester.tcl
@@ -390,10 +390,29 @@
390 protOut " Expected:\n [join $expected "\n "]" 1
391 protOut " Got:\n [join $result "\n "]" 1
392 test $name 0 $constraints
393 }
394 }
395
396 # Perform a test on the contents of a file
397 #
398 proc test_file_contents {name path expected {constraints ""}} {
399 if {[file exists $path]} {
400 set result [read_file $path]
401 set passed [expr {$result eq $expected}]
402 if {!$passed} {
403 set expectedLines [split $expected "\n"]
404 set resultLines [split $result "\n"]
405 protOut " Expected:\n [join $expectedLines "\n "]" 1
406 protOut " Got:\n [join $resultLines "\n "]" 1
407 }
408 } else {
409 set passed 0
410 protOut " File does not exist: $path" 1
411 }
412 test $name $passed $constraints
413 }
414
415 # Append all arguments into a single value and then returns it.
416 #
417 proc appendArgs {args} {
418 eval append result $args
419

Keyboard Shortcuts

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