/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

  • Committer: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

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,
53
52
    errors,
54
53
    generate_ids,
55
54
    globbing,
62
61
    revisiontree,
63
62
    trace,
64
63
    transform,
65
 
    transport,
66
64
    ui,
67
65
    views,
68
66
    xml5,
69
67
    xml7,
70
68
    )
 
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
81
80
from bzrlib.lockable_files import LockableFiles
82
81
from bzrlib.lockdir import LockDir
83
82
import bzrlib.mutabletree
169
168
 
170
169
 
171
170
class WorkingTree(bzrlib.mutabletree.MutableTree,
172
 
    controldir.ControlComponent):
 
171
    bzrdir.ControlComponent):
173
172
    """Working copy tree.
174
173
 
175
174
    The inventory is held in the `Branch` working-inventory, and the
177
176
 
178
177
    It is possible for a `WorkingTree` to have a filename which is
179
178
    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).
183
179
    """
184
180
 
185
181
    # override this to set the strategy for storing views
350
346
        if path is None:
351
347
            path = osutils.getcwd()
352
348
        control, relpath = bzrdir.BzrDir.open_containing(path)
 
349
 
353
350
        return control.open_workingtree(), relpath
354
351
 
355
352
    @staticmethod
356
 
    def open_containing_paths(file_list, default_directory=None,
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
 
        if default_directory is None:
366
 
            default_directory = u'.'
367
 
        # recommended replacement for builtins.internal_tree_files
368
 
        if file_list is None or len(file_list) == 0:
369
 
            tree = WorkingTree.open_containing(default_directory)[0]
370
 
            # XXX: doesn't really belong here, and seems to have the strange
371
 
            # side effect of making it return a bunch of files, not the whole
372
 
            # tree -- mbp 20100716
373
 
            if tree.supports_views() and apply_view:
374
 
                view_files = tree.views.lookup_view()
375
 
                if view_files:
376
 
                    file_list = view_files
377
 
                    view_str = views.view_display_str(view_files)
378
 
                    note("Ignoring files outside view. View is %s" % view_str)
379
 
            return tree, file_list
380
 
        if default_directory == u'.':
381
 
            seed = file_list[0]
382
 
        else:
383
 
            seed = default_directory
384
 
            file_list = [osutils.pathjoin(default_directory, f)
385
 
                         for f in file_list]
386
 
        tree = WorkingTree.open_containing(seed)[0]
387
 
        return tree, tree.safe_relpath_files(file_list, canonicalize,
388
 
                                             apply_view=apply_view)
389
 
 
390
 
    def safe_relpath_files(self, file_list, canonicalize=True, apply_view=True):
391
 
        """Convert file_list into a list of relpaths in tree.
392
 
 
393
 
        :param self: A tree to operate on.
394
 
        :param file_list: A list of user provided paths or None.
395
 
        :param apply_view: if True and a view is set, apply it or check that
396
 
            specified files are within it
397
 
        :return: A list of relative paths.
398
 
        :raises errors.PathNotChild: When a provided path is in a different self
399
 
            than self.
400
 
        """
401
 
        if file_list is None:
402
 
            return None
403
 
        if self.supports_views() and apply_view:
404
 
            view_files = self.views.lookup_view()
405
 
        else:
406
 
            view_files = []
407
 
        new_list = []
408
 
        # self.relpath exists as a "thunk" to osutils, but canonical_relpath
409
 
        # doesn't - fix that up here before we enter the loop.
410
 
        if canonicalize:
411
 
            fixer = lambda p: osutils.canonical_relpath(self.basedir, p)
412
 
        else:
413
 
            fixer = self.relpath
414
 
        for filename in file_list:
415
 
            relpath = fixer(osutils.dereference_path(filename))
416
 
            if view_files and not osutils.is_inside_any(view_files, relpath):
417
 
                raise errors.FileOutsideView(filename, view_files)
418
 
            new_list.append(relpath)
419
 
        return new_list
420
 
 
421
 
    @staticmethod
422
353
    def open_downlevel(path=None):
423
354
        """Open an unsupported working tree.
424
355
 
437
368
                return True, None
438
369
            else:
439
370
                return True, tree
440
 
        t = transport.get_transport(location)
441
 
        iterator = bzrdir.BzrDir.find_bzrdirs(t, evaluate=evaluate,
 
371
        transport = get_transport(location)
 
372
        iterator = bzrdir.BzrDir.find_bzrdirs(transport, evaluate=evaluate,
442
373
                                              list_current=list_current)
443
 
        return [tr for tr in iterator if tr is not None]
 
374
        return [t for t in iterator if t is not None]
444
375
 
445
376
    # should be deprecated - this is slow and in any case treating them as a
446
377
    # container is (we now know) bad style -- mbp 20070302
531
462
        return (file_obj, stat_value)
532
463
 
533
464
    def get_file_text(self, file_id, path=None, filtered=True):
534
 
        my_file = self.get_file(file_id, path=path, filtered=filtered)
535
 
        try:
536
 
            return my_file.read()
537
 
        finally:
538
 
            my_file.close()
 
465
        return self.get_file(file_id, path=path, filtered=filtered).read()
539
466
 
540
467
    def get_file_byname(self, filename, filtered=True):
541
468
        path = self.abspath(filename)
595
522
 
596
523
        # Now we have the parents of this content
597
524
        annotator = self.branch.repository.texts.get_annotator()
598
 
        text = self.get_file_text(file_id)
 
525
        text = self.get_file(file_id).read()
599
526
        this_key =(file_id, default_revision)
600
527
        annotator.add_special_text(this_key, file_parent_keys, text)
601
528
        annotations = [(key[-1], line)
1275
1202
                # absolute path
1276
1203
                fap = from_dir_abspath + '/' + f
1277
1204
 
1278
 
                dir_ie = inv[from_dir_id]
1279
 
                if dir_ie.kind == 'directory':
1280
 
                    f_ie = dir_ie.children.get(f)
1281
 
                else:
1282
 
                    f_ie = None
 
1205
                f_ie = inv.get_child(from_dir_id, f)
1283
1206
                if f_ie:
1284
1207
                    c = 'V'
1285
1208
                elif self.is_ignored(fp[1:]):
1286
1209
                    c = 'I'
1287
1210
                else:
1288
 
                    # we may not have found this file, because of a unicode
1289
 
                    # issue, or because the directory was actually a symlink.
 
1211
                    # we may not have found this file, because of a unicode issue
1290
1212
                    f_norm, can_access = osutils.normalized_filename(f)
1291
1213
                    if f == f_norm or not can_access:
1292
1214
                        # No change, so treat this file normally
1335
1257
                stack.pop()
1336
1258
 
1337
1259
    @needs_tree_write_lock
1338
 
    def move(self, from_paths, to_dir=None, after=False):
 
1260
    def move(self, from_paths, to_dir=None, after=False, **kwargs):
1339
1261
        """Rename files.
1340
1262
 
1341
1263
        to_dir must exist in the inventory.
1375
1297
 
1376
1298
        # check for deprecated use of signature
1377
1299
        if to_dir is None:
1378
 
            raise TypeError('You must supply a target directory')
 
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
 
1379
1308
        # check destination directory
1380
1309
        if isinstance(from_paths, basestring):
1381
1310
            raise ValueError()
1671
1600
 
1672
1601
    @needs_write_lock
1673
1602
    def pull(self, source, overwrite=False, stop_revision=None,
1674
 
             change_reporter=None, possible_transports=None, local=False,
1675
 
             show_base=False):
 
1603
             change_reporter=None, possible_transports=None, local=False):
1676
1604
        source.lock_read()
1677
1605
        try:
1678
1606
            old_revision_info = self.branch.last_revision_info()
1692
1620
                                basis_tree,
1693
1621
                                this_tree=self,
1694
1622
                                pb=None,
1695
 
                                change_reporter=change_reporter,
1696
 
                                show_base=show_base)
 
1623
                                change_reporter=change_reporter)
1697
1624
                    basis_root_id = basis_tree.get_root_id()
1698
1625
                    new_root_id = new_basis_tree.get_root_id()
1699
1626
                    if basis_root_id != new_root_id:
1871
1798
            raise errors.ObjectNotLocked(self)
1872
1799
 
1873
1800
    def lock_read(self):
1874
 
        """Lock the tree for reading.
1875
 
 
1876
 
        This also locks the branch, and can be unlocked via self.unlock().
1877
 
 
1878
 
        :return: A bzrlib.lock.LogicalLockResult.
1879
 
        """
 
1801
        """See Branch.lock_read, and WorkingTree.unlock."""
1880
1802
        if not self.is_locked():
1881
1803
            self._reset_data()
1882
1804
        self.branch.lock_read()
1883
1805
        try:
1884
 
            self._control_files.lock_read()
1885
 
            return LogicalLockResult(self.unlock)
 
1806
            return self._control_files.lock_read()
1886
1807
        except:
1887
1808
            self.branch.unlock()
1888
1809
            raise
1889
1810
 
1890
1811
    def lock_tree_write(self):
1891
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1892
 
 
1893
 
        :return: A bzrlib.lock.LogicalLockResult.
1894
 
        """
 
1812
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
1895
1813
        if not self.is_locked():
1896
1814
            self._reset_data()
1897
1815
        self.branch.lock_read()
1898
1816
        try:
1899
 
            self._control_files.lock_write()
1900
 
            return LogicalLockResult(self.unlock)
 
1817
            return self._control_files.lock_write()
1901
1818
        except:
1902
1819
            self.branch.unlock()
1903
1820
            raise
1904
1821
 
1905
1822
    def lock_write(self):
1906
 
        """See MutableTree.lock_write, and WorkingTree.unlock.
1907
 
 
1908
 
        :return: A bzrlib.lock.LogicalLockResult.
1909
 
        """
 
1823
        """See MutableTree.lock_write, and WorkingTree.unlock."""
1910
1824
        if not self.is_locked():
1911
1825
            self._reset_data()
1912
1826
        self.branch.lock_write()
1913
1827
        try:
1914
 
            self._control_files.lock_write()
1915
 
            return LogicalLockResult(self.unlock)
 
1828
            return self._control_files.lock_write()
1916
1829
        except:
1917
1830
            self.branch.unlock()
1918
1831
            raise
2035
1948
 
2036
1949
        inv_delta = []
2037
1950
 
2038
 
        all_files = set() # specified and nested files 
 
1951
        new_files=set()
2039
1952
        unknown_nested_files=set()
2040
1953
        if to_file is None:
2041
1954
            to_file = sys.stdout
2042
1955
 
2043
 
        files_to_backup = []
2044
 
 
2045
1956
        def recurse_directory_to_add_files(directory):
2046
1957
            # Recurse directory and add all files
2047
1958
            # so we can check if they have changed.
2048
 
            for parent_info, file_infos in self.walkdirs(directory):
 
1959
            for parent_info, file_infos in\
 
1960
                self.walkdirs(directory):
2049
1961
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
2050
1962
                    # Is it versioned or ignored?
2051
 
                    if self.path2id(relpath):
 
1963
                    if self.path2id(relpath) or self.is_ignored(relpath):
2052
1964
                        # Add nested content for deletion.
2053
 
                        all_files.add(relpath)
 
1965
                        new_files.add(relpath)
2054
1966
                    else:
2055
 
                        # Files which are not versioned
 
1967
                        # Files which are not versioned and not ignored
2056
1968
                        # should be treated as unknown.
2057
 
                        files_to_backup.append(relpath)
 
1969
                        unknown_nested_files.add((relpath, None, kind))
2058
1970
 
2059
1971
        for filename in files:
2060
1972
            # Get file name into canonical form.
2061
1973
            abspath = self.abspath(filename)
2062
1974
            filename = self.relpath(abspath)
2063
1975
            if len(filename) > 0:
2064
 
                all_files.add(filename)
 
1976
                new_files.add(filename)
2065
1977
                recurse_directory_to_add_files(filename)
2066
1978
 
2067
 
        files = list(all_files)
 
1979
        files = list(new_files)
2068
1980
 
2069
1981
        if len(files) == 0:
2070
1982
            return # nothing to do
2074
1986
 
2075
1987
        # Bail out if we are going to delete files we shouldn't
2076
1988
        if not keep_files and not force:
2077
 
            for (file_id, path, content_change, versioned, parent_id, name,
2078
 
                 kind, executable) in self.iter_changes(self.basis_tree(),
2079
 
                     include_unchanged=True, require_versioned=False,
2080
 
                     want_unversioned=True, specific_files=files):
2081
 
                if versioned[0] == False:
2082
 
                    # The record is unknown or newly added
2083
 
                    files_to_backup.append(path[1])
2084
 
                elif (content_change and (kind[1] is not None) and
2085
 
                        osutils.is_inside_any(files, path[1])):
2086
 
                    # Versioned and changed, but not deleted, and still
2087
 
                    # in one of the dirs to be deleted.
2088
 
                    files_to_backup.append(path[1])
 
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
2089
2005
 
2090
 
        def backup(file_to_backup):
2091
 
            backup_name = self.bzrdir._available_backup_name(file_to_backup)
2092
 
            osutils.rename(abs_path, self.abspath(backup_name))
2093
 
            return "removed %s (but kept a copy: %s)" % (file_to_backup,
2094
 
                                                         backup_name)
 
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)
2095
2015
 
2096
2016
        # Build inv_delta and delete files where applicable,
2097
2017
        # do this before any modifications to inventory.
2121
2041
                        len(os.listdir(abs_path)) > 0):
2122
2042
                        if force:
2123
2043
                            osutils.rmtree(abs_path)
2124
 
                            message = "deleted %s" % (f,)
2125
2044
                        else:
2126
 
                            message = backup(f)
 
2045
                            message = "%s is not an empty directory "\
 
2046
                                "and won't be deleted." % (f,)
2127
2047
                    else:
2128
 
                        if f in files_to_backup:
2129
 
                            message = backup(f)
2130
 
                        else:
2131
 
                            osutils.delete_any(abs_path)
2132
 
                            message = "deleted %s" % (f,)
 
2048
                        osutils.delete_any(abs_path)
 
2049
                        message = "deleted %s" % (f,)
2133
2050
                elif message is not None:
2134
2051
                    # Only care if we haven't done anything yet.
2135
2052
                    message = "%s does not exist." % (f,)
2272
2189
    _marker = object()
2273
2190
 
2274
2191
    def update(self, change_reporter=None, possible_transports=None,
2275
 
               revision=None, old_tip=_marker, show_base=False):
 
2192
               revision=None, old_tip=_marker):
2276
2193
        """Update a working tree along its branch.
2277
2194
 
2278
2195
        This will update the branch if its bound too, which means we have
2315
2232
            else:
2316
2233
                if old_tip is self._marker:
2317
2234
                    old_tip = None
2318
 
            return self._update_tree(old_tip, change_reporter, revision, show_base)
 
2235
            return self._update_tree(old_tip, change_reporter, revision)
2319
2236
        finally:
2320
2237
            self.unlock()
2321
2238
 
2322
2239
    @needs_tree_write_lock
2323
 
    def _update_tree(self, old_tip=None, change_reporter=None, revision=None,
2324
 
                     show_base=False):
 
2240
    def _update_tree(self, old_tip=None, change_reporter=None, revision=None):
2325
2241
        """Update a tree to the master branch.
2326
2242
 
2327
2243
        :param old_tip: if supplied, the previous tip revision the branch,
2354
2270
            other_tree = self.branch.repository.revision_tree(old_tip)
2355
2271
            nb_conflicts = merge.merge_inner(self.branch, other_tree,
2356
2272
                                             base_tree, this_tree=self,
2357
 
                                             change_reporter=change_reporter,
2358
 
                                             show_base=show_base)
 
2273
                                             change_reporter=change_reporter)
2359
2274
            if nb_conflicts:
2360
2275
                self.add_parent_tree((old_tip, other_tree))
2361
2276
                trace.note('Rerun update after fixing the conflicts.')
2385
2300
 
2386
2301
            nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
2387
2302
                                             this_tree=self,
2388
 
                                             change_reporter=change_reporter,
2389
 
                                             show_base=show_base)
 
2303
                                             change_reporter=change_reporter)
2390
2304
            self.set_last_revision(revision)
2391
2305
            # TODO - dedup parents list with things merged by pull ?
2392
2306
            # reuse the tree we've updated to to set the basis:
2722
2636
 
2723
2637
        In Format2 WorkingTrees we have a single lock for the branch and tree
2724
2638
        so lock_tree_write() degrades to lock_write().
2725
 
 
2726
 
        :return: An object with an unlock method which will release the lock
2727
 
            obtained.
2728
2639
        """
2729
2640
        self.branch.lock_write()
2730
2641
        try:
2731
 
            self._control_files.lock_write()
2732
 
            return self
 
2642
            return self._control_files.lock_write()
2733
2643
        except:
2734
2644
            self.branch.unlock()
2735
2645
            raise