~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

Revert the dirstate/transform changes, so we have a pure 'lstat/fstat' change.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
49
49
    branch,
50
50
    bzrdir,
51
51
    conflicts as _mod_conflicts,
 
52
    controldir,
52
53
    errors,
53
54
    generate_ids,
54
55
    globbing,
57
58
    ignores,
58
59
    inventory,
59
60
    merge,
 
61
    registry,
60
62
    revision as _mod_revision,
61
63
    revisiontree,
62
64
    trace,
63
65
    transform,
 
66
    transport,
64
67
    ui,
65
68
    views,
66
69
    xml5,
67
70
    xml7,
68
71
    )
69
 
import bzrlib.branch
70
 
from bzrlib.transport import get_transport
71
72
from bzrlib.workingtree_4 import (
72
73
    WorkingTreeFormat4,
73
74
    WorkingTreeFormat5,
77
78
 
78
79
from bzrlib import symbol_versioning
79
80
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
81
from bzrlib.lock import LogicalLockResult
80
82
from bzrlib.lockable_files import LockableFiles
81
83
from bzrlib.lockdir import LockDir
82
84
import bzrlib.mutabletree
168
170
 
169
171
 
170
172
class WorkingTree(bzrlib.mutabletree.MutableTree,
171
 
    bzrdir.ControlComponent):
 
173
    controldir.ControlComponent):
172
174
    """Working copy tree.
173
175
 
174
176
    The inventory is held in the `Branch` working-inventory, and the
176
178
 
177
179
    It is possible for a `WorkingTree` to have a filename which is
178
180
    not listed in the Inventory and vice versa.
 
181
 
 
182
    :ivar basedir: The root of the tree on disk. This is a unicode path object
 
183
        (as opposed to a URL).
179
184
    """
180
185
 
181
186
    # override this to set the strategy for storing views
205
210
        else:
206
211
            self._branch = self.bzrdir.open_branch()
207
212
        self.basedir = realpath(basedir)
208
 
        # if branch is at our basedir and is a format 6 or less
209
 
        if isinstance(self._format, WorkingTreeFormat2):
210
 
            # share control object
211
 
            self._control_files = self.branch.control_files
212
 
        else:
213
 
            # assume all other formats have their own control files.
214
 
            self._control_files = _control_files
 
213
        self._control_files = _control_files
215
214
        self._transport = self._control_files._transport
216
215
        # update the whole cache up front and write to disk if anything changed;
217
216
        # in the future we might want to do this more selectively
255
254
    def control_transport(self):
256
255
        return self._transport
257
256
 
 
257
    def is_control_filename(self, filename):
 
258
        """True if filename is the name of a control file in this tree.
 
259
 
 
260
        :param filename: A filename within the tree. This is a relative path
 
261
        from the root of this tree.
 
262
 
 
263
        This is true IF and ONLY IF the filename is part of the meta data
 
264
        that bzr controls in this tree. I.E. a random .bzr directory placed
 
265
        on disk will not be a control file for this tree.
 
266
        """
 
267
        return self.bzrdir.is_control_filename(filename)
 
268
 
258
269
    def _detect_case_handling(self):
259
270
        wt_trans = self.bzrdir.get_workingtree_transport(None)
260
271
        try:
261
 
            wt_trans.stat("FoRMaT")
 
272
            wt_trans.stat(self._format.case_sensitive_filename)
262
273
        except errors.NoSuchFile:
263
274
            self.case_sensitive = True
264
275
        else:
346
357
        if path is None:
347
358
            path = osutils.getcwd()
348
359
        control, relpath = bzrdir.BzrDir.open_containing(path)
349
 
 
350
360
        return control.open_workingtree(), relpath
351
361
 
352
362
    @staticmethod
 
363
    def open_containing_paths(file_list, default_directory=None,
 
364
                              canonicalize=True, apply_view=True):
 
365
        """Open the WorkingTree that contains a set of paths.
 
366
 
 
367
        Fail if the paths given are not all in a single tree.
 
368
 
 
369
        This is used for the many command-line interfaces that take a list of
 
370
        any number of files and that require they all be in the same tree.
 
371
        """
 
372
        if default_directory is None:
 
373
            default_directory = u'.'
 
374
        # recommended replacement for builtins.internal_tree_files
 
375
        if file_list is None or len(file_list) == 0:
 
376
            tree = WorkingTree.open_containing(default_directory)[0]
 
377
            # XXX: doesn't really belong here, and seems to have the strange
 
378
            # side effect of making it return a bunch of files, not the whole
 
379
            # tree -- mbp 20100716
 
380
            if tree.supports_views() and apply_view:
 
381
                view_files = tree.views.lookup_view()
 
382
                if view_files:
 
383
                    file_list = view_files
 
384
                    view_str = views.view_display_str(view_files)
 
385
                    note("Ignoring files outside view. View is %s" % view_str)
 
386
            return tree, file_list
 
387
        if default_directory == u'.':
 
388
            seed = file_list[0]
 
389
        else:
 
390
            seed = default_directory
 
391
            file_list = [osutils.pathjoin(default_directory, f)
 
392
                         for f in file_list]
 
393
        tree = WorkingTree.open_containing(seed)[0]
 
394
        return tree, tree.safe_relpath_files(file_list, canonicalize,
 
395
                                             apply_view=apply_view)
 
396
 
 
397
    def safe_relpath_files(self, file_list, canonicalize=True, apply_view=True):
 
398
        """Convert file_list into a list of relpaths in tree.
 
399
 
 
400
        :param self: A tree to operate on.
 
401
        :param file_list: A list of user provided paths or None.
 
402
        :param apply_view: if True and a view is set, apply it or check that
 
403
            specified files are within it
 
404
        :return: A list of relative paths.
 
405
        :raises errors.PathNotChild: When a provided path is in a different self
 
406
            than self.
 
407
        """
 
408
        if file_list is None:
 
409
            return None
 
410
        if self.supports_views() and apply_view:
 
411
            view_files = self.views.lookup_view()
 
412
        else:
 
413
            view_files = []
 
414
        new_list = []
 
415
        # self.relpath exists as a "thunk" to osutils, but canonical_relpath
 
416
        # doesn't - fix that up here before we enter the loop.
 
417
        if canonicalize:
 
418
            fixer = lambda p: osutils.canonical_relpath(self.basedir, p)
 
419
        else:
 
420
            fixer = self.relpath
 
421
        for filename in file_list:
 
422
            relpath = fixer(osutils.dereference_path(filename))
 
423
            if view_files and not osutils.is_inside_any(view_files, relpath):
 
424
                raise errors.FileOutsideView(filename, view_files)
 
425
            new_list.append(relpath)
 
426
        return new_list
 
427
 
 
428
    @staticmethod
353
429
    def open_downlevel(path=None):
354
430
        """Open an unsupported working tree.
355
431
 
368
444
                return True, None
369
445
            else:
370
446
                return True, tree
371
 
        transport = get_transport(location)
372
 
        iterator = bzrdir.BzrDir.find_bzrdirs(transport, evaluate=evaluate,
 
447
        t = transport.get_transport(location)
 
448
        iterator = bzrdir.BzrDir.find_bzrdirs(t, evaluate=evaluate,
373
449
                                              list_current=list_current)
374
 
        return [t for t in iterator if t is not None]
 
450
        return [tr for tr in iterator if tr is not None]
375
451
 
376
452
    # should be deprecated - this is slow and in any case treating them as a
377
453
    # container is (we now know) bad style -- mbp 20070302
450
526
        return self.get_file_with_stat(file_id, path, filtered=filtered)[0]
451
527
 
452
528
    def get_file_with_stat(self, file_id, path=None, filtered=True,
453
 
        _fstat=os.fstat):
 
529
                           _fstat=osutils.fstat):
454
530
        """See Tree.get_file_with_stat."""
455
531
        if path is None:
456
532
            path = self.id2path(file_id)
462
538
        return (file_obj, stat_value)
463
539
 
464
540
    def get_file_text(self, file_id, path=None, filtered=True):
465
 
        return self.get_file(file_id, path=path, filtered=filtered).read()
 
541
        my_file = self.get_file(file_id, path=path, filtered=filtered)
 
542
        try:
 
543
            return my_file.read()
 
544
        finally:
 
545
            my_file.close()
466
546
 
467
547
    def get_file_byname(self, filename, filtered=True):
468
548
        path = self.abspath(filename)
522
602
 
523
603
        # Now we have the parents of this content
524
604
        annotator = self.branch.repository.texts.get_annotator()
525
 
        text = self.get_file(file_id).read()
 
605
        text = self.get_file_text(file_id)
526
606
        this_key =(file_id, default_revision)
527
607
        annotator.add_special_text(this_key, file_parent_keys, text)
528
608
        annotations = [(key[-1], line)
1202
1282
                # absolute path
1203
1283
                fap = from_dir_abspath + '/' + f
1204
1284
 
1205
 
                f_ie = inv.get_child(from_dir_id, f)
 
1285
                dir_ie = inv[from_dir_id]
 
1286
                if dir_ie.kind == 'directory':
 
1287
                    f_ie = dir_ie.children.get(f)
 
1288
                else:
 
1289
                    f_ie = None
1206
1290
                if f_ie:
1207
1291
                    c = 'V'
1208
1292
                elif self.is_ignored(fp[1:]):
1209
1293
                    c = 'I'
1210
1294
                else:
1211
 
                    # we may not have found this file, because of a unicode issue
 
1295
                    # we may not have found this file, because of a unicode
 
1296
                    # issue, or because the directory was actually a symlink.
1212
1297
                    f_norm, can_access = osutils.normalized_filename(f)
1213
1298
                    if f == f_norm or not can_access:
1214
1299
                        # No change, so treat this file normally
1257
1342
                stack.pop()
1258
1343
 
1259
1344
    @needs_tree_write_lock
1260
 
    def move(self, from_paths, to_dir=None, after=False, **kwargs):
 
1345
    def move(self, from_paths, to_dir=None, after=False):
1261
1346
        """Rename files.
1262
1347
 
1263
1348
        to_dir must exist in the inventory.
1297
1382
 
1298
1383
        # check for deprecated use of signature
1299
1384
        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
 
 
 
1385
            raise TypeError('You must supply a target directory')
1308
1386
        # check destination directory
1309
1387
        if isinstance(from_paths, basestring):
1310
1388
            raise ValueError()
1319
1397
        to_dir_id = inv.path2id(to_dir)
1320
1398
        if to_dir_id is None:
1321
1399
            raise errors.BzrMoveFailedError('',to_dir,
1322
 
                errors.NotVersionedError(path=str(to_dir)))
 
1400
                errors.NotVersionedError(path=to_dir))
1323
1401
 
1324
1402
        to_dir_ie = inv[to_dir_id]
1325
1403
        if to_dir_ie.kind != 'directory':
1332
1410
            from_id = inv.path2id(from_rel)
1333
1411
            if from_id is None:
1334
1412
                raise errors.BzrMoveFailedError(from_rel,to_dir,
1335
 
                    errors.NotVersionedError(path=str(from_rel)))
 
1413
                    errors.NotVersionedError(path=from_rel))
1336
1414
 
1337
1415
            from_entry = inv[from_id]
1338
1416
            from_parent_id = from_entry.parent_id
1380
1458
            # check the inventory for source and destination
1381
1459
            if from_id is None:
1382
1460
                raise errors.BzrMoveFailedError(from_rel,to_rel,
1383
 
                    errors.NotVersionedError(path=str(from_rel)))
 
1461
                    errors.NotVersionedError(path=from_rel))
1384
1462
            if to_id is not None:
1385
1463
                raise errors.BzrMoveFailedError(from_rel,to_rel,
1386
 
                    errors.AlreadyVersionedError(path=str(to_rel)))
 
1464
                    errors.AlreadyVersionedError(path=to_rel))
1387
1465
 
1388
1466
            # try to determine the mode for rename (only change inv or change
1389
1467
            # inv and file system)
1390
1468
            if after:
1391
1469
                if not self.has_filename(to_rel):
1392
1470
                    raise errors.BzrMoveFailedError(from_id,to_rel,
1393
 
                        errors.NoSuchFile(path=str(to_rel),
 
1471
                        errors.NoSuchFile(path=to_rel,
1394
1472
                        extra="New file has not been created yet"))
1395
1473
                only_change_inv = True
1396
1474
            elif not self.has_filename(from_rel) and self.has_filename(to_rel):
1498
1576
            from_id = basis_tree.path2id(from_rel)
1499
1577
            if from_id is None:
1500
1578
                raise errors.BzrRenameFailedError(from_rel,to_rel,
1501
 
                    errors.NotVersionedError(path=str(from_rel)))
 
1579
                    errors.NotVersionedError(path=from_rel))
1502
1580
            # put entry back in the inventory so we can rename it
1503
1581
            from_entry = basis_tree.inventory[from_id].copy()
1504
1582
            inv.add(from_entry)
1522
1600
        # versioned
1523
1601
        if to_dir_id is None:
1524
1602
            raise errors.BzrMoveFailedError(from_rel,to_rel,
1525
 
                errors.NotVersionedError(path=str(to_dir)))
 
1603
                errors.NotVersionedError(path=to_dir))
1526
1604
 
1527
1605
        # all checks done. now we can continue with our actual work
1528
1606
        mutter('rename_one:\n'
1587
1665
            # - RBC 20060907
1588
1666
            self._write_inventory(self._inventory)
1589
1667
 
1590
 
    def _iter_conflicts(self):
1591
 
        conflicted = set()
1592
 
        for info in self.list_files():
1593
 
            path = info[0]
1594
 
            stem = get_conflicted_stem(path)
1595
 
            if stem is None:
1596
 
                continue
1597
 
            if stem not in conflicted:
1598
 
                conflicted.add(stem)
1599
 
                yield stem
1600
 
 
1601
1668
    @needs_write_lock
1602
1669
    def pull(self, source, overwrite=False, stop_revision=None,
1603
 
             change_reporter=None, possible_transports=None, local=False):
 
1670
             change_reporter=None, possible_transports=None, local=False,
 
1671
             show_base=False):
1604
1672
        source.lock_read()
1605
1673
        try:
1606
1674
            old_revision_info = self.branch.last_revision_info()
1620
1688
                                basis_tree,
1621
1689
                                this_tree=self,
1622
1690
                                pb=None,
1623
 
                                change_reporter=change_reporter)
 
1691
                                change_reporter=change_reporter,
 
1692
                                show_base=show_base)
1624
1693
                    basis_root_id = basis_tree.get_root_id()
1625
1694
                    new_root_id = new_basis_tree.get_root_id()
1626
1695
                    if basis_root_id != new_root_id:
1802
1871
 
1803
1872
        This also locks the branch, and can be unlocked via self.unlock().
1804
1873
 
1805
 
        :return: An object with an unlock method which will release the lock
1806
 
            obtained.
 
1874
        :return: A bzrlib.lock.LogicalLockResult.
1807
1875
        """
1808
1876
        if not self.is_locked():
1809
1877
            self._reset_data()
1810
1878
        self.branch.lock_read()
1811
1879
        try:
1812
1880
            self._control_files.lock_read()
1813
 
            return self
 
1881
            return LogicalLockResult(self.unlock)
1814
1882
        except:
1815
1883
            self.branch.unlock()
1816
1884
            raise
1818
1886
    def lock_tree_write(self):
1819
1887
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1820
1888
 
1821
 
        :return: An object with an unlock method which will release the lock
1822
 
            obtained.
 
1889
        :return: A bzrlib.lock.LogicalLockResult.
1823
1890
        """
1824
1891
        if not self.is_locked():
1825
1892
            self._reset_data()
1826
1893
        self.branch.lock_read()
1827
1894
        try:
1828
1895
            self._control_files.lock_write()
1829
 
            return self
 
1896
            return LogicalLockResult(self.unlock)
1830
1897
        except:
1831
1898
            self.branch.unlock()
1832
1899
            raise
1834
1901
    def lock_write(self):
1835
1902
        """See MutableTree.lock_write, and WorkingTree.unlock.
1836
1903
 
1837
 
        :return: An object with an unlock method which will release the lock
1838
 
            obtained.
 
1904
        :return: A bzrlib.lock.LogicalLockResult.
1839
1905
        """
1840
1906
        if not self.is_locked():
1841
1907
            self._reset_data()
1842
1908
        self.branch.lock_write()
1843
1909
        try:
1844
1910
            self._control_files.lock_write()
1845
 
            return self
 
1911
            return LogicalLockResult(self.unlock)
1846
1912
        except:
1847
1913
            self.branch.unlock()
1848
1914
            raise
1965
2031
 
1966
2032
        inv_delta = []
1967
2033
 
1968
 
        new_files=set()
 
2034
        all_files = set() # specified and nested files 
1969
2035
        unknown_nested_files=set()
1970
2036
        if to_file is None:
1971
2037
            to_file = sys.stdout
1972
2038
 
 
2039
        files_to_backup = []
 
2040
 
1973
2041
        def recurse_directory_to_add_files(directory):
1974
2042
            # Recurse directory and add all files
1975
2043
            # so we can check if they have changed.
1976
 
            for parent_info, file_infos in\
1977
 
                self.walkdirs(directory):
 
2044
            for parent_info, file_infos in self.walkdirs(directory):
1978
2045
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
1979
2046
                    # Is it versioned or ignored?
1980
 
                    if self.path2id(relpath) or self.is_ignored(relpath):
 
2047
                    if self.path2id(relpath):
1981
2048
                        # Add nested content for deletion.
1982
 
                        new_files.add(relpath)
 
2049
                        all_files.add(relpath)
1983
2050
                    else:
1984
 
                        # Files which are not versioned and not ignored
 
2051
                        # Files which are not versioned
1985
2052
                        # should be treated as unknown.
1986
 
                        unknown_nested_files.add((relpath, None, kind))
 
2053
                        files_to_backup.append(relpath)
1987
2054
 
1988
2055
        for filename in files:
1989
2056
            # Get file name into canonical form.
1990
2057
            abspath = self.abspath(filename)
1991
2058
            filename = self.relpath(abspath)
1992
2059
            if len(filename) > 0:
1993
 
                new_files.add(filename)
 
2060
                all_files.add(filename)
1994
2061
                recurse_directory_to_add_files(filename)
1995
2062
 
1996
 
        files = list(new_files)
 
2063
        files = list(all_files)
1997
2064
 
1998
2065
        if len(files) == 0:
1999
2066
            return # nothing to do
2003
2070
 
2004
2071
        # Bail out if we are going to delete files we shouldn't
2005
2072
        if not keep_files and not force:
2006
 
            has_changed_files = len(unknown_nested_files) > 0
2007
 
            if not has_changed_files:
2008
 
                for (file_id, path, content_change, versioned, parent_id, name,
2009
 
                     kind, executable) in self.iter_changes(self.basis_tree(),
2010
 
                         include_unchanged=True, require_versioned=False,
2011
 
                         want_unversioned=True, specific_files=files):
2012
 
                    if versioned == (False, False):
2013
 
                        # The record is unknown ...
2014
 
                        if not self.is_ignored(path[1]):
2015
 
                            # ... but not ignored
2016
 
                            has_changed_files = True
2017
 
                            break
2018
 
                    elif content_change and (kind[1] is not None):
2019
 
                        # Versioned and changed, but not deleted
2020
 
                        has_changed_files = True
2021
 
                        break
 
2073
            for (file_id, path, content_change, versioned, parent_id, name,
 
2074
                 kind, executable) in self.iter_changes(self.basis_tree(),
 
2075
                     include_unchanged=True, require_versioned=False,
 
2076
                     want_unversioned=True, specific_files=files):
 
2077
                if versioned[0] == False:
 
2078
                    # The record is unknown or newly added
 
2079
                    files_to_backup.append(path[1])
 
2080
                elif (content_change and (kind[1] is not None) and
 
2081
                        osutils.is_inside_any(files, path[1])):
 
2082
                    # Versioned and changed, but not deleted, and still
 
2083
                    # in one of the dirs to be deleted.
 
2084
                    files_to_backup.append(path[1])
2022
2085
 
2023
 
            if has_changed_files:
2024
 
                # Make delta show ALL applicable changes in error message.
2025
 
                tree_delta = self.changes_from(self.basis_tree(),
2026
 
                    require_versioned=False, want_unversioned=True,
2027
 
                    specific_files=files)
2028
 
                for unknown_file in unknown_nested_files:
2029
 
                    if unknown_file not in tree_delta.unversioned:
2030
 
                        tree_delta.unversioned.extend((unknown_file,))
2031
 
                raise errors.BzrRemoveChangedFilesError(tree_delta)
 
2086
        def backup(file_to_backup):
 
2087
            backup_name = self.bzrdir._available_backup_name(file_to_backup)
 
2088
            osutils.rename(abs_path, self.abspath(backup_name))
 
2089
            return "removed %s (but kept a copy: %s)" % (file_to_backup,
 
2090
                                                         backup_name)
2032
2091
 
2033
2092
        # Build inv_delta and delete files where applicable,
2034
2093
        # do this before any modifications to inventory.
2058
2117
                        len(os.listdir(abs_path)) > 0):
2059
2118
                        if force:
2060
2119
                            osutils.rmtree(abs_path)
 
2120
                            message = "deleted %s" % (f,)
2061
2121
                        else:
2062
 
                            message = "%s is not an empty directory "\
2063
 
                                "and won't be deleted." % (f,)
 
2122
                            message = backup(f)
2064
2123
                    else:
2065
 
                        osutils.delete_any(abs_path)
2066
 
                        message = "deleted %s" % (f,)
 
2124
                        if f in files_to_backup:
 
2125
                            message = backup(f)
 
2126
                        else:
 
2127
                            osutils.delete_any(abs_path)
 
2128
                            message = "deleted %s" % (f,)
2067
2129
                elif message is not None:
2068
2130
                    # Only care if we haven't done anything yet.
2069
2131
                    message = "%s does not exist." % (f,)
2206
2268
    _marker = object()
2207
2269
 
2208
2270
    def update(self, change_reporter=None, possible_transports=None,
2209
 
               revision=None, old_tip=_marker):
 
2271
               revision=None, old_tip=_marker, show_base=False):
2210
2272
        """Update a working tree along its branch.
2211
2273
 
2212
2274
        This will update the branch if its bound too, which means we have
2249
2311
            else:
2250
2312
                if old_tip is self._marker:
2251
2313
                    old_tip = None
2252
 
            return self._update_tree(old_tip, change_reporter, revision)
 
2314
            return self._update_tree(old_tip, change_reporter, revision, show_base)
2253
2315
        finally:
2254
2316
            self.unlock()
2255
2317
 
2256
2318
    @needs_tree_write_lock
2257
 
    def _update_tree(self, old_tip=None, change_reporter=None, revision=None):
 
2319
    def _update_tree(self, old_tip=None, change_reporter=None, revision=None,
 
2320
                     show_base=False):
2258
2321
        """Update a tree to the master branch.
2259
2322
 
2260
2323
        :param old_tip: if supplied, the previous tip revision the branch,
2287
2350
            other_tree = self.branch.repository.revision_tree(old_tip)
2288
2351
            nb_conflicts = merge.merge_inner(self.branch, other_tree,
2289
2352
                                             base_tree, this_tree=self,
2290
 
                                             change_reporter=change_reporter)
 
2353
                                             change_reporter=change_reporter,
 
2354
                                             show_base=show_base)
2291
2355
            if nb_conflicts:
2292
2356
                self.add_parent_tree((old_tip, other_tree))
2293
2357
                trace.note('Rerun update after fixing the conflicts.')
2317
2381
 
2318
2382
            nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
2319
2383
                                             this_tree=self,
2320
 
                                             change_reporter=change_reporter)
 
2384
                                             change_reporter=change_reporter,
 
2385
                                             show_base=show_base)
2321
2386
            self.set_last_revision(revision)
2322
2387
            # TODO - dedup parents list with things merged by pull ?
2323
2388
            # reuse the tree we've updated to to set the basis:
2364
2429
    def add_conflicts(self, arg):
2365
2430
        raise errors.UnsupportedOperation(self.add_conflicts, self)
2366
2431
 
2367
 
    @needs_read_lock
2368
2432
    def conflicts(self):
2369
 
        conflicts = _mod_conflicts.ConflictList()
2370
 
        for conflicted in self._iter_conflicts():
2371
 
            text = True
2372
 
            try:
2373
 
                if file_kind(self.abspath(conflicted)) != "file":
2374
 
                    text = False
2375
 
            except errors.NoSuchFile:
2376
 
                text = False
2377
 
            if text is True:
2378
 
                for suffix in ('.THIS', '.OTHER'):
2379
 
                    try:
2380
 
                        kind = file_kind(self.abspath(conflicted+suffix))
2381
 
                        if kind != "file":
2382
 
                            text = False
2383
 
                    except errors.NoSuchFile:
2384
 
                        text = False
2385
 
                    if text == False:
2386
 
                        break
2387
 
            ctype = {True: 'text conflict', False: 'contents conflict'}[text]
2388
 
            conflicts.append(_mod_conflicts.Conflict.factory(ctype,
2389
 
                             path=conflicted,
2390
 
                             file_id=self.path2id(conflicted)))
2391
 
        return conflicts
 
2433
        raise NotImplementedError(self.conflicts)
2392
2434
 
2393
2435
    def walkdirs(self, prefix=""):
2394
2436
        """Walk the directories of this tree.
2612
2654
        """
2613
2655
        return
2614
2656
 
 
2657
    @needs_read_lock
 
2658
    def check_state(self):
 
2659
        """Check that the working state is/isn't valid."""
 
2660
        check_refs = self._get_check_refs()
 
2661
        refs = {}
 
2662
        for ref in check_refs:
 
2663
            kind, value = ref
 
2664
            if kind == 'trees':
 
2665
                refs[ref] = self.branch.repository.revision_tree(value)
 
2666
        self._check(refs)
 
2667
 
 
2668
    @needs_tree_write_lock
 
2669
    def reset_state(self, revision_ids=None):
 
2670
        """Reset the state of the working tree.
 
2671
 
 
2672
        This does a hard-reset to a last-known-good state. This is a way to
 
2673
        fix if something got corrupted (like the .bzr/checkout/dirstate file)
 
2674
        """
 
2675
        if revision_ids is None:
 
2676
            revision_ids = self.get_parent_ids()
 
2677
        if not revision_ids:
 
2678
            rt = self.branch.repository.revision_tree(
 
2679
                _mod_revision.NULL_REVISION)
 
2680
        else:
 
2681
            rt = self.branch.repository.revision_tree(revision_ids[0])
 
2682
        self._write_inventory(rt.inventory)
 
2683
        self.set_parent_ids(revision_ids)
 
2684
 
2615
2685
    def _get_rules_searcher(self, default_searcher):
2616
2686
        """See Tree._get_rules_searcher."""
2617
2687
        if self._rules_searcher is None:
2625
2695
        return ShelfManager(self, self._transport)
2626
2696
 
2627
2697
 
2628
 
class WorkingTree2(WorkingTree):
2629
 
    """This is the Format 2 working tree.
2630
 
 
2631
 
    This was the first weave based working tree.
2632
 
     - uses os locks for locking.
2633
 
     - uses the branch last-revision.
2634
 
    """
2635
 
 
2636
 
    def __init__(self, *args, **kwargs):
2637
 
        super(WorkingTree2, self).__init__(*args, **kwargs)
2638
 
        # WorkingTree2 has more of a constraint that self._inventory must
2639
 
        # exist. Because this is an older format, we don't mind the overhead
2640
 
        # caused by the extra computation here.
2641
 
 
2642
 
        # Newer WorkingTree's should only have self._inventory set when they
2643
 
        # have a read lock.
2644
 
        if self._inventory is None:
2645
 
            self.read_working_inventory()
2646
 
 
2647
 
    def _get_check_refs(self):
2648
 
        """Return the references needed to perform a check of this tree."""
2649
 
        return [('trees', self.last_revision())]
2650
 
 
2651
 
    def lock_tree_write(self):
2652
 
        """See WorkingTree.lock_tree_write().
2653
 
 
2654
 
        In Format2 WorkingTrees we have a single lock for the branch and tree
2655
 
        so lock_tree_write() degrades to lock_write().
2656
 
 
2657
 
        :return: An object with an unlock method which will release the lock
2658
 
            obtained.
2659
 
        """
2660
 
        self.branch.lock_write()
2661
 
        try:
2662
 
            self._control_files.lock_write()
2663
 
            return self
2664
 
        except:
2665
 
            self.branch.unlock()
2666
 
            raise
2667
 
 
2668
 
    def unlock(self):
2669
 
        # do non-implementation specific cleanup
2670
 
        self._cleanup()
2671
 
 
2672
 
        # we share control files:
2673
 
        if self._control_files._lock_count == 3:
2674
 
            # _inventory_is_modified is always False during a read lock.
2675
 
            if self._inventory_is_modified:
2676
 
                self.flush()
2677
 
            self._write_hashcache_if_dirty()
2678
 
 
2679
 
        # reverse order of locking.
2680
 
        try:
2681
 
            return self._control_files.unlock()
2682
 
        finally:
2683
 
            self.branch.unlock()
2684
 
 
2685
 
 
2686
2698
class WorkingTree3(WorkingTree):
2687
2699
    """This is the Format 3 working tree.
2688
2700
 
2761
2773
            self.branch.unlock()
2762
2774
 
2763
2775
 
2764
 
def get_conflicted_stem(path):
2765
 
    for suffix in _mod_conflicts.CONFLICT_SUFFIXES:
2766
 
        if path.endswith(suffix):
2767
 
            return path[:-len(suffix)]
2768
 
 
2769
 
 
2770
 
class WorkingTreeFormat(object):
 
2776
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
 
2777
    """Registry for working tree formats."""
 
2778
 
 
2779
    def __init__(self, other_registry=None):
 
2780
        super(WorkingTreeFormatRegistry, self).__init__(other_registry)
 
2781
        self._default_format = None
 
2782
 
 
2783
    def get_default(self):
 
2784
        """Return the current default format."""
 
2785
        return self._default_format
 
2786
 
 
2787
    def set_default(self, format):
 
2788
        self._default_format = format
 
2789
 
 
2790
 
 
2791
format_registry = WorkingTreeFormatRegistry()
 
2792
 
 
2793
 
 
2794
class WorkingTreeFormat(controldir.ControlComponentFormat):
2771
2795
    """An encapsulation of the initialization and open routines for a format.
2772
2796
 
2773
2797
    Formats provide three things:
2785
2809
    object will be created every time regardless.
2786
2810
    """
2787
2811
 
2788
 
    _default_format = None
2789
 
    """The default format used for new trees."""
2790
 
 
2791
 
    _formats = {}
2792
 
    """The known formats."""
2793
 
 
2794
2812
    requires_rich_root = False
2795
2813
 
2796
2814
    upgrade_recommended = False
2797
2815
 
 
2816
    requires_normalized_unicode_filenames = False
 
2817
 
 
2818
    case_sensitive_filename = "FoRMaT"
 
2819
 
 
2820
    missing_parent_conflicts = False
 
2821
    """If this format supports missing parent conflicts."""
 
2822
 
2798
2823
    @classmethod
2799
2824
    def find_format(klass, a_bzrdir):
2800
2825
        """Return the format for the working tree object in a_bzrdir."""
2801
2826
        try:
2802
2827
            transport = a_bzrdir.get_workingtree_transport(None)
2803
2828
            format_string = transport.get_bytes("format")
2804
 
            return klass._formats[format_string]
 
2829
            return format_registry.get(format_string)
2805
2830
        except errors.NoSuchFile:
2806
2831
            raise errors.NoWorkingTree(base=transport.base)
2807
2832
        except KeyError:
2808
2833
            raise errors.UnknownFormatError(format=format_string,
2809
2834
                                            kind="working tree")
2810
2835
 
 
2836
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
2837
                   accelerator_tree=None, hardlink=False):
 
2838
        """Initialize a new working tree in a_bzrdir.
 
2839
 
 
2840
        :param a_bzrdir: BzrDir to initialize the working tree in.
 
2841
        :param revision_id: allows creating a working tree at a different
 
2842
            revision than the branch is at.
 
2843
        :param from_branch: Branch to checkout
 
2844
        :param accelerator_tree: A tree which can be used for retrieving file
 
2845
            contents more quickly than the revision tree, i.e. a workingtree.
 
2846
            The revision tree will be used for cases where accelerator_tree's
 
2847
            content is different.
 
2848
        :param hardlink: If true, hard-link files from accelerator_tree,
 
2849
            where possible.
 
2850
        """
 
2851
        raise NotImplementedError(self.initialize)
 
2852
 
2811
2853
    def __eq__(self, other):
2812
2854
        return self.__class__ is other.__class__
2813
2855
 
2815
2857
        return not (self == other)
2816
2858
 
2817
2859
    @classmethod
 
2860
    @symbol_versioning.deprecated_method(
 
2861
        symbol_versioning.deprecated_in((2, 4, 0)))
2818
2862
    def get_default_format(klass):
2819
2863
        """Return the current default format."""
2820
 
        return klass._default_format
 
2864
        return format_registry.get_default()
2821
2865
 
2822
2866
    def get_format_string(self):
2823
2867
        """Return the ASCII format string that identifies this format."""
2845
2889
        return False
2846
2890
 
2847
2891
    @classmethod
 
2892
    @symbol_versioning.deprecated_method(
 
2893
        symbol_versioning.deprecated_in((2, 4, 0)))
2848
2894
    def register_format(klass, format):
2849
 
        klass._formats[format.get_format_string()] = format
2850
 
 
2851
 
    @classmethod
 
2895
        format_registry.register(format)
 
2896
 
 
2897
    @classmethod
 
2898
    @symbol_versioning.deprecated_method(
 
2899
        symbol_versioning.deprecated_in((2, 4, 0)))
 
2900
    def register_extra_format(klass, format):
 
2901
        format_registry.register_extra(format)
 
2902
 
 
2903
    @classmethod
 
2904
    @symbol_versioning.deprecated_method(
 
2905
        symbol_versioning.deprecated_in((2, 4, 0)))
 
2906
    def unregister_extra_format(klass, format):
 
2907
        format_registry.unregister_extra(format)
 
2908
 
 
2909
    @classmethod
 
2910
    @symbol_versioning.deprecated_method(
 
2911
        symbol_versioning.deprecated_in((2, 4, 0)))
 
2912
    def get_formats(klass):
 
2913
        return format_registry._get_all()
 
2914
 
 
2915
    @classmethod
 
2916
    @symbol_versioning.deprecated_method(
 
2917
        symbol_versioning.deprecated_in((2, 4, 0)))
2852
2918
    def set_default_format(klass, format):
2853
 
        klass._default_format = format
 
2919
        format_registry.set_default(format)
2854
2920
 
2855
2921
    @classmethod
 
2922
    @symbol_versioning.deprecated_method(
 
2923
        symbol_versioning.deprecated_in((2, 4, 0)))
2856
2924
    def unregister_format(klass, format):
2857
 
        del klass._formats[format.get_format_string()]
2858
 
 
2859
 
 
2860
 
class WorkingTreeFormat2(WorkingTreeFormat):
2861
 
    """The second working tree format.
2862
 
 
2863
 
    This format modified the hash cache from the format 1 hash cache.
2864
 
    """
2865
 
 
2866
 
    upgrade_recommended = True
2867
 
 
2868
 
    def get_format_description(self):
2869
 
        """See WorkingTreeFormat.get_format_description()."""
2870
 
        return "Working tree format 2"
2871
 
 
2872
 
    def _stub_initialize_on_transport(self, transport, file_mode):
2873
 
        """Workaround: create control files for a remote working tree.
2874
 
 
2875
 
        This ensures that it can later be updated and dealt with locally,
2876
 
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2877
 
        no working tree.  (See bug #43064).
2878
 
        """
2879
 
        sio = StringIO()
2880
 
        inv = inventory.Inventory()
2881
 
        xml5.serializer_v5.write_inventory(inv, sio, working=True)
2882
 
        sio.seek(0)
2883
 
        transport.put_file('inventory', sio, file_mode)
2884
 
        transport.put_bytes('pending-merges', '', file_mode)
2885
 
 
2886
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2887
 
                   accelerator_tree=None, hardlink=False):
2888
 
        """See WorkingTreeFormat.initialize()."""
2889
 
        if not isinstance(a_bzrdir.transport, LocalTransport):
2890
 
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2891
 
        if from_branch is not None:
2892
 
            branch = from_branch
2893
 
        else:
2894
 
            branch = a_bzrdir.open_branch()
2895
 
        if revision_id is None:
2896
 
            revision_id = _mod_revision.ensure_null(branch.last_revision())
2897
 
        branch.lock_write()
2898
 
        try:
2899
 
            branch.generate_revision_history(revision_id)
2900
 
        finally:
2901
 
            branch.unlock()
2902
 
        inv = inventory.Inventory()
2903
 
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2904
 
                         branch,
2905
 
                         inv,
2906
 
                         _internal=True,
2907
 
                         _format=self,
2908
 
                         _bzrdir=a_bzrdir)
2909
 
        basis_tree = branch.repository.revision_tree(revision_id)
2910
 
        if basis_tree.inventory.root is not None:
2911
 
            wt.set_root_id(basis_tree.get_root_id())
2912
 
        # set the parent list and cache the basis tree.
2913
 
        if _mod_revision.is_null(revision_id):
2914
 
            parent_trees = []
2915
 
        else:
2916
 
            parent_trees = [(revision_id, basis_tree)]
2917
 
        wt.set_parent_trees(parent_trees)
2918
 
        transform.build_tree(basis_tree, wt)
2919
 
        return wt
2920
 
 
2921
 
    def __init__(self):
2922
 
        super(WorkingTreeFormat2, self).__init__()
2923
 
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
2924
 
 
2925
 
    def open(self, a_bzrdir, _found=False):
2926
 
        """Return the WorkingTree object for a_bzrdir
2927
 
 
2928
 
        _found is a private parameter, do not use it. It is used to indicate
2929
 
               if format probing has already been done.
2930
 
        """
2931
 
        if not _found:
2932
 
            # we are being called directly and must probe.
2933
 
            raise NotImplementedError
2934
 
        if not isinstance(a_bzrdir.transport, LocalTransport):
2935
 
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2936
 
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2937
 
                           _internal=True,
2938
 
                           _format=self,
2939
 
                           _bzrdir=a_bzrdir)
2940
 
        return wt
 
2925
        format_registry.remove(format)
 
2926
 
2941
2927
 
2942
2928
class WorkingTreeFormat3(WorkingTreeFormat):
2943
2929
    """The second working tree format updated to record a format marker.
2953
2939
 
2954
2940
    upgrade_recommended = True
2955
2941
 
 
2942
    missing_parent_conflicts = True
 
2943
 
2956
2944
    def get_format_string(self):
2957
2945
        """See WorkingTreeFormat.get_format_string()."""
2958
2946
        return "Bazaar-NG Working Tree format 3"
3071
3059
 
3072
3060
 
3073
3061
__default_format = WorkingTreeFormat6()
3074
 
WorkingTreeFormat.register_format(__default_format)
3075
 
WorkingTreeFormat.register_format(WorkingTreeFormat5())
3076
 
WorkingTreeFormat.register_format(WorkingTreeFormat4())
3077
 
WorkingTreeFormat.register_format(WorkingTreeFormat3())
3078
 
WorkingTreeFormat.set_default_format(__default_format)
3079
 
# formats which have no format string are not discoverable
3080
 
# and not independently creatable, so are not registered.
3081
 
_legacy_formats = [WorkingTreeFormat2(),
3082
 
                   ]
 
3062
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
 
3063
    "bzrlib.workingtree_4", "WorkingTreeFormat4")
 
3064
format_registry.register_lazy("Bazaar Working Tree Format 5 (bzr 1.11)\n",
 
3065
    "bzrlib.workingtree_4", "WorkingTreeFormat5")
 
3066
format_registry.register_lazy("Bazaar Working Tree Format 6 (bzr 1.14)\n",
 
3067
    "bzrlib.workingtree_4", "WorkingTreeFormat6")
 
3068
format_registry.register(WorkingTreeFormat3())
 
3069
format_registry.set_default(__default_format)