diff -r e5449ff273d6 -r 7fea205fd5dc mercurial/merge.py --- a/mercurial/merge.py Thu Sep 06 23:37:24 2018 -0400 +++ b/mercurial/merge.py Thu Sep 06 18:30:12 2018 -0700 @@ -9,6 +9,7 @@ import errno import hashlib +import os import shutil import struct @@ -2240,3 +2241,71 @@ # fix up dirstate for copies and renames copies.duplicatecopies(repo, repo[None], ctx.rev(), pctx.rev()) return stats + +def purge(repo, matcher, ignored=False, removeemptydirs=True, + removefiles=True, abortonerror=False, noop=False): + """Purge the working directory of untracked files. + + ``matcher`` is a matcher configured to scan the working directory - + potentially a subset. + + ``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. + + ``noop`` controls whether to actually remove files. If not defined, actions + will be taken. + + Returns an iterable 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) + + # There's no API to copy a matcher. So mutate the passed matcher and + # restore it when we're done. + oldexplicitdir = matcher.explicitdir + oldtraversedir = matcher.traversedir + + res = [] + + try: + if removeemptydirs: + directories = [] + matcher.explicitdir = matcher.traversedir = directories.append + + status = repo.status(match=matcher, 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) + res.append(f) + + if removeemptydirs: + for f in sorted(directories, reverse=True): + if matcher(f) and not os.listdir(repo.wvfs.join(f)): + if not noop: + repo.ui.note(_('removing directory %s\n') % f) + remove(os.rmdir, f) + res.append(f) + + return res + + finally: + matcher.explicitdir = oldexplicitdir + matcher.traversedir = oldtraversedir