diff --git a/hgext/purge.py b/hgext/purge.py --- a/hgext/purge.py +++ b/hgext/purge.py @@ -25,16 +25,12 @@ '''command to delete untracked files from the working directory''' from __future__ import absolute_import -import os - from mercurial.i18n import _ from mercurial import ( cmdutil, - error, + merge as mergemod, pycompat, registrar, - scmutil, - util, ) cmdtable = {} @@ -86,44 +82,27 @@ option. ''' opts = pycompat.byteskwargs(opts) + act = not opts.get('print') eol = '\n' if opts.get('print0'): eol = '\0' - act = False # --print0 implies --print + act = False # --print0 implies --print + removefiles = opts.get('files') removedirs = opts.get('dirs') + if not removefiles and not removedirs: removefiles = True removedirs = True - def remove(remove_func, name): - if act: - try: - remove_func(repo.wjoin(name)) - except OSError: - m = _('%s cannot be removed') % name - if opts.get('abort_on_err'): - raise error.Abort(m) - ui.warn(_('warning: %s\n') % m) - else: - ui.write('%s%s' % (name, eol)) + paths = mergemod.purge( + repo, dirs, ignored=opts.get('all', False), + removeemptydirs=removedirs, removefiles=removefiles, + abortonerror=opts.get('abort_on_err'), + include=opts.get('include'), exclude=opts.get('exclude'), + noop=opts.get('print') or opts.get('print0')) - match = scmutil.match(repo[None], dirs, opts) - if removedirs: - directories = [] - match.explicitdir = match.traversedir = directories.append - status = repo.status(match=match, ignored=opts.get('all'), unknown=True) - - if removefiles: - for f in sorted(status.unknown + status.ignored): - if act: - ui.note(_('removing file %s\n') % f) - remove(util.unlink, f) - - if removedirs: - for f in sorted(directories, reverse=True): - if match(f) and not os.listdir(repo.wjoin(f)): - if act: - ui.note(_('removing directory %s\n') % f) - remove(os.rmdir, f) + for path in paths: + if not act: + ui.write('%s%s' % (path, eol)) diff --git a/mercurial/merge.py b/mercurial/merge.py --- a/mercurial/merge.py +++ b/mercurial/merge.py @@ -9,6 +9,7 @@ import errno import hashlib +import os import shutil import struct @@ -2240,3 +2241,63 @@ # fix up dirstate for copies and renames copies.duplicatecopies(repo, repo[None], ctx.rev(), pctx.rev()) return stats + +def purge(repo, dirs=None, ignored=False, removeemptydirs=True, + removefiles=True, abortonerror=False, include=None, exclude=None, + noop=False): + """Purge the working directory of untracked files. + + ``dirs`` is an iterable of directories to purge. If not defined, all + directories will be examined. + + ``ignored`` controls whether ignored files should also be purged. + + ``removeemptydirs`` controls whether empty directories should be removed. + + ``removefiles`` controls whether files are removed. + + ``abortonerror`` causes an exception to be raised if an error occurs + deleting a file or directory. + + ``include`` and ``exclude`` are optional path filters to include or exclude. + + ``noop`` controls whether to actually remove files. If not defined, actions + will be taken. + + The function is a generator of relative paths in the working directory + that were or would be removed. + """ + + def remove(removefn, path): + try: + removefn(repo.wvfs.join(path)) + except OSError: + m = _('%s cannot be removed') % path + if abortonerror: + raise error.Abort(m) + else: + repo.ui.warn(_('warning: %s\n') % m) + + match = scmutil.match(repo[None], dirs or (), + {'include': include, 'exclude': exclude}) + + if removeemptydirs: + directories = [] + match.explicitdir = match.traversedir = directories.append + + status = repo.status(match=match, ignored=ignored, unknown=True) + + if removefiles: + for f in sorted(status.unknown + status.ignored): + if not noop: + repo.ui.note(_('removing file %s\n') % f) + remove(util.unlink, f) + yield f + + if removeemptydirs: + for f in sorted(directories, reverse=True): + if match(f) and not os.listdir(repo.wvfs.join(f)): + if not noop: + repo.ui.note(_('removing directory %s\n') % f) + remove(os.rmdir, f) + yield f