The options provide a prompt to the user before permanent deletion are made.
The prompt is currently not aware of directory deletion. I'll fix this in the
next changesets.
| Alphare |
| hg-reviewers |
The options provide a prompt to the user before permanent deletion are made.
The prompt is currently not aware of directory deletion. I'll fix this in the
next changesets.
| Automatic diff as part of commit; lint not applicable. |
| Automatic diff as part of commit; unit tests not applicable. |
| Path | Packages | |||
|---|---|---|---|---|
| M | hgext/purge.py (3 lines) | |||
| M | mercurial/merge.py (22 lines) | |||
| M | tests/test-purge.t (17 lines) |
| b'0', | b'0', | ||||
| b'print0', | b'print0', | ||||
| None, | None, | ||||
| _( | _( | ||||
| b'end filenames with NUL, for use with xargs' | b'end filenames with NUL, for use with xargs' | ||||
| b' (implies -p/--print)' | b' (implies -p/--print)' | ||||
| ), | ), | ||||
| ), | ), | ||||
| (b'', b'confirm', None, _(b'ask before permanently deleting files')), | |||||
| ] | ] | ||||
| + cmdutil.walkopts, | + cmdutil.walkopts, | ||||
| _(b'hg purge [OPTION]... [DIR]...'), | _(b'hg purge [OPTION]... [DIR]...'), | ||||
| helpcategory=command.CATEGORY_WORKING_DIRECTORY, | helpcategory=command.CATEGORY_WORKING_DIRECTORY, | ||||
| ) | ) | ||||
| def purge(ui, repo, *dirs, **opts): | def purge(ui, repo, *dirs, **opts): | ||||
| """removes files not tracked by Mercurial | """removes files not tracked by Mercurial | ||||
| ignored = True | ignored = True | ||||
| unknown = True | unknown = True | ||||
| else: | else: | ||||
| ignored = opts.get(b'ignored', False) | ignored = opts.get(b'ignored', False) | ||||
| unknown = not ignored | unknown = not ignored | ||||
| removefiles = opts.get(b'files') | removefiles = opts.get(b'files') | ||||
| removedirs = opts.get(b'dirs') | removedirs = opts.get(b'dirs') | ||||
| confirm = opts.get(b'confirm') | |||||
| if not removefiles and not removedirs: | if not removefiles and not removedirs: | ||||
| removefiles = True | removefiles = True | ||||
| removedirs = True | removedirs = True | ||||
| match = scmutil.match(repo[None], dirs, opts) | match = scmutil.match(repo[None], dirs, opts) | ||||
| paths = mergemod.purge( | paths = mergemod.purge( | ||||
| repo, | repo, | ||||
| match, | match, | ||||
| unknown=unknown, | unknown=unknown, | ||||
| ignored=ignored, | ignored=ignored, | ||||
| removeemptydirs=removedirs, | removeemptydirs=removedirs, | ||||
| removefiles=removefiles, | removefiles=removefiles, | ||||
| abortonerror=opts.get(b'abort_on_err'), | abortonerror=opts.get(b'abort_on_err'), | ||||
| noop=not act, | noop=not act, | ||||
| confirm=confirm, | |||||
| ) | ) | ||||
| for path in paths: | for path in paths: | ||||
| if not act: | if not act: | ||||
| ui.write(b'%s%s' % (path, eol)) | ui.write(b'%s%s' % (path, eol)) | ||||
| repo, | repo, | ||||
| matcher, | matcher, | ||||
| unknown=True, | unknown=True, | ||||
| ignored=False, | ignored=False, | ||||
| removeemptydirs=True, | removeemptydirs=True, | ||||
| removefiles=True, | removefiles=True, | ||||
| abortonerror=False, | abortonerror=False, | ||||
| noop=False, | noop=False, | ||||
| confirm=False, | |||||
| ): | ): | ||||
| """Purge the working directory of untracked files. | """Purge the working directory of untracked files. | ||||
| ``matcher`` is a matcher configured to scan the working directory - | ``matcher`` is a matcher configured to scan the working directory - | ||||
| potentially a subset. | potentially a subset. | ||||
| ``unknown`` controls whether unknown files should be purged. | ``unknown`` controls whether unknown files should be purged. | ||||
| ``ignored`` controls whether ignored files should be purged. | ``ignored`` controls whether ignored files should be purged. | ||||
| ``removeemptydirs`` controls whether empty directories should be removed. | ``removeemptydirs`` controls whether empty directories should be removed. | ||||
| ``removefiles`` controls whether files are removed. | ``removefiles`` controls whether files are removed. | ||||
| ``abortonerror`` causes an exception to be raised if an error occurs | ``abortonerror`` causes an exception to be raised if an error occurs | ||||
| deleting a file or directory. | deleting a file or directory. | ||||
| ``noop`` controls whether to actually remove files. If not defined, actions | ``noop`` controls whether to actually remove files. If not defined, actions | ||||
| will be taken. | will be taken. | ||||
| ``confirm`` ask confirmation before actually removing anything. | |||||
| Returns an iterable of relative paths in the working directory that were | Returns an iterable of relative paths in the working directory that were | ||||
| or would be removed. | or would be removed. | ||||
| """ | """ | ||||
| def remove(removefn, path): | def remove(removefn, path): | ||||
| try: | try: | ||||
| removefn(path) | removefn(path) | ||||
| except OSError: | except OSError: | ||||
| try: | try: | ||||
| if removeemptydirs: | if removeemptydirs: | ||||
| directories = [] | directories = [] | ||||
| matcher.traversedir = directories.append | matcher.traversedir = directories.append | ||||
| status = repo.status(match=matcher, ignored=ignored, unknown=unknown) | status = repo.status(match=matcher, ignored=ignored, unknown=unknown) | ||||
| if confirm: | |||||
| nb_ignored = len(status.ignored) | |||||
| nb_unkown = len(status.unknown) | |||||
| if nb_unkown and nb_ignored: | |||||
| msg = _(b"permanently delete %d unkown and %d ignored files?") | |||||
| msg %= (nb_unkown, nb_ignored) | |||||
| elif nb_unkown: | |||||
| msg = _(b"permanently delete %d unkown files?") | |||||
| msg %= nb_unkown | |||||
| elif nb_ignored: | |||||
| msg = _(b"permanently delete %d ignored files?") | |||||
| msg %= nb_ignored | |||||
| else: | |||||
| # XXX we might be missing directory there | |||||
| return res | |||||
| msg += b" (yN)$$ &Yes $$ &No" | |||||
| if repo.ui.promptchoice(msg, default=1) == 1: | |||||
| raise error.CanceledError(_(b'removal cancelled')) | |||||
| if removefiles: | if removefiles: | ||||
| for f in sorted(status.unknown + status.ignored): | for f in sorted(status.unknown + status.ignored): | ||||
| if not noop: | if not noop: | ||||
| repo.ui.note(_(b'removing file %s\n') % f) | repo.ui.note(_(b'removing file %s\n') % f) | ||||
| remove(repo.wvfs.unlink, f) | remove(repo.wvfs.unlink, f) | ||||
| res.append(f) | res.append(f) | ||||
| if removeemptydirs: | if removeemptydirs: | ||||
| > import os | > import os | ||||
| > import stat | > import stat | ||||
| > f = 'untracked_file_readonly' | > f = 'untracked_file_readonly' | ||||
| > os.chmod(f, stat.S_IMODE(os.stat(f).st_mode) & ~stat.S_IWRITE) | > os.chmod(f, stat.S_IMODE(os.stat(f).st_mode) & ~stat.S_IWRITE) | ||||
| > EOF | > EOF | ||||
| $ hg purge -p | $ hg purge -p | ||||
| untracked_file | untracked_file | ||||
| untracked_file_readonly | untracked_file_readonly | ||||
| $ hg purge --confirm | |||||
| permanently delete 2 unkown files? (yN) n | |||||
| abort: removal cancelled | |||||
| [250] | |||||
| $ hg purge -v | $ hg purge -v | ||||
| removing file untracked_file | removing file untracked_file | ||||
| removing file untracked_file_readonly | removing file untracked_file_readonly | ||||
| $ ls -A | $ ls -A | ||||
| .hg | .hg | ||||
| .hgignore | .hgignore | ||||
| directory | directory | ||||
| r1 | r1 | ||||
| delete only part of the tree | delete only part of the tree | ||||
| $ mkdir -p untracked_directory/nested_directory | $ mkdir -p untracked_directory/nested_directory | ||||
| $ touch directory/untracked_file | $ touch directory/untracked_file | ||||
| $ cd directory | $ cd directory | ||||
| $ hg purge -p ../untracked_directory | $ hg purge -p ../untracked_directory | ||||
| untracked_directory/nested_directory | untracked_directory/nested_directory | ||||
| $ hg purge --confirm | |||||
| permanently delete 1 unkown files? (yN) n | |||||
| abort: removal cancelled | |||||
| [250] | |||||
| $ hg purge -v ../untracked_directory | $ hg purge -v ../untracked_directory | ||||
| removing directory untracked_directory/nested_directory | removing directory untracked_directory/nested_directory | ||||
| removing directory untracked_directory | removing directory untracked_directory | ||||
| $ cd .. | $ cd .. | ||||
| $ ls -A | $ ls -A | ||||
| .hg | .hg | ||||
| .hgignore | .hgignore | ||||
| directory | directory | ||||
| r1 | r1 | ||||
| $ ls directory/untracked_file | $ ls directory/untracked_file | ||||
| directory/untracked_file | directory/untracked_file | ||||
| $ rm directory/untracked_file | $ rm directory/untracked_file | ||||
| skip ignored files if -i or --all not specified | skip ignored files if -i or --all not specified | ||||
| $ touch ignored | $ touch ignored | ||||
| $ hg purge -p | $ hg purge -p | ||||
| $ hg purge --confirm | |||||
| $ hg purge -v | $ hg purge -v | ||||
| $ touch untracked_file | $ touch untracked_file | ||||
| $ ls | $ ls | ||||
| directory | directory | ||||
| ignored | ignored | ||||
| r1 | r1 | ||||
| untracked_file | untracked_file | ||||
| $ hg purge -p -i | $ hg purge -p -i | ||||
| ignored | ignored | ||||
| $ hg purge --confirm -i | |||||
| permanently delete 1 ignored files? (yN) n | |||||
| abort: removal cancelled | |||||
| [250] | |||||
| $ hg purge -v -i | $ hg purge -v -i | ||||
| removing file ignored | removing file ignored | ||||
| $ ls -A | $ ls -A | ||||
| .hg | .hg | ||||
| .hgignore | .hgignore | ||||
| directory | directory | ||||
| r1 | r1 | ||||
| untracked_file | untracked_file | ||||
| $ touch ignored | $ touch ignored | ||||
| $ hg purge -p --all | $ hg purge -p --all | ||||
| ignored | ignored | ||||
| untracked_file | untracked_file | ||||
| $ hg purge --confirm --all | |||||
| permanently delete 1 unkown and 1 ignored files? (yN) n | |||||
| abort: removal cancelled | |||||
| [250] | |||||
| $ hg purge -v --all | $ hg purge -v --all | ||||
| removing file ignored | removing file ignored | ||||
| removing file untracked_file | removing file untracked_file | ||||
| $ ls | $ ls | ||||
| directory | directory | ||||
| r1 | r1 | ||||
| abort with missing files until we support name mangling filesystems | abort with missing files until we support name mangling filesystems | ||||