Fossil

Check-in [a5c85348]
Login

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

Overview
Comment:Make "fossil clean -x" less dangerous by respecting the "keep-glob" setting. Fix a few historical merge errors
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | cleanX
Files: files | file ages | folders
SHA1:a5c85348c60125b6092f1b8802eee7b247377d21
User & Date: jan.nijtmans 2014-03-23 10:11:22
Context
2014-03-23
16:13
Remove some dead code (matchKeep is always false) and improve efficiency (only do glob_match() when extremeFlags is set, otherwise we already know it will be false) check-in: b0b723fb user: jan.nijtmans tags: cleanX
10:11
Make "fossil clean -x" less dangerous by respecting the "keep-glob" setting. Fix a few historical merge errors check-in: a5c85348 user: jan.nijtmans tags: cleanX
09:42
merge trunk check-in: d7f04ea4 user: jan.nijtmans tags: cleanX
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/checkin.c.

493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
...
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
...
559
560
561
562
563
564
565



566
567
568
569
570
571
572
...
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
...
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
...
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
** files that are not officially part of the checkout. This operation
** cannot be undone. If paths are specified, only the directories or
** files specified will be considered for cleaning.
**
** WARNING:  Normally, only the files unknown to Fossil are removed;
** however, if the --extreme option is specified, all files that are
** not part of the current checkout will be removed as well, without
** regard for the "ignore-glob" and "keep-glob" settings and their
** associated command line options.
**
** You will be prompted before removing each eligible file unless the
** --force flag is in use or it matches the --clean option.  The
** GLOBPATTERN specified by the "ignore-glob" setting is used if the
** --ignore option is omitted, the same with "clean-glob" and --clean
** as well as "keep-glob" and --keep.  If you are sure you wish to
** remove all "extra" files except the ones specified with --ignore
................................................................................
**    --keep <CSG>     Keep files matching this comma separated
**                     list of glob patterns.
**    -n|--dry-run     If given, display instead of run actions.
**    --temp           Remove only Fossil-generated temporary files.
**    -v|--verbose     Show all files as they are removed.
**    -x|--extreme     Remove all files not part of the current
**                     checkout, without taking into consideration
**                     the "ignore-glob" and "keep-glob" settings
**                     and their associated command line options.
**                     Compatibile with "git clean -x".
**
** See also: addremove, extra, status
*/
void clean_cmd(void){
  int allFileFlag, allDirFlag, dryRunFlag, verboseFlag, extremeFlag;
  int emptyDirsFlag, dirsOnlyFlag;
................................................................................
  Glob *pIgnore, *pKeep, *pClean;
  int nRoot;

  dryRunFlag = find_option("dry-run","n",0)!=0;
  if( !dryRunFlag ){
    dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
  }



  extremeFlag = find_option("extreme","x",0)!=0;
  allFileFlag = allDirFlag = find_option("force","f",0)!=0;
  dirsOnlyFlag = find_option("dirsonly",0,0)!=0;
  emptyDirsFlag = find_option("emptydirs","d",0)!=0 || dirsOnlyFlag;
  if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL;
  if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP;
  if( find_option("allckouts",0,0)!=0 ) scanFlags |= SCAN_NESTED;
................................................................................
  }
  verify_all_options();
  if( extremeFlag ){
    Blob extremeAnswer;
    char *extremePrompt =
      "\n\nWARNING: The --extreme option is enabled and all untracked files\n"
      "that would otherwise be left alone will be deleted (i.e. those\n"
      "matching the \"ignore-glob\" and \"keep-glob\" settings and their\n"
      "associated command line options).  As a precaution, in order to\n"
      "proceed with this clean operation, the string \"YES\" must be\n"
      "entered in all upper case; any other response will cancel the\n"
      "clean operation.\n\nDo you still wish to proceed with the clean "
      "operation? ";
    blob_zero(&extremeAnswer);
    prompt_user(extremePrompt, &extremeAnswer);
    if( fossil_strcmp(blob_str(&extremeAnswer), "YES")!=0 ){
      fossil_print("Extreme clean operation canceled.\n");
      blob_reset(&extremeAnswer);
      return;
    }
................................................................................
  pKeep = glob_create(zKeepFlag);
  pClean = glob_create(zCleanFlag);
  nRoot = (int)strlen(g.zLocalRoot);
  if( !dirsOnlyFlag ){
    Stmt q;
    Blob repo;
    locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags,
                           extremeFlag ? 0 : pIgnore, extremeFlag ? 0 : pKeep);
    db_prepare(&q,
        "SELECT %Q || x FROM sfile"
        " WHERE x NOT IN (%s)"
        " ORDER BY 1",
        g.zLocalRoot, fossil_all_reserved_names(0)
    );
    if( file_tree_name(g.zRepositoryName, &repo, 0) ){
      db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
    }
    db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)");
    while( db_step(&q)==SQLITE_ROW ){
      const char *zName = db_column_text(&q, 0);
      if( !allFileFlag && !glob_match(pClean, zName+nRoot) ){
        Blob ans;
        char cReply;
        int matchIgnore = glob_match(pIgnore, zName+nRoot);
        int matchKeep = glob_match(pKeep, zName+nRoot);
        char *prompt = mprintf("%sRemove %s file \"%s\" (a=all/y/N)? ",
                               (matchIgnore || matchKeep) ? "WARNING: " : "",
                               matchKeep ? "\"KEPT\"" : (matchIgnore ?
................................................................................
  }
  if( emptyDirsFlag ){
    Glob *pEmptyDirs = glob_create(db_get("empty-dirs", 0));
    Stmt q;
    Blob root;
    blob_init(&root, g.zLocalRoot, nRoot - 1);
    vfile_dir_scan(&root, blob_size(&root), scanFlags,
                   extremeFlag ? 0 : pIgnore, extremeFlag ? 0 : pKeep,
                   extremeFlag ? 0 : pEmptyDirs);
    blob_reset(&root);
    db_prepare(&q,
        "SELECT %Q || x FROM dscan_temp"
        " WHERE x NOT IN (%s) AND y = 0"
        " ORDER BY 1 DESC",
        g.zLocalRoot, fossil_all_reserved_names(0)
    );
    while( db_step(&q)==SQLITE_ROW ){
      const char *zName = db_column_text(&q, 0);
      if( !allDirFlag && !glob_match(pClean, zName+nRoot) ){
        Blob ans;
        char cReply;
        int matchIgnore = glob_match(pIgnore, zName+nRoot);
        int matchKeep = glob_match(pKeep, zName+nRoot);
        int matchEmpty = glob_match(pEmptyDirs, zName+nRoot);
        char *prompt = mprintf("%sRemove %s empty directory \"%s\" "
                               "(a=all/y/N)? ",







|
|







 







|
|







 







>
>
>







 







|
|
|
|
|
<







 







|












|







 







|










|







493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
...
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
...
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
...
590
591
592
593
594
595
596
597
598
599
600
601

602
603
604
605
606
607
608
...
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
...
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
** files that are not officially part of the checkout. This operation
** cannot be undone. If paths are specified, only the directories or
** files specified will be considered for cleaning.
**
** WARNING:  Normally, only the files unknown to Fossil are removed;
** however, if the --extreme option is specified, all files that are
** not part of the current checkout will be removed as well, without
** regard for the "ignore-glob" setting and the --ignore command line
** option.
**
** You will be prompted before removing each eligible file unless the
** --force flag is in use or it matches the --clean option.  The
** GLOBPATTERN specified by the "ignore-glob" setting is used if the
** --ignore option is omitted, the same with "clean-glob" and --clean
** as well as "keep-glob" and --keep.  If you are sure you wish to
** remove all "extra" files except the ones specified with --ignore
................................................................................
**    --keep <CSG>     Keep files matching this comma separated
**                     list of glob patterns.
**    -n|--dry-run     If given, display instead of run actions.
**    --temp           Remove only Fossil-generated temporary files.
**    -v|--verbose     Show all files as they are removed.
**    -x|--extreme     Remove all files not part of the current
**                     checkout, without taking into consideration
**                     the "ignore-glob" setting and the --ignore
**                     command line option.
**                     Compatibile with "git clean -x".
**
** See also: addremove, extra, status
*/
void clean_cmd(void){
  int allFileFlag, allDirFlag, dryRunFlag, verboseFlag, extremeFlag;
  int emptyDirsFlag, dirsOnlyFlag;
................................................................................
  Glob *pIgnore, *pKeep, *pClean;
  int nRoot;

  dryRunFlag = find_option("dry-run","n",0)!=0;
  if( !dryRunFlag ){
    dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
  }
  if( !dryRunFlag ){
    dryRunFlag = find_option("whatif",0,0)!=0;
  }
  extremeFlag = find_option("extreme","x",0)!=0;
  allFileFlag = allDirFlag = find_option("force","f",0)!=0;
  dirsOnlyFlag = find_option("dirsonly",0,0)!=0;
  emptyDirsFlag = find_option("emptydirs","d",0)!=0 || dirsOnlyFlag;
  if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL;
  if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP;
  if( find_option("allckouts",0,0)!=0 ) scanFlags |= SCAN_NESTED;
................................................................................
  }
  verify_all_options();
  if( extremeFlag ){
    Blob extremeAnswer;
    char *extremePrompt =
      "\n\nWARNING: The --extreme option is enabled and all untracked files\n"
      "that would otherwise be left alone will be deleted (i.e. those\n"
      "matching the \"ignore-glob\" setting and the --ignore command line\n"
      "option).  As a precaution, in order to proceed with this clean\n"
      "operation, the string \"YES\" must be entered in all upper case;\n"
      "any other response will cancel the\nclean operation.\n\n"
      "Do you still wish to proceed with the clean operation? ";

    blob_zero(&extremeAnswer);
    prompt_user(extremePrompt, &extremeAnswer);
    if( fossil_strcmp(blob_str(&extremeAnswer), "YES")!=0 ){
      fossil_print("Extreme clean operation canceled.\n");
      blob_reset(&extremeAnswer);
      return;
    }
................................................................................
  pKeep = glob_create(zKeepFlag);
  pClean = glob_create(zCleanFlag);
  nRoot = (int)strlen(g.zLocalRoot);
  if( !dirsOnlyFlag ){
    Stmt q;
    Blob repo;
    locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags,
                           extremeFlag ? 0 : pIgnore, pKeep);
    db_prepare(&q,
        "SELECT %Q || x FROM sfile"
        " WHERE x NOT IN (%s)"
        " ORDER BY 1",
        g.zLocalRoot, fossil_all_reserved_names(0)
    );
    if( file_tree_name(g.zRepositoryName, &repo, 0) ){
      db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
    }
    db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)");
    while( db_step(&q)==SQLITE_ROW ){
      const char *zName = db_column_text(&q, 0);
      if( !allFileFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){
        Blob ans;
        char cReply;
        int matchIgnore = glob_match(pIgnore, zName+nRoot);
        int matchKeep = glob_match(pKeep, zName+nRoot);
        char *prompt = mprintf("%sRemove %s file \"%s\" (a=all/y/N)? ",
                               (matchIgnore || matchKeep) ? "WARNING: " : "",
                               matchKeep ? "\"KEPT\"" : (matchIgnore ?
................................................................................
  }
  if( emptyDirsFlag ){
    Glob *pEmptyDirs = glob_create(db_get("empty-dirs", 0));
    Stmt q;
    Blob root;
    blob_init(&root, g.zLocalRoot, nRoot - 1);
    vfile_dir_scan(&root, blob_size(&root), scanFlags,
                   extremeFlag ? 0 : pIgnore, pKeep,
                   extremeFlag ? 0 : pEmptyDirs);
    blob_reset(&root);
    db_prepare(&q,
        "SELECT %Q || x FROM dscan_temp"
        " WHERE x NOT IN (%s) AND y = 0"
        " ORDER BY 1 DESC",
        g.zLocalRoot, fossil_all_reserved_names(0)
    );
    while( db_step(&q)==SQLITE_ROW ){
      const char *zName = db_column_text(&q, 0);
      if( !allDirFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){
        Blob ans;
        char cReply;
        int matchIgnore = glob_match(pIgnore, zName+nRoot);
        int matchKeep = glob_match(pKeep, zName+nRoot);
        int matchEmpty = glob_match(pEmptyDirs, zName+nRoot);
        char *prompt = mprintf("%sRemove %s empty directory \"%s\" "
                               "(a=all/y/N)? ",