Fossil

Check-in [3a39ae4c]
Login

Check-in [3a39ae4c]

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

Overview
Comment:Fix a bug in the "fossil patch pull" command that caused "fossil revert" that occurs after a "fossil merge" to be lost. Also, improve "fossil patch pull" so that it does not mess with the undo log and does not do unnecessary syncs.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 3a39ae4c87773acf4c86e5229c49d7cd4a0c851a8222776834ce47e9dac18e77
User & Date: drh 2024-12-16 16:17:49
References
2024-12-16
16:21 Reply: Bug in "fossil patch pull" ... (artifact: 2456e414 user: drh)
Context
2024-12-16
16:22
Add missing word in the help text for 'merge'. ... (check-in: bdc6bb1c user: danield tags: trunk)
16:17
Fix a bug in the "fossil patch pull" command that caused "fossil revert" that occurs after a "fossil merge" to be lost. Also, improve "fossil patch pull" so that it does not mess with the undo log and does not do unnecessary syncs. ... (check-in: 3a39ae4c user: drh tags: trunk)
16:15
Do not sync or fill the undo log when running subcommands due to patch. ... (Closed-Leaf check-in: cf8f0e4d user: drh tags: patch-pull-fix)
07:40
Internal tweaks and docs in the diff-toggling code. No visible changes. ... (check-in: 6f263954 user: stephan tags: trunk)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/merge.c.

685
686
687
688
689
690
691

692
693
694
695
696
697
698
**   --force-missing         Force the merge even if there is missing content
**   --integrate             Merged branch will be closed when committing
**   -K|--keep-merge-files   On merge conflict, retain the temporary files
**                           used for merging, named *-baseline, *-original,
**                           and *-merge.
**   -n|--dry-run            Do not actually change files on disk
**   --nosync                Do not auto-sync prior to merging

**   -v|--verbose            Show additional details of the merge
*/
void merge_cmd(void){
  int vid;              /* Current version "V" */
  int mid;              /* Version we are merging from "M" */
  int pid = 0;          /* The pivot version - most recent common ancestor P */
  int nid = 0;          /* The name pivot version "N" */







>







685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
**   --force-missing         Force the merge even if there is missing content
**   --integrate             Merged branch will be closed when committing
**   -K|--keep-merge-files   On merge conflict, retain the temporary files
**                           used for merging, named *-baseline, *-original,
**                           and *-merge.
**   -n|--dry-run            Do not actually change files on disk
**   --nosync                Do not auto-sync prior to merging
**   --noundo                Do not changes in the undo log
**   -v|--verbose            Show additional details of the merge
*/
void merge_cmd(void){
  int vid;              /* Current version "V" */
  int mid;              /* Version we are merging from "M" */
  int pid = 0;          /* The pivot version - most recent common ancestor P */
  int nid = 0;          /* The name pivot version "N" */
710
711
712
713
714
715
716

717
718
719
720
721
722
723
  int keepMergeFlag;    /* True if --keep-merge-files is present */
  int nConflict = 0;    /* Number of conflicts seen */
  int nOverwrite = 0;   /* Number of unmanaged files overwritten */
  char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */
  const char *zVersion; /* The VERSION argument */
  int bMultiMerge = 0;  /* True if there are two or more VERSION arguments */
  int nMerge = 0;       /* Number of prior merges processed */

  Stmt q;               /* SQL statment used for merge processing */


  /* Notation:
  **
  **      V     The current check-out
  **      M     The version being merged in







>







711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
  int keepMergeFlag;    /* True if --keep-merge-files is present */
  int nConflict = 0;    /* Number of conflicts seen */
  int nOverwrite = 0;   /* Number of unmanaged files overwritten */
  char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */
  const char *zVersion; /* The VERSION argument */
  int bMultiMerge = 0;  /* True if there are two or more VERSION arguments */
  int nMerge = 0;       /* Number of prior merges processed */
  int useUndo = 1;      /* True to record changes in the undo log */
  Stmt q;               /* SQL statment used for merge processing */


  /* Notation:
  **
  **      V     The current check-out
  **      M     The version being merged in
758
759
760
761
762
763
764


765
766
767
768
769
770
771
  ** Hints:
  **   *  Combine --debug and --verbose for still more output.
  **   *  The --dry-run option is also useful in combination with --debug.
  */
  debugFlag = find_option("debug",0,0)!=0;
  if( debugFlag && verboseFlag ) debugFlag = 2;
  showVfileFlag = find_option("show-vfile",0,0)!=0;



  verify_all_options();
  db_must_be_within_tree();
  if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
  vid = db_lget_int("checkout", 0);
  if( vid==0 ){
    fossil_fatal("nothing is checked out");







>
>







760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
  ** Hints:
  **   *  Combine --debug and --verbose for still more output.
  **   *  The --dry-run option is also useful in combination with --debug.
  */
  debugFlag = find_option("debug",0,0)!=0;
  if( debugFlag && verboseFlag ) debugFlag = 2;
  showVfileFlag = find_option("show-vfile",0,0)!=0;
  useUndo = find_option("noundo",0,0)==0;
  if( dryRunFlag ) useUndo = 0;

  verify_all_options();
  db_must_be_within_tree();
  if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
  vid = db_lget_int("checkout", 0);
  if( vid==0 ){
    fossil_fatal("nothing is checked out");
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
  if( verboseFlag ){
    print_checkin_description(mid, 12,
              integrateFlag ? "integrate:" : "merge-from:");
    print_checkin_description(pid, 12, "baseline:");
  }
  vfile_check_signature(vid, CKSIG_ENOTFILE);
  if( nMerge==0 ) db_begin_transaction();
  if( !dryRunFlag ) undo_begin();
  if( load_vfile_from_rid(mid) && !forceMissingFlag ){
    fossil_fatal("missing content, unable to merge");
  }
  if( load_vfile_from_rid(pid) && !forceMissingFlag ){
    fossil_fatal("missing content, unable to merge");
  }
  if( zPivot ){







|







928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
  if( verboseFlag ){
    print_checkin_description(mid, 12,
              integrateFlag ? "integrate:" : "merge-from:");
    print_checkin_description(pid, 12, "baseline:");
  }
  vfile_check_signature(vid, CKSIG_ENOTFILE);
  if( nMerge==0 ) db_begin_transaction();
  if( useUndo ) undo_begin();
  if( load_vfile_from_rid(mid) && !forceMissingFlag ){
    fossil_fatal("missing content, unable to merge");
  }
  if( load_vfile_from_rid(pid) && !forceMissingFlag ){
    fossil_fatal("missing content, unable to merge");
  }
  if( zPivot ){
1181
1182
1183
1184
1185
1186
1187

1188
1189
1190
1191
1192
1193
1194
1195
1196
  while( db_step(&q)==SQLITE_ROW ){
    int idv = db_column_int(&q, 0);
    int ridm = db_column_int(&q, 1);
    const char *zName = db_column_text(&q, 2);
    int islinkm = db_column_int(&q, 3);
    /* Copy content from idm over into idv.  Overwrite idv. */
    fossil_print("UPDATE %s\n", zName);

    if( !dryRunFlag ){
      undo_save(zName);
      db_multi_exec(
        "UPDATE vfile SET mtime=0, mrid=%d, chnged=%d, islink=%d,"
        " mhash=CASE WHEN rid<>%d"
                   " THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END"
        " WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv
      );
      vfile_to_disk(0, idv, 0, 0);







>

<







1185
1186
1187
1188
1189
1190
1191
1192
1193

1194
1195
1196
1197
1198
1199
1200
  while( db_step(&q)==SQLITE_ROW ){
    int idv = db_column_int(&q, 0);
    int ridm = db_column_int(&q, 1);
    const char *zName = db_column_text(&q, 2);
    int islinkm = db_column_int(&q, 3);
    /* Copy content from idm over into idv.  Overwrite idv. */
    fossil_print("UPDATE %s\n", zName);
    if( useUndo ) undo_save(zName);
    if( !dryRunFlag ){

      db_multi_exec(
        "UPDATE vfile SET mtime=0, mrid=%d, chnged=%d, islink=%d,"
        " mhash=CASE WHEN rid<>%d"
                   " THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END"
        " WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv
      );
      vfile_to_disk(0, idv, 0, 0);
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
        /* fnr  */ zName
      ); 
    }else{
      i64 sz;
      const char *zErrMsg = 0;
      int nc = 0;

      if( !dryRunFlag ) undo_save(zName);
      zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
      sz = file_size(zFullPath, ExtFILE);
      content_get(ridp, &p);
      content_get(ridm, &m);
      if( isBinary ){
        rc = -1;
        blob_zero(&r);







|







1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
        /* fnr  */ zName
      ); 
    }else{
      i64 sz;
      const char *zErrMsg = 0;
      int nc = 0;

      if( useUndo ) undo_save(zName);
      zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
      sz = file_size(zFullPath, ExtFILE);
      content_get(ridp, &p);
      content_get(ridm, &m);
      if( isBinary ){
        rc = -1;
        blob_zero(&r);
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
      ridv = 0;
      nc = 1;
      zErrMsg = "local edits lost";
      zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
      sz = file_size(zFullPath, ExtFILE);
      fossil_free(zFullPath);
    }
    if( !dryRunFlag ) undo_save(zName);
    db_multi_exec(
      "UPDATE vfile SET deleted=1 WHERE id=%d", idv
    );
    if( !dryRunFlag ){
      char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
      file_delete(zFullPath);
      free(zFullPath);







|







1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
      ridv = 0;
      nc = 1;
      zErrMsg = "local edits lost";
      zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
      sz = file_size(zFullPath, ExtFILE);
      fossil_free(zFullPath);
    }
    if( useUndo ) undo_save(zName);
    db_multi_exec(
      "UPDATE vfile SET deleted=1 WHERE id=%d", idv
    );
    if( !dryRunFlag ){
      char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
      file_delete(zFullPath);
      free(zFullPath);
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
  );
  while( db_step(&q)==SQLITE_ROW ){
    int idv = db_column_int(&q, 0);
    const char *zOldName = db_column_text(&q, 1);
    const char *zNewName = db_column_text(&q, 2);
    int isExe = db_column_int(&q, 3);
    fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
    if( !dryRunFlag ) undo_save(zOldName);
    if( !dryRunFlag ) undo_save(zNewName);
    db_multi_exec(
      "UPDATE mergestat SET fnr=fnm WHERE fnp=%Q",
      zOldName
    );
    db_multi_exec(
      "UPDATE vfile SET pathname=NULL, origname=pathname"
      " WHERE vid=%d AND pathname=%Q;"







|
|







1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
  );
  while( db_step(&q)==SQLITE_ROW ){
    int idv = db_column_int(&q, 0);
    const char *zOldName = db_column_text(&q, 1);
    const char *zNewName = db_column_text(&q, 2);
    int isExe = db_column_int(&q, 3);
    fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
    if( useUndo ) undo_save(zOldName);
    if( useUndo ) undo_save(zNewName);
    db_multi_exec(
      "UPDATE mergestat SET fnr=fnm WHERE fnp=%Q",
      zOldName
    );
    db_multi_exec(
      "UPDATE vfile SET pathname=NULL, origname=pathname"
      " WHERE vid=%d AND pathname=%Q;"
1496
1497
1498
1499
1500
1501
1502

1503
1504
1505
1506
1507
1508
1509
1510
1511
    db_multi_exec(
      "INSERT INTO mergestat(op,fnm,ridm,fnr)"
      "VALUES('ADDED',%Q,%d,%Q)",
      /* fnm  */ zName,
      /* ridm */ db_column_int(&q,2),
      /* fnr  */ zName
    );

    if( !dryRunFlag ){
      undo_save(zName);
      vfile_to_disk(0, idm, 0, 0);
    }
  }
  db_finalize(&q);

  /* Report on conflicts
  */







>

<







1500
1501
1502
1503
1504
1505
1506
1507
1508

1509
1510
1511
1512
1513
1514
1515
    db_multi_exec(
      "INSERT INTO mergestat(op,fnm,ridm,fnr)"
      "VALUES('ADDED',%Q,%d,%Q)",
      /* fnm  */ zName,
      /* ridm */ db_column_int(&q,2),
      /* fnr  */ zName
    );
    if( useUndo ) undo_save(zName);
    if( !dryRunFlag ){

      vfile_to_disk(0, idm, 0, 0);
    }
  }
  db_finalize(&q);

  /* Report on conflicts
  */
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
  }else{
    vmerge_insert(0, mid);
  }
  if( bMultiMerge && nConflict==0 ){
    nMerge++;
    goto merge_next_child;
  }
  if( !dryRunFlag ) undo_finish();

  db_end_transaction(dryRunFlag);
}







|



1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
  }else{
    vmerge_insert(0, mid);
  }
  if( bMultiMerge && nConflict==0 ){
    nMerge++;
    goto merge_next_child;
  }
  if( useUndo ) undo_finish();

  db_end_transaction(dryRunFlag);
}

Changes to src/patch.c.

385
386
387
388
389
390
391
392
393
394
395
396
397
398
399

  blob_init(&cmd, 0, 0);
  if( unsaved_changes(0) ){
    if( (mFlags & PATCH_FORCE)==0 ){
      fossil_fatal("Cannot apply patch: there are unsaved changes "
                   "in the current check-out");
    }else{
      blob_appendf(&cmd, "%$ revert", g.nameOfExe);
      if( mFlags & PATCH_DRYRUN ){
        fossil_print("%s\n", blob_str(&cmd));
      }else{
        int rc = fossil_system(blob_str(&cmd));
        if( rc ){
          fossil_fatal("unable to revert preexisting changes: %s",
                       blob_str(&cmd));







|







385
386
387
388
389
390
391
392
393
394
395
396
397
398
399

  blob_init(&cmd, 0, 0);
  if( unsaved_changes(0) ){
    if( (mFlags & PATCH_FORCE)==0 ){
      fossil_fatal("Cannot apply patch: there are unsaved changes "
                   "in the current check-out");
    }else{
      blob_appendf(&cmd, "%$ revert --noundo", g.nameOfExe);
      if( mFlags & PATCH_DRYRUN ){
        fossil_print("%s\n", blob_str(&cmd));
      }else{
        int rc = fossil_system(blob_str(&cmd));
        if( rc ){
          fossil_fatal("unable to revert preexisting changes: %s",
                       blob_str(&cmd));
427
428
429
430
431
432
433

434
435
436
437
438
439
440
441
442
443

444
445

446

447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462



































463
464
465
466
467
468
469
        fossil_fatal("unable to update to the baseline check-out: %s",
                     blob_str(&cmd));
      }
    }
  }
  blob_reset(&cmd);
  if( db_table_exists("patch","patchmerge") ){

    db_prepare(&q,
      "SELECT type, mhash, upper(type) FROM patch.patchmerge"
      " WHERE type IN ('merge','cherrypick','backout','integrate')"
      "   AND mhash NOT GLOB '*[^a-fA-F0-9]*';"
    );
    while( db_step(&q)==SQLITE_ROW ){
      const char *zType = db_column_text(&q,0);
      blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
      if( strcmp(zType,"merge")==0 ){
        blob_appendf(&cmd, " merge %s\n", db_column_text(&q,1));

      }else{
        blob_appendf(&cmd, " merge --%s %s\n", zType, db_column_text(&q,1));

      }

      if( mFlags & PATCH_VERBOSE ){
        fossil_print("%-10s %s\n", db_column_text(&q,2),
                    db_column_text(&q,0));
      }
    }
    db_finalize(&q);
    if( mFlags & PATCH_DRYRUN ){
      fossil_print("%s", blob_str(&cmd));
    }else{
      int rc = fossil_unsafe_system(blob_str(&cmd));
      if( rc ){
        fossil_fatal("unable to do merges:\n%s",
                     blob_str(&cmd));
      }
    }
    blob_reset(&cmd);



































  }

  /* Deletions */
  db_prepare(&q, "SELECT pathname FROM patch.chng"
                 " WHERE origname IS NULL AND delta IS NULL");
  while( db_step(&q)==SQLITE_ROW ){
    if( blob_size(&cmd)==0 ){







>









|
>

|
>

>
















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







427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
        fossil_fatal("unable to update to the baseline check-out: %s",
                     blob_str(&cmd));
      }
    }
  }
  blob_reset(&cmd);
  if( db_table_exists("patch","patchmerge") ){
    int nMerge = 0;
    db_prepare(&q,
      "SELECT type, mhash, upper(type) FROM patch.patchmerge"
      " WHERE type IN ('merge','cherrypick','backout','integrate')"
      "   AND mhash NOT GLOB '*[^a-fA-F0-9]*';"
    );
    while( db_step(&q)==SQLITE_ROW ){
      const char *zType = db_column_text(&q,0);
      blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
      if( strcmp(zType,"merge")==0 ){
        blob_appendf(&cmd, " merge --noundo --nosync %s\n",
                     db_column_text(&q,1));
      }else{
        blob_appendf(&cmd, " merge --%s --noundo --nosync %s\n",
                     zType, db_column_text(&q,1));
      }
      nMerge++;
      if( mFlags & PATCH_VERBOSE ){
        fossil_print("%-10s %s\n", db_column_text(&q,2),
                    db_column_text(&q,0));
      }
    }
    db_finalize(&q);
    if( mFlags & PATCH_DRYRUN ){
      fossil_print("%s", blob_str(&cmd));
    }else{
      int rc = fossil_unsafe_system(blob_str(&cmd));
      if( rc ){
        fossil_fatal("unable to do merges:\n%s",
                     blob_str(&cmd));
      }
    }
    blob_reset(&cmd);

    /* 2024-12-16 https://fossil-scm.org/home/forumpost/51a37054
    ** If one or more merge operations occurred in the patch and there are
    ** files that are marked as "chnged' in the local VFILE but which
    ** are not mentioned as having been modified in the patch, then
    ** revert those files.
    */
    if( nMerge ){
      int vid = db_lget_int("checkout", 0);
      int nRevert = 0;
      blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
      blob_appendf(&cmd, " revert --noundo ");
      db_prepare(&q,
        "SELECT pathname FROM vfile WHERE vid=%d AND chnged "
        "EXCEPT SELECT pathname FROM chng",
        vid
      );
      while( db_step(&q)==SQLITE_ROW ){
        blob_append_escaped_arg(&cmd, db_column_text(&q,0), 1);
        nRevert++;
      }
      db_finalize(&q);
      if( nRevert ){
        if( mFlags & PATCH_DRYRUN ){
          fossil_print("%s", blob_str(&cmd));
        }else{
          int rc = fossil_unsafe_system(blob_str(&cmd));
          if( rc ){
            fossil_fatal("unable to do reverts:\n%s",
                         blob_str(&cmd));
          }
        }
      }
      blob_reset(&cmd);
    }
  }

  /* Deletions */
  db_prepare(&q, "SELECT pathname FROM patch.chng"
                 " WHERE origname IS NULL AND delta IS NULL");
  while( db_step(&q)==SQLITE_ROW ){
    if( blob_size(&cmd)==0 ){

Changes to src/update.c.

852
853
854
855
856
857
858

859
860
861
862
863
864
865
866
867
868
869
870
871

872
873
874
875
876
877
878

879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894

895



896
897
898
899
900
901
902
**
** Revert all files if no file name is provided.
**
** If a file is reverted accidentally, it can be restored using
** the "fossil undo" command.
**
** Options:

**   -r|--revision VERSION    Revert given FILE(s) back to given
**                            VERSION
**
** See also: [[redo]], [[undo]], [[checkout]], [[update]]
*/
void revert_cmd(void){
  Manifest *pCoManifest;          /* Manifest of current check-out */
  Manifest *pRvManifest;          /* Manifest of selected revert version */
  ManifestFile *pCoFile;          /* File within current check-out manifest */
  ManifestFile *pRvFile;          /* File within revert version manifest */
  const char *zFile;              /* Filename relative to check-out root */
  const char *zRevision;          /* Selected revert version, NULL if current */
  Blob record = BLOB_INITIALIZER; /* Contents of each reverted file */

  int i;
  Stmt q;
  int revertAll = 0;
  int revisionOptNotSupported = 0;

  undo_capture_command_line();
  zRevision = find_option("revision", "r", 1);

  verify_all_options();

  if( g.argc<2 ){
    usage("?OPTIONS? [FILE] ...");
  }
  if( zRevision && g.argc<3 ){
    fossil_fatal("directories or the entire tree can only be reverted"
                 " back to current version");
  }
  db_must_be_within_tree();

  /* Get manifests of revert version and (if different) current check-out. */
  pRvManifest = historical_manifest(zRevision);
  pCoManifest = zRevision ? historical_manifest(0) : 0;

  db_begin_transaction();

  undo_begin();



  db_multi_exec("CREATE TEMP TABLE torevert(name UNIQUE);");

  if( g.argc>2 ){
    for(i=2; i<g.argc; i++){
      Blob fname;
      zFile = mprintf("%/", g.argv[i]);
      blob_zero(&fname);







>













>







>
















>
|
>
>
>







852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
**
** Revert all files if no file name is provided.
**
** If a file is reverted accidentally, it can be restored using
** the "fossil undo" command.
**
** Options:
**   --noundo                 Do not record changes in the undo/redo log.
**   -r|--revision VERSION    Revert given FILE(s) back to given
**                            VERSION
**
** See also: [[redo]], [[undo]], [[checkout]], [[update]]
*/
void revert_cmd(void){
  Manifest *pCoManifest;          /* Manifest of current check-out */
  Manifest *pRvManifest;          /* Manifest of selected revert version */
  ManifestFile *pCoFile;          /* File within current check-out manifest */
  ManifestFile *pRvFile;          /* File within revert version manifest */
  const char *zFile;              /* Filename relative to check-out root */
  const char *zRevision;          /* Selected revert version, NULL if current */
  Blob record = BLOB_INITIALIZER; /* Contents of each reverted file */
  int useUndo = 1;                /* True to record changes in UNDO */
  int i;
  Stmt q;
  int revertAll = 0;
  int revisionOptNotSupported = 0;

  undo_capture_command_line();
  zRevision = find_option("revision", "r", 1);
  useUndo = find_option("noundo", 0, 0)==0;
  verify_all_options();

  if( g.argc<2 ){
    usage("?OPTIONS? [FILE] ...");
  }
  if( zRevision && g.argc<3 ){
    fossil_fatal("directories or the entire tree can only be reverted"
                 " back to current version");
  }
  db_must_be_within_tree();

  /* Get manifests of revert version and (if different) current check-out. */
  pRvManifest = historical_manifest(zRevision);
  pCoManifest = zRevision ? historical_manifest(0) : 0;

  db_begin_transaction();
  if( useUndo ){
    undo_begin();
  }else{
    undo_reset();
  }
  db_multi_exec("CREATE TEMP TABLE torevert(name UNIQUE);");

  if( g.argc>2 ){
    for(i=2; i<g.argc; i++){
      Blob fname;
      zFile = mprintf("%/", g.argv[i]);
      blob_zero(&fname);
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
    zFull = mprintf("%/%/", g.zLocalRoot, zFile);
    pRvFile = pRvManifest? manifest_file_find(pRvManifest, zFile) : 0;
    if( !pRvFile ){
      if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q",
                 zFile, zFile)==0 ){
        fossil_print("UNMANAGE %s\n", zFile);
      }else{
        undo_save(zFile);
        file_delete(zFull);
        fossil_print("DELETE   %s\n", zFile);
      }
      db_multi_exec(
        "UPDATE OR REPLACE vfile"
        "   SET pathname=origname, origname=NULL"
        " WHERE pathname=%Q AND origname!=pathname;"







|







992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
    zFull = mprintf("%/%/", g.zLocalRoot, zFile);
    pRvFile = pRvManifest? manifest_file_find(pRvManifest, zFile) : 0;
    if( !pRvFile ){
      if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q",
                 zFile, zFile)==0 ){
        fossil_print("UNMANAGE %s\n", zFile);
      }else{
        if( useUndo ) undo_save(zFile);
        file_delete(zFull);
        fossil_print("DELETE   %s\n", zFile);
      }
      db_multi_exec(
        "UPDATE OR REPLACE vfile"
        "   SET pathname=origname, origname=NULL"
        " WHERE pathname=%Q AND origname!=pathname;"
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
        rvChnged = manifest_file_mperm(pRvFile)!=rvPerm
                || fossil_strcmp(pRvFile->zUuid, pCoFile->zUuid)!=0;
      }

      /* Get contents of reverted-to file. */
      content_get(fast_uuid_to_rid(pRvFile->zUuid), &record);

      undo_save(zFile);
      if( file_size(zFull, RepoFILE)>=0
       && (rvPerm==PERM_LNK || file_islink(0))
      ){
        file_delete(zFull);
      }
      if( rvPerm==PERM_LNK ){
        symlink_create(blob_str(&record), zFull);







|







1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
        rvChnged = manifest_file_mperm(pRvFile)!=rvPerm
                || fossil_strcmp(pRvFile->zUuid, pCoFile->zUuid)!=0;
      }

      /* Get contents of reverted-to file. */
      content_get(fast_uuid_to_rid(pRvFile->zUuid), &record);

      if( useUndo ) undo_save(zFile);
      if( file_size(zFull, RepoFILE)>=0
       && (rvPerm==PERM_LNK || file_islink(0))
      ){
        file_delete(zFull);
      }
      if( rvPerm==PERM_LNK ){
        symlink_create(blob_str(&record), zFull);
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
         mtime, rvChnged, rvPerm==PERM_EXE, rvPerm==PERM_LNK, zFile, zFile
      );
    }
    blob_reset(&record);
    free(zFull);
  }
  db_finalize(&q);
  undo_finish();
  db_end_transaction(0);

  /* Deallocate parsed manifest structures. */
  manifest_destroy(pRvManifest);
  manifest_destroy(pCoManifest);
}







|






1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
         mtime, rvChnged, rvPerm==PERM_EXE, rvPerm==PERM_LNK, zFile, zFile
      );
    }
    blob_reset(&record);
    free(zFull);
  }
  db_finalize(&q);
  if( useUndo) undo_finish();
  db_end_transaction(0);

  /* Deallocate parsed manifest structures. */
  manifest_destroy(pRvManifest);
  manifest_destroy(pCoManifest);
}