mercurial/merge.py
changeset 39463 7fea205fd5dc
parent 38757 d49e490a9e85
child 39464 3dd34b401bc2
--- 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