/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 breezy/transform.py

  • Committer: Jelmer Vernooij
  • Date: 2019-03-04 00:16:27 UTC
  • mfrom: (7293 work)
  • mto: This revision was merged to the branch mainline in revision 7318.
  • Revision ID: jelmer@jelmer.uk-20190304001627-v6u7o6pf97tukhek
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
58
58
from .osutils import (
59
59
    delete_any,
60
60
    file_kind,
61
 
    has_symlinks,
62
61
    pathjoin,
63
62
    sha_file,
64
63
    splitpath,
 
64
    supports_symlinks,
65
65
    )
66
66
from .progress import ProgressPhase
67
67
from .sixish import (
76
76
 
77
77
ROOT_PARENT = "root-parent"
78
78
 
 
79
 
79
80
def unique_add(map, key, value):
80
81
    if key in map:
81
82
        raise DuplicateKey(key=key)
82
83
    map[key] = value
83
84
 
84
85
 
85
 
 
86
86
class _TransformResults(object):
87
87
    def __init__(self, modified_paths, rename_count):
88
88
        object.__init__(self)
174
174
    def _assign_id(self):
175
175
        """Produce a new tranform id"""
176
176
        new_id = "new-%s" % self._id_number
177
 
        self._id_number +=1
 
177
        self._id_number += 1
178
178
        return new_id
179
179
 
180
180
    def create_path(self, name, parent):
258
258
            self.unversion_file(old_new_root)
259
259
        # if, at this stage, root still has an old file_id, zap it so we can
260
260
        # stick a new one in.
261
 
        if (self.tree_file_id(self._new_root) is not None and
262
 
            self._new_root not in self._removed_id):
 
261
        if (self.tree_file_id(self._new_root) is not None
 
262
                and self._new_root not in self._removed_id):
263
263
            self.unversion_file(self._new_root)
264
264
        if file_id is not None:
265
265
            self.version_file(file_id, self._new_root)
424
424
        changed_ids.update(changed_kind)
425
425
        # To find entries with changed parent_ids, find parents which existed,
426
426
        # but changed file_id.
427
 
        changed_file_id = set(t for t in new_file_id if t in self._removed_id)
428
427
        # Now add all their children to the set.
429
428
        for parent_trans_id in new_file_id:
430
429
            changed_ids.update(self.iter_tree_children(parent_trans_id))
511
510
        by_parent = {}
512
511
        items = list(viewitems(self._new_parent))
513
512
        items.extend((t, self.final_parent(t))
514
 
            for t in list(self._tree_id_paths))
 
513
                     for t in list(self._tree_id_paths))
515
514
        for trans_id, parent_id in items:
516
515
            if parent_id not in by_parent:
517
516
                by_parent[parent_id] = set()
604
603
 
605
604
        :param name: The basename of the file.
606
605
 
607
 
        :param target_id: The directory trans_id where the backup should 
 
606
        :param target_id: The directory trans_id where the backup should
608
607
            be placed.
609
608
        """
610
609
        known_children = self.by_parent().get(target_id, [])
642
641
            for child_id in children:
643
642
                if self.final_file_id(child_id) is not None:
644
643
                    conflicts.append(('unversioned parent', parent_id))
645
 
                    break;
 
644
                    break
646
645
        return conflicts
647
646
 
648
647
    def _improper_versioning(self):
653
652
        conflicts = []
654
653
        for trans_id in self._new_id:
655
654
            kind = self.final_kind(trans_id)
656
 
            if kind == 'symlink' and not has_symlinks():
 
655
            if kind == 'symlink' and not self._tree.supports_symlinks():
657
656
                # Ignore symlinks as they are not supported on this platform
658
657
                continue
659
658
            if kind is None:
688
687
                continue
689
688
            if trans_id not in self._removed_contents:
690
689
                conflicts.append(('overwrite', trans_id,
691
 
                                 self.final_name(trans_id)))
 
690
                                  self.final_name(trans_id)))
692
691
        return conflicts
693
692
 
694
693
    def _duplicate_entries(self, by_parent):
715
714
                    continue
716
715
                if name == last_name:
717
716
                    conflicts.append(('duplicate', last_trans_id, trans_id,
718
 
                    name))
 
717
                                      name))
719
718
                last_name = name
720
719
                last_trans_id = trans_id
721
720
        return conflicts
723
722
    def _duplicate_ids(self):
724
723
        """Each inventory id may only be used once"""
725
724
        conflicts = []
 
725
        try:
 
726
            all_ids = self._tree.all_file_ids()
 
727
        except errors.UnsupportedOperation:
 
728
            # it's okay for non-file-id trees to raise UnsupportedOperation.
 
729
            return []
726
730
        removed_tree_ids = set((self.tree_file_id(trans_id) for trans_id in
727
731
                                self._removed_id))
728
 
        all_ids = self._tree.all_file_ids()
729
732
        active_tree_ids = all_ids.difference(removed_tree_ids)
730
733
        for trans_id, file_id in viewitems(self._new_id):
731
734
            if file_id in active_tree_ids:
905
908
        if from_versioned:
906
909
            # get data from working tree if versioned
907
910
            from_entry = next(self._tree.iter_entries_by_dir(
908
 
                    specific_files=[from_path]))[1]
 
911
                specific_files=[from_path]))[1]
909
912
            from_name = from_entry.name
910
913
            from_parent = from_entry.parent_id
911
914
        else:
992
995
            if from_kind != to_kind:
993
996
                modified = True
994
997
            elif to_kind in ('file', 'symlink') and (
995
 
                to_trans_id != from_trans_id or
996
 
                to_trans_id in self._new_contents):
 
998
                    to_trans_id != from_trans_id
 
999
                    or to_trans_id in self._new_contents):
997
1000
                modified = True
998
 
            if (not modified and from_versioned == to_versioned and
999
 
                from_parent==to_parent and from_name == to_name and
1000
 
                from_executable == to_executable):
 
1001
            if (not modified and from_versioned == to_versioned
 
1002
                and from_parent == to_parent and from_name == to_name
 
1003
                    and from_executable == to_executable):
1001
1004
                continue
1002
1005
            results.append((file_id, (from_path, to_path), modified,
1003
 
                   (from_versioned, to_versioned),
1004
 
                   (from_parent, to_parent),
1005
 
                   (from_name, to_name),
1006
 
                   (from_kind, to_kind),
1007
 
                   (from_executable, to_executable)))
 
1006
                            (from_versioned, to_versioned),
 
1007
                            (from_parent, to_parent),
 
1008
                            (from_name, to_name),
 
1009
                            (from_kind, to_kind),
 
1010
                            (from_executable, to_executable)))
1008
1011
 
1009
1012
        def path_key(t):
1010
1013
            paths = t[1]
1148
1151
                if not isinstance(content, bytes):
1149
1152
                    content = content.encode('utf-8')
1150
1153
            yield serializer.bytes_record(
1151
 
                    content, ((trans_id.encode('utf-8'), kind.encode('ascii')),))
 
1154
                content, ((trans_id.encode('utf-8'), kind.encode('ascii')),))
1152
1155
 
1153
1156
    def deserialize(self, records):
1154
1157
        """Deserialize a stored TreeTransform.
1163
1166
                          for k, v in viewitems(attribs[b'_new_name'])}
1164
1167
        self._new_parent = {k.decode('utf-8'): v.decode('utf-8')
1165
1168
                            for k, v in viewitems(attribs[b'_new_parent'])}
1166
 
        self._new_executability = {k.decode('utf-8'): bool(v)
 
1169
        self._new_executability = {
 
1170
            k.decode('utf-8'): bool(v)
1167
1171
            for k, v in viewitems(attribs[b'_new_executability'])}
1168
1172
        self._new_id = {k.decode('utf-8'): v
1169
1173
                        for k, v in viewitems(attribs[b'_new_id'])}
1177
1181
            self._tree_id_paths[trans_id] = path
1178
1182
        self._removed_id = {trans_id.decode('utf-8')
1179
1183
                            for trans_id in attribs[b'_removed_id']}
1180
 
        self._removed_contents = set(trans_id.decode('utf-8')
1181
 
                                     for trans_id in attribs[b'_removed_contents'])
1182
 
        self._non_present_ids = {k: v.decode('utf-8')
1183
 
                                 for k, v in viewitems(attribs[b'_non_present_ids'])}
 
1184
        self._removed_contents = set(
 
1185
            trans_id.decode('utf-8')
 
1186
            for trans_id in attribs[b'_removed_contents'])
 
1187
        self._non_present_ids = {
 
1188
            k: v.decode('utf-8')
 
1189
            for k, v in viewitems(attribs[b'_non_present_ids'])}
1184
1190
        for ((trans_id, kind),), content in records:
1185
1191
            trans_id = trans_id.decode('utf-8')
1186
1192
            kind = kind.decode('ascii')
1197
1203
class DiskTreeTransform(TreeTransformBase):
1198
1204
    """Tree transform storing its contents on disk."""
1199
1205
 
1200
 
    def __init__(self, tree, limbodir, pb=None,
1201
 
                 case_sensitive=True):
 
1206
    def __init__(self, tree, limbodir, pb=None, case_sensitive=True):
1202
1207
        """Constructor.
1203
1208
        :param tree: The tree that will be transformed, but not necessarily
1204
1209
            the output tree.
1282
1287
        previous_parent = self._new_parent.get(trans_id)
1283
1288
        previous_name = self._new_name.get(trans_id)
1284
1289
        TreeTransformBase.adjust_path(self, name, parent, trans_id)
1285
 
        if (trans_id in self._limbo_files and
1286
 
            trans_id not in self._needs_rename):
 
1290
        if (trans_id in self._limbo_files
 
1291
                and trans_id not in self._needs_rename):
1287
1292
            self._rename_in_limbo([trans_id])
1288
1293
            if previous_parent != parent:
1289
1294
                self._limbo_children[previous_parent].remove(trans_id)
1371
1376
            raise errors.HardLinkNotSupported(path)
1372
1377
        try:
1373
1378
            unique_add(self._new_contents, trans_id, 'file')
1374
 
        except:
 
1379
        except BaseException:
1375
1380
            # Clean up the file, it never got registered so
1376
1381
            # TreeTransform.finalize() won't clean it up.
1377
1382
            os.unlink(name)
1391
1396
        target is a bytestring.
1392
1397
        See also new_symlink.
1393
1398
        """
1394
 
        if has_symlinks():
 
1399
        if osutils.supports_symlinks(self._limbodir):
1395
1400
            os.symlink(target, self._limbo_name(trans_id))
1396
1401
        else:
1397
1402
            try:
1398
1403
                path = FinalPaths(self).get_path(trans_id)
1399
1404
            except KeyError:
1400
1405
                path = None
1401
 
            trace.warning('bzr: warning: Unable to create symlink "%s" on '
1402
 
                'this platform.' % (path,))
 
1406
            trace.warning(
 
1407
                'bzr: warning: Unable to create symlink "%s" on '
 
1408
                'this platform/filesystem.' % (path,))
1403
1409
        # We add symlink to _new_contents even if they are unsupported
1404
1410
        # and not created. These entries are subsequently used to avoid
1405
1411
        # conflicts on platforms that don't support symlink
1561
1567
    FileMover does not delete files until it is sure that a rollback will not
1562
1568
    happen.
1563
1569
    """
 
1570
 
1564
1571
    def __init__(self, tree, pb=None):
1565
1572
        """Note: a tree_write lock is taken on the tree.
1566
1573
 
1579
1586
            osutils.ensure_empty_directory_exists(
1580
1587
                deletiondir,
1581
1588
                errors.ExistingPendingDeletion)
1582
 
        except:
 
1589
        except BaseException:
1583
1590
            tree.unlock()
1584
1591
            raise
1585
1592
 
1658
1665
        try:
1659
1666
            children = os.listdir(self._tree.abspath(path))
1660
1667
        except OSError as e:
1661
 
            if not (osutils._is_error_enotdir(e)
1662
 
                    or e.errno in (errno.ENOENT, errno.ESRCH)):
 
1668
            if not (osutils._is_error_enotdir(e) or
 
1669
                    e.errno in (errno.ENOENT, errno.ESRCH)):
1663
1670
                raise
1664
1671
            return
1665
1672
 
1696
1703
                # if it is already associated with this trans_id.
1697
1704
                elif self._case_sensitive_target:
1698
1705
                    if (self._limbo_children_names[parent].get(filename)
1699
 
                        in (trans_id, None)):
 
1706
                            in (trans_id, None)):
1700
1707
                        use_direct_path = True
1701
1708
                else:
1702
1709
                    for l_filename, l_trans_id in viewitems(
1751
1758
                self._apply_removals(mover)
1752
1759
                child_pb.update(gettext('Apply phase'), 1 + offset, 2 + offset)
1753
1760
                modified_paths = self._apply_insertions(mover)
1754
 
            except:
 
1761
            except BaseException:
1755
1762
                mover.rollback()
1756
1763
                raise
1757
1764
            else:
1772
1779
        with ui.ui_factory.nested_progress_bar() as child_pb:
1773
1780
            for num, trans_id in enumerate(self._removed_id):
1774
1781
                if (num % 10) == 0:
1775
 
                    child_pb.update(gettext('removing file'), num, total_entries)
 
1782
                    child_pb.update(gettext('removing file'),
 
1783
                                    num, total_entries)
1776
1784
                if trans_id == self._new_root:
1777
1785
                    file_id = self._tree.get_root_id()
1778
1786
                else:
1784
1792
                inventory_delta.append((path, None, file_id, None))
1785
1793
            new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
1786
1794
                                     new_paths)
1787
 
            final_kinds = {}
1788
1795
            for num, (path, trans_id) in enumerate(new_paths):
1789
1796
                if (num % 10) == 0:
1790
1797
                    child_pb.update(gettext('adding file'),
1792
1799
                file_id = new_path_file_ids[trans_id]
1793
1800
                if file_id is None:
1794
1801
                    continue
1795
 
                needs_entry = False
1796
1802
                kind = self.final_kind(trans_id)
1797
1803
                if kind is None:
1798
 
                    kind = self._tree.stored_kind(
1799
 
                            self._tree.id2path(file_id), file_id)
 
1804
                    kind = self._tree.stored_kind(self._tree.id2path(file_id))
1800
1805
                parent_trans_id = self.final_parent(trans_id)
1801
1806
                parent_file_id = new_path_file_ids.get(parent_trans_id)
1802
1807
                if parent_file_id is None:
1809
1814
                        None, self._new_reference_revision[trans_id])
1810
1815
                else:
1811
1816
                    new_entry = inventory.make_entry(kind,
1812
 
                        self.final_name(trans_id),
1813
 
                        parent_file_id, file_id)
 
1817
                                                     self.final_name(trans_id),
 
1818
                                                     parent_file_id, file_id)
1814
1819
                try:
1815
1820
                    old_path = self._tree.id2path(new_entry.file_id)
1816
1821
                except errors.NoSuchId:
1842
1847
                if trans_id in self._removed_contents:
1843
1848
                    delete_path = os.path.join(self._deletiondir, trans_id)
1844
1849
                    mover.pre_delete(full_path, delete_path)
1845
 
                elif (trans_id in self._new_name
1846
 
                      or trans_id in self._new_parent):
 
1850
                elif (trans_id in self._new_name or
 
1851
                      trans_id in self._new_parent):
1847
1852
                    try:
1848
1853
                        mover.rename(full_path, self._limbo_name(trans_id))
1849
1854
                    except errors.TransformRenameFailed as e:
1864
1869
        """
1865
1870
        new_paths = self.new_paths(filesystem_only=True)
1866
1871
        modified_paths = []
1867
 
        new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
1868
 
                                 new_paths)
1869
1872
        with ui.ui_factory.nested_progress_bar() as child_pb:
1870
1873
            for num, (path, trans_id) in enumerate(new_paths):
1871
1874
                if (num % 10) == 0:
1872
 
                    child_pb.update(gettext('adding file'), num, len(new_paths))
 
1875
                    child_pb.update(gettext('adding file'),
 
1876
                                    num, len(new_paths))
1873
1877
                full_path = self._tree.abspath(path)
1874
1878
                if trans_id in self._needs_rename:
1875
1879
                    try:
1883
1887
                    # TODO: if trans_id in self._observed_sha1s, we should
1884
1888
                    #       re-stat the final target, since ctime will be
1885
1889
                    #       updated by the change.
1886
 
                if (trans_id in self._new_contents or
1887
 
                    self.path_changed(trans_id)):
 
1890
                if (trans_id in self._new_contents
 
1891
                        or self.path_changed(trans_id)):
1888
1892
                    if trans_id in self._new_contents:
1889
1893
                        modified_paths.append(full_path)
1890
1894
                if trans_id in self._new_executability:
1919
1923
        paths = FinalPaths(self)
1920
1924
        for trans_id, observed in viewitems(self._observed_sha1s):
1921
1925
            path = paths.get_path(trans_id)
1922
 
            # We could get the file_id, but dirstate prefers to use the path
1923
 
            # anyway, and it is 'cheaper' to determine.
1924
 
            # file_id = self._new_id[trans_id]
1925
 
            self._tree._observed_sha1(None, path, observed)
 
1926
            self._tree._observed_sha1(path, observed)
1926
1927
 
1927
1928
 
1928
1929
class TransformPreview(DiskTreeTransform):
1967
1968
            return
1968
1969
        try:
1969
1970
            entry = next(self._tree.iter_entries_by_dir(
1970
 
                    specific_files=[path]))[1]
 
1971
                specific_files=[path]))[1]
1971
1972
        except StopIteration:
1972
1973
            return
1973
1974
        children = getattr(entry, 'children', {})
2015
2016
 
2016
2017
    def _get_file_revision(self, path, file_id, vf, tree_revision):
2017
2018
        parent_keys = [
2018
 
                (file_id, t.get_file_revision(t.id2path(file_id), file_id))
2019
 
                for t in self._iter_parent_trees()]
 
2019
            (file_id, t.get_file_revision(t.id2path(file_id)))
 
2020
            for t in self._iter_parent_trees()]
2020
2021
        vf.add_lines((file_id, tree_revision), parent_keys,
2021
 
                     self.get_file_lines(path, file_id))
 
2022
                     self.get_file_lines(path))
2022
2023
        repo = self._get_repository()
2023
2024
        base_vf = repo.texts
2024
2025
        if base_vf not in vf.fallback_versionedfiles:
2042
2043
            executable = False
2043
2044
        else:
2044
2045
            file_id = self._transform.final_file_id(self._path2trans_id(path))
2045
 
            executable = self.is_executable(path, file_id)
 
2046
            executable = self.is_executable(path)
2046
2047
        return kind, executable, None
2047
2048
 
2048
2049
    def is_locked(self):
2071
2072
        return tree_ids
2072
2073
 
2073
2074
    def all_versioned_paths(self):
2074
 
        return {self.id2path(fid) for fid in self.all_file_ids()}
 
2075
        tree_paths = set(self._transform._tree.all_versioned_paths())
 
2076
 
 
2077
        tree_paths.difference_update(
 
2078
            self._transform.trans_id_tree_path(t)
 
2079
            for t in self._transform._removed_id)
 
2080
 
 
2081
        tree_paths.update(
 
2082
            self._final_paths._determine_path(t)
 
2083
            for t in self._transform._new_id)
 
2084
 
 
2085
        return tree_paths
2075
2086
 
2076
2087
    def _has_id(self, file_id, fallback_check):
2077
2088
        if file_id in self._transform._r_new_id:
2078
2089
            return True
2079
2090
        elif file_id in {self._transform.tree_file_id(trans_id) for
2080
 
            trans_id in self._transform._removed_id}:
 
2091
                         trans_id in self._transform._removed_id}:
2081
2092
            return False
2082
2093
        else:
2083
2094
            return fallback_check(file_id)
2154
2165
            file_id = self._transform.final_file_id(trans_id)
2155
2166
            if file_id is None:
2156
2167
                continue
2157
 
            if (specific_files is not None and
2158
 
                self._final_paths.get_path(trans_id) not in specific_files):
 
2168
            if (specific_files is not None
 
2169
                    and self._final_paths.get_path(trans_id) not in specific_files):
2159
2170
                continue
2160
2171
            kind = self._transform.final_kind(trans_id)
2161
2172
            if kind is None:
2162
2173
                kind = self._transform._tree.stored_kind(
2163
 
                    self._transform._tree.id2path(file_id),
2164
 
                    file_id)
 
2174
                    self._transform._tree.id2path(file_id))
2165
2175
            new_entry = inventory.make_entry(
2166
2176
                kind,
2167
2177
                self._transform.final_name(trans_id),
2182
2192
                ordered_ids.append((trans_id, parent_file_id))
2183
2193
        return ordered_ids
2184
2194
 
2185
 
    def iter_child_entries(self, path, file_id=None):
 
2195
    def iter_child_entries(self, path):
2186
2196
        trans_id = self._path2trans_id(path)
2187
2197
        if trans_id is None:
2188
2198
            raise errors.NoSuchFile(path)
2199
2209
        # position.
2200
2210
        ordered_ids = self._list_files_by_dir()
2201
2211
        for entry, trans_id in self._make_inv_entries(ordered_ids,
2202
 
            specific_files):
 
2212
                                                      specific_files):
2203
2213
            yield self._final_paths.get_path(trans_id), entry
2204
2214
 
2205
2215
    def _iter_entries_for_dir(self, dir_path):
2219
2229
        """See WorkingTree.list_files."""
2220
2230
        # XXX This should behave like WorkingTree.list_files, but is really
2221
2231
        # more like RevisionTree.list_files.
 
2232
        if from_dir == '.':
 
2233
            from_dir = None
2222
2234
        if recursive:
2223
2235
            prefix = None
2224
2236
            if from_dir:
2231
2243
                    if not path.startswith(prefix):
2232
2244
                        continue
2233
2245
                    path = path[len(prefix):]
2234
 
                yield path, 'V', entry.kind, entry.file_id, entry
 
2246
                yield path, 'V', entry.kind, entry
2235
2247
        else:
2236
2248
            if from_dir is None and include_root is True:
2237
 
                root_entry = inventory.make_entry('directory', '',
2238
 
                    ROOT_PARENT, self.get_root_id())
2239
 
                yield '', 'V', 'directory', root_entry.file_id, root_entry
 
2249
                root_entry = inventory.make_entry(
 
2250
                    'directory', '', ROOT_PARENT, self.get_root_id())
 
2251
                yield '', 'V', 'directory', root_entry
2240
2252
            entries = self._iter_entries_for_dir(from_dir or '')
2241
2253
            for path, entry in entries:
2242
 
                yield path, 'V', entry.kind, entry.file_id, entry
 
2254
                yield path, 'V', entry.kind, entry
2243
2255
 
2244
 
    def kind(self, path, file_id=None):
 
2256
    def kind(self, path):
2245
2257
        trans_id = self._path2trans_id(path)
2246
2258
        if trans_id is None:
2247
2259
            raise errors.NoSuchFile(path)
2248
2260
        return self._transform.final_kind(trans_id)
2249
2261
 
2250
 
    def stored_kind(self, path, file_id=None):
 
2262
    def stored_kind(self, path):
2251
2263
        trans_id = self._path2trans_id(path)
2252
2264
        if trans_id is None:
2253
2265
            raise errors.NoSuchFile(path)
2254
2266
        try:
2255
2267
            return self._transform._new_contents[trans_id]
2256
2268
        except KeyError:
2257
 
            return self._transform._tree.stored_kind(path, file_id)
 
2269
            return self._transform._tree.stored_kind(path)
2258
2270
 
2259
 
    def get_file_mtime(self, path, file_id=None):
 
2271
    def get_file_mtime(self, path):
2260
2272
        """See Tree.get_file_mtime"""
2261
 
        if file_id is None:
2262
 
            file_id = self.path2id(path)
 
2273
        file_id = self.path2id(path)
2263
2274
        if file_id is None:
2264
2275
            raise errors.NoSuchFile(path)
2265
2276
        if not self._content_change(file_id):
2266
2277
            return self._transform._tree.get_file_mtime(
2267
 
                    self._transform._tree.id2path(file_id), file_id)
 
2278
                self._transform._tree.id2path(file_id))
2268
2279
        trans_id = self._path2trans_id(path)
2269
2280
        return self._stat_limbo_file(trans_id).st_mtime
2270
2281
 
2271
 
    def get_file_size(self, path, file_id=None):
 
2282
    def get_file_size(self, path):
2272
2283
        """See Tree.get_file_size"""
2273
2284
        trans_id = self._path2trans_id(path)
2274
2285
        if trans_id is None:
2278
2289
            return None
2279
2290
        if trans_id in self._transform._new_contents:
2280
2291
            return self._stat_limbo_file(trans_id).st_size
2281
 
        if self.kind(path, file_id) == 'file':
2282
 
            return self._transform._tree.get_file_size(path, file_id)
 
2292
        if self.kind(path) == 'file':
 
2293
            return self._transform._tree.get_file_size(path)
2283
2294
        else:
2284
2295
            return None
2285
2296
 
2286
 
    def get_file_verifier(self, path, file_id=None, stat_value=None):
 
2297
    def get_file_verifier(self, path, stat_value=None):
2287
2298
        trans_id = self._path2trans_id(path)
2288
2299
        if trans_id is None:
2289
2300
            raise errors.NoSuchFile(path)
2290
2301
        kind = self._transform._new_contents.get(trans_id)
2291
2302
        if kind is None:
2292
 
            return self._transform._tree.get_file_verifier(path, file_id)
 
2303
            return self._transform._tree.get_file_verifier(path)
2293
2304
        if kind == 'file':
2294
 
            with self.get_file(path, file_id) as fileobj:
 
2305
            with self.get_file(path) as fileobj:
2295
2306
                return ("SHA1", sha_file(fileobj))
2296
2307
 
2297
 
    def get_file_sha1(self, path, file_id=None, stat_value=None):
 
2308
    def get_file_sha1(self, path, stat_value=None):
2298
2309
        trans_id = self._path2trans_id(path)
2299
2310
        if trans_id is None:
2300
2311
            raise errors.NoSuchFile(path)
2301
2312
        kind = self._transform._new_contents.get(trans_id)
2302
2313
        if kind is None:
2303
 
            return self._transform._tree.get_file_sha1(path, file_id)
 
2314
            return self._transform._tree.get_file_sha1(path)
2304
2315
        if kind == 'file':
2305
 
            with self.get_file(path, file_id) as fileobj:
 
2316
            with self.get_file(path) as fileobj:
2306
2317
                return sha_file(fileobj)
2307
2318
 
2308
 
    def is_executable(self, path, file_id=None):
 
2319
    def is_executable(self, path):
2309
2320
        trans_id = self._path2trans_id(path)
2310
2321
        if trans_id is None:
2311
2322
            return False
2313
2324
            return self._transform._new_executability[trans_id]
2314
2325
        except KeyError:
2315
2326
            try:
2316
 
                return self._transform._tree.is_executable(path, file_id)
 
2327
                return self._transform._tree.is_executable(path)
2317
2328
            except OSError as e:
2318
2329
                if e.errno == errno.ENOENT:
2319
2330
                    return False
2363
2374
        return kind, size, executable, link_or_sha1
2364
2375
 
2365
2376
    def iter_changes(self, from_tree, include_unchanged=False,
2366
 
                      specific_files=None, pb=None, extra_trees=None,
2367
 
                      require_versioned=True, want_unversioned=False):
 
2377
                     specific_files=None, pb=None, extra_trees=None,
 
2378
                     require_versioned=True, want_unversioned=False):
2368
2379
        """See InterTree.iter_changes.
2369
2380
 
2370
2381
        This has a fast path that is only used when the from_tree matches
2371
2382
        the transform tree, and no fancy options are supplied.
2372
2383
        """
2373
 
        if (from_tree is not self._transform._tree or include_unchanged or
2374
 
            specific_files or want_unversioned):
 
2384
        if (from_tree is not self._transform._tree or include_unchanged
 
2385
                or specific_files or want_unversioned):
2375
2386
            return tree.InterTree(from_tree, self).iter_changes(
2376
2387
                include_unchanged=include_unchanged,
2377
2388
                specific_files=specific_files,
2383
2394
            raise ValueError('want_unversioned is not supported')
2384
2395
        return self._transform.iter_changes()
2385
2396
 
2386
 
    def get_file(self, path, file_id=None):
 
2397
    def get_file(self, path):
2387
2398
        """See Tree.get_file"""
2388
 
        if file_id is None:
2389
 
            file_id = self.path2id(path)
 
2399
        file_id = self.path2id(path)
2390
2400
        if not self._content_change(file_id):
2391
 
            return self._transform._tree.get_file(path, file_id)
 
2401
            return self._transform._tree.get_file(path)
2392
2402
        trans_id = self._path2trans_id(path)
2393
2403
        name = self._transform._limbo_name(trans_id)
2394
2404
        return open(name, 'rb')
2395
2405
 
2396
 
    def get_file_with_stat(self, path, file_id=None):
2397
 
        return self.get_file(path, file_id), None
 
2406
    def get_file_with_stat(self, path):
 
2407
        return self.get_file(path), None
2398
2408
 
2399
 
    def annotate_iter(self, path, file_id=None,
 
2409
    def annotate_iter(self, path,
2400
2410
                      default_revision=_mod_revision.CURRENT_REVISION):
2401
 
        if file_id is None:
2402
 
            file_id = self.path2id(path)
 
2411
        file_id = self.path2id(path)
2403
2412
        changes = self._iter_changes_cache.get(file_id)
2404
2413
        if changes is None:
2405
2414
            get_old = True
2411
2420
            get_old = (kind[0] == 'file' and versioned[0])
2412
2421
        if get_old:
2413
2422
            old_annotation = self._transform._tree.annotate_iter(
2414
 
                    path, file_id=file_id, default_revision=default_revision)
 
2423
                path, default_revision=default_revision)
2415
2424
        else:
2416
2425
            old_annotation = []
2417
2426
        if changes is None:
2426
2435
        #       It would be nice to be able to use the new Annotator based
2427
2436
        #       approach, as well.
2428
2437
        return annotate.reannotate([old_annotation],
2429
 
                                   self.get_file(path, file_id).readlines(),
 
2438
                                   self.get_file(path).readlines(),
2430
2439
                                   default_revision)
2431
2440
 
2432
 
    def get_symlink_target(self, path, file_id=None):
 
2441
    def get_symlink_target(self, path):
2433
2442
        """See Tree.get_symlink_target"""
2434
 
        if file_id is None:
2435
 
            file_id = self.path2id(path)
 
2443
        file_id = self.path2id(path)
2436
2444
        if not self._content_change(file_id):
2437
2445
            return self._transform._tree.get_symlink_target(path)
2438
2446
        trans_id = self._path2trans_id(path)
2452
2460
                path_from_root = self._final_paths.get_path(child_id)
2453
2461
                basename = self._transform.final_name(child_id)
2454
2462
                file_id = self._transform.final_file_id(child_id)
2455
 
                kind  = self._transform.final_kind(child_id)
 
2463
                kind = self._transform.final_kind(child_id)
2456
2464
                if kind is not None:
2457
2465
                    versioned_kind = kind
2458
2466
                else:
2459
2467
                    kind = 'unknown'
2460
2468
                    versioned_kind = self._transform._tree.stored_kind(
2461
 
                            self._transform._tree.id2path(file_id),
2462
 
                            file_id)
 
2469
                        self._transform._tree.id2path(file_id))
2463
2470
                if versioned_kind == 'directory':
2464
2471
                    subdirs.append(child_id)
2465
2472
                children.append((path_from_root, basename, kind, None,
2494
2501
    The underlying tree must not be manipulated between calls, or else
2495
2502
    the results will likely be incorrect.
2496
2503
    """
 
2504
 
2497
2505
    def __init__(self, transform):
2498
2506
        object.__init__(self)
2499
2507
        self._known_paths = {}
2519
2527
        return [(self.get_path(t), t) for t in trans_ids]
2520
2528
 
2521
2529
 
2522
 
 
2523
2530
def topology_sorted_ids(tree):
2524
2531
    """Determine the topological order of the ids in a tree"""
2525
2532
    file_ids = list(tree)
2606
2613
                for dir, files in wt.walkdirs():
2607
2614
                    existing_files.update(f[0] for f in files)
2608
2615
            for num, (tree_path, entry) in \
2609
 
                enumerate(tree.iter_entries_by_dir()):
2610
 
                pb.update(gettext("Building tree"), num - len(deferred_contents), total)
 
2616
                    enumerate(tree.iter_entries_by_dir()):
 
2617
                pb.update(gettext("Building tree"), num
 
2618
                          - len(deferred_contents), total)
2611
2619
                if entry.parent_id is None:
2612
2620
                    continue
2613
2621
                reparent = False
2624
2632
                            pass
2625
2633
                        else:
2626
2634
                            divert.add(file_id)
2627
 
                    if (file_id not in divert and
2628
 
                        _content_match(tree, entry, tree_path, file_id, kind,
2629
 
                        target_path)):
 
2635
                    if (file_id not in divert
 
2636
                        and _content_match(
 
2637
                            tree, entry, tree_path, file_id, kind,
 
2638
                            target_path)):
2630
2639
                        tt.delete_contents(tt.trans_id_tree_path(tree_path))
2631
2640
                        if kind == 'directory':
2632
2641
                            reparent = True
2637
2646
                    trans_id = tt.create_path(entry.name, parent_id)
2638
2647
                    file_trans_id[file_id] = trans_id
2639
2648
                    tt.version_file(file_id, trans_id)
2640
 
                    executable = tree.is_executable(tree_path, file_id)
 
2649
                    executable = tree.is_executable(tree_path)
2641
2650
                    if executable:
2642
2651
                        tt.set_executability(executable, trans_id)
2643
 
                    trans_data = (trans_id, file_id, tree_path, entry.text_sha1)
 
2652
                    trans_data = (trans_id, file_id,
 
2653
                                  tree_path, entry.text_sha1)
2644
2654
                    deferred_contents.append((tree_path, trans_data))
2645
2655
                else:
2646
2656
                    file_trans_id[file_id] = new_by_entry(
2647
 
                            tree_path, tt, entry, parent_id, tree)
 
2657
                        tree_path, tt, entry, parent_id, tree)
2648
2658
                if reparent:
2649
2659
                    new_trans_id = file_trans_id[file_id]
2650
2660
                    old_parent = tt.trans_id_tree_path(tree_path)
2654
2664
                          accelerator_tree, hardlink)
2655
2665
        pp.next_phase()
2656
2666
        divert_trans = set(file_trans_id[f] for f in divert)
2657
 
        resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
 
2667
 
 
2668
        def resolver(t, c):
 
2669
            return resolve_checkout(t, c, divert_trans)
2658
2670
        raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
2659
2671
        if len(raw_conflicts) > 0:
2660
2672
            precomputed_delta = None
2693
2705
            accelerator_path = unchanged.get(tree_path)
2694
2706
            if accelerator_path is None:
2695
2707
                new_desired_files.append((tree_path,
2696
 
                    (trans_id, file_id, tree_path, text_sha1)))
 
2708
                                          (trans_id, file_id, tree_path, text_sha1)))
2697
2709
                continue
2698
2710
            pb.update(gettext('Adding file contents'), count + offset, total)
2699
2711
            if hardlink:
2700
2712
                tt.create_hardlink(accelerator_tree.abspath(accelerator_path),
2701
2713
                                   trans_id)
2702
2714
            else:
2703
 
                with accelerator_tree.get_file(accelerator_path, file_id) as f:
 
2715
                with accelerator_tree.get_file(accelerator_path) as f:
2704
2716
                    chunks = osutils.file_iterator(f)
2705
2717
                    if wt.supports_content_filtering():
2706
2718
                        filters = wt._content_filter_stack(tree_path)
2707
2719
                        chunks = filtered_output_bytes(chunks, filters,
2708
 
                            ContentFilterContext(tree_path, tree))
 
2720
                                                       ContentFilterContext(tree_path, tree))
2709
2721
                    tt.create_file(chunks, trans_id, sha1=text_sha1)
2710
2722
            count += 1
2711
2723
        offset += count
2714
2726
        if wt.supports_content_filtering():
2715
2727
            filters = wt._content_filter_stack(tree_path)
2716
2728
            contents = filtered_output_bytes(contents, filters,
2717
 
                ContentFilterContext(tree_path, tree))
 
2729
                                             ContentFilterContext(tree_path, tree))
2718
2730
        tt.create_file(contents, trans_id, sha1=text_sha1)
2719
2731
        pb.update(gettext('Adding file contents'), count + offset, total)
2720
2732
 
2738
2750
        return True
2739
2751
    if entry.kind == "file":
2740
2752
        with open(target_path, 'rb') as f1, \
2741
 
             tree.get_file(tree_path, file_id) as f2:
 
2753
                tree.get_file(tree_path) as f2:
2742
2754
            if osutils.compare_files(f1, f2):
2743
2755
                return True
2744
2756
    elif entry.kind == "symlink":
2745
 
        if tree.get_symlink_target(tree_path, file_id) == os.readlink(target_path):
 
2757
        if tree.get_symlink_target(tree_path) == os.readlink(target_path):
2746
2758
            return True
2747
2759
    return False
2748
2760
 
2765
2777
        # resolved
2766
2778
        final_parent = tt.final_parent(old_file)
2767
2779
        if new_file in divert:
2768
 
            new_name = tt.final_name(old_file)+'.diverted'
 
2780
            new_name = tt.final_name(old_file) + '.diverted'
2769
2781
            tt.adjust_path(new_name, final_parent, new_file)
2770
2782
            new_conflicts.add((c_type, 'Diverted to',
2771
2783
                               new_file, old_file))
2772
2784
        else:
2773
 
            new_name = tt.final_name(old_file)+'.moved'
 
2785
            new_name = tt.final_name(old_file) + '.moved'
2774
2786
            tt.adjust_path(new_name, final_parent, old_file)
2775
2787
            new_conflicts.add((c_type, 'Moved existing file to',
2776
2788
                               old_file, new_file))
2782
2794
    name = entry.name
2783
2795
    kind = entry.kind
2784
2796
    if kind == 'file':
2785
 
        with tree.get_file(path, entry.file_id) as f:
2786
 
            executable = tree.is_executable(path, entry.file_id)
 
2797
        with tree.get_file(path) as f:
 
2798
            executable = tree.is_executable(path)
2787
2799
            return tt.new_file(
2788
 
                    name, parent_id, osutils.file_iterator(f), entry.file_id,
2789
 
                    executable)
 
2800
                name, parent_id, osutils.file_iterator(f), entry.file_id,
 
2801
                executable)
2790
2802
    elif kind in ('directory', 'tree-reference'):
2791
2803
        trans_id = tt.new_directory(name, parent_id, entry.file_id)
2792
2804
        if kind == 'tree-reference':
2793
2805
            tt.set_tree_reference(entry.reference_revision, trans_id)
2794
2806
        return trans_id
2795
2807
    elif kind == 'symlink':
2796
 
        target = tree.get_symlink_target(path, entry.file_id)
 
2808
        target = tree.get_symlink_target(path)
2797
2809
        return tt.new_symlink(name, parent_id, target, entry.file_id)
2798
2810
    else:
2799
2811
        raise errors.BadFileKindError(name, kind)
2800
2812
 
2801
2813
 
2802
2814
def create_from_tree(tt, trans_id, tree, path, file_id=None, chunks=None,
2803
 
    filter_tree_path=None):
 
2815
                     filter_tree_path=None):
2804
2816
    """Create new file contents according to tree contents.
2805
2817
 
2806
2818
    :param filter_tree_path: the tree path to use to lookup
2807
2819
      content filters to apply to the bytes output in the working tree.
2808
2820
      This only applies if the working tree supports content filtering.
2809
2821
    """
2810
 
    kind = tree.kind(path, file_id)
 
2822
    kind = tree.kind(path)
2811
2823
    if kind == 'directory':
2812
2824
        tt.create_directory(trans_id)
2813
2825
    elif kind == "file":
2814
2826
        if chunks is None:
2815
 
            f = tree.get_file(path, file_id)
 
2827
            f = tree.get_file(path)
2816
2828
            chunks = osutils.file_iterator(f)
2817
2829
        else:
2818
2830
            f = None
2820
2832
            wt = tt._tree
2821
2833
            if wt.supports_content_filtering() and filter_tree_path is not None:
2822
2834
                filters = wt._content_filter_stack(filter_tree_path)
2823
 
                chunks = filtered_output_bytes(chunks, filters,
 
2835
                chunks = filtered_output_bytes(
 
2836
                    chunks, filters,
2824
2837
                    ContentFilterContext(filter_tree_path, tree))
2825
2838
            tt.create_file(chunks, trans_id)
2826
2839
        finally:
2827
2840
            if f is not None:
2828
2841
                f.close()
2829
2842
    elif kind == "symlink":
2830
 
        tt.create_symlink(tree.get_symlink_target(path, file_id), trans_id)
 
2843
        tt.create_symlink(tree.get_symlink_target(path), trans_id)
2831
2844
    else:
2832
2845
        raise AssertionError('Unknown kind %r' % kind)
2833
2846
 
2872
2885
                                      child_pb, filenames, backups,
2873
2886
                                      merge_modified, basis_tree)
2874
2887
    with ui.ui_factory.nested_progress_bar() as child_pb:
2875
 
        raw_conflicts = resolve_conflicts(tt, child_pb,
2876
 
            lambda t, c: conflict_pass(t, c, target_tree))
 
2888
        raw_conflicts = resolve_conflicts(
 
2889
            tt, child_pb, lambda t, c: conflict_pass(t, c, target_tree))
2877
2890
    conflicts = cook_conflicts(raw_conflicts, tt)
2878
2891
    return conflicts, merge_modified
2879
2892
 
2886
2899
    # than the target changes relative to the working tree. Because WT4 has an
2887
2900
    # optimizer to compare itself to a target, but no optimizer for the
2888
2901
    # reverse.
2889
 
    change_list = working_tree.iter_changes(target_tree,
2890
 
        specific_files=specific_files, pb=pb)
 
2902
    change_list = working_tree.iter_changes(
 
2903
        target_tree, specific_files=specific_files, pb=pb)
2891
2904
    if not target_tree.is_versioned(u''):
2892
2905
        skip_root = True
2893
2906
    else:
2895
2908
    try:
2896
2909
        deferred_files = []
2897
2910
        for id_num, (file_id, path, changed_content, versioned, parent, name,
2898
 
                kind, executable) in enumerate(change_list):
 
2911
                     kind, executable) in enumerate(change_list):
2899
2912
            target_path, wt_path = path
2900
2913
            target_versioned, wt_versioned = versioned
2901
2914
            target_parent, wt_parent = parent
2909
2922
            if changed_content:
2910
2923
                keep_content = False
2911
2924
                if wt_kind == 'file' and (backups or target_kind is None):
2912
 
                    wt_sha1 = working_tree.get_file_sha1(wt_path, file_id)
 
2925
                    wt_sha1 = working_tree.get_file_sha1(wt_path)
2913
2926
                    if merge_modified.get(file_id) != wt_sha1:
2914
2927
                        # acquire the basis tree lazily to prevent the
2915
2928
                        # expense of accessing it when it's not needed ?
2917
2930
                        if basis_tree is None:
2918
2931
                            basis_tree = working_tree.basis_tree()
2919
2932
                            basis_tree.lock_read()
2920
 
                        basis_path = find_previous_path(working_tree, basis_tree, wt_path)
 
2933
                        basis_path = find_previous_path(
 
2934
                            working_tree, basis_tree, wt_path)
2921
2935
                        if basis_path is None:
2922
2936
                            if target_kind is None and not target_versioned:
2923
2937
                                keep_content = True
2924
2938
                        else:
2925
 
                            if wt_sha1 != basis_tree.get_file_sha1(basis_path, file_id):
 
2939
                            if wt_sha1 != basis_tree.get_file_sha1(basis_path):
2926
2940
                                keep_content = True
2927
2941
                if wt_kind is not None:
2928
2942
                    if not keep_content:
2944
2958
                    tt.create_directory(trans_id)
2945
2959
                    if target_kind == 'tree-reference':
2946
2960
                        revision = target_tree.get_reference_revision(
2947
 
                                target_path, file_id)
 
2961
                            target_path)
2948
2962
                        tt.set_tree_reference(revision, trans_id)
2949
2963
                elif target_kind == 'symlink':
2950
2964
                    tt.create_symlink(target_tree.get_symlink_target(
2951
 
                            target_path, file_id), trans_id)
 
2965
                        target_path), trans_id)
2952
2966
                elif target_kind == 'file':
2953
 
                    deferred_files.append((target_path, (trans_id, mode_id, file_id)))
 
2967
                    deferred_files.append(
 
2968
                        (target_path, (trans_id, mode_id, file_id)))
2954
2969
                    if basis_tree is None:
2955
2970
                        basis_tree = working_tree.basis_tree()
2956
2971
                        basis_tree.lock_read()
2957
 
                    new_sha1 = target_tree.get_file_sha1(target_path, file_id)
 
2972
                    new_sha1 = target_tree.get_file_sha1(target_path)
2958
2973
                    basis_path = find_previous_path(target_tree, basis_tree, target_path)
2959
2974
                    if (basis_path is not None and
2960
 
                        new_sha1 == basis_tree.get_file_sha1(basis_path, file_id)):
 
2975
                            new_sha1 == basis_tree.get_file_sha1(basis_path)):
2961
2976
                        if file_id in merge_modified:
2962
2977
                            del merge_modified[file_id]
2963
2978
                    else:
2972
2987
                tt.version_file(file_id, trans_id)
2973
2988
            if wt_versioned and not target_versioned:
2974
2989
                tt.unversion_file(trans_id)
2975
 
            if (target_name is not None and
2976
 
                (wt_name != target_name or wt_parent != target_parent)):
 
2990
            if (target_name is not None
 
2991
                    and (wt_name != target_name or wt_parent != target_parent)):
2977
2992
                if target_name == '' and target_parent is None:
2978
2993
                    parent_trans = ROOT_PARENT
2979
2994
                else:
2986
3001
                tt.set_executability(target_executable, trans_id)
2987
3002
        if working_tree.supports_content_filtering():
2988
3003
            for (trans_id, mode_id, file_id), bytes in (
2989
 
                target_tree.iter_files_bytes(deferred_files)):
 
3004
                    target_tree.iter_files_bytes(deferred_files)):
2990
3005
                # We're reverting a tree to the target tree so using the
2991
3006
                # target tree to find the file path seems the best choice
2992
3007
                # here IMO - Ian C 27/Oct/2009
2993
3008
                filter_tree_path = target_tree.id2path(file_id)
2994
3009
                filters = working_tree._content_filter_stack(filter_tree_path)
2995
 
                bytes = filtered_output_bytes(bytes, filters,
 
3010
                bytes = filtered_output_bytes(
 
3011
                    bytes, filters,
2996
3012
                    ContentFilterContext(filter_tree_path, working_tree))
2997
3013
                tt.create_file(bytes, trans_id, mode_id)
2998
3014
        else:
2999
3015
            for (trans_id, mode_id, file_id), bytes in target_tree.iter_files_bytes(
3000
 
                deferred_files):
 
3016
                    deferred_files):
3001
3017
                tt.create_file(bytes, trans_id, mode_id)
3002
3018
        tt.fixup_new_roots()
3003
3019
    finally:
3013
3029
    new_conflicts = set()
3014
3030
    with ui.ui_factory.nested_progress_bar() as pb:
3015
3031
        for n in range(10):
3016
 
            pb.update(gettext('Resolution pass'), n+1, 10)
 
3032
            pb.update(gettext('Resolution pass'), n + 1, 10)
3017
3033
            conflicts = tt.find_conflicts()
3018
3034
            if len(conflicts) == 0:
3019
3035
                return new_conflicts
3111
3127
            file_id = tt.inactive_file_id(conflict[1])
3112
3128
            # special-case the other tree root (move its children instead)
3113
3129
            if path_tree and path_tree.path2id('') == file_id:
3114
 
                    # This is the root entry, skip it
3115
 
                    continue
 
3130
                # This is the root entry, skip it
 
3131
                continue
3116
3132
            tt.version_file(file_id, conflict[1])
3117
3133
            new_conflicts.add((c_type, 'Versioned directory', conflict[1]))
3118
3134
        elif c_type == 'non-directory parent':
3121
3137
            parent_name = tt.final_name(parent_id)
3122
3138
            parent_file_id = tt.final_file_id(parent_id)
3123
3139
            new_parent_id = tt.new_directory(parent_name + '.new',
3124
 
                parent_parent, parent_file_id)
 
3140
                                             parent_parent, parent_file_id)
3125
3141
            _reparent_transform_children(tt, parent_id, new_parent_id)
3126
3142
            if parent_file_id is not None:
3127
3143
                tt.unversion_file(parent_id)
3195
3211
            except OSError as e:
3196
3212
                raise errors.TransformRenameFailed(to, from_, str(e), e.errno)
3197
3213
        # after rollback, don't reuse _FileMover
3198
 
        past_renames = None
3199
 
        pending_deletions = None
 
3214
        self.past_renames = None
 
3215
        self.pending_deletions = None
3200
3216
 
3201
3217
    def apply_deletions(self):
3202
3218
        """Apply all marked deletions"""
3203
3219
        for path in self.pending_deletions:
3204
3220
            delete_any(path)
3205
3221
        # after apply_deletions, don't reuse _FileMover
3206
 
        past_renames = None
3207
 
        pending_deletions = None
 
3222
        self.past_renames = None
 
3223
        self.pending_deletions = None
3208
3224
 
3209
3225
 
3210
3226
def link_tree(target_tree, source_tree):
3217
3233
    try:
3218
3234
        for (file_id, paths, changed_content, versioned, parent, name, kind,
3219
3235
             executable) in target_tree.iter_changes(source_tree,
3220
 
             include_unchanged=True):
 
3236
                                                     include_unchanged=True):
3221
3237
            if changed_content:
3222
3238
                continue
3223
3239
            if kind != ('file', 'file'):