contrib/purge/purge.py
changeset 4307 c8919eb0f315
parent 4155 4c714ed245d6
equal deleted inserted replaced
4306:d4f0405fadac 4307:c8919eb0f315
    29 
    29 
    30 from mercurial import hg, util
    30 from mercurial import hg, util
    31 from mercurial.i18n import _
    31 from mercurial.i18n import _
    32 import os
    32 import os
    33 
    33 
    34 def dopurge(ui, repo, dirs=None, act=True, abort_on_err=False, eol='\n'):
    34 def dopurge(ui, repo, dirs=None, act=True, abort_on_err=False, eol='\n',
       
    35             force=False):
    35     def error(msg):
    36     def error(msg):
    36         if abort_on_err:
    37         if abort_on_err:
    37             raise util.Abort(msg)
    38             raise util.Abort(msg)
    38         else:
    39         else:
    39             ui.warn(_('warning: %s\n') % msg)
    40             ui.warn(_('warning: %s\n') % msg)
    47         else:
    48         else:
    48             ui.write('%s%s' % (name, eol))
    49             ui.write('%s%s' % (name, eol))
    49 
    50 
    50     directories = []
    51     directories = []
    51     files = []
    52     files = []
       
    53     missing = []
    52     roots, match, anypats = util.cmdmatcher(repo.root, repo.getcwd(), dirs)
    54     roots, match, anypats = util.cmdmatcher(repo.root, repo.getcwd(), dirs)
    53     for src, f, st in repo.dirstate.statwalk(files=roots, match=match,
    55     for src, f, st in repo.dirstate.statwalk(files=roots, match=match,
    54                                              ignored=True, directories=True):
    56                                              ignored=True, directories=True):
    55         if src == 'd':
    57         if src == 'd':
    56             directories.append(f)
    58             directories.append(f)
       
    59         elif src == 'm':
       
    60             missing.append(f)
    57         elif src == 'f' and f not in repo.dirstate:
    61         elif src == 'f' and f not in repo.dirstate:
    58             files.append(f)
    62             files.append(f)
       
    63 
       
    64     _check_missing(ui, repo, missing, force)
    59 
    65 
    60     directories.sort()
    66     directories.sort()
    61 
    67 
    62     for f in files:
    68     for f in files:
    63         if f not in repo.dirstate:
    69         if f not in repo.dirstate:
    66 
    72 
    67     for f in directories[::-1]:
    73     for f in directories[::-1]:
    68         if not os.listdir(repo.wjoin(f)):
    74         if not os.listdir(repo.wjoin(f)):
    69             ui.note(_('Removing directory %s\n') % f)
    75             ui.note(_('Removing directory %s\n') % f)
    70             remove(os.rmdir, f)
    76             remove(os.rmdir, f)
       
    77 
       
    78 def _check_missing(ui, repo, missing, force=False):
       
    79     """Abort if there is the chance of having problems with name-mangling fs
       
    80 
       
    81     In a name mangling filesystem (e.g. a case insensitive one)
       
    82     dirstate.walk() can yield filenames different from the ones
       
    83     stored in the dirstate. This already confuses the status and
       
    84     add commands, but with purge this may cause data loss.
       
    85     
       
    86     To prevent this, _check_missing will abort if there are missing
       
    87     files. The force option will let the user skip the check if he 
       
    88     knows it is safe.
       
    89     
       
    90     Even with the force option this function will check if any of the 
       
    91     missing files is still available in the working dir: if so there
       
    92     may be some problem with the underlying filesystem, so it
       
    93     aborts unconditionally."""
       
    94 
       
    95     found = [f for f in missing if util.lexists(repo.wjoin(f))]
       
    96 
       
    97     if found:
       
    98         if not ui.quiet:
       
    99             ui.warn(_("The following tracked files weren't listed by the "
       
   100                       "filesystem, but could still be found:\n"))
       
   101             for f in found:
       
   102                 ui.warn("%s\n" % f)
       
   103             if util.checkfolding(repo.path):
       
   104                 ui.warn(_("This is probably due to a case-insensitive "
       
   105                           "filesystem\n"))
       
   106         raise util.Abort(_("purging on name mangling filesystems is not "
       
   107                            "yet fully supported"))
       
   108 
       
   109     if missing and not force:
       
   110         raise util.Abort(_("there are missing files in the working dir and "
       
   111                            "purge still has problems with them due to name "
       
   112                            "mangling filesystems. "
       
   113                            "Use --force if you know what you are doing"))
    71 
   114 
    72 
   115 
    73 def purge(ui, repo, *dirs, **opts):
   116 def purge(ui, repo, *dirs, **opts):
    74     '''removes files not tracked by mercurial
   117     '''removes files not tracked by mercurial
    75 
   118 
    98     abort_on_err = bool(opts['abort_on_err'])
   141     abort_on_err = bool(opts['abort_on_err'])
    99     eol = opts['print0'] and '\0' or '\n'
   142     eol = opts['print0'] and '\0' or '\n'
   100     if eol == '\0':
   143     if eol == '\0':
   101         # --print0 implies --print
   144         # --print0 implies --print
   102         act = False
   145         act = False
   103     dopurge(ui, repo, dirs, act, abort_on_err, eol)
   146     force = bool(opts['force'])
       
   147     dopurge(ui, repo, dirs, act, abort_on_err, eol, force)
   104 
   148 
   105 
   149 
   106 cmdtable = {
   150 cmdtable = {
   107     'purge':
   151     'purge':
   108         (purge,
   152         (purge,
   109          [('a', 'abort-on-err', None, _('abort if an error occurs')),
   153          [('a', 'abort-on-err', None, _('abort if an error occurs')),
       
   154           ('f', 'force', None, _('purge even when missing files are detected')),
   110           ('p', 'print', None, _('print the file names instead of deleting them')),
   155           ('p', 'print', None, _('print the file names instead of deleting them')),
   111           ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
   156           ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
   112                                   ' (implies -p)'))],
   157                                   ' (implies -p)'))],
   113          _('hg purge [OPTION]... [DIR]...'))
   158          _('hg purge [OPTION]... [DIR]...'))
   114 }
   159 }