14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from __future__ import absolute_import
28
from .osutils import isdir
29
from .trace import note
30
from .workingtree import WorkingTree
31
from .i18n import gettext
21
from bzrlib import bzrdir, errors
22
from bzrlib.osutils import isdir
23
from bzrlib.trace import note
24
from bzrlib.workingtree import WorkingTree
34
27
def is_detritus(subp):
55
48
dry_run=False, no_prompt=False):
56
49
"""Remove files in the specified classes from the tree"""
57
50
tree = WorkingTree.open_containing(directory)[0]
58
with tree.lock_read():
59
53
deletables = list(iter_deletables(tree, unknown=unknown,
60
ignored=ignored, detritus=detritus))
61
deletables = _filter_out_nested_controldirs(deletables)
54
ignored=ignored, detritus=detritus))
55
deletables = _filter_out_nested_bzrdirs(deletables)
62
56
if len(deletables) == 0:
63
note(gettext('Nothing to delete.'))
57
note('Nothing to delete.')
66
60
for path, subp in deletables:
67
ui.ui_factory.note(subp)
68
prompt = gettext('Are you sure you wish to delete these')
69
if not ui.ui_factory.get_boolean(prompt):
70
ui.ui_factory.note(gettext('Canceled'))
61
# FIXME using print is very bad idea
62
# clean_tree should accept to_file argument to write the output
64
val = raw_input('Are you sure you wish to delete these [y/N]?')
65
if val.lower() not in ('y', 'yes'):
72
68
delete_items(deletables, dry_run=dry_run)
75
def _filter_out_nested_controldirs(deletables):
73
def _filter_out_nested_bzrdirs(deletables):
77
75
for path, subp in deletables:
78
76
# bzr won't recurse into unknowns/ignored directories by default
79
77
# so we don't pay a penalty for checking subdirs of path for nested
81
79
# That said we won't detect the branch in the subdir of non-branch
82
80
# directory and therefore delete it. (worth to FIXME?)
85
controldir.ControlDir.open(path)
83
bzrdir.BzrDir.open(path)
86
84
except errors.NotBranchError:
87
result.append((path, subp))
85
result.append((path,subp))
89
87
# TODO may be we need to notify user about skipped directories?
92
result.append((path, subp))
90
result.append((path,subp))
96
94
def delete_items(deletables, dry_run=False):
97
95
"""Delete files in the deletables iterable"""
98
def onerror(function, path, excinfo):
99
"""Show warning for errors seen by rmtree.
101
# Handle only permission error while removing files.
102
# Other errors are re-raised.
103
if function is not os.remove or excinfo[1].errno != errno.EACCES:
105
ui.ui_factory.show_warning(gettext('unable to remove %s') % path)
106
96
has_deleted = False
107
97
for path, subp in deletables:
108
98
if not has_deleted:
109
note(gettext("deleting paths:"))
99
note("deleting paths:")
110
100
has_deleted = True
113
shutil.rmtree(path, onerror=onerror)
119
# We handle only permission error here
120
if e.errno != errno.EACCES:
122
ui.ui_factory.show_warning(gettext(
123
'unable to remove "{0}": {1}.').format(
127
107
if not has_deleted:
128
note(gettext("No files deleted."))
108
note("No files deleted.")