Fossil SCM

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

drh 2016-05-16 17:46 trunk merge
Commit 41c2220934de8cb8d90126d5083df3e95e961b8c
+249 -143
--- 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,23 +225,22 @@
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 */
232
+ char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */
198233
Stmt q;
199234
200235
201236
/* Notation:
202237
**
203238
** V The current checkout
204239
** M The version being merged in
205240
** P The "pivot" - the most recent common ancestor of V and M.
241
+ ** N The "name pivot" - for detecting renames
206242
*/
207243
208244
undo_capture_command_line();
209245
verboseFlag = find_option("verbose","v",0)!=0;
210246
forceMissingFlag = find_option("force-missing",0,0)!=0;
@@ -291,37 +327,49 @@
291327
fossil_fatal("not a version: %s", zPivot);
292328
}
293329
if( pickFlag ){
294330
fossil_fatal("incompatible options: --cherrypick & --baseline");
295331
}
296
- }else if( pickFlag || backoutFlag ){
332
+ }
333
+ if( pickFlag || backoutFlag ){
297334
if( integrateFlag ){
298335
fossil_fatal("incompatible options: --integrate & --cherrypick or --backout");
299336
}
300337
pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid);
301338
if( pid<=0 ){
302339
fossil_fatal("cannot find an ancestor for %s", g.argv[2]);
303340
}
304341
}else{
342
+ if( !zPivot ){
343
+ pivot_set_primary(mid);
344
+ pivot_set_secondary(vid);
345
+ db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0");
346
+ while( db_step(&q)==SQLITE_ROW ){
347
+ pivot_set_secondary(db_column_int(&q,0));
348
+ }
349
+ db_finalize(&q);
350
+ pid = pivot_find(0);
351
+ if( pid<=0 ){
352
+ fossil_fatal("cannot find a common ancestor between the current "
353
+ "checkout and %s", g.argv[2]);
354
+ }
355
+ }
305356
pivot_set_primary(mid);
306357
pivot_set_secondary(vid);
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]);
358
+ nid = pivot_find(1);
359
+ if( nid!=pid ){
360
+ pivot_set_primary(nid);
361
+ pivot_set_secondary(pid);
362
+ nid = pivot_find(1);
316363
}
317364
}
318365
if( backoutFlag ){
319366
int t = pid;
320367
pid = mid;
321368
mid = t;
322369
}
370
+ if( nid==0 ) nid = pid;
323371
if( !is_a_version(pid) ){
324372
fossil_fatal("not a version: record #%d", pid);
325373
}
326374
if( !forceFlag && mid==pid ){
327375
fossil_print("Merge skipped because it is a no-op. "
@@ -343,12 +391,25 @@
343391
fossil_fatal("missing content, unable to merge");
344392
}
345393
if( load_vfile_from_rid(pid) && !forceMissingFlag ){
346394
fossil_fatal("missing content, unable to merge");
347395
}
396
+ if( zPivot ){
397
+ vAncestor = db_exists(
398
+ "WITH RECURSIVE ancestor(id) AS ("
399
+ " VALUES(%d)"
400
+ " UNION ALL"
401
+ " SELECT pid FROM plink, ancestor"
402
+ " WHERE cid=ancestor.id AND pid!=%d AND cid!=%d)"
403
+ "SELECT 1 FROM ancestor WHERE id=%d LIMIT 1",
404
+ vid, nid, pid, pid
405
+ ) ? 'p' : 'n';
406
+ }
348407
if( debugFlag ){
349408
char *z;
409
+ z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nid);
410
+ fossil_print("N=%d %z\n", nid, z);
350411
z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pid);
351412
fossil_print("P=%d %z\n", pid, z);
352413
z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
353414
fossil_print("M=%d %z\n", mid, z);
354415
z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
@@ -361,115 +422,102 @@
361422
** in the current checkout, the pivot, and the version being merged.
362423
*/
363424
db_multi_exec(
364425
"DROP TABLE IF EXISTS fv;"
365426
"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 */
427
+ " fn TEXT UNIQUE %s," /* The filename */
428
+ " idv INTEGER DEFAULT 0," /* VFILE entry for current version */
429
+ " idp INTEGER DEFAULT 0," /* VFILE entry for the pivot */
430
+ " idm INTEGER DEFAULT 0," /* VFILE entry for version merging in */
370431
" 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 */
432
+ " ridv INTEGER DEFAULT 0," /* Record ID for current version */
433
+ " ridp INTEGER DEFAULT 0," /* Record ID for pivot */
434
+ " ridm INTEGER DEFAULT 0," /* Record ID for merge */
374435
" isexe BOOLEAN," /* Execute permission enabled */
375
- " fnp TEXT %s," /* The filename in the pivot */
376
- " fnm TEXT %s," /* the filename in the merged version */
436
+ " fnp TEXT UNIQUE %s," /* The filename in the pivot */
437
+ " fnm TEXT UNIQUE %s," /* The filename in the merged version */
438
+ " fnn TEXT UNIQUE %s," /* The filename in the name pivot */
377439
" islinkv BOOLEAN," /* True if current version is a symlink */
378440
" islinkm BOOLEAN" /* True if merged version in is a symlink */
379441
");",
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
- */
442
+ filename_collation(), filename_collation(), filename_collation(),
443
+ filename_collation()
444
+ );
445
+
446
+ /*
447
+ ** Compute name changes from N to V, P, and M
448
+ */
449
+ add_renames("fn", vid, nid, 0, debugFlag ? "N->V" : 0);
450
+ add_renames("fnp", pid, nid, 0, debugFlag ? "N->P" : 0);
451
+ add_renames("fnm", mid, nid, backoutFlag, debugFlag ? "N->M" : 0);
452
+
453
+ /*
454
+ ** Add files found in V
455
+ */
456
+ db_multi_exec(
457
+ "UPDATE OR IGNORE fv SET fn=coalesce(fn%c,fnn) WHERE fn IS NULL;"
458
+ "REPLACE INTO fv(fn,fnp,fnm,fnn,idv,ridv,islinkv,isexe,chnged)"
459
+ " SELECT pathname, fnp, fnm, fnn, id, rid, islink, vf.isexe, vf.chnged"
460
+ " FROM vfile vf"
461
+ " LEFT JOIN fv ON fn=coalesce(origname,pathname)"
462
+ " AND rid>0 AND vf.chnged NOT IN (3,5)"
463
+ " WHERE vid=%d;",
464
+ vAncestor, vid
465
+ );
466
+
467
+ /*
468
+ ** Add files found in P
469
+ */
470
+ db_multi_exec(
471
+ "UPDATE OR IGNORE fv SET fnp=coalesce(fnn,"
472
+ " (SELECT coalesce(origname,pathname) FROM vfile WHERE id=idv))"
473
+ " WHERE fnp IS NULL;"
474
+ "INSERT OR IGNORE INTO fv(fnp)"
475
+ " SELECT coalesce(origname,pathname) FROM vfile WHERE vid=%d;",
476
+ pid
477
+ );
478
+
479
+ /*
480
+ ** Add files found in M
481
+ */
482
+ db_multi_exec(
483
+ "UPDATE OR IGNORE fv SET fnm=fnp WHERE fnm IS NULL;"
484
+ "INSERT OR IGNORE INTO fv(fnm)"
485
+ " SELECT pathname FROM vfile WHERE vid=%d;",
486
+ mid
487
+ );
488
+
489
+ /*
490
+ ** Compute the file version ids for P and M
491
+ */
492
+ if( pid==vid ){
493
+ db_multi_exec(
494
+ "UPDATE fv SET idp=idv, ridp=ridv WHERE ridv>0 AND chnged NOT IN (3,5)"
495
+ );
496
+ }else{
497
+ db_multi_exec(
498
+ "UPDATE fv SET"
499
+ " idp=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnp=pathname),0),"
500
+ " ridp=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnp=pathname),0)",
501
+ pid, pid
502
+ );
503
+ }
454504
db_multi_exec(
455505
"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),"
458506
" idm=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnm=pathname),0),"
459507
" ridm=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnm=pathname),0),"
460
- " islinkv=coalesce((SELECT islink FROM vfile"
508
+ " islinkm=coalesce((SELECT islink FROM vfile"
461509
" 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
510
+ " isexe=coalesce((SELECT isexe FROM vfile WHERE vid=%d AND fnm=pathname),"
511
+ " isexe)",
512
+ mid, mid, mid, mid
465513
);
466514
467515
if( debugFlag ){
468516
db_prepare(&q,
469517
"SELECT rowid, fn, fnp, fnm, chnged, ridv, ridp, ridm, "
470
- " isexe, islinkv, islinkm FROM fv"
518
+ " isexe, islinkv, islinkm, fnn FROM fv"
471519
);
472520
while( db_step(&q)==SQLITE_ROW ){
473521
fossil_print("%3d: ridv=%-4d ridp=%-4d ridm=%-4d chnged=%d isexe=%d "
474522
" islinkv=%d islinkm=%d\n",
475523
db_column_int(&q, 0),
@@ -481,14 +529,36 @@
481529
db_column_int(&q, 9),
482530
db_column_int(&q, 10));
483531
fossil_print(" fn = [%s]\n", db_column_text(&q, 1));
484532
fossil_print(" fnp = [%s]\n", db_column_text(&q, 2));
485533
fossil_print(" fnm = [%s]\n", db_column_text(&q, 3));
534
+ fossil_print(" fnn = [%s]\n", db_column_text(&q, 11));
486535
}
487536
db_finalize(&q);
488537
}
489538
539
+ /*
540
+ ** Update the execute bit on files where it's changed from P->M but not P->V
541
+ */
542
+ db_prepare(&q,
543
+ "SELECT idv, fn, fv.isexe FROM fv, vfile p, vfile v"
544
+ " WHERE p.id=idp AND v.id=idv AND fv.isexe!=p.isexe AND v.isexe=p.isexe"
545
+ );
546
+ while( db_step(&q)==SQLITE_ROW ){
547
+ int idv = db_column_int(&q, 0);
548
+ const char *zName = db_column_text(&q, 1);
549
+ int isExe = db_column_int(&q, 2);
550
+ fossil_print("%s %s\n", isExe ? "EXECUTABLE" : "UNEXEC", zName);
551
+ if( !dryRunFlag ){
552
+ char *zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
553
+ file_wd_setexe(zFullPath, isExe);
554
+ free(zFullPath);
555
+ db_multi_exec("UPDATE vfile SET isexe=%d WHERE id=%d", isExe, idv);
556
+ }
557
+ }
558
+ db_finalize(&q);
559
+
490560
/*
491561
** Find files in M and V but not in P and report conflicts.
492562
** The file in M will be ignored. It will be treated as if it
493563
** does not exist.
494564
*/
@@ -500,46 +570,10 @@
500570
char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm);
501571
fossil_warning("WARNING: no common ancestor for %s", zName);
502572
free(zName);
503573
db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm);
504574
}
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
- }
541575
db_finalize(&q);
542576
543577
/*
544578
** Find files that have changed from P->M but not P->V.
545579
** Copy the M content over into V.
@@ -661,45 +695,117 @@
661695
free(zFullPath);
662696
}
663697
}
664698
db_finalize(&q);
665699
700
+ /* For certain sets of renames (e.g. A -> B and B -> A), a file that is
701
+ ** being renamed must first be moved to a temporary location to avoid
702
+ ** being overwritten by another rename operation. A row is added to the
703
+ ** TMPRN table for each of these temporary renames.
704
+ */
705
+ db_multi_exec(
706
+ "DROP TABLE IF EXISTS tmprn;"
707
+ "CREATE TEMP TABLE tmprn(fn UNIQUE, tmpfn);"
708
+ );
709
+
666710
/*
667711
** Rename files that have taken a rename on P->M but which keep the same
668712
** name on P->V. If a file is renamed on P->V only or on both P->V and
669713
** P->M then we retain the V name of the file.
670714
*/
671715
db_prepare(&q,
672
- "SELECT idv, fnp, fnm FROM fv"
716
+ "SELECT idv, fnp, fnm, isexe FROM fv"
673717
" WHERE idv>0 AND idp>0 AND idm>0 AND fnp=fn AND fnm!=fnp"
674718
);
675719
while( db_step(&q)==SQLITE_ROW ){
676720
int idv = db_column_int(&q, 0);
677721
const char *zOldName = db_column_text(&q, 1);
678722
const char *zNewName = db_column_text(&q, 2);
723
+ int isExe = db_column_int(&q, 3);
679724
fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
680725
if( !dryRunFlag ) undo_save(zOldName);
681726
if( !dryRunFlag ) undo_save(zNewName);
682727
db_multi_exec(
728
+ "UPDATE vfile SET pathname=NULL, origname=pathname"
729
+ " WHERE vid=%d AND pathname=%Q;"
683730
"UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)"
684
- " WHERE id=%d AND vid=%d", zNewName, idv, vid
731
+ " WHERE id=%d;",
732
+ vid, zNewName, zNewName, idv
685733
);
686734
if( !dryRunFlag ){
687
- char *zFullOldPath = mprintf("%s%s", g.zLocalRoot, zOldName);
688
- char *zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
735
+ char *zFullOldPath, *zFullNewPath;
736
+ zFullOldPath = db_text(0,"SELECT tmpfn FROM tmprn WHERE fn=%Q", zOldName);
737
+ if( !zFullOldPath ){
738
+ zFullOldPath = mprintf("%s%s", g.zLocalRoot, zOldName);
739
+ }
740
+ zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
741
+ if( file_wd_size(zFullNewPath)>=0 ){
742
+ char zTmpPath[300];
743
+ file_tempname(sizeof(zTmpPath), zTmpPath);
744
+ db_multi_exec("INSERT INTO tmprn(fn,tmpfn) VALUES(%Q,%Q)",
745
+ zNewName, zTmpPath);
746
+ if( file_wd_islink(zFullNewPath) ){
747
+ symlink_copy(zFullNewPath, zTmpPath);
748
+ }else{
749
+ file_copy(zFullNewPath, zTmpPath);
750
+ }
751
+ }
689752
if( file_wd_islink(zFullOldPath) ){
690753
symlink_copy(zFullOldPath, zFullNewPath);
691754
}else{
692755
file_copy(zFullOldPath, zFullNewPath);
693756
}
757
+ file_wd_setexe(zFullNewPath, isExe);
694758
file_delete(zFullOldPath);
695759
free(zFullNewPath);
696760
free(zFullOldPath);
697761
}
698762
}
699763
db_finalize(&q);
700764
765
+ /* A file that has been deleted and replaced by a renamed file will have a
766
+ ** NULL pathname. Change it to something that makes the output of "status"
767
+ ** and similar commands make sense for such files and that will (most likely)
768
+ ** not be an actual existing pathname.
769
+ */
770
+ db_multi_exec(
771
+ "UPDATE vfile SET pathname=origname || ' (overwritten by rename)'"
772
+ " WHERE pathname IS NULL"
773
+ );
774
+
775
+ /*
776
+ ** Add to V files that are not in V or P but are in M
777
+ */
778
+ db_prepare(&q,
779
+ "SELECT idm, fnm FROM fv"
780
+ " WHERE idp=0 AND idv=0 AND idm>0"
781
+ );
782
+ while( db_step(&q)==SQLITE_ROW ){
783
+ int idm = db_column_int(&q, 0);
784
+ const char *zName;
785
+ char *zFullName;
786
+ db_multi_exec(
787
+ "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,isexe,islink,pathname)"
788
+ " SELECT %d,%d,0,rid,mrid,isexe,islink,pathname FROM vfile WHERE id=%d",
789
+ vid, integrateFlag?5:3, idm
790
+ );
791
+ zName = db_column_text(&q, 1);
792
+ zFullName = mprintf("%s%s", g.zLocalRoot, zName);
793
+ if( file_wd_isfile_or_link(zFullName)
794
+ && !db_exists("SELECT 1 FROM fv WHERE fn=%Q", zName) ){
795
+ fossil_print("ADDED %s (overwrites an unmanaged file)\n", zName);
796
+ nOverwrite++;
797
+ }else{
798
+ fossil_print("ADDED %s\n", zName);
799
+ }
800
+ fossil_free(zFullName);
801
+ if( !dryRunFlag ){
802
+ undo_save(zName);
803
+ vfile_to_disk(0, idm, 0, 0);
804
+ }
805
+ }
806
+ db_finalize(&q);
701807
702808
/* Report on conflicts
703809
*/
704810
if( nConflict ){
705811
fossil_warning("WARNING: %d merge conflicts", nConflict);
706812
--- 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,23 +225,22 @@
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 /* 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,37 +327,49 @@
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 ){
302 fossil_fatal("cannot find an ancestor for %s", g.argv[2]);
303 }
304 }else{
 
 
 
 
 
 
 
 
 
 
 
 
 
 
305 pivot_set_primary(mid);
306 pivot_set_secondary(vid);
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. "
@@ -343,12 +391,25 @@
343 fossil_fatal("missing content, unable to merge");
344 }
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 +422,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,14 +529,36 @@
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 /*
491 ** Find files in M and V but not in P and report conflicts.
492 ** The file in M will be ignored. It will be treated as if it
493 ** does not exist.
494 */
@@ -500,46 +570,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 +695,117 @@
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,23 +225,22 @@
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 char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */
233 Stmt q;
234
235
236 /* Notation:
237 **
238 ** V The current checkout
239 ** M The version being merged in
240 ** P The "pivot" - the most recent common ancestor of V and M.
241 ** N The "name pivot" - for detecting renames
242 */
243
244 undo_capture_command_line();
245 verboseFlag = find_option("verbose","v",0)!=0;
246 forceMissingFlag = find_option("force-missing",0,0)!=0;
@@ -291,37 +327,49 @@
327 fossil_fatal("not a version: %s", zPivot);
328 }
329 if( pickFlag ){
330 fossil_fatal("incompatible options: --cherrypick & --baseline");
331 }
332 }
333 if( pickFlag || backoutFlag ){
334 if( integrateFlag ){
335 fossil_fatal("incompatible options: --integrate & --cherrypick or --backout");
336 }
337 pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid);
338 if( pid<=0 ){
339 fossil_fatal("cannot find an ancestor for %s", g.argv[2]);
340 }
341 }else{
342 if( !zPivot ){
343 pivot_set_primary(mid);
344 pivot_set_secondary(vid);
345 db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0");
346 while( db_step(&q)==SQLITE_ROW ){
347 pivot_set_secondary(db_column_int(&q,0));
348 }
349 db_finalize(&q);
350 pid = pivot_find(0);
351 if( pid<=0 ){
352 fossil_fatal("cannot find a common ancestor between the current "
353 "checkout and %s", g.argv[2]);
354 }
355 }
356 pivot_set_primary(mid);
357 pivot_set_secondary(vid);
358 nid = pivot_find(1);
359 if( nid!=pid ){
360 pivot_set_primary(nid);
361 pivot_set_secondary(pid);
362 nid = pivot_find(1);
 
 
 
 
363 }
364 }
365 if( backoutFlag ){
366 int t = pid;
367 pid = mid;
368 mid = t;
369 }
370 if( nid==0 ) nid = pid;
371 if( !is_a_version(pid) ){
372 fossil_fatal("not a version: record #%d", pid);
373 }
374 if( !forceFlag && mid==pid ){
375 fossil_print("Merge skipped because it is a no-op. "
@@ -343,12 +391,25 @@
391 fossil_fatal("missing content, unable to merge");
392 }
393 if( load_vfile_from_rid(pid) && !forceMissingFlag ){
394 fossil_fatal("missing content, unable to merge");
395 }
396 if( zPivot ){
397 vAncestor = db_exists(
398 "WITH RECURSIVE ancestor(id) AS ("
399 " VALUES(%d)"
400 " UNION ALL"
401 " SELECT pid FROM plink, ancestor"
402 " WHERE cid=ancestor.id AND pid!=%d AND cid!=%d)"
403 "SELECT 1 FROM ancestor WHERE id=%d LIMIT 1",
404 vid, nid, pid, pid
405 ) ? 'p' : 'n';
406 }
407 if( debugFlag ){
408 char *z;
409 z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nid);
410 fossil_print("N=%d %z\n", nid, z);
411 z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pid);
412 fossil_print("P=%d %z\n", pid, z);
413 z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
414 fossil_print("M=%d %z\n", mid, z);
415 z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
@@ -361,115 +422,102 @@
422 ** in the current checkout, the pivot, and the version being merged.
423 */
424 db_multi_exec(
425 "DROP TABLE IF EXISTS fv;"
426 "CREATE TEMP TABLE fv("
427 " fn TEXT UNIQUE %s," /* The filename */
428 " idv INTEGER DEFAULT 0," /* VFILE entry for current version */
429 " idp INTEGER DEFAULT 0," /* VFILE entry for the pivot */
430 " idm INTEGER DEFAULT 0," /* VFILE entry for version merging in */
431 " chnged BOOLEAN," /* True if current version has been edited */
432 " ridv INTEGER DEFAULT 0," /* Record ID for current version */
433 " ridp INTEGER DEFAULT 0," /* Record ID for pivot */
434 " ridm INTEGER DEFAULT 0," /* Record ID for merge */
435 " isexe BOOLEAN," /* Execute permission enabled */
436 " fnp TEXT UNIQUE %s," /* The filename in the pivot */
437 " fnm TEXT UNIQUE %s," /* The filename in the merged version */
438 " fnn TEXT UNIQUE %s," /* The filename in the name pivot */
439 " islinkv BOOLEAN," /* True if current version is a symlink */
440 " islinkm BOOLEAN" /* True if merged version in is a symlink */
441 ");",
442 filename_collation(), filename_collation(), filename_collation(),
443 filename_collation()
444 );
445
446 /*
447 ** Compute name changes from N to V, P, and M
448 */
449 add_renames("fn", vid, nid, 0, debugFlag ? "N->V" : 0);
450 add_renames("fnp", pid, nid, 0, debugFlag ? "N->P" : 0);
451 add_renames("fnm", mid, nid, backoutFlag, debugFlag ? "N->M" : 0);
452
453 /*
454 ** Add files found in V
455 */
456 db_multi_exec(
457 "UPDATE OR IGNORE fv SET fn=coalesce(fn%c,fnn) WHERE fn IS NULL;"
458 "REPLACE INTO fv(fn,fnp,fnm,fnn,idv,ridv,islinkv,isexe,chnged)"
459 " SELECT pathname, fnp, fnm, fnn, id, rid, islink, vf.isexe, vf.chnged"
460 " FROM vfile vf"
461 " LEFT JOIN fv ON fn=coalesce(origname,pathname)"
462 " AND rid>0 AND vf.chnged NOT IN (3,5)"
463 " WHERE vid=%d;",
464 vAncestor, vid
465 );
466
467 /*
468 ** Add files found in P
469 */
470 db_multi_exec(
471 "UPDATE OR IGNORE fv SET fnp=coalesce(fnn,"
472 " (SELECT coalesce(origname,pathname) FROM vfile WHERE id=idv))"
473 " WHERE fnp IS NULL;"
474 "INSERT OR IGNORE INTO fv(fnp)"
475 " SELECT coalesce(origname,pathname) FROM vfile WHERE vid=%d;",
476 pid
477 );
478
479 /*
480 ** Add files found in M
481 */
482 db_multi_exec(
483 "UPDATE OR IGNORE fv SET fnm=fnp WHERE fnm IS NULL;"
484 "INSERT OR IGNORE INTO fv(fnm)"
485 " SELECT pathname FROM vfile WHERE vid=%d;",
486 mid
487 );
488
489 /*
490 ** Compute the file version ids for P and M
491 */
492 if( pid==vid ){
493 db_multi_exec(
494 "UPDATE fv SET idp=idv, ridp=ridv WHERE ridv>0 AND chnged NOT IN (3,5)"
495 );
496 }else{
497 db_multi_exec(
498 "UPDATE fv SET"
499 " idp=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnp=pathname),0),"
500 " ridp=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnp=pathname),0)",
501 pid, pid
502 );
503 }
 
 
 
 
 
 
 
 
 
 
 
 
504 db_multi_exec(
505 "UPDATE fv SET"
 
 
506 " idm=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnm=pathname),0),"
507 " ridm=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnm=pathname),0),"
508 " islinkm=coalesce((SELECT islink FROM vfile"
509 " WHERE vid=%d AND fnm=pathname),0),"
510 " isexe=coalesce((SELECT isexe FROM vfile WHERE vid=%d AND fnm=pathname),"
511 " isexe)",
512 mid, mid, mid, mid
513 );
514
515 if( debugFlag ){
516 db_prepare(&q,
517 "SELECT rowid, fn, fnp, fnm, chnged, ridv, ridp, ridm, "
518 " isexe, islinkv, islinkm, fnn FROM fv"
519 );
520 while( db_step(&q)==SQLITE_ROW ){
521 fossil_print("%3d: ridv=%-4d ridp=%-4d ridm=%-4d chnged=%d isexe=%d "
522 " islinkv=%d islinkm=%d\n",
523 db_column_int(&q, 0),
@@ -481,14 +529,36 @@
529 db_column_int(&q, 9),
530 db_column_int(&q, 10));
531 fossil_print(" fn = [%s]\n", db_column_text(&q, 1));
532 fossil_print(" fnp = [%s]\n", db_column_text(&q, 2));
533 fossil_print(" fnm = [%s]\n", db_column_text(&q, 3));
534 fossil_print(" fnn = [%s]\n", db_column_text(&q, 11));
535 }
536 db_finalize(&q);
537 }
538
539 /*
540 ** Update the execute bit on files where it's changed from P->M but not P->V
541 */
542 db_prepare(&q,
543 "SELECT idv, fn, fv.isexe FROM fv, vfile p, vfile v"
544 " WHERE p.id=idp AND v.id=idv AND fv.isexe!=p.isexe AND v.isexe=p.isexe"
545 );
546 while( db_step(&q)==SQLITE_ROW ){
547 int idv = db_column_int(&q, 0);
548 const char *zName = db_column_text(&q, 1);
549 int isExe = db_column_int(&q, 2);
550 fossil_print("%s %s\n", isExe ? "EXECUTABLE" : "UNEXEC", zName);
551 if( !dryRunFlag ){
552 char *zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
553 file_wd_setexe(zFullPath, isExe);
554 free(zFullPath);
555 db_multi_exec("UPDATE vfile SET isexe=%d WHERE id=%d", isExe, idv);
556 }
557 }
558 db_finalize(&q);
559
560 /*
561 ** Find files in M and V but not in P and report conflicts.
562 ** The file in M will be ignored. It will be treated as if it
563 ** does not exist.
564 */
@@ -500,46 +570,10 @@
570 char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm);
571 fossil_warning("WARNING: no common ancestor for %s", zName);
572 free(zName);
573 db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm);
574 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575 db_finalize(&q);
576
577 /*
578 ** Find files that have changed from P->M but not P->V.
579 ** Copy the M content over into V.
@@ -661,45 +695,117 @@
695 free(zFullPath);
696 }
697 }
698 db_finalize(&q);
699
700 /* For certain sets of renames (e.g. A -> B and B -> A), a file that is
701 ** being renamed must first be moved to a temporary location to avoid
702 ** being overwritten by another rename operation. A row is added to the
703 ** TMPRN table for each of these temporary renames.
704 */
705 db_multi_exec(
706 "DROP TABLE IF EXISTS tmprn;"
707 "CREATE TEMP TABLE tmprn(fn UNIQUE, tmpfn);"
708 );
709
710 /*
711 ** Rename files that have taken a rename on P->M but which keep the same
712 ** name on P->V. If a file is renamed on P->V only or on both P->V and
713 ** P->M then we retain the V name of the file.
714 */
715 db_prepare(&q,
716 "SELECT idv, fnp, fnm, isexe FROM fv"
717 " WHERE idv>0 AND idp>0 AND idm>0 AND fnp=fn AND fnm!=fnp"
718 );
719 while( db_step(&q)==SQLITE_ROW ){
720 int idv = db_column_int(&q, 0);
721 const char *zOldName = db_column_text(&q, 1);
722 const char *zNewName = db_column_text(&q, 2);
723 int isExe = db_column_int(&q, 3);
724 fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
725 if( !dryRunFlag ) undo_save(zOldName);
726 if( !dryRunFlag ) undo_save(zNewName);
727 db_multi_exec(
728 "UPDATE vfile SET pathname=NULL, origname=pathname"
729 " WHERE vid=%d AND pathname=%Q;"
730 "UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)"
731 " WHERE id=%d;",
732 vid, zNewName, zNewName, idv
733 );
734 if( !dryRunFlag ){
735 char *zFullOldPath, *zFullNewPath;
736 zFullOldPath = db_text(0,"SELECT tmpfn FROM tmprn WHERE fn=%Q", zOldName);
737 if( !zFullOldPath ){
738 zFullOldPath = mprintf("%s%s", g.zLocalRoot, zOldName);
739 }
740 zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
741 if( file_wd_size(zFullNewPath)>=0 ){
742 char zTmpPath[300];
743 file_tempname(sizeof(zTmpPath), zTmpPath);
744 db_multi_exec("INSERT INTO tmprn(fn,tmpfn) VALUES(%Q,%Q)",
745 zNewName, zTmpPath);
746 if( file_wd_islink(zFullNewPath) ){
747 symlink_copy(zFullNewPath, zTmpPath);
748 }else{
749 file_copy(zFullNewPath, zTmpPath);
750 }
751 }
752 if( file_wd_islink(zFullOldPath) ){
753 symlink_copy(zFullOldPath, zFullNewPath);
754 }else{
755 file_copy(zFullOldPath, zFullNewPath);
756 }
757 file_wd_setexe(zFullNewPath, isExe);
758 file_delete(zFullOldPath);
759 free(zFullNewPath);
760 free(zFullOldPath);
761 }
762 }
763 db_finalize(&q);
764
765 /* A file that has been deleted and replaced by a renamed file will have a
766 ** NULL pathname. Change it to something that makes the output of "status"
767 ** and similar commands make sense for such files and that will (most likely)
768 ** not be an actual existing pathname.
769 */
770 db_multi_exec(
771 "UPDATE vfile SET pathname=origname || ' (overwritten by rename)'"
772 " WHERE pathname IS NULL"
773 );
774
775 /*
776 ** Add to V files that are not in V or P but are in M
777 */
778 db_prepare(&q,
779 "SELECT idm, fnm FROM fv"
780 " WHERE idp=0 AND idv=0 AND idm>0"
781 );
782 while( db_step(&q)==SQLITE_ROW ){
783 int idm = db_column_int(&q, 0);
784 const char *zName;
785 char *zFullName;
786 db_multi_exec(
787 "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,isexe,islink,pathname)"
788 " SELECT %d,%d,0,rid,mrid,isexe,islink,pathname FROM vfile WHERE id=%d",
789 vid, integrateFlag?5:3, idm
790 );
791 zName = db_column_text(&q, 1);
792 zFullName = mprintf("%s%s", g.zLocalRoot, zName);
793 if( file_wd_isfile_or_link(zFullName)
794 && !db_exists("SELECT 1 FROM fv WHERE fn=%Q", zName) ){
795 fossil_print("ADDED %s (overwrites an unmanaged file)\n", zName);
796 nOverwrite++;
797 }else{
798 fossil_print("ADDED %s\n", zName);
799 }
800 fossil_free(zFullName);
801 if( !dryRunFlag ){
802 undo_save(zName);
803 vfile_to_disk(0, idm, 0, 0);
804 }
805 }
806 db_finalize(&q);
807
808 /* Report on conflicts
809 */
810 if( nConflict ){
811 fossil_warning("WARNING: %d merge conflicts", nConflict);
812
-1
--- src/path.c
+++ src/path.c
@@ -452,11 +452,10 @@
452452
if( nChng ){
453453
aChng = *aiChng = fossil_malloc( nChng*2*sizeof(int) );
454454
for(pChng=pAll, i=0; pChng; pChng=pChng->pNext){
455455
if( pChng->newName==0 ) continue;
456456
if( pChng->origName==0 ) continue;
457
- if( pChng->newName==pChng->origName ) continue;
458457
aChng[i] = pChng->origName;
459458
aChng[i+1] = pChng->newName;
460459
if( zDebug ){
461460
fossil_print("%s summary %d[%z] -> %d[%z]\n",
462461
zDebug,
463462
--- src/path.c
+++ src/path.c
@@ -452,11 +452,10 @@
452 if( nChng ){
453 aChng = *aiChng = fossil_malloc( nChng*2*sizeof(int) );
454 for(pChng=pAll, i=0; pChng; pChng=pChng->pNext){
455 if( pChng->newName==0 ) continue;
456 if( pChng->origName==0 ) continue;
457 if( pChng->newName==pChng->origName ) continue;
458 aChng[i] = pChng->origName;
459 aChng[i+1] = pChng->newName;
460 if( zDebug ){
461 fossil_print("%s summary %d[%z] -> %d[%z]\n",
462 zDebug,
463
--- src/path.c
+++ src/path.c
@@ -452,11 +452,10 @@
452 if( nChng ){
453 aChng = *aiChng = fossil_malloc( nChng*2*sizeof(int) );
454 for(pChng=pAll, i=0; pChng; pChng=pChng->pNext){
455 if( pChng->newName==0 ) continue;
456 if( pChng->origName==0 ) continue;
 
457 aChng[i] = pChng->origName;
458 aChng[i+1] = pChng->newName;
459 if( zDebug ){
460 fossil_print("%s summary %d[%z] -> %d[%z]\n",
461 zDebug,
462
+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
7373
ADDED test/merge_exe.test
--- 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
73 DDED test/merge_exe.test
--- 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
73 DDED test/merge_exe.test
--- a/test/merge_exe.test
+++ b/test/merge_exe.test
@@ -0,0 +1,93 @@
1
+#
2
+# Copyright (c) 2016 D. Richard Hipp
3
+#
4
+# This program is free software; you can redistribute it and/or
5
+# modify it under the terms of the Simplified BSD License (also
6
+# known as the "2-Clause License" or "FreeBSD License".)
7
+#
8
+# This program is distributed in the hope that it will be useful,
9
+# but without any warranty; without even the implied warranty of
10
+# merchantability or fitness for a particular purpose.
11
+#
12
+# Author contact information:
13
+# [email protected]
14
+# http://www.hwaci.com/drh/
15
+#
16
+############################################################################
17
+#
18
+# Testing changes to a file's execute bit caused by a merge
19
+#
20
+
21
+if {$tcl_platform(platform) eq "unix"} {
22
+ proc setx {fn isexe} {
23
+ file attributes $fn -permissions [expr {$isexe ? "+" : "-"}]x
24
+ }
25
+
26
+ proc test_exe {fn expected} {
27
+ test merge_exe-$fn {[file executable $fn]==$expected}
28
+ }
29
+} else {
30
+ # WARNING: This is a hack for setting and testing a file's execute bit
31
+ # on Windows. Never operate directly on Fossil database files like this
32
+ # unless you really need to and really know what you're doing.
33
+
34
+ proc query {sql} {
35
+ return [exec $::fossilexe sqlite3 --no-repository _FOSSIL_ $sql]
36
+ }
37
+
38
+ proc setx {fn isexe} {
39
+ set isexe [expr {bool($isexe)}]
40
+ query "UPDATE vfile SET isexe=$isexe WHERE pathname='$fn'"
41
+ }
42
+
43
+ proc test_exe {fn expected} {
44
+ set result [query "SELECT isexe FROM vfile WHERE pathname='$fn'"]
45
+ test merge_exe-$fn {$result==$expected}
46
+ }
47
+}
48
+
49
+test_setup
50
+
51
+write_file f1 "line"
52
+write_file f2 "line"
53
+write_file f3 "line"
54
+write_file f4 "line"
55
+fossil addremove
56
+setx f3 1
57
+setx f4 1
58
+fossil commit -m "add files"
59
+
60
+write_file f0 "f0"
61
+fossil add f0
62
+setx f0 1
63
+fossil mv --hard f1 f1n
64
+setx f1n 1
65
+write_file f2 "line\nline2"
66
+setx f2 1
67
+write_file f3 "line\nline2"
68
+setx f3 0
69
+setx f4 0
70
+fossil commit -b b -m "changes"
71
+
72
+fossil update trunk
73
+write_file f3 "line3\nline"
74
+fossil commit -m "edit f3"
75
+
76
+fossil merge b
77
+test_status_list merge_exe-mrg $RESULT {
78
+ EXECUTABLE f1
79
+ EXECUTABLE f2
80
+ UNEXEC f3
81
+ UNEXEC f4
82
+ UPDATE f2
83
+ MERGE f3
84
+ RENAME f1 -> f1n
85
+ ADDED f0
86
+}
87
+foreach {fn isexe} {f0 1 f1n 1 f2 1 f3 0 f4 0} {
88
+ test_exe $fn $isexe
89
+}
90
+
91
+###############################################################################
92
+
93
+test_cleanup
--- a/test/merge_exe.test
+++ b/test/merge_exe.test
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/test/merge_exe.test
+++ b/test/merge_exe.test
@@ -0,0 +1,93 @@
1 #
2 # Copyright (c) 2016 D. Richard Hipp
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the Simplified BSD License (also
6 # known as the "2-Clause License" or "FreeBSD License".)
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but without any warranty; without even the implied warranty of
10 # merchantability or fitness for a particular purpose.
11 #
12 # Author contact information:
13 # [email protected]
14 # http://www.hwaci.com/drh/
15 #
16 ############################################################################
17 #
18 # Testing changes to a file's execute bit caused by a merge
19 #
20
21 if {$tcl_platform(platform) eq "unix"} {
22 proc setx {fn isexe} {
23 file attributes $fn -permissions [expr {$isexe ? "+" : "-"}]x
24 }
25
26 proc test_exe {fn expected} {
27 test merge_exe-$fn {[file executable $fn]==$expected}
28 }
29 } else {
30 # WARNING: This is a hack for setting and testing a file's execute bit
31 # on Windows. Never operate directly on Fossil database files like this
32 # unless you really need to and really know what you're doing.
33
34 proc query {sql} {
35 return [exec $::fossilexe sqlite3 --no-repository _FOSSIL_ $sql]
36 }
37
38 proc setx {fn isexe} {
39 set isexe [expr {bool($isexe)}]
40 query "UPDATE vfile SET isexe=$isexe WHERE pathname='$fn'"
41 }
42
43 proc test_exe {fn expected} {
44 set result [query "SELECT isexe FROM vfile WHERE pathname='$fn'"]
45 test merge_exe-$fn {$result==$expected}
46 }
47 }
48
49 test_setup
50
51 write_file f1 "line"
52 write_file f2 "line"
53 write_file f3 "line"
54 write_file f4 "line"
55 fossil addremove
56 setx f3 1
57 setx f4 1
58 fossil commit -m "add files"
59
60 write_file f0 "f0"
61 fossil add f0
62 setx f0 1
63 fossil mv --hard f1 f1n
64 setx f1n 1
65 write_file f2 "line\nline2"
66 setx f2 1
67 write_file f3 "line\nline2"
68 setx f3 0
69 setx f4 0
70 fossil commit -b b -m "changes"
71
72 fossil update trunk
73 write_file f3 "line3\nline"
74 fossil commit -m "edit f3"
75
76 fossil merge b
77 test_status_list merge_exe-mrg $RESULT {
78 EXECUTABLE f1
79 EXECUTABLE f2
80 UNEXEC f3
81 UNEXEC f4
82 UPDATE f2
83 MERGE f3
84 RENAME f1 -> f1n
85 ADDED f0
86 }
87 foreach {fn isexe} {f0 1 f1n 1 f2 1 f3 0 f4 0} {
88 test_exe $fn $isexe
89 }
90
91 ###############################################################################
92
93 test_cleanup
--- 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,325 @@
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.1"
257
+fossil add f1
258
+fossil commit -b b1 -m "add f1"
259
+
260
+fossil update trunk
261
+write_file f1 "f1.2"
262
+fossil add f1
263
+fossil commit -b b2 -m "add f1"
264
+
265
+fossil update trunk
266
+fossil merge b1
267
+fossil merge b2
268
+test_status_list merge_renames-8-1 $RESULT {
269
+ WARNING: no common ancestor for f1
270
+}
271
+
272
+fossil revert
273
+fossil merge --integrate b1
274
+fossil merge b2
275
+test_status_list merge_renames-8-2 $RESULT {
276
+ WARNING: no common ancestor for f1
277
+}
278
+
279
+#############################################
280
+# Test 9 #
281
+# Merging a delete/rename/add combination #
282
+#############################################
283
+
284
+test_setup
285
+
286
+write_file f1 "f1"
287
+write_file f2 "f2"
288
+fossil add f1 f2
289
+fossil commit -m "add files"
290
+
291
+fossil rm --hard f2
292
+fossil commit -b b -m "delete f2"
293
+
294
+fossil mv --hard f1 f2
295
+fossil commit -m "f1 -> f2"
296
+
297
+write_file f1 "f1.1"
298
+fossil add f1
299
+fossil commit -m "add new f1"
300
+
301
+fossil update trunk
302
+fossil merge b
303
+set expectedMerge {
304
+ DELETE f2
305
+ RENAME f1 -> f2
306
+ ADDED f1
307
+}
308
+test_status_list merge_renames-9-1 $RESULT $expectedMerge
309
+fossil changes
310
+test_status_list merge_renames-9-2 $RESULT "
311
+ MERGED_WITH [commit_id b]
312
+ ADDED_BY_MERGE f1
313
+ RENAMED f2
314
+ DELETED f2 (overwritten by rename)
315
+"
316
+test_file_contents merge_renames-9-3 f1 "f1.1"
317
+test_file_contents merge_renames-9-4 f2 "f1"
318
+
319
+# Undo and ensure a dry run merge results in no changes
320
+fossil undo
321
+test_status_list merge_renames-9-5 $RESULT {
322
+ UNDO f1
323
+ UNDO f2
324
+}
325
+fossil merge -n b
326
+test_status_list merge_renames-9-6 $RESULT "
327
+ $expectedMerge
328
+ REMINDER: this was a dry run - no files were actually changed.
329
+"
330
+test merge_renames-9-7 {[fossil changes] eq ""}
331
+
332
+###################################################################
333
+# Test 10 #
334
+# Merge swapped filenames, backout the swap, then merge changes #
335
+###################################################################
336
+
337
+test_setup
338
+
339
+write_file f1 "f1"
340
+write_file f2 "f2"
341
+fossil add f1 f2
342
+fossil commit -m "add files" ;# N
343
+
344
+fossil mv --hard f1 f1-tmp
345
+fossil mv --hard f2 f1
346
+fossil mv --hard f1-tmp f2
347
+fossil commit -b b -m "swap f1, f2" ;# P
348
+
349
+fossil update trunk
350
+fossil merge b
351
+test_status_list merge_renames-10-1 $RESULT {
352
+ RENAME f1 -> f2
353
+ RENAME f2 -> f1
354
+}
355
+test_file_contents merge_renames-10-2 f1 "f2"
356
+test_file_contents merge_renames-10-3 f2 "f1"
357
+fossil commit -m "merge b"
358
+
359
+fossil update b
360
+write_file f1 f1.1
361
+write_file f2 f2.1
362
+fossil commit -m "edit" ;# M
363
+
364
+fossil update trunk
365
+fossil merge --backout trunk
366
+test_status_list merge_renames-10-4 $RESULT {
367
+ RENAME f1 -> f2
368
+ RENAME f2 -> f1
369
+}
370
+test_file_contents merge_renames-10-5 f1 "f1"
371
+test_file_contents merge_renames-10-6 f2 "f2"
372
+test_status_list merge_renames-10-7 [fossil changes] "
373
+ RENAMED f1
374
+ RENAMED f2
375
+ BACKOUT [commit_id trunk]
376
+"
377
+fossil commit -m "swap back" ;# V
378
+
379
+fossil merge b
380
+test_status_list merge_renames-10-8 $RESULT {
381
+ UPDATE f1
382
+ UPDATE f2
383
+}
384
+
385
+test_file_contents merge_renames-10-9 f1 "f2.1"
386
+test_file_contents merge_renames-10-10 f2 "f1.1"
387
+
388
+############################################
389
+# Test 11 #
390
+# Specifying a baseline #
391
+############################################
392
+
393
+test_setup
394
+
395
+write_file f1 "line"
396
+fossil add f1
397
+fossil commit -m "add f1"
398
+
399
+write_file f1 "line\nline2"
400
+fossil commit -b b -m "edit f2" --tag p1
401
+
402
+fossil mv --hard f1 f2
403
+fossil commit -m "f1 -> f2"
404
+
405
+write_file f2 "line\nline2\nline3"
406
+fossil commit -m "edit f2" --tag p2
407
+
408
+write_file f2 "line\nline2\nline3\nline4"
409
+fossil commit -m "edit f2"
410
+
411
+fossil update trunk
412
+fossil merge --baseline p1 b
413
+test_status_list merge_renames-11-1 $RESULT {
414
+ MERGE f1
415
+ RENAME f1 -> f2
416
+}
417
+test_file_contents merge_renames-11-2 f2 "line\nline3\nline4"
418
+fossil revert
419
+fossil merge --baseline p2 b
420
+test_status_list merge_renames-11-3 $RESULT {MERGE f1}
421
+test_file_contents merge_renames-11-4 f1 "line\nline4"
422
+
423
+#################################################################
424
+# Test 12 #
425
+# Merge involving a pivot that isn't a first-parent ancestor #
426
+# of either the checked-out commit or the commit being merged #
427
+#################################################################
428
+
429
+test_setup
430
+
431
+write_file f1 "f1\n"
432
+fossil add f1
433
+fossil commit -m "add f1" --tag n
434
+
435
+fossil mv --hard f1 f1n
436
+fossil commit -m "f1 -> f1n"
437
+
438
+fossil mv --hard f1n f1v
439
+write_file f1v "f1v\n"
440
+fossil commit -b v -m "f1n -> f1v, edit f1v"
441
+
442
+fossil update trunk
443
+fossil mv --hard f1n f1m
444
+fossil commit -b m -m "f1n -> f1m"
445
+
446
+fossil update n
447
+fossil mv --hard f1 f1p
448
+write_file f1p "f1\np"
449
+fossil commit -b p -m "f1 -> f1p, edit f1p"
450
+
451
+fossil update m
452
+fossil merge p
453
+test_status_list merge_renames-12-1 $RESULT {UPDATE f1m}
454
+test_file_contents merge_renames-12-2 f1m "f1\np"
455
+fossil commit -m "merge p"
456
+
457
+write_file f1m "f1\nm"
458
+fossil commit -m "edit f1m"
459
+
460
+fossil update v
461
+fossil merge p
462
+test_status_list merge_renames-12-3 $RESULT {MERGE f1v}
463
+test_file_contents merge_renames-12-4 f1v "f1v\np"
464
+fossil commit -m "merge p"
465
+
466
+fossil merge m
467
+test_status_list merge_renames-12-5 $RESULT {MERGE f1v}
468
+test_file_contents merge_renames-12-6 f1v "f1v\nm"
469
+fossil commit -m "merge m"
200470
201471
######################################
202472
#
203473
# Tests for troubles not specifically linked with renames but that I'd like to
204474
# write:
205475
--- 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,325 @@
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,325 @@
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.1"
257 fossil add f1
258 fossil commit -b b1 -m "add f1"
259
260 fossil update trunk
261 write_file f1 "f1.2"
262 fossil add f1
263 fossil commit -b b2 -m "add f1"
264
265 fossil update trunk
266 fossil merge b1
267 fossil merge b2
268 test_status_list merge_renames-8-1 $RESULT {
269 WARNING: no common ancestor for f1
270 }
271
272 fossil revert
273 fossil merge --integrate b1
274 fossil merge b2
275 test_status_list merge_renames-8-2 $RESULT {
276 WARNING: no common ancestor for f1
277 }
278
279 #############################################
280 # Test 9 #
281 # Merging a delete/rename/add combination #
282 #############################################
283
284 test_setup
285
286 write_file f1 "f1"
287 write_file f2 "f2"
288 fossil add f1 f2
289 fossil commit -m "add files"
290
291 fossil rm --hard f2
292 fossil commit -b b -m "delete f2"
293
294 fossil mv --hard f1 f2
295 fossil commit -m "f1 -> f2"
296
297 write_file f1 "f1.1"
298 fossil add f1
299 fossil commit -m "add new f1"
300
301 fossil update trunk
302 fossil merge b
303 set expectedMerge {
304 DELETE f2
305 RENAME f1 -> f2
306 ADDED f1
307 }
308 test_status_list merge_renames-9-1 $RESULT $expectedMerge
309 fossil changes
310 test_status_list merge_renames-9-2 $RESULT "
311 MERGED_WITH [commit_id b]
312 ADDED_BY_MERGE f1
313 RENAMED f2
314 DELETED f2 (overwritten by rename)
315 "
316 test_file_contents merge_renames-9-3 f1 "f1.1"
317 test_file_contents merge_renames-9-4 f2 "f1"
318
319 # Undo and ensure a dry run merge results in no changes
320 fossil undo
321 test_status_list merge_renames-9-5 $RESULT {
322 UNDO f1
323 UNDO f2
324 }
325 fossil merge -n b
326 test_status_list merge_renames-9-6 $RESULT "
327 $expectedMerge
328 REMINDER: this was a dry run - no files were actually changed.
329 "
330 test merge_renames-9-7 {[fossil changes] eq ""}
331
332 ###################################################################
333 # Test 10 #
334 # Merge swapped filenames, backout the swap, then merge changes #
335 ###################################################################
336
337 test_setup
338
339 write_file f1 "f1"
340 write_file f2 "f2"
341 fossil add f1 f2
342 fossil commit -m "add files" ;# N
343
344 fossil mv --hard f1 f1-tmp
345 fossil mv --hard f2 f1
346 fossil mv --hard f1-tmp f2
347 fossil commit -b b -m "swap f1, f2" ;# P
348
349 fossil update trunk
350 fossil merge b
351 test_status_list merge_renames-10-1 $RESULT {
352 RENAME f1 -> f2
353 RENAME f2 -> f1
354 }
355 test_file_contents merge_renames-10-2 f1 "f2"
356 test_file_contents merge_renames-10-3 f2 "f1"
357 fossil commit -m "merge b"
358
359 fossil update b
360 write_file f1 f1.1
361 write_file f2 f2.1
362 fossil commit -m "edit" ;# M
363
364 fossil update trunk
365 fossil merge --backout trunk
366 test_status_list merge_renames-10-4 $RESULT {
367 RENAME f1 -> f2
368 RENAME f2 -> f1
369 }
370 test_file_contents merge_renames-10-5 f1 "f1"
371 test_file_contents merge_renames-10-6 f2 "f2"
372 test_status_list merge_renames-10-7 [fossil changes] "
373 RENAMED f1
374 RENAMED f2
375 BACKOUT [commit_id trunk]
376 "
377 fossil commit -m "swap back" ;# V
378
379 fossil merge b
380 test_status_list merge_renames-10-8 $RESULT {
381 UPDATE f1
382 UPDATE f2
383 }
384
385 test_file_contents merge_renames-10-9 f1 "f2.1"
386 test_file_contents merge_renames-10-10 f2 "f1.1"
387
388 ############################################
389 # Test 11 #
390 # Specifying a baseline #
391 ############################################
392
393 test_setup
394
395 write_file f1 "line"
396 fossil add f1
397 fossil commit -m "add f1"
398
399 write_file f1 "line\nline2"
400 fossil commit -b b -m "edit f2" --tag p1
401
402 fossil mv --hard f1 f2
403 fossil commit -m "f1 -> f2"
404
405 write_file f2 "line\nline2\nline3"
406 fossil commit -m "edit f2" --tag p2
407
408 write_file f2 "line\nline2\nline3\nline4"
409 fossil commit -m "edit f2"
410
411 fossil update trunk
412 fossil merge --baseline p1 b
413 test_status_list merge_renames-11-1 $RESULT {
414 MERGE f1
415 RENAME f1 -> f2
416 }
417 test_file_contents merge_renames-11-2 f2 "line\nline3\nline4"
418 fossil revert
419 fossil merge --baseline p2 b
420 test_status_list merge_renames-11-3 $RESULT {MERGE f1}
421 test_file_contents merge_renames-11-4 f1 "line\nline4"
422
423 #################################################################
424 # Test 12 #
425 # Merge involving a pivot that isn't a first-parent ancestor #
426 # of either the checked-out commit or the commit being merged #
427 #################################################################
428
429 test_setup
430
431 write_file f1 "f1\n"
432 fossil add f1
433 fossil commit -m "add f1" --tag n
434
435 fossil mv --hard f1 f1n
436 fossil commit -m "f1 -> f1n"
437
438 fossil mv --hard f1n f1v
439 write_file f1v "f1v\n"
440 fossil commit -b v -m "f1n -> f1v, edit f1v"
441
442 fossil update trunk
443 fossil mv --hard f1n f1m
444 fossil commit -b m -m "f1n -> f1m"
445
446 fossil update n
447 fossil mv --hard f1 f1p
448 write_file f1p "f1\np"
449 fossil commit -b p -m "f1 -> f1p, edit f1p"
450
451 fossil update m
452 fossil merge p
453 test_status_list merge_renames-12-1 $RESULT {UPDATE f1m}
454 test_file_contents merge_renames-12-2 f1m "f1\np"
455 fossil commit -m "merge p"
456
457 write_file f1m "f1\nm"
458 fossil commit -m "edit f1m"
459
460 fossil update v
461 fossil merge p
462 test_status_list merge_renames-12-3 $RESULT {MERGE f1v}
463 test_file_contents merge_renames-12-4 f1v "f1v\np"
464 fossil commit -m "merge p"
465
466 fossil merge m
467 test_status_list merge_renames-12-5 $RESULT {MERGE f1v}
468 test_file_contents merge_renames-12-6 f1v "f1v\nm"
469 fossil commit -m "merge m"
470
471 ######################################
472 #
473 # Tests for troubles not specifically linked with renames but that I'd like to
474 # write:
475
--- 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