~jelmer/brz/bundle-git-plugin

« back to all changes in this revision

Viewing changes to breezy/transform.py

  • Committer: Jelmer Vernooij
  • Date: 2018-03-25 00:39:16 UTC
  • mfrom: (6926 work)
  • mto: This revision was merged to the branch mainline in revision 6928.
  • Revision ID: jelmer@jelmer.uk-20180325003916-mqa5uj4uq55hrjq9
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
69
69
    viewitems,
70
70
    viewvalues,
71
71
    )
 
72
from .tree import (
 
73
    find_previous_path,
 
74
    )
72
75
 
73
76
 
74
77
ROOT_PARENT = "root-parent"
90
93
class TreeTransformBase(object):
91
94
    """The base class for TreeTransform and its kin."""
92
95
 
93
 
    def __init__(self, tree, pb=None,
94
 
                 case_sensitive=True):
 
96
    def __init__(self, tree, pb=None, case_sensitive=True):
95
97
        """Constructor.
96
98
 
97
99
        :param tree: The tree that will be transformed, but not necessarily
132
134
        # The trans_id that will be used as the tree root
133
135
        root_id = tree.get_root_id()
134
136
        if root_id is not None:
135
 
            self._new_root = self.trans_id_tree_file_id(root_id)
 
137
            self._new_root = self.trans_id_tree_path('')
136
138
        else:
137
139
            self._new_root = None
138
140
        # Indicator of whether the transform has been applied
216
218
        # the physical root needs a new transaction id
217
219
        self._tree_path_ids.pop("")
218
220
        self._tree_id_paths.pop(old_root)
219
 
        self._new_root = self.trans_id_tree_file_id(self._tree.get_root_id())
 
221
        self._new_root = self.trans_id_tree_path('')
220
222
        if parent == old_root:
221
223
            parent = self._new_root
222
224
        self.adjust_path(name, parent, old_root)
285
287
        del self._new_parent[old_new_root]
286
288
        del self._new_name[old_new_root]
287
289
 
288
 
    def trans_id_tree_file_id(self, inventory_id):
289
 
        """Determine the transaction id of a working tree file.
290
 
 
291
 
        This reflects only files that already exist, not ones that will be
292
 
        added by transactions.
293
 
        """
294
 
        if inventory_id is None:
295
 
            raise ValueError('None is not a valid file id')
296
 
        path = self._tree.id2path(inventory_id)
297
 
        return self.trans_id_tree_path(path)
298
 
 
299
290
    def trans_id_file_id(self, file_id):
300
291
        """Determine or set the transaction id associated with a file ID.
301
292
        A new id is only created for file_ids that were never present.  If
308
299
            return self._r_new_id[file_id]
309
300
        else:
310
301
            try:
311
 
                next(self._tree.iter_entries_by_dir([file_id]))
312
 
            except StopIteration:
 
302
                path = self._tree.id2path(file_id)
 
303
            except errors.NoSuchId:
313
304
                if file_id in self._non_present_ids:
314
305
                    return self._non_present_ids[file_id]
315
306
                else:
317
308
                    self._non_present_ids[file_id] = trans_id
318
309
                    return trans_id
319
310
            else:
320
 
                return self.trans_id_tree_file_id(file_id)
 
311
                return self.trans_id_tree_path(path)
321
312
 
322
313
    def trans_id_tree_path(self, path):
323
314
        """Determine (and maybe set) the transaction ID for a tree path."""
454
445
        else:
455
446
            return self.tree_kind(trans_id)
456
447
 
 
448
    def tree_path(self, trans_id):
 
449
        """Determine the tree path associated with the trans_id."""
 
450
        return self._tree_id_paths.get(trans_id)
 
451
 
457
452
    def tree_file_id(self, trans_id):
458
453
        """Determine the file id associated with the trans_id in the tree"""
459
 
        try:
460
 
            path = self._tree_id_paths[trans_id]
461
 
        except KeyError:
462
 
            # the file is a new, unversioned file, or invalid trans_id
 
454
        path = self.tree_path(trans_id)
 
455
        if path is None:
463
456
            return None
464
457
        # the file is old; the old id is still valid
465
458
        if self._new_root == trans_id:
567
560
        parents.extend([t for t in self._removed_contents if
568
561
                        self.tree_kind(t) == 'directory'])
569
562
        for trans_id in self._removed_id:
570
 
            file_id = self.tree_file_id(trans_id)
571
 
            path = self._tree.id2path(file_id)
572
 
            if file_id is not None:
573
 
                if self._tree.stored_kind(path, file_id) == 'directory':
 
563
            path = self.tree_path(trans_id)
 
564
            if path is not None:
 
565
                if self._tree.stored_kind(path) == 'directory':
574
566
                    parents.append(trans_id)
575
567
            elif self.tree_kind(trans_id) == 'directory':
576
568
                parents.append(trans_id)
735
727
        active_tree_ids = all_ids.difference(removed_tree_ids)
736
728
        for trans_id, file_id in viewitems(self._new_id):
737
729
            if file_id in active_tree_ids:
738
 
                old_trans_id = self.trans_id_tree_file_id(file_id)
 
730
                path = self._tree.id2path(file_id)
 
731
                old_trans_id = self.trans_id_tree_path(path)
739
732
                conflicts.append(('duplicate id', old_trans_id, trans_id))
740
733
        return conflicts
741
734
 
901
894
                to_trans_ids[to_file_id] = trans_id
902
895
        return from_trans_ids, to_trans_ids
903
896
 
904
 
    def _from_file_data(self, from_trans_id, from_versioned, file_id):
 
897
    def _from_file_data(self, from_trans_id, from_versioned, from_path):
905
898
        """Get data about a file in the from (tree) state
906
899
 
907
900
        Return a (name, parent, kind, executable) tuple
909
902
        from_path = self._tree_id_paths.get(from_trans_id)
910
903
        if from_versioned:
911
904
            # get data from working tree if versioned
912
 
            from_entry = next(self._tree.iter_entries_by_dir([file_id]))[1]
 
905
            from_entry = next(self._tree.iter_entries_by_dir(
 
906
                    specific_files=[from_path]))[1]
913
907
            from_name = from_entry.name
914
908
            from_parent = from_entry.parent_id
915
909
        else:
978
972
            else:
979
973
                to_versioned = True
980
974
 
981
 
            from_name, from_parent, from_kind, from_executable = \
982
 
                self._from_file_data(from_trans_id, from_versioned, file_id)
983
 
 
984
 
            to_name, to_parent, to_kind, to_executable = \
985
 
                self._to_file_data(to_trans_id, from_trans_id, from_executable)
986
 
 
987
975
            if not from_versioned:
988
976
                from_path = None
989
977
            else:
992
980
                to_path = None
993
981
            else:
994
982
                to_path = final_paths.get_path(to_trans_id)
 
983
 
 
984
            from_name, from_parent, from_kind, from_executable = \
 
985
                self._from_file_data(from_trans_id, from_versioned, from_path)
 
986
 
 
987
            to_name, to_parent, to_kind, to_executable = \
 
988
                self._to_file_data(to_trans_id, from_trans_id, from_executable)
 
989
 
995
990
            if from_kind != to_kind:
996
991
                modified = True
997
992
            elif to_kind in ('file', 'symlink') and (
1077
1072
        return revision_id
1078
1073
 
1079
1074
    def _text_parent(self, trans_id):
1080
 
        file_id = self.tree_file_id(trans_id)
 
1075
        path = self.tree_path(trans_id)
1081
1076
        try:
1082
 
            if (file_id is None or
1083
 
                    self._tree.kind(self._tree.id2path(file_id), file_id) != 'file'):
 
1077
            if path is None or self._tree.kind(path) != 'file':
1084
1078
                return None
1085
1079
        except errors.NoSuchFile:
1086
1080
            return None
1087
 
        return file_id
 
1081
        return path
1088
1082
 
1089
1083
    def _get_parents_texts(self, trans_id):
1090
1084
        """Get texts for compression parents of this file."""
1091
 
        file_id = self._text_parent(trans_id)
1092
 
        if file_id is None:
 
1085
        path = self._text_parent(trans_id)
 
1086
        if path is None:
1093
1087
            return ()
1094
 
        return (self._tree.get_file_text(self._tree.id2path(file_id), file_id),)
 
1088
        return (self._tree.get_file_text(path),)
1095
1089
 
1096
1090
    def _get_parents_lines(self, trans_id):
1097
1091
        """Get lines for compression parents of this file."""
1098
 
        file_id = self._text_parent(trans_id)
1099
 
        if file_id is None:
 
1092
        path = self._text_parent(trans_id)
 
1093
        if path is None:
1100
1094
            return ()
1101
 
        return (self._tree.get_file_lines(self._tree.id2path(file_id), file_id),)
 
1095
        return (self._tree.get_file_lines(path),)
1102
1096
 
1103
1097
    def serialize(self, serializer):
1104
1098
        """Serialize this TreeTransform.
1407
1401
 
1408
1402
    def new_orphan(self, trans_id, parent_id):
1409
1403
        conf = self._tree.get_config_stack()
1410
 
        handle_orphan = conf.get('bzr.transform.orphan_policy')
 
1404
        handle_orphan = conf.get('transform.orphan_policy')
1411
1405
        handle_orphan(self, trans_id, parent_id)
1412
1406
 
1413
1407
 
1477
1471
 
1478
1472
 
1479
1473
opt_transform_orphan = _mod_config.RegistryOption(
1480
 
    'bzr.transform.orphan_policy', orphaning_registry,
 
1474
    'transform.orphan_policy', orphaning_registry,
1481
1475
    help='Policy for orphaned files during transform operations.',
1482
1476
    invalid='warning')
1483
1477
 
1721
1715
            hook(self._tree, self)
1722
1716
        if not no_conflicts:
1723
1717
            self._check_malformed()
1724
 
        child_pb = ui.ui_factory.nested_progress_bar()
1725
 
        try:
 
1718
        with ui.ui_factory.nested_progress_bar() as child_pb:
1726
1719
            if precomputed_delta is None:
1727
1720
                child_pb.update(gettext('Apply phase'), 0, 2)
1728
1721
                inventory_delta = self._generate_inventory_delta()
1744
1737
                raise
1745
1738
            else:
1746
1739
                mover.apply_deletions()
1747
 
        finally:
1748
 
            child_pb.finished()
1749
1740
        if self.final_file_id(self.root) is None:
1750
1741
            inventory_delta = [e for e in inventory_delta if e[0] != '']
1751
1742
        self._tree.apply_inventory_delta(inventory_delta)
1757
1748
    def _generate_inventory_delta(self):
1758
1749
        """Generate an inventory delta for the current transform."""
1759
1750
        inventory_delta = []
1760
 
        child_pb = ui.ui_factory.nested_progress_bar()
1761
1751
        new_paths = self._inventory_altered()
1762
1752
        total_entries = len(new_paths) + len(self._removed_id)
1763
 
        try:
 
1753
        with ui.ui_factory.nested_progress_bar() as child_pb:
1764
1754
            for num, trans_id in enumerate(self._removed_id):
1765
1755
                if (num % 10) == 0:
1766
1756
                    child_pb.update(gettext('removing file'), num, total_entries)
1775
1765
                inventory_delta.append((path, None, file_id, None))
1776
1766
            new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
1777
1767
                                     new_paths)
1778
 
            entries = self._tree.iter_entries_by_dir(
1779
 
                viewvalues(new_path_file_ids))
1780
 
            old_paths = dict((e.file_id, p) for p, e in entries)
1781
1768
            final_kinds = {}
1782
1769
            for num, (path, trans_id) in enumerate(new_paths):
1783
1770
                if (num % 10) == 0:
1805
1792
                    new_entry = inventory.make_entry(kind,
1806
1793
                        self.final_name(trans_id),
1807
1794
                        parent_file_id, file_id)
1808
 
                old_path = old_paths.get(new_entry.file_id)
 
1795
                try:
 
1796
                    old_path = self._tree.id2path(new_entry.file_id)
 
1797
                except errors.NoSuchId:
 
1798
                    old_path = None
1809
1799
                new_executability = self._new_executability.get(trans_id)
1810
1800
                if new_executability is not None:
1811
1801
                    new_entry.executable = new_executability
1812
1802
                inventory_delta.append(
1813
1803
                    (old_path, path, new_entry.file_id, new_entry))
1814
 
        finally:
1815
 
            child_pb.finished()
1816
1804
        return inventory_delta
1817
1805
 
1818
1806
    def _apply_removals(self, mover):
1825
1813
        If inventory_delta is None, no inventory delta generation is performed.
1826
1814
        """
1827
1815
        tree_paths = sorted(viewitems(self._tree_path_ids), reverse=True)
1828
 
        child_pb = ui.ui_factory.nested_progress_bar()
1829
 
        try:
 
1816
        with ui.ui_factory.nested_progress_bar() as child_pb:
1830
1817
            for num, (path, trans_id) in enumerate(tree_paths):
1831
1818
                # do not attempt to move root into a subdirectory of itself.
1832
1819
                if path == '':
1845
1832
                            raise
1846
1833
                    else:
1847
1834
                        self.rename_count += 1
1848
 
        finally:
1849
 
            child_pb.finished()
1850
1835
 
1851
1836
    def _apply_insertions(self, mover):
1852
1837
        """Perform tree operations that insert directory/inventory names.
1862
1847
        modified_paths = []
1863
1848
        new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
1864
1849
                                 new_paths)
1865
 
        child_pb = ui.ui_factory.nested_progress_bar()
1866
 
        try:
 
1850
        with ui.ui_factory.nested_progress_bar() as child_pb:
1867
1851
            for num, (path, trans_id) in enumerate(new_paths):
1868
1852
                if (num % 10) == 0:
1869
1853
                    child_pb.update(gettext('adding file'), num, len(new_paths))
1890
1874
                    o_sha1, o_st_val = self._observed_sha1s[trans_id]
1891
1875
                    st = osutils.lstat(full_path)
1892
1876
                    self._observed_sha1s[trans_id] = (o_sha1, st)
1893
 
        finally:
1894
 
            child_pb.finished()
1895
1877
        for path, trans_id in new_paths:
1896
1878
            # new_paths includes stuff like workingtree conflicts. Only the
1897
1879
            # stuff in new_contents actually comes from limbo.
1964
1946
            path = self._tree_id_paths[parent_id]
1965
1947
        except KeyError:
1966
1948
            return
1967
 
        file_id = self.tree_file_id(parent_id)
1968
 
        if file_id is None:
1969
 
            return
1970
 
        entry = next(self._tree.iter_entries_by_dir([file_id]))[1]
 
1949
        entry = next(self._tree.iter_entries_by_dir(
 
1950
                specific_files=[path]))[1]
1971
1951
        children = getattr(entry, 'children', {})
1972
1952
        for child in children:
1973
1953
            childpath = joinpath(path, child)
2023
2003
            vf.fallback_versionedfiles.append(base_vf)
2024
2004
        return tree_revision
2025
2005
 
2026
 
    def _stat_limbo_file(self, file_id=None, trans_id=None):
2027
 
        if trans_id is None:
2028
 
            trans_id = self._transform.trans_id_file_id(file_id)
 
2006
    def _stat_limbo_file(self, trans_id):
2029
2007
        name = self._transform._limbo_name(trans_id)
2030
2008
        return os.lstat(name)
2031
2009
 
2135
2113
        self._all_children_cache[trans_id] = children
2136
2114
        return children
2137
2115
 
2138
 
    def iter_children(self, file_id):
 
2116
    def _iter_children(self, file_id):
2139
2117
        trans_id = self._transform.trans_id_file_id(file_id)
2140
2118
        for child_trans_id in self._all_children(trans_id):
2141
2119
            yield self._transform.final_file_id(child_trans_id)
2149
2127
            if self._transform.final_file_id(trans_id) is None:
2150
2128
                yield self._final_paths._determine_path(trans_id)
2151
2129
 
2152
 
    def _make_inv_entries(self, ordered_entries, specific_file_ids=None,
 
2130
    def _make_inv_entries(self, ordered_entries, specific_files=None,
2153
2131
        yield_parents=False):
2154
2132
        for trans_id, parent_file_id in ordered_entries:
2155
2133
            file_id = self._transform.final_file_id(trans_id)
2156
2134
            if file_id is None:
2157
2135
                continue
2158
 
            if (specific_file_ids is not None
2159
 
                and file_id not in specific_file_ids):
 
2136
            if (specific_files is not None and
 
2137
                unicode(self._final_paths.get_path(trans_id)) not in specific_files):
2160
2138
                continue
2161
2139
            kind = self._transform.final_kind(trans_id)
2162
2140
            if kind is None:
2184
2162
        return ordered_ids
2185
2163
 
2186
2164
    def iter_child_entries(self, path, file_id=None):
2187
 
        if file_id is None:
2188
 
            file_id = self.path2id(path)
2189
 
        if file_id is None:
 
2165
        trans_id = self._path2trans_id(path)
 
2166
        if trans_id is None:
2190
2167
            raise errors.NoSuchFile(path)
2191
 
        trans_id = self._transform.trans_id_file_id(file_id)
2192
2168
        todo = [(child_trans_id, trans_id) for child_trans_id in
2193
2169
                self._all_children(trans_id)]
2194
2170
        for entry, trans_id in self._make_inv_entries(todo):
2195
2171
            yield entry
2196
2172
 
2197
 
    def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
 
2173
    def iter_entries_by_dir(self, specific_files=None, yield_parents=False):
2198
2174
        # This may not be a maximally efficient implementation, but it is
2199
2175
        # reasonably straightforward.  An implementation that grafts the
2200
2176
        # TreeTransform changes onto the tree's iter_entries_by_dir results
2202
2178
        # position.
2203
2179
        ordered_ids = self._list_files_by_dir()
2204
2180
        for entry, trans_id in self._make_inv_entries(ordered_ids,
2205
 
            specific_file_ids, yield_parents=yield_parents):
 
2181
            specific_files, yield_parents=yield_parents):
2206
2182
            yield unicode(self._final_paths.get_path(trans_id)), entry
2207
2183
 
2208
2184
    def _iter_entries_for_dir(self, dir_path):
2209
2185
        """Return path, entry for items in a directory without recursing down."""
2210
 
        dir_file_id = self.path2id(dir_path)
2211
2186
        ordered_ids = []
2212
 
        for file_id in self.iter_children(dir_file_id):
2213
 
            trans_id = self._transform.trans_id_file_id(file_id)
2214
 
            ordered_ids.append((trans_id, file_id))
 
2187
        dir_trans_id = self._path2trans_id(dir_path)
 
2188
        dir_id = self._transform.final_file_id(dir_trans_id)
 
2189
        for child_trans_id in self._all_children(dir_trans_id):
 
2190
            ordered_ids.append((child_trans_id, dir_id))
2215
2191
        for entry, trans_id in self._make_inv_entries(ordered_ids):
2216
2192
            yield unicode(self._final_paths.get_path(trans_id)), entry
2217
2193
 
2242
2218
                yield path, 'V', entry.kind, entry.file_id, entry
2243
2219
 
2244
2220
    def kind(self, path, file_id=None):
2245
 
        if file_id is None:
2246
 
            file_id = self.path2id(path)
2247
 
        trans_id = self._transform.trans_id_file_id(file_id)
 
2221
        trans_id = self._path2trans_id(path)
 
2222
        if trans_id is None:
 
2223
            raise errors.NoSuchFile(path)
2248
2224
        return self._transform.final_kind(trans_id)
2249
2225
 
2250
2226
    def stored_kind(self, path, file_id=None):
2251
 
        if file_id is None:
2252
 
            file_id = self.path2id(path)
2253
 
        trans_id = self._transform.trans_id_file_id(file_id)
 
2227
        trans_id = self._path2trans_id(path)
 
2228
        if trans_id is None:
 
2229
            raise errors.NoSuchFile(path)
2254
2230
        try:
2255
2231
            return self._transform._new_contents[trans_id]
2256
2232
        except KeyError:
2265
2241
        if not self._content_change(file_id):
2266
2242
            return self._transform._tree.get_file_mtime(
2267
2243
                    self._transform._tree.id2path(file_id), file_id)
2268
 
        return self._stat_limbo_file(file_id).st_mtime
2269
 
 
2270
 
    def _file_size(self, entry, stat_value):
2271
 
        path = self.id2path(entry.file_id)
2272
 
        return self.get_file_size(path, entry.file_id)
 
2244
        trans_id = self._path2trans_id(path)
 
2245
        return self._stat_limbo_file(trans_id).st_mtime
2273
2246
 
2274
2247
    def get_file_size(self, path, file_id=None):
2275
2248
        """See Tree.get_file_size"""
2276
 
        if file_id is None:
2277
 
            file_id = self.path2id(path)
2278
 
        trans_id = self._transform.trans_id_file_id(file_id)
 
2249
        trans_id = self._path2trans_id(path)
 
2250
        if trans_id is None:
 
2251
            raise errors.NoSuchFile(path)
2279
2252
        kind = self._transform.final_kind(trans_id)
2280
2253
        if kind != 'file':
2281
2254
            return None
2282
2255
        if trans_id in self._transform._new_contents:
2283
 
            return self._stat_limbo_file(trans_id=trans_id).st_size
 
2256
            return self._stat_limbo_file(trans_id).st_size
2284
2257
        if self.kind(path, file_id) == 'file':
2285
2258
            return self._transform._tree.get_file_size(path, file_id)
2286
2259
        else:
2287
2260
            return None
2288
2261
 
2289
2262
    def get_file_verifier(self, path, file_id=None, stat_value=None):
2290
 
        if file_id is None:
2291
 
            file_id = self.path2id(path)
2292
 
        trans_id = self._transform.trans_id_file_id(file_id)
 
2263
        trans_id = self._path2trans_id(path)
 
2264
        if trans_id is None:
 
2265
            raise errors.NoSuchFile(path)
2293
2266
        kind = self._transform._new_contents.get(trans_id)
2294
2267
        if kind is None:
2295
2268
            return self._transform._tree.get_file_verifier(path, file_id)
2301
2274
                fileobj.close()
2302
2275
 
2303
2276
    def get_file_sha1(self, path, file_id=None, stat_value=None):
2304
 
        if file_id is None:
2305
 
            file_id = self.path2id(path)
2306
 
        trans_id = self._transform.trans_id_file_id(file_id)
 
2277
        trans_id = self._path2trans_id(path)
 
2278
        if trans_id is None:
 
2279
            raise errors.NoSuchFile(path)
2307
2280
        kind = self._transform._new_contents.get(trans_id)
2308
2281
        if kind is None:
2309
2282
            return self._transform._tree.get_file_sha1(path, file_id)
2315
2288
                fileobj.close()
2316
2289
 
2317
2290
    def is_executable(self, path, file_id=None):
2318
 
        if file_id is None:
2319
 
            file_id = self.path2id(path)
2320
 
        if file_id is None:
 
2291
        trans_id = self._path2trans_id(path)
 
2292
        if trans_id is None:
2321
2293
            return False
2322
 
        trans_id = self._transform.trans_id_file_id(file_id)
2323
2294
        try:
2324
2295
            return self._transform._new_executability[trans_id]
2325
2296
        except KeyError:
2398
2369
            file_id = self.path2id(path)
2399
2370
        if not self._content_change(file_id):
2400
2371
            return self._transform._tree.get_file(path, file_id)
2401
 
        trans_id = self._transform.trans_id_file_id(file_id)
 
2372
        trans_id = self._path2trans_id(path)
2402
2373
        name = self._transform._limbo_name(trans_id)
2403
2374
        return open(name, 'rb')
2404
2375
 
2444
2415
            file_id = self.path2id(path)
2445
2416
        if not self._content_change(file_id):
2446
2417
            return self._transform._tree.get_symlink_target(path)
2447
 
        trans_id = self._transform.trans_id_file_id(file_id)
 
2418
        trans_id = self._path2trans_id(path)
2448
2419
        name = self._transform._limbo_name(trans_id)
2449
2420
        return osutils.readlink(name)
2450
2421
 
2596
2567
    divert = set()
2597
2568
    try:
2598
2569
        pp.next_phase()
2599
 
        file_trans_id[wt.get_root_id()] = \
2600
 
            tt.trans_id_tree_file_id(wt.get_root_id())
2601
 
        pb = ui.ui_factory.nested_progress_bar()
2602
 
        try:
 
2570
        file_trans_id[wt.get_root_id()] = tt.trans_id_tree_path('')
 
2571
        with ui.ui_factory.nested_progress_bar() as pb:
2603
2572
            deferred_contents = []
2604
2573
            num = 0
2605
2574
            total = len(tree.all_versioned_paths())
2636
2605
                        else:
2637
2606
                            divert.add(file_id)
2638
2607
                    if (file_id not in divert and
2639
 
                        _content_match(tree, entry, file_id, kind,
 
2608
                        _content_match(tree, entry, tree_path, file_id, kind,
2640
2609
                        target_path)):
2641
2610
                        tt.delete_contents(tt.trans_id_tree_path(tree_path))
2642
2611
                        if kind == 'directory':
2651
2620
                    executable = tree.is_executable(tree_path, file_id)
2652
2621
                    if executable:
2653
2622
                        tt.set_executability(executable, trans_id)
2654
 
                    trans_data = (trans_id, tree_path, entry.text_sha1)
2655
 
                    deferred_contents.append((file_id, trans_data))
 
2623
                    trans_data = (trans_id, file_id, tree_path, entry.text_sha1)
 
2624
                    deferred_contents.append((tree_path, trans_data))
2656
2625
                else:
2657
2626
                    file_trans_id[file_id] = new_by_entry(
2658
2627
                            tree_path, tt, entry, parent_id, tree)
2663
2632
            offset = num + 1 - len(deferred_contents)
2664
2633
            _create_files(tt, tree, deferred_contents, pb, offset,
2665
2634
                          accelerator_tree, hardlink)
2666
 
        finally:
2667
 
            pb.finished()
2668
2635
        pp.next_phase()
2669
2636
        divert_trans = set(file_trans_id[f] for f in divert)
2670
2637
        resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
2694
2661
        new_desired_files = desired_files
2695
2662
    else:
2696
2663
        iter = accelerator_tree.iter_changes(tree, include_unchanged=True)
2697
 
        unchanged = [(f, p[1]) for (f, p, c, v, d, n, k, e)
 
2664
        unchanged = [(p[0], p[1]) for (f, p, c, v, d, n, k, e)
2698
2665
                     in iter if not (c or e[0] != e[1])]
2699
2666
        if accelerator_tree.supports_content_filtering():
2700
 
            unchanged = [(f, p) for (f, p) in unchanged
2701
 
                         if not next(accelerator_tree.iter_search_rules([p]))]
 
2667
            unchanged = [(tp, ap) for (tp, ap) in unchanged
 
2668
                         if not next(accelerator_tree.iter_search_rules([ap]))]
2702
2669
        unchanged = dict(unchanged)
2703
2670
        new_desired_files = []
2704
2671
        count = 0
2705
 
        for file_id, (trans_id, tree_path, text_sha1) in desired_files:
2706
 
            accelerator_path = unchanged.get(file_id)
 
2672
        for unused_tree_path, (trans_id, file_id, tree_path, text_sha1) in desired_files:
 
2673
            accelerator_path = unchanged.get(tree_path)
2707
2674
            if accelerator_path is None:
2708
 
                new_desired_files.append((file_id,
2709
 
                    (trans_id, tree_path, text_sha1)))
 
2675
                new_desired_files.append((tree_path,
 
2676
                    (trans_id, file_id, tree_path, text_sha1)))
2710
2677
                continue
2711
2678
            pb.update(gettext('Adding file contents'), count + offset, total)
2712
2679
            if hardlink:
2728
2695
                        pass
2729
2696
            count += 1
2730
2697
        offset += count
2731
 
    for count, ((trans_id, tree_path, text_sha1), contents) in enumerate(
 
2698
    for count, ((trans_id, file_id, tree_path, text_sha1), contents) in enumerate(
2732
2699
            tree.iter_files_bytes(new_desired_files)):
2733
2700
        if wt.supports_content_filtering():
2734
2701
            filters = wt._content_filter_stack(tree_path)
2750
2717
    return by_parent[old_parent]
2751
2718
 
2752
2719
 
2753
 
def _content_match(tree, entry, file_id, kind, target_path):
 
2720
def _content_match(tree, entry, tree_path, file_id, kind, target_path):
2754
2721
    if entry.kind != kind:
2755
2722
        return False
2756
2723
    if entry.kind == "directory":
2757
2724
        return True
2758
 
    path = tree.id2path(file_id)
2759
2725
    if entry.kind == "file":
2760
2726
        f = file(target_path, 'rb')
2761
2727
        try:
2762
 
            if tree.get_file_text(path, file_id) == f.read():
 
2728
            if tree.get_file_text(tree_path, file_id) == f.read():
2763
2729
                return True
2764
2730
        finally:
2765
2731
            f.close()
2766
2732
    elif entry.kind == "symlink":
2767
 
        if tree.get_symlink_target(path, file_id) == os.readlink(target_path):
 
2733
        if tree.get_symlink_target(tree_path, file_id) == os.readlink(target_path):
2768
2734
            return True
2769
2735
    return False
2770
2736
 
2874
2840
            trace.warning(unicode(conflict))
2875
2841
        pp.next_phase()
2876
2842
        tt.apply()
2877
 
        working_tree.set_merge_modified(merge_modified)
 
2843
        if working_tree.supports_merge_modified():
 
2844
            working_tree.set_merge_modified(merge_modified)
2878
2845
    finally:
2879
2846
        target_tree.unlock()
2880
2847
        tt.finalize()
2885
2852
def _prepare_revert_transform(working_tree, target_tree, tt, filenames,
2886
2853
                              backups, pp, basis_tree=None,
2887
2854
                              merge_modified=None):
2888
 
    child_pb = ui.ui_factory.nested_progress_bar()
2889
 
    try:
 
2855
    with ui.ui_factory.nested_progress_bar() as child_pb:
2890
2856
        if merge_modified is None:
2891
2857
            merge_modified = working_tree.merge_modified()
2892
2858
        merge_modified = _alter_files(working_tree, target_tree, tt,
2893
2859
                                      child_pb, filenames, backups,
2894
2860
                                      merge_modified, basis_tree)
2895
 
    finally:
2896
 
        child_pb.finished()
2897
 
    child_pb = ui.ui_factory.nested_progress_bar()
2898
 
    try:
 
2861
    with ui.ui_factory.nested_progress_bar() as child_pb:
2899
2862
        raw_conflicts = resolve_conflicts(tt, child_pb,
2900
2863
            lambda t, c: conflict_pass(t, c, target_tree))
2901
 
    finally:
2902
 
        child_pb.finished()
2903
2864
    conflicts = cook_conflicts(raw_conflicts, tt)
2904
2865
    return conflicts, merge_modified
2905
2866
 
2943
2904
                        if basis_tree is None:
2944
2905
                            basis_tree = working_tree.basis_tree()
2945
2906
                            basis_tree.lock_read()
2946
 
                        try:
2947
 
                            basis_path = basis_tree.id2path(file_id)
2948
 
                        except errors.NoSuchId:
 
2907
                        basis_path = find_previous_path(working_tree, basis_tree, wt_path)
 
2908
                        if basis_path is None:
2949
2909
                            if target_kind is None and not target_versioned:
2950
2910
                                keep_content = True
2951
2911
                        else:
2977
2937
                    tt.create_symlink(target_tree.get_symlink_target(
2978
2938
                            target_path, file_id), trans_id)
2979
2939
                elif target_kind == 'file':
2980
 
                    deferred_files.append((file_id, (trans_id, mode_id)))
 
2940
                    deferred_files.append((target_path, (trans_id, mode_id, file_id)))
2981
2941
                    if basis_tree is None:
2982
2942
                        basis_tree = working_tree.basis_tree()
2983
2943
                        basis_tree.lock_read()
2984
2944
                    new_sha1 = target_tree.get_file_sha1(target_path, file_id)
2985
 
                    try:
2986
 
                        basis_path = basis_tree.id2path(file_id)
2987
 
                    except errors.NoSuchId:
2988
 
                        basis_path = None
 
2945
                    basis_path = find_previous_path(target_tree, basis_tree, target_path)
2989
2946
                    if (basis_path is not None and
2990
2947
                        new_sha1 == basis_tree.get_file_sha1(basis_path, file_id)):
2991
2948
                        if file_id in merge_modified:
3015
2972
            if wt_executable != target_executable and target_kind == "file":
3016
2973
                tt.set_executability(target_executable, trans_id)
3017
2974
        if working_tree.supports_content_filtering():
3018
 
            for index, ((trans_id, mode_id), bytes) in enumerate(
 
2975
            for (trans_id, mode_id, file_id), bytes in (
3019
2976
                target_tree.iter_files_bytes(deferred_files)):
3020
 
                file_id = deferred_files[index][0]
3021
2977
                # We're reverting a tree to the target tree so using the
3022
2978
                # target tree to find the file path seems the best choice
3023
2979
                # here IMO - Ian C 27/Oct/2009
3027
2983
                    ContentFilterContext(filter_tree_path, working_tree))
3028
2984
                tt.create_file(bytes, trans_id, mode_id)
3029
2985
        else:
3030
 
            for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
 
2986
            for (trans_id, mode_id, file_id), bytes in target_tree.iter_files_bytes(
3031
2987
                deferred_files):
3032
2988
                tt.create_file(bytes, trans_id, mode_id)
3033
2989
        tt.fixup_new_roots()
3042
2998
    if pass_func is None:
3043
2999
        pass_func = conflict_pass
3044
3000
    new_conflicts = set()
3045
 
    pb = ui.ui_factory.nested_progress_bar()
3046
 
    try:
 
3001
    with ui.ui_factory.nested_progress_bar() as pb:
3047
3002
        for n in range(10):
3048
3003
            pb.update(gettext('Resolution pass'), n+1, 10)
3049
3004
            conflicts = tt.find_conflicts()
3051
3006
                return new_conflicts
3052
3007
            new_conflicts.update(pass_func(tt, conflicts))
3053
3008
        raise MalformedTransform(conflicts=conflicts)
3054
 
    finally:
3055
 
        pb.finished()
3056
3009
 
3057
3010
 
3058
3011
def conflict_pass(tt, conflicts, path_tree=None):
3123
3076
                        if file_id is None:
3124
3077
                            file_id = tt.inactive_file_id(trans_id)
3125
3078
                        _, entry = next(path_tree.iter_entries_by_dir(
3126
 
                            [file_id]))
 
3079
                            specific_files=[path_tree.id2path(file_id)]))
3127
3080
                        # special-case the other tree root (move its
3128
3081
                        # children to current root)
3129
3082
                        if entry.parent_id is None:
3144
3097
        elif c_type == 'unversioned parent':
3145
3098
            file_id = tt.inactive_file_id(conflict[1])
3146
3099
            # special-case the other tree root (move its children instead)
3147
 
            if path_tree and path_tree.has_id(file_id):
3148
 
                if path_tree.path2id('') == file_id:
 
3100
            if path_tree and path_tree.path2id('') == file_id:
3149
3101
                    # This is the root entry, skip it
3150
3102
                    continue
3151
3103
            tt.version_file(file_id, conflict[1])
3259
3211
                continue
3260
3212
            if executable[0] != executable[1]:
3261
3213
                continue
3262
 
            trans_id = tt.trans_id_tree_file_id(file_id)
 
3214
            trans_id = tt.trans_id_tree_path(paths[1])
3263
3215
            tt.delete_contents(trans_id)
3264
 
            tt.create_hardlink(source_tree.id2abspath(file_id), trans_id)
 
3216
            tt.create_hardlink(source_tree.abspath(paths[0]), trans_id)
3265
3217
        tt.apply()
3266
3218
    finally:
3267
3219
        tt.finalize()