/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/workingtree.py

Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.

This is used to replace various ad hoc implementations of the same logic,
notably the version used in registry's _LazyObjectGetter which had a bug when
getting a module without also getting a member.  And of course, this new
function has unit tests, unlike the replaced code.

This also adds a KnownHooksRegistry subclass to provide a more natural home for
some other logic.

I'm not thrilled about the name of the new module or the new functions, but it's
hard to think of good names for such generic functionality.

Show diffs side-by-side

added added

removed removed

Lines of Context:
49
49
    branch,
50
50
    bzrdir,
51
51
    conflicts as _mod_conflicts,
 
52
    controldir,
52
53
    errors,
53
54
    generate_ids,
54
55
    globbing,
61
62
    revisiontree,
62
63
    trace,
63
64
    transform,
 
65
    transport,
64
66
    ui,
65
67
    views,
66
68
    xml5,
67
69
    xml7,
68
70
    )
69
 
import bzrlib.branch
70
 
from bzrlib.transport import get_transport
71
71
from bzrlib.workingtree_4 import (
72
72
    WorkingTreeFormat4,
73
73
    WorkingTreeFormat5,
77
77
 
78
78
from bzrlib import symbol_versioning
79
79
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
80
from bzrlib.lock import LogicalLockResult
80
81
from bzrlib.lockable_files import LockableFiles
81
82
from bzrlib.lockdir import LockDir
82
83
import bzrlib.mutabletree
168
169
 
169
170
 
170
171
class WorkingTree(bzrlib.mutabletree.MutableTree,
171
 
    bzrdir.ControlComponent):
 
172
    controldir.ControlComponent):
172
173
    """Working copy tree.
173
174
 
174
175
    The inventory is held in the `Branch` working-inventory, and the
176
177
 
177
178
    It is possible for a `WorkingTree` to have a filename which is
178
179
    not listed in the Inventory and vice versa.
 
180
 
 
181
    :ivar basedir: The root of the tree on disk. This is a unicode path object
 
182
        (as opposed to a URL).
179
183
    """
180
184
 
181
185
    # override this to set the strategy for storing views
346
350
        if path is None:
347
351
            path = osutils.getcwd()
348
352
        control, relpath = bzrdir.BzrDir.open_containing(path)
349
 
 
350
353
        return control.open_workingtree(), relpath
351
354
 
352
355
    @staticmethod
 
356
    def open_containing_paths(file_list, default_directory='.',
 
357
        canonicalize=True, apply_view=True):
 
358
        """Open the WorkingTree that contains a set of paths.
 
359
 
 
360
        Fail if the paths given are not all in a single tree.
 
361
 
 
362
        This is used for the many command-line interfaces that take a list of
 
363
        any number of files and that require they all be in the same tree.
 
364
        """
 
365
        # recommended replacement for builtins.internal_tree_files
 
366
        if file_list is None or len(file_list) == 0:
 
367
            tree = WorkingTree.open_containing(default_directory)[0]
 
368
            # XXX: doesn't really belong here, and seems to have the strange
 
369
            # side effect of making it return a bunch of files, not the whole
 
370
            # tree -- mbp 20100716
 
371
            if tree.supports_views() and apply_view:
 
372
                view_files = tree.views.lookup_view()
 
373
                if view_files:
 
374
                    file_list = view_files
 
375
                    view_str = views.view_display_str(view_files)
 
376
                    note("Ignoring files outside view. View is %s" % view_str)
 
377
            return tree, file_list
 
378
        tree = WorkingTree.open_containing(file_list[0])[0]
 
379
        return tree, tree.safe_relpath_files(file_list, canonicalize,
 
380
            apply_view=apply_view)
 
381
 
 
382
    def safe_relpath_files(self, file_list, canonicalize=True, apply_view=True):
 
383
        """Convert file_list into a list of relpaths in tree.
 
384
 
 
385
        :param self: A tree to operate on.
 
386
        :param file_list: A list of user provided paths or None.
 
387
        :param apply_view: if True and a view is set, apply it or check that
 
388
            specified files are within it
 
389
        :return: A list of relative paths.
 
390
        :raises errors.PathNotChild: When a provided path is in a different self
 
391
            than self.
 
392
        """
 
393
        if file_list is None:
 
394
            return None
 
395
        if self.supports_views() and apply_view:
 
396
            view_files = self.views.lookup_view()
 
397
        else:
 
398
            view_files = []
 
399
        new_list = []
 
400
        # self.relpath exists as a "thunk" to osutils, but canonical_relpath
 
401
        # doesn't - fix that up here before we enter the loop.
 
402
        if canonicalize:
 
403
            fixer = lambda p: osutils.canonical_relpath(self.basedir, p)
 
404
        else:
 
405
            fixer = self.relpath
 
406
        for filename in file_list:
 
407
            relpath = fixer(osutils.dereference_path(filename))
 
408
            if view_files and not osutils.is_inside_any(view_files, relpath):
 
409
                raise errors.FileOutsideView(filename, view_files)
 
410
            new_list.append(relpath)
 
411
        return new_list
 
412
 
 
413
    @staticmethod
353
414
    def open_downlevel(path=None):
354
415
        """Open an unsupported working tree.
355
416
 
368
429
                return True, None
369
430
            else:
370
431
                return True, tree
371
 
        transport = get_transport(location)
372
 
        iterator = bzrdir.BzrDir.find_bzrdirs(transport, evaluate=evaluate,
 
432
        t = transport.get_transport(location)
 
433
        iterator = bzrdir.BzrDir.find_bzrdirs(t, evaluate=evaluate,
373
434
                                              list_current=list_current)
374
 
        return [t for t in iterator if t is not None]
 
435
        return [tr for tr in iterator if tr is not None]
375
436
 
376
437
    # should be deprecated - this is slow and in any case treating them as a
377
438
    # container is (we now know) bad style -- mbp 20070302
462
523
        return (file_obj, stat_value)
463
524
 
464
525
    def get_file_text(self, file_id, path=None, filtered=True):
465
 
        return self.get_file(file_id, path=path, filtered=filtered).read()
 
526
        my_file = self.get_file(file_id, path=path, filtered=filtered)
 
527
        try:
 
528
            return my_file.read()
 
529
        finally:
 
530
            my_file.close()
466
531
 
467
532
    def get_file_byname(self, filename, filtered=True):
468
533
        path = self.abspath(filename)
522
587
 
523
588
        # Now we have the parents of this content
524
589
        annotator = self.branch.repository.texts.get_annotator()
525
 
        text = self.get_file(file_id).read()
 
590
        text = self.get_file_text(file_id)
526
591
        this_key =(file_id, default_revision)
527
592
        annotator.add_special_text(this_key, file_parent_keys, text)
528
593
        annotations = [(key[-1], line)
1202
1267
                # absolute path
1203
1268
                fap = from_dir_abspath + '/' + f
1204
1269
 
1205
 
                f_ie = inv.get_child(from_dir_id, f)
 
1270
                dir_ie = inv[from_dir_id]
 
1271
                if dir_ie.kind == 'directory':
 
1272
                    f_ie = dir_ie.children.get(f)
 
1273
                else:
 
1274
                    f_ie = None
1206
1275
                if f_ie:
1207
1276
                    c = 'V'
1208
1277
                elif self.is_ignored(fp[1:]):
1209
1278
                    c = 'I'
1210
1279
                else:
1211
 
                    # we may not have found this file, because of a unicode issue
 
1280
                    # we may not have found this file, because of a unicode
 
1281
                    # issue, or because the directory was actually a symlink.
1212
1282
                    f_norm, can_access = osutils.normalized_filename(f)
1213
1283
                    if f == f_norm or not can_access:
1214
1284
                        # No change, so treat this file normally
1257
1327
                stack.pop()
1258
1328
 
1259
1329
    @needs_tree_write_lock
1260
 
    def move(self, from_paths, to_dir=None, after=False, **kwargs):
 
1330
    def move(self, from_paths, to_dir=None, after=False):
1261
1331
        """Rename files.
1262
1332
 
1263
1333
        to_dir must exist in the inventory.
1297
1367
 
1298
1368
        # check for deprecated use of signature
1299
1369
        if to_dir is None:
1300
 
            to_dir = kwargs.get('to_name', None)
1301
 
            if to_dir is None:
1302
 
                raise TypeError('You must supply a target directory')
1303
 
            else:
1304
 
                symbol_versioning.warn('The parameter to_name was deprecated'
1305
 
                                       ' in version 0.13. Use to_dir instead',
1306
 
                                       DeprecationWarning)
1307
 
 
 
1370
            raise TypeError('You must supply a target directory')
1308
1371
        # check destination directory
1309
1372
        if isinstance(from_paths, basestring):
1310
1373
            raise ValueError()
1798
1861
            raise errors.ObjectNotLocked(self)
1799
1862
 
1800
1863
    def lock_read(self):
1801
 
        """See Branch.lock_read, and WorkingTree.unlock."""
 
1864
        """Lock the tree for reading.
 
1865
 
 
1866
        This also locks the branch, and can be unlocked via self.unlock().
 
1867
 
 
1868
        :return: A bzrlib.lock.LogicalLockResult.
 
1869
        """
1802
1870
        if not self.is_locked():
1803
1871
            self._reset_data()
1804
1872
        self.branch.lock_read()
1805
1873
        try:
1806
 
            return self._control_files.lock_read()
 
1874
            self._control_files.lock_read()
 
1875
            return LogicalLockResult(self.unlock)
1807
1876
        except:
1808
1877
            self.branch.unlock()
1809
1878
            raise
1810
1879
 
1811
1880
    def lock_tree_write(self):
1812
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
 
1881
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
 
1882
 
 
1883
        :return: A bzrlib.lock.LogicalLockResult.
 
1884
        """
1813
1885
        if not self.is_locked():
1814
1886
            self._reset_data()
1815
1887
        self.branch.lock_read()
1816
1888
        try:
1817
 
            return self._control_files.lock_write()
 
1889
            self._control_files.lock_write()
 
1890
            return LogicalLockResult(self.unlock)
1818
1891
        except:
1819
1892
            self.branch.unlock()
1820
1893
            raise
1821
1894
 
1822
1895
    def lock_write(self):
1823
 
        """See MutableTree.lock_write, and WorkingTree.unlock."""
 
1896
        """See MutableTree.lock_write, and WorkingTree.unlock.
 
1897
 
 
1898
        :return: A bzrlib.lock.LogicalLockResult.
 
1899
        """
1824
1900
        if not self.is_locked():
1825
1901
            self._reset_data()
1826
1902
        self.branch.lock_write()
1827
1903
        try:
1828
 
            return self._control_files.lock_write()
 
1904
            self._control_files.lock_write()
 
1905
            return LogicalLockResult(self.unlock)
1829
1906
        except:
1830
1907
            self.branch.unlock()
1831
1908
            raise
1948
2025
 
1949
2026
        inv_delta = []
1950
2027
 
1951
 
        new_files=set()
 
2028
        all_files = set() # specified and nested files 
1952
2029
        unknown_nested_files=set()
1953
2030
        if to_file is None:
1954
2031
            to_file = sys.stdout
1955
2032
 
 
2033
        files_to_backup = []
 
2034
 
1956
2035
        def recurse_directory_to_add_files(directory):
1957
2036
            # Recurse directory and add all files
1958
2037
            # so we can check if they have changed.
1959
 
            for parent_info, file_infos in\
1960
 
                self.walkdirs(directory):
 
2038
            for parent_info, file_infos in self.walkdirs(directory):
1961
2039
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
1962
2040
                    # Is it versioned or ignored?
1963
 
                    if self.path2id(relpath) or self.is_ignored(relpath):
 
2041
                    if self.path2id(relpath):
1964
2042
                        # Add nested content for deletion.
1965
 
                        new_files.add(relpath)
 
2043
                        all_files.add(relpath)
1966
2044
                    else:
1967
 
                        # Files which are not versioned and not ignored
 
2045
                        # Files which are not versioned
1968
2046
                        # should be treated as unknown.
1969
 
                        unknown_nested_files.add((relpath, None, kind))
 
2047
                        files_to_backup.append(relpath)
1970
2048
 
1971
2049
        for filename in files:
1972
2050
            # Get file name into canonical form.
1973
2051
            abspath = self.abspath(filename)
1974
2052
            filename = self.relpath(abspath)
1975
2053
            if len(filename) > 0:
1976
 
                new_files.add(filename)
 
2054
                all_files.add(filename)
1977
2055
                recurse_directory_to_add_files(filename)
1978
2056
 
1979
 
        files = list(new_files)
 
2057
        files = list(all_files)
1980
2058
 
1981
2059
        if len(files) == 0:
1982
2060
            return # nothing to do
1986
2064
 
1987
2065
        # Bail out if we are going to delete files we shouldn't
1988
2066
        if not keep_files and not force:
1989
 
            has_changed_files = len(unknown_nested_files) > 0
1990
 
            if not has_changed_files:
1991
 
                for (file_id, path, content_change, versioned, parent_id, name,
1992
 
                     kind, executable) in self.iter_changes(self.basis_tree(),
1993
 
                         include_unchanged=True, require_versioned=False,
1994
 
                         want_unversioned=True, specific_files=files):
1995
 
                    if versioned == (False, False):
1996
 
                        # The record is unknown ...
1997
 
                        if not self.is_ignored(path[1]):
1998
 
                            # ... but not ignored
1999
 
                            has_changed_files = True
2000
 
                            break
2001
 
                    elif content_change and (kind[1] is not None):
2002
 
                        # Versioned and changed, but not deleted
2003
 
                        has_changed_files = True
2004
 
                        break
 
2067
            for (file_id, path, content_change, versioned, parent_id, name,
 
2068
                 kind, executable) in self.iter_changes(self.basis_tree(),
 
2069
                     include_unchanged=True, require_versioned=False,
 
2070
                     want_unversioned=True, specific_files=files):
 
2071
                if versioned[0] == False:
 
2072
                    # The record is unknown or newly added
 
2073
                    files_to_backup.append(path[1])
 
2074
                elif (content_change and (kind[1] is not None) and
 
2075
                        osutils.is_inside_any(files, path[1])):
 
2076
                    # Versioned and changed, but not deleted, and still
 
2077
                    # in one of the dirs to be deleted.
 
2078
                    files_to_backup.append(path[1])
2005
2079
 
2006
 
            if has_changed_files:
2007
 
                # Make delta show ALL applicable changes in error message.
2008
 
                tree_delta = self.changes_from(self.basis_tree(),
2009
 
                    require_versioned=False, want_unversioned=True,
2010
 
                    specific_files=files)
2011
 
                for unknown_file in unknown_nested_files:
2012
 
                    if unknown_file not in tree_delta.unversioned:
2013
 
                        tree_delta.unversioned.extend((unknown_file,))
2014
 
                raise errors.BzrRemoveChangedFilesError(tree_delta)
 
2080
        def backup(file_to_backup):
 
2081
            backup_name = self.bzrdir.generate_backup_name(file_to_backup)
 
2082
            osutils.rename(abs_path, self.abspath(backup_name))
 
2083
            return "removed %s (but kept a copy: %s)" % (file_to_backup, backup_name)
2015
2084
 
2016
2085
        # Build inv_delta and delete files where applicable,
2017
2086
        # do this before any modifications to inventory.
2041
2110
                        len(os.listdir(abs_path)) > 0):
2042
2111
                        if force:
2043
2112
                            osutils.rmtree(abs_path)
 
2113
                            message = "deleted %s" % (f,)
2044
2114
                        else:
2045
 
                            message = "%s is not an empty directory "\
2046
 
                                "and won't be deleted." % (f,)
 
2115
                            message = backup(f)
2047
2116
                    else:
2048
 
                        osutils.delete_any(abs_path)
2049
 
                        message = "deleted %s" % (f,)
 
2117
                        if f in files_to_backup:
 
2118
                            message = backup(f)
 
2119
                        else:
 
2120
                            osutils.delete_any(abs_path)
 
2121
                            message = "deleted %s" % (f,)
2050
2122
                elif message is not None:
2051
2123
                    # Only care if we haven't done anything yet.
2052
2124
                    message = "%s does not exist." % (f,)
2636
2708
 
2637
2709
        In Format2 WorkingTrees we have a single lock for the branch and tree
2638
2710
        so lock_tree_write() degrades to lock_write().
 
2711
 
 
2712
        :return: An object with an unlock method which will release the lock
 
2713
            obtained.
2639
2714
        """
2640
2715
        self.branch.lock_write()
2641
2716
        try:
2642
 
            return self._control_files.lock_write()
 
2717
            self._control_files.lock_write()
 
2718
            return self
2643
2719
        except:
2644
2720
            self.branch.unlock()
2645
2721
            raise