~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

  • Committer: Martin
  • Date: 2010-05-16 15:18:43 UTC
  • mfrom: (5235 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5239.
  • Revision ID: gzlist@googlemail.com-20100516151843-lu53u7caehm3ie3i
Merge bzr.dev to resolve conflicts in NEWS and _chk_map_pyx

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2007-2010 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
28
28
 
29
29
from bzrlib.lazy_import import lazy_import
30
30
lazy_import(globals(), """
31
 
from bisect import bisect_left
32
 
import collections
33
 
from copy import deepcopy
34
31
import errno
35
 
import itertools
36
 
import operator
37
32
import stat
38
 
from time import time
39
 
import warnings
40
33
 
41
34
import bzrlib
42
35
from bzrlib import (
43
36
    bzrdir,
44
37
    cache_utf8,
45
 
    conflicts as _mod_conflicts,
46
38
    debug,
47
 
    delta,
48
39
    dirstate,
49
40
    errors,
50
41
    generate_ids,
51
 
    globbing,
52
 
    ignores,
53
 
    merge,
54
42
    osutils,
55
43
    revision as _mod_revision,
56
44
    revisiontree,
57
 
    textui,
58
45
    trace,
59
46
    transform,
60
 
    urlutils,
61
47
    views,
62
 
    xml5,
63
 
    xml6,
64
48
    )
65
49
import bzrlib.branch
66
 
from bzrlib.transport import get_transport
67
50
import bzrlib.ui
68
51
""")
69
52
 
70
 
from bzrlib import symbol_versioning
71
53
from bzrlib.decorators import needs_read_lock, needs_write_lock
72
54
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
73
 
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, entry_factory
74
 
import bzrlib.mutabletree
 
55
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
 
56
from bzrlib.lock import LogicalLockResult
75
57
from bzrlib.mutabletree import needs_tree_write_lock
76
58
from bzrlib.osutils import (
77
59
    file_kind,
78
60
    isdir,
79
 
    normpath,
80
61
    pathjoin,
81
 
    rand_chars,
82
62
    realpath,
83
63
    safe_unicode,
84
 
    splitpath,
85
64
    )
86
 
from bzrlib.trace import mutter, note
 
65
from bzrlib.trace import mutter
87
66
from bzrlib.transport.local import LocalTransport
88
67
from bzrlib.tree import InterTree
89
 
from bzrlib.progress import DummyProgress, ProgressPhase
90
 
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
91
 
from bzrlib.rio import RioReader, rio_file, Stanza
92
 
from bzrlib.symbol_versioning import (deprecated_passed,
93
 
        deprecated_method,
94
 
        deprecated_function,
95
 
        DEPRECATED_PARAMETER,
96
 
        )
97
68
from bzrlib.tree import Tree
98
69
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
99
70
 
351
322
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
352
323
                elif kind == 'tree-reference':
353
324
                    if not self._repo_supports_tree_reference:
354
 
                        raise AssertionError(
355
 
                            "repository of %r "
356
 
                            "doesn't support tree references "
357
 
                            "required by entry %r"
358
 
                            % (self, name))
 
325
                        raise errors.UnsupportedOperation(
 
326
                            self._generate_inventory,
 
327
                            self.branch.repository)
359
328
                    inv_entry.reference_revision = link_or_sha1 or None
360
329
                elif kind != 'symlink':
361
330
                    raise AssertionError("unknown kind %r" % kind)
466
435
        return osutils.lexists(pathjoin(
467
436
                    self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
468
437
 
 
438
    def has_or_had_id(self, file_id):
 
439
        state = self.current_dirstate()
 
440
        row, parents = self._get_entry(file_id=file_id)
 
441
        return row is not None
 
442
 
469
443
    @needs_read_lock
470
444
    def id2path(self, file_id):
471
445
        "Convert a file-id to a path."
594
568
            return _mod_revision.NULL_REVISION
595
569
 
596
570
    def lock_read(self):
597
 
        """See Branch.lock_read, and WorkingTree.unlock."""
 
571
        """See Branch.lock_read, and WorkingTree.unlock.
 
572
 
 
573
        :return: A bzrlib.lock.LogicalLockResult.
 
574
        """
598
575
        self.branch.lock_read()
599
576
        try:
600
577
            self._control_files.lock_read()
613
590
        except:
614
591
            self.branch.unlock()
615
592
            raise
 
593
        return LogicalLockResult(self.unlock)
616
594
 
617
595
    def _lock_self_write(self):
618
596
        """This should be called after the branch is locked."""
633
611
        except:
634
612
            self.branch.unlock()
635
613
            raise
 
614
        return LogicalLockResult(self.unlock)
636
615
 
637
616
    def lock_tree_write(self):
638
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
 
617
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
 
618
 
 
619
        :return: A bzrlib.lock.LogicalLockResult.
 
620
        """
639
621
        self.branch.lock_read()
640
 
        self._lock_self_write()
 
622
        return self._lock_self_write()
641
623
 
642
624
    def lock_write(self):
643
 
        """See MutableTree.lock_write, and WorkingTree.unlock."""
 
625
        """See MutableTree.lock_write, and WorkingTree.unlock.
 
626
 
 
627
        :return: A bzrlib.lock.LogicalLockResult.
 
628
        """
644
629
        self.branch.lock_write()
645
 
        self._lock_self_write()
 
630
        return self._lock_self_write()
646
631
 
647
632
    @needs_tree_write_lock
648
633
    def move(self, from_paths, to_dir, after=False):
718
703
            from_entry = self._get_entry(path=from_rel)
719
704
            if from_entry == (None, None):
720
705
                raise errors.BzrMoveFailedError(from_rel,to_dir,
721
 
                    errors.NotVersionedError(path=str(from_rel)))
 
706
                    errors.NotVersionedError(path=from_rel))
722
707
 
723
708
            from_id = from_entry[0][2]
724
709
            to_rel = pathjoin(to_dir, from_tail)
1053
1038
    def set_last_revision(self, new_revision):
1054
1039
        """Change the last revision in the working tree."""
1055
1040
        parents = self.get_parent_ids()
1056
 
        if new_revision in (NULL_REVISION, None):
 
1041
        if new_revision in (_mod_revision.NULL_REVISION, None):
1057
1042
            if len(parents) >= 2:
1058
1043
                raise AssertionError(
1059
1044
                    "setting the last parent to none with a pending merge is "
1226
1211
                # just forget the whole block.
1227
1212
                entry_index = 0
1228
1213
                while entry_index < len(block[1]):
1229
 
                    # Mark this file id as having been removed
1230
1214
                    entry = block[1][entry_index]
1231
 
                    ids_to_unversion.discard(entry[0][2])
1232
 
                    if (entry[1][0][0] in 'ar' # don't remove absent or renamed
1233
 
                                               # entries
1234
 
                        or not state._make_absent(entry)):
 
1215
                    if entry[1][0][0] in 'ar':
 
1216
                        # don't remove absent or renamed entries
1235
1217
                        entry_index += 1
 
1218
                    else:
 
1219
                        # Mark this file id as having been removed
 
1220
                        ids_to_unversion.discard(entry[0][2])
 
1221
                        if not state._make_absent(entry):
 
1222
                            # The block has not shrunk.
 
1223
                            entry_index += 1
1236
1224
                # go to the next block. (At the moment we dont delete empty
1237
1225
                # dirblocks)
1238
1226
                block_index += 1
1290
1278
        if self._dirty:
1291
1279
            raise AssertionError("attempting to write an inventory when the "
1292
1280
                "dirstate is dirty will lose pending changes")
1293
 
        self.current_dirstate().set_state_from_inventory(inv)
1294
 
        self._make_dirty(reset_inventory=False)
1295
 
        if self._inventory is not None:
 
1281
        had_inventory = self._inventory is not None
 
1282
        # Setting self._inventory = None forces the dirstate to regenerate the
 
1283
        # working inventory. We do this because self.inventory may be inv, or
 
1284
        # may have been modified, and either case would prevent a clean delta
 
1285
        # being created.
 
1286
        self._inventory = None
 
1287
        # generate a delta,
 
1288
        delta = inv._make_delta(self.inventory)
 
1289
        # and apply it.
 
1290
        self.apply_inventory_delta(delta)
 
1291
        if had_inventory:
1296
1292
            self._inventory = inv
1297
1293
        self.flush()
1298
1294
 
1303
1299
        self.tree = tree
1304
1300
 
1305
1301
    def sha1(self, abspath):
1306
 
        """Return the sha1 of a file given its absolute path."""
1307
 
        filters = self.tree._content_filter_stack(self.tree.relpath(abspath))
 
1302
        """See dirstate.SHA1Provider.sha1()."""
 
1303
        filters = self.tree._content_filter_stack(
 
1304
            self.tree.relpath(osutils.safe_unicode(abspath)))
1308
1305
        return internal_size_sha_file_byname(abspath, filters)[1]
1309
1306
 
1310
1307
    def stat_and_sha1(self, abspath):
1311
 
        """Return the stat and sha1 of a file given its absolute path."""
1312
 
        filters = self.tree._content_filter_stack(self.tree.relpath(abspath))
 
1308
        """See dirstate.SHA1Provider.stat_and_sha1()."""
 
1309
        filters = self.tree._content_filter_stack(
 
1310
            self.tree.relpath(osutils.safe_unicode(abspath)))
1313
1311
        file_obj = file(abspath, 'rb', 65000)
1314
1312
        try:
1315
1313
            statvalue = os.fstat(file_obj.fileno())
1321
1319
        return statvalue, sha1
1322
1320
 
1323
1321
 
 
1322
class ContentFilteringDirStateWorkingTree(DirStateWorkingTree):
 
1323
    """Dirstate working tree that supports content filtering.
 
1324
 
 
1325
    The dirstate holds the hash and size of the canonical form of the file, 
 
1326
    and most methods must return that.
 
1327
    """
 
1328
 
 
1329
    def _file_content_summary(self, path, stat_result):
 
1330
        # This is to support the somewhat obsolete path_content_summary method
 
1331
        # with content filtering: see
 
1332
        # <https://bugs.edge.launchpad.net/bzr/+bug/415508>.
 
1333
        #
 
1334
        # If the dirstate cache is up to date and knows the hash and size,
 
1335
        # return that.
 
1336
        # Otherwise if there are no content filters, return the on-disk size
 
1337
        # and leave the hash blank.
 
1338
        # Otherwise, read and filter the on-disk file and use its size and
 
1339
        # hash.
 
1340
        #
 
1341
        # The dirstate doesn't store the size of the canonical form so we
 
1342
        # can't trust it for content-filtered trees.  We just return None.
 
1343
        dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result)
 
1344
        executable = self._is_executable_from_path_and_stat(path, stat_result)
 
1345
        return ('file', None, executable, dirstate_sha1)
 
1346
 
 
1347
 
1324
1348
class WorkingTree4(DirStateWorkingTree):
1325
1349
    """This is the Format 4 working tree.
1326
1350
 
1334
1358
    """
1335
1359
 
1336
1360
 
1337
 
class WorkingTree5(DirStateWorkingTree):
 
1361
class WorkingTree5(ContentFilteringDirStateWorkingTree):
1338
1362
    """This is the Format 5 working tree.
1339
1363
 
1340
1364
    This differs from WorkingTree4 by:
1341
1365
     - Supporting content filtering.
 
1366
 
 
1367
    This is new in bzr 1.11.
 
1368
    """
 
1369
 
 
1370
 
 
1371
class WorkingTree6(ContentFilteringDirStateWorkingTree):
 
1372
    """This is the Format 6 working tree.
 
1373
 
 
1374
    This differs from WorkingTree5 by:
1342
1375
     - Supporting a current view that may mask the set of files in a tree
1343
1376
       impacted by most user operations.
1344
1377
 
1345
 
    This is new in bzr 1.11.
 
1378
    This is new in bzr 1.14.
1346
1379
    """
1347
1380
 
1348
1381
    def _make_views(self):
1350
1383
 
1351
1384
 
1352
1385
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
 
1386
 
1353
1387
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1354
1388
                   accelerator_tree=None, hardlink=False):
1355
1389
        """See WorkingTreeFormat.initialize().
1394
1428
        wt.lock_tree_write()
1395
1429
        try:
1396
1430
            self._init_custom_control_files(wt)
1397
 
            if revision_id in (None, NULL_REVISION):
 
1431
            if revision_id in (None, _mod_revision.NULL_REVISION):
1398
1432
                if branch.repository.supports_rich_root():
1399
1433
                    wt._set_root_id(generate_ids.gen_root_id())
1400
1434
                else:
1411
1445
                    pass
1412
1446
            if basis is None:
1413
1447
                basis = branch.repository.revision_tree(revision_id)
1414
 
            if revision_id == NULL_REVISION:
 
1448
            if revision_id == _mod_revision.NULL_REVISION:
1415
1449
                parents_list = []
1416
1450
            else:
1417
1451
                parents_list = [(revision_id, basis)]
1425
1459
                if basis_root_id is not None:
1426
1460
                    wt._set_root_id(basis_root_id)
1427
1461
                    wt.flush()
 
1462
                if wt.supports_content_filtering():
 
1463
                    # The original tree may not have the same content filters
 
1464
                    # applied so we can't safely build the inventory delta from
 
1465
                    # the source tree.
 
1466
                    delta_from_tree = False
 
1467
                else:
 
1468
                    delta_from_tree = True
1428
1469
                # delta_from_tree is safe even for DirStateRevisionTrees,
1429
1470
                # because wt4.apply_inventory_delta does not mutate the input
1430
1471
                # inventory entries.
1431
1472
                transform.build_tree(basis, wt, accelerator_tree,
1432
 
                                     hardlink=hardlink, delta_from_tree=True)
 
1473
                                     hardlink=hardlink,
 
1474
                                     delta_from_tree=delta_from_tree)
1433
1475
            finally:
1434
1476
                basis.unlock()
1435
1477
        finally:
1496
1538
 
1497
1539
 
1498
1540
class WorkingTreeFormat5(DirStateWorkingTreeFormat):
1499
 
    """WorkingTree format supporting views.
 
1541
    """WorkingTree format supporting content filtering.
1500
1542
    """
1501
1543
 
1502
1544
    upgrade_recommended = False
1511
1553
        """See WorkingTreeFormat.get_format_description()."""
1512
1554
        return "Working tree format 5"
1513
1555
 
 
1556
    def supports_content_filtering(self):
 
1557
        return True
 
1558
 
 
1559
 
 
1560
class WorkingTreeFormat6(DirStateWorkingTreeFormat):
 
1561
    """WorkingTree format supporting views.
 
1562
    """
 
1563
 
 
1564
    upgrade_recommended = False
 
1565
 
 
1566
    _tree_class = WorkingTree6
 
1567
 
 
1568
    def get_format_string(self):
 
1569
        """See WorkingTreeFormat.get_format_string()."""
 
1570
        return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
 
1571
 
 
1572
    def get_format_description(self):
 
1573
        """See WorkingTreeFormat.get_format_description()."""
 
1574
        return "Working tree format 6"
 
1575
 
1514
1576
    def _init_custom_control_files(self, wt):
1515
1577
        """Subclasses with custom control files should override this method."""
1516
1578
        wt._transport.put_bytes('views', '', mode=wt.bzrdir._get_file_mode())
1523
1585
 
1524
1586
 
1525
1587
class DirStateRevisionTree(Tree):
1526
 
    """A revision tree pulling the inventory from a dirstate."""
 
1588
    """A revision tree pulling the inventory from a dirstate.
 
1589
    
 
1590
    Note that this is one of the historical (ie revision) trees cached in the
 
1591
    dirstate for easy access, not the workingtree.
 
1592
    """
1527
1593
 
1528
1594
    def __init__(self, dirstate, revision_id, repository):
1529
1595
        self._dirstate = dirstate
1701
1767
            return None
1702
1768
        parent_index = self._get_parent_index()
1703
1769
        last_changed_revision = entry[1][parent_index][4]
1704
 
        return self._repository.get_revision(last_changed_revision).timestamp
 
1770
        try:
 
1771
            rev = self._repository.get_revision(last_changed_revision)
 
1772
        except errors.NoSuchRevision:
 
1773
            raise errors.FileTimestampUnavailable(self.id2path(file_id))
 
1774
        return rev.timestamp
1705
1775
 
1706
1776
    def get_file_sha1(self, file_id, path=None, stat_value=None):
1707
1777
        entry = self._get_entry(file_id=file_id, path=path)
1745
1815
        if entry[1][parent_index][0] != 'l':
1746
1816
            return None
1747
1817
        else:
1748
 
            # At present, none of the tree implementations supports non-ascii
1749
 
            # symlink targets. So we will just assume that the dirstate path is
1750
 
            # correct.
1751
 
            return entry[1][parent_index][1]
 
1818
            target = entry[1][parent_index][1]
 
1819
            target = target.decode('utf8')
 
1820
            return target
1752
1821
 
1753
1822
    def get_revision_id(self):
1754
1823
        """Return the revision id for this tree."""
1775
1844
        entry = self._get_entry(file_id=file_id)[1]
1776
1845
        if entry is None:
1777
1846
            raise errors.NoSuchId(tree=self, file_id=file_id)
1778
 
        return dirstate.DirState._minikind_to_kind[entry[1][0]]
 
1847
        parent_index = self._get_parent_index()
 
1848
        return dirstate.DirState._minikind_to_kind[entry[parent_index][0]]
1779
1849
 
1780
1850
    def stored_kind(self, file_id):
1781
1851
        """See Tree.stored_kind"""
1801
1871
            return None
1802
1872
        return ie.executable
1803
1873
 
1804
 
    def list_files(self, include_root=False):
 
1874
    def is_locked(self):
 
1875
        return self._locked
 
1876
 
 
1877
    def list_files(self, include_root=False, from_dir=None, recursive=True):
1805
1878
        # We use a standard implementation, because DirStateRevisionTree is
1806
1879
        # dealing with one of the parents of the current state
1807
1880
        inv = self._get_inventory()
1808
 
        entries = inv.iter_entries()
1809
 
        if self.inventory.root is not None and not include_root:
 
1881
        if from_dir is None:
 
1882
            from_dir_id = None
 
1883
        else:
 
1884
            from_dir_id = inv.path2id(from_dir)
 
1885
            if from_dir_id is None:
 
1886
                # Directory not versioned
 
1887
                return
 
1888
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
 
1889
        if inv.root is not None and not include_root and from_dir is None:
1810
1890
            entries.next()
1811
1891
        for path, entry in entries:
1812
1892
            yield path, 'V', entry.kind, entry.file_id, entry
1813
1893
 
1814
1894
    def lock_read(self):
1815
 
        """Lock the tree for a set of operations."""
 
1895
        """Lock the tree for a set of operations.
 
1896
 
 
1897
        :return: A bzrlib.lock.LogicalLockResult.
 
1898
        """
1816
1899
        if not self._locked:
1817
1900
            self._repository.lock_read()
1818
1901
            if self._dirstate._lock_token is None:
1819
1902
                self._dirstate.lock_read()
1820
1903
                self._dirstate_locked = True
1821
1904
        self._locked += 1
 
1905
        return LogicalLockResult(self.unlock)
1822
1906
 
1823
1907
    def _must_be_locked(self):
1824
1908
        if not self._locked:
1914
1998
        return result
1915
1999
 
1916
2000
    @classmethod
1917
 
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
 
2001
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
 
2002
                                                  target):
1918
2003
        from bzrlib.tests.test__dirstate_helpers import \
1919
 
            CompiledDirstateHelpersFeature
1920
 
        if not CompiledDirstateHelpersFeature.available():
1921
 
            from bzrlib.tests import UnavailableFeature
1922
 
            raise UnavailableFeature(CompiledDirstateHelpersFeature)
1923
 
        from bzrlib._dirstate_helpers_c import ProcessEntryC
 
2004
            compiled_dirstate_helpers_feature
 
2005
        test_case.requireFeature(compiled_dirstate_helpers_feature)
 
2006
        from bzrlib._dirstate_helpers_pyx import ProcessEntryC
1924
2007
        result = klass.make_source_parent_tree(source, target)
1925
2008
        result[1]._iter_changes = ProcessEntryC
1926
2009
        return result
1956
2039
            output. An unversioned file is defined as one with (False, False)
1957
2040
            for the versioned pair.
1958
2041
        """
1959
 
        # NB: show_status depends on being able to pass in non-versioned files
1960
 
        # and report them as unknown
1961
2042
        # TODO: handle extra trees in the dirstate.
1962
2043
        if (extra_trees or specific_files == []):
1963
2044
            # we can't fast-path these cases (yet)
1966
2047
                require_versioned, want_unversioned=want_unversioned)
1967
2048
        parent_ids = self.target.get_parent_ids()
1968
2049
        if not (self.source._revision_id in parent_ids
1969
 
                or self.source._revision_id == NULL_REVISION):
 
2050
                or self.source._revision_id == _mod_revision.NULL_REVISION):
1970
2051
            raise AssertionError(
1971
2052
                "revision {%s} is not stored in {%s}, but %s "
1972
2053
                "can only be used for trees stored in the dirstate"
1973
2054
                % (self.source._revision_id, self.target, self.iter_changes))
1974
2055
        target_index = 0
1975
 
        if self.source._revision_id == NULL_REVISION:
 
2056
        if self.source._revision_id == _mod_revision.NULL_REVISION:
1976
2057
            source_index = None
1977
2058
            indices = (target_index,)
1978
2059
        else:
1993
2074
        else:
1994
2075
            specific_files = set([''])
1995
2076
        # -- specific_files is now a utf8 path set --
1996
 
        search_specific_files = set()
 
2077
 
1997
2078
        # -- get the state object and prepare it.
1998
2079
        state = self.target.current_dirstate()
1999
2080
        state._read_dirblocks_if_needed()
2000
2081
        if require_versioned:
2001
2082
            # -- check all supplied paths are versioned in a search tree. --
2002
 
            all_versioned = True
 
2083
            not_versioned = []
2003
2084
            for path in specific_files:
2004
2085
                path_entries = state._entries_for_path(path)
2005
2086
                if not path_entries:
2006
2087
                    # this specified path is not present at all: error
2007
 
                    all_versioned = False
2008
 
                    break
 
2088
                    not_versioned.append(path)
 
2089
                    continue
2009
2090
                found_versioned = False
2010
2091
                # for each id at this path
2011
2092
                for entry in path_entries:
2018
2099
                if not found_versioned:
2019
2100
                    # none of the indexes was not 'absent' at all ids for this
2020
2101
                    # path.
2021
 
                    all_versioned = False
2022
 
                    break
2023
 
            if not all_versioned:
2024
 
                raise errors.PathsNotVersionedError(specific_files)
 
2102
                    not_versioned.append(path)
 
2103
            if len(not_versioned) > 0:
 
2104
                raise errors.PathsNotVersionedError(not_versioned)
2025
2105
        # -- remove redundancy in supplied specific_files to prevent over-scanning --
2026
 
        for path in specific_files:
2027
 
            other_specific_files = specific_files.difference(set([path]))
2028
 
            if not osutils.is_inside_any(other_specific_files, path):
2029
 
                # this is a top level path, we must check it.
2030
 
                search_specific_files.add(path)
 
2106
        search_specific_files = osutils.minimum_path_selection(specific_files)
2031
2107
 
2032
2108
        use_filesystem_for_exec = (sys.platform != 'win32')
2033
2109
        iter_changes = self.target._iter_changes(include_unchanged,
2045
2121
            (revisiontree.RevisionTree, DirStateRevisionTree)):
2046
2122
            return False
2047
2123
        # the source revid must be in the target dirstate
2048
 
        if not (source._revision_id == NULL_REVISION or
 
2124
        if not (source._revision_id == _mod_revision.NULL_REVISION or
2049
2125
            source._revision_id in target.get_parent_ids()):
2050
2126
            # TODO: what about ghosts? it may well need to
2051
2127
            # check for them explicitly.
2112
2188
        # tree during upgrade.
2113
2189
        tree._control_files.lock_write()
2114
2190
        try:
 
2191
            self.update_format(tree)
 
2192
        finally:
 
2193
            tree._control_files.unlock()
 
2194
 
 
2195
    def update_format(self, tree):
 
2196
        """Change the format marker."""
 
2197
        tree._transport.put_bytes('format',
 
2198
            self.target_format.get_format_string(),
 
2199
            mode=tree.bzrdir._get_file_mode())
 
2200
 
 
2201
 
 
2202
class Converter4or5to6(object):
 
2203
    """Perform an in-place upgrade of format 4 or 5 to format 6 trees."""
 
2204
 
 
2205
    def __init__(self):
 
2206
        self.target_format = WorkingTreeFormat6()
 
2207
 
 
2208
    def convert(self, tree):
 
2209
        # lock the control files not the tree, so that we don't get tree
 
2210
        # on-unlock behaviours, and so that no-one else diddles with the
 
2211
        # tree during upgrade.
 
2212
        tree._control_files.lock_write()
 
2213
        try:
2115
2214
            self.init_custom_control_files(tree)
2116
2215
            self.update_format(tree)
2117
2216
        finally: