/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/clean_tree.py

  • Committer: Vincent Ladeuil
  • Date: 2012-01-18 14:09:19 UTC
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120118140919-rlvdrhpc0nq1lbwi
Change set/remove to require a lock for the branch config files.

This means that tests (or any plugin for that matter) do not requires an
explicit lock on the branch anymore to change a single option. This also
means the optimisation becomes "opt-in" and as such won't be as
spectacular as it may be and/or harder to get right (nothing fails
anymore).

This reduces the diff by ~300 lines.

Code/tests that were updating more than one config option is still taking
a lock to at least avoid some IOs and demonstrate the benefits through
the decreased number of hpss calls.

The duplication between BranchStack and BranchOnlyStack will be removed
once the same sharing is in place for local config files, at which point
the Stack class itself may be able to host the changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2009, 2010 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
from __future__ import absolute_import
 
18
 
 
19
import errno
 
20
import os
 
21
import shutil
 
22
 
 
23
from bzrlib import (
 
24
    controldir,
 
25
    errors,
 
26
    ui,
 
27
    )
 
28
from bzrlib.osutils import isdir
 
29
from bzrlib.trace import note
 
30
from bzrlib.workingtree import WorkingTree
 
31
from bzrlib.i18n import gettext
 
32
 
 
33
def is_detritus(subp):
 
34
    """Return True if the supplied path is detritus, False otherwise"""
 
35
    return subp.endswith('.THIS') or subp.endswith('.BASE') or\
 
36
        subp.endswith('.OTHER') or subp.endswith('~') or subp.endswith('.tmp')
 
37
 
 
38
 
 
39
def iter_deletables(tree, unknown=False, ignored=False, detritus=False):
 
40
    """Iterate through files that may be deleted"""
 
41
    for subp in tree.extras():
 
42
        if detritus and is_detritus(subp):
 
43
            yield tree.abspath(subp), subp
 
44
            continue
 
45
        if tree.is_ignored(subp):
 
46
            if ignored:
 
47
                yield tree.abspath(subp), subp
 
48
        else:
 
49
            if unknown:
 
50
                yield tree.abspath(subp), subp
 
51
 
 
52
 
 
53
def clean_tree(directory, unknown=False, ignored=False, detritus=False,
 
54
               dry_run=False, no_prompt=False):
 
55
    """Remove files in the specified classes from the tree"""
 
56
    tree = WorkingTree.open_containing(directory)[0]
 
57
    tree.lock_read()
 
58
    try:
 
59
        deletables = list(iter_deletables(tree, unknown=unknown,
 
60
            ignored=ignored, detritus=detritus))
 
61
        deletables = _filter_out_nested_bzrdirs(deletables)
 
62
        if len(deletables) == 0:
 
63
            note(gettext('Nothing to delete.'))
 
64
            return 0
 
65
        if not no_prompt:
 
66
            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'))
 
71
                return 0
 
72
        delete_items(deletables, dry_run=dry_run)
 
73
    finally:
 
74
        tree.unlock()
 
75
 
 
76
 
 
77
def _filter_out_nested_bzrdirs(deletables):
 
78
    result = []
 
79
    for path, subp in deletables:
 
80
        # bzr won't recurse into unknowns/ignored directories by default
 
81
        # so we don't pay a penalty for checking subdirs of path for nested
 
82
        # bzrdir.
 
83
        # That said we won't detect the branch in the subdir of non-branch
 
84
        # directory and therefore delete it. (worth to FIXME?)
 
85
        if isdir(path):
 
86
            try:
 
87
                controldir.ControlDir.open(path)
 
88
            except errors.NotBranchError:
 
89
                result.append((path,subp))
 
90
            else:
 
91
                # TODO may be we need to notify user about skipped directories?
 
92
                pass
 
93
        else:
 
94
            result.append((path,subp))
 
95
    return result
 
96
 
 
97
 
 
98
def delete_items(deletables, dry_run=False):
 
99
    """Delete files in the deletables iterable"""
 
100
    def onerror(function, path, excinfo):
 
101
        """Show warning for errors seen by rmtree.
 
102
        """
 
103
        # Handle only permission error while removing files.
 
104
        # Other errors are re-raised.
 
105
        if function is not os.remove or excinfo[1].errno != errno.EACCES:
 
106
            raise
 
107
        ui.ui_factory.show_warning(gettext('unable to remove %s') % path)
 
108
    has_deleted = False
 
109
    for path, subp in deletables:
 
110
        if not has_deleted:
 
111
            note(gettext("deleting paths:"))
 
112
            has_deleted = True
 
113
        if not dry_run:
 
114
            if isdir(path):
 
115
                shutil.rmtree(path, onerror=onerror)
 
116
            else:
 
117
                try:
 
118
                    os.unlink(path)
 
119
                    note('  ' + subp)
 
120
                except OSError, e:
 
121
                    # We handle only permission error here
 
122
                    if e.errno != errno.EACCES:
 
123
                        raise e
 
124
                    ui.ui_factory.show_warning(gettext(
 
125
                        'unable to remove "{0}": {1}.').format(
 
126
                                                    path, e.strerror))
 
127
        else:
 
128
            note('  ' + subp)
 
129
    if not has_deleted:
 
130
        note(gettext("No files deleted."))