~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

  • Committer: Jelmer Vernooij
  • Date: 2011-12-16 19:18:39 UTC
  • mto: This revision was merged to the branch mainline in revision 6391.
  • Revision ID: jelmer@samba.org-20111216191839-eg681lxqibi1qxu1
Fix remaining tests.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
23
23
    errors,
24
24
    lazy_import,
25
25
    registry,
 
26
    trace,
 
27
    tree,
26
28
    )
27
29
lazy_import.lazy_import(globals(), """
28
30
from bzrlib import (
29
31
    annotate,
30
32
    bencode,
31
 
    bzrdir,
 
33
    controldir,
32
34
    commit,
 
35
    conflicts,
33
36
    delta,
34
 
    errors,
35
37
    inventory,
36
38
    multiparent,
37
39
    osutils,
38
40
    revision as _mod_revision,
39
 
    trace,
40
41
    ui,
 
42
    urlutils,
41
43
    )
 
44
from bzrlib.i18n import gettext
42
45
""")
43
 
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
 
46
from bzrlib.errors import (DuplicateKey, MalformedTransform,
44
47
                           ReusingTransform, CantMoveRoot,
45
48
                           ExistingLimbo, ImmortalLimbo, NoFinalPath,
46
49
                           UnableCreateSymlink)
47
50
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
48
 
from bzrlib.inventory import InventoryEntry
49
51
from bzrlib.osutils import (
50
52
    delete_any,
51
53
    file_kind,
52
54
    has_symlinks,
53
 
    lexists,
54
55
    pathjoin,
55
56
    sha_file,
56
57
    splitpath,
57
58
    supports_executable,
58
 
)
 
59
    )
59
60
from bzrlib.progress import ProgressPhase
60
61
from bzrlib.symbol_versioning import (
61
 
        deprecated_function,
62
 
        deprecated_in,
63
 
        deprecated_method,
64
 
        )
65
 
from bzrlib.trace import mutter, warning
66
 
from bzrlib import tree
67
 
import bzrlib.ui
68
 
import bzrlib.urlutils as urlutils
 
62
    deprecated_function,
 
63
    deprecated_in,
 
64
    deprecated_method,
 
65
    )
69
66
 
70
67
 
71
68
ROOT_PARENT = "root-parent"
106
103
        self._new_parent = {}
107
104
        # mapping of trans_id with new contents -> new file_kind
108
105
        self._new_contents = {}
 
106
        # mapping of trans_id => (sha1 of content, stat_value)
 
107
        self._observed_sha1s = {}
109
108
        # Set of trans_ids whose contents will be removed
110
109
        self._removed_contents = set()
111
110
        # Mapping of trans_id -> new execute-bit value
130
129
            self._new_root = self.trans_id_tree_file_id(root_id)
131
130
        else:
132
131
            self._new_root = None
133
 
        # Indictor of whether the transform has been applied
 
132
        # Indicator of whether the transform has been applied
134
133
        self._done = False
135
134
        # A progress bar
136
135
        self._pb = pb
139
138
        # A counter of how many files have been renamed
140
139
        self.rename_count = 0
141
140
 
 
141
    def __enter__(self):
 
142
        """Support Context Manager API."""
 
143
        return self
 
144
 
 
145
    def __exit__(self, exc_type, exc_val, exc_tb):
 
146
        """Support Context Manager API."""
 
147
        self.finalize()
 
148
 
142
149
    def finalize(self):
143
150
        """Release the working tree lock, if held.
144
151
 
219
226
        This means that the old root trans-id becomes obsolete, so it is
220
227
        recommended only to invoke this after the root trans-id has become
221
228
        irrelevant.
 
229
 
222
230
        """
223
231
        new_roots = [k for k, v in self._new_parent.iteritems() if v is
224
232
                     ROOT_PARENT]
230
238
            self._new_root = new_roots[0]
231
239
            return
232
240
        old_new_root = new_roots[0]
233
 
        # TODO: What to do if a old_new_root is present, but self._new_root is
234
 
        #       not listed as being removed? This code explicitly unversions
235
 
        #       the old root and versions it with the new file_id. Though that
236
 
        #       seems like an incomplete delta
237
 
 
238
241
        # unversion the new root's directory.
239
 
        file_id = self.final_file_id(old_new_root)
 
242
        if self.final_kind(self._new_root) is None:
 
243
            file_id = self.final_file_id(old_new_root)
 
244
        else:
 
245
            file_id = self.final_file_id(self._new_root)
240
246
        if old_new_root in self._new_id:
241
247
            self.cancel_versioning(old_new_root)
242
248
        else:
246
252
        if (self.tree_file_id(self._new_root) is not None and
247
253
            self._new_root not in self._removed_id):
248
254
            self.unversion_file(self._new_root)
249
 
        self.version_file(file_id, self._new_root)
 
255
        if file_id is not None:
 
256
            self.version_file(file_id, self._new_root)
250
257
 
251
258
        # Now move children of new root into old root directory.
252
259
        # Ensure all children are registered with the transaction, but don't
386
393
        return sorted(FinalPaths(self).get_paths(new_ids))
387
394
 
388
395
    def _inventory_altered(self):
389
 
        """Get the trans_ids and paths of files needing new inv entries."""
390
 
        new_ids = set()
391
 
        for id_set in [self._new_name, self._new_parent, self._new_id,
 
396
        """Determine which trans_ids need new Inventory entries.
 
397
 
 
398
        An new entry is needed when anything that would be reflected by an
 
399
        inventory entry changes, including file name, file_id, parent file_id,
 
400
        file kind, and the execute bit.
 
401
 
 
402
        Some care is taken to return entries with real changes, not cases
 
403
        where the value is deleted and then restored to its original value,
 
404
        but some actually unchanged values may be returned.
 
405
 
 
406
        :returns: A list of (path, trans_id) for all items requiring an
 
407
            inventory change. Ordered by path.
 
408
        """
 
409
        changed_ids = set()
 
410
        # Find entries whose file_ids are new (or changed).
 
411
        new_file_id = set(t for t in self._new_id
 
412
                          if self._new_id[t] != self.tree_file_id(t))
 
413
        for id_set in [self._new_name, self._new_parent, new_file_id,
392
414
                       self._new_executability]:
393
 
            new_ids.update(id_set)
 
415
            changed_ids.update(id_set)
 
416
        # removing implies a kind change
394
417
        changed_kind = set(self._removed_contents)
 
418
        # so does adding
395
419
        changed_kind.intersection_update(self._new_contents)
396
 
        changed_kind.difference_update(new_ids)
 
420
        # Ignore entries that are already known to have changed.
 
421
        changed_kind.difference_update(changed_ids)
 
422
        #  to keep only the truly changed ones
397
423
        changed_kind = (t for t in changed_kind
398
424
                        if self.tree_kind(t) != self.final_kind(t))
399
 
        new_ids.update(changed_kind)
400
 
        return sorted(FinalPaths(self).get_paths(new_ids))
 
425
        # all kind changes will alter the inventory
 
426
        changed_ids.update(changed_kind)
 
427
        # To find entries with changed parent_ids, find parents which existed,
 
428
        # but changed file_id.
 
429
        changed_file_id = set(t for t in new_file_id if t in self._removed_id)
 
430
        # Now add all their children to the set.
 
431
        for parent_trans_id in new_file_id:
 
432
            changed_ids.update(self.iter_tree_children(parent_trans_id))
 
433
        return sorted(FinalPaths(self).get_paths(changed_ids))
401
434
 
402
435
    def final_kind(self, trans_id):
403
436
        """Determine the final file kind, after any changes applied.
528
561
        for trans_id in self._removed_id:
529
562
            file_id = self.tree_file_id(trans_id)
530
563
            if file_id is not None:
531
 
                if self._tree.inventory[file_id].kind == 'directory':
 
564
                if self._tree.stored_kind(file_id) == 'directory':
532
565
                    parents.append(trans_id)
533
566
            elif self.tree_kind(trans_id) == 'directory':
534
567
                parents.append(trans_id)
628
661
            if kind is None:
629
662
                conflicts.append(('versioning no contents', trans_id))
630
663
                continue
631
 
            if not InventoryEntry.versionable_kind(kind):
 
664
            if not inventory.InventoryEntry.versionable_kind(kind):
632
665
                conflicts.append(('versioning bad kind', trans_id, kind))
633
666
        return conflicts
634
667
 
666
699
        if (self._new_name, self._new_parent) == ({}, {}):
667
700
            return conflicts
668
701
        for children in by_parent.itervalues():
669
 
            name_ids = [(self.final_name(t), t) for t in children]
670
 
            if not self._case_sensitive_target:
671
 
                name_ids = [(n.lower(), t) for n, t in name_ids]
 
702
            name_ids = []
 
703
            for child_tid in children:
 
704
                name = self.final_name(child_tid)
 
705
                if name is not None:
 
706
                    # Keep children only if they still exist in the end
 
707
                    if not self._case_sensitive_target:
 
708
                        name = name.lower()
 
709
                    name_ids.append((name, child_tid))
672
710
            name_ids.sort()
673
711
            last_name = None
674
712
            last_trans_id = None
698
736
        return conflicts
699
737
 
700
738
    def _parent_type_conflicts(self, by_parent):
701
 
        """parents must have directory 'contents'."""
 
739
        """Children must have a directory parent"""
702
740
        conflicts = []
703
741
        for parent_id, children in by_parent.iteritems():
704
742
            if parent_id is ROOT_PARENT:
705
743
                continue
706
 
            if not self._any_contents(children):
 
744
            no_children = True
 
745
            for child_id in children:
 
746
                if self.final_kind(child_id) is not None:
 
747
                    no_children = False
 
748
                    break
 
749
            if no_children:
707
750
                continue
 
751
            # There is at least a child, so we need an existing directory to
 
752
            # contain it.
708
753
            kind = self.final_kind(parent_id)
709
754
            if kind is None:
 
755
                # The directory will be deleted
710
756
                conflicts.append(('missing parent', parent_id))
711
757
            elif kind != "directory":
 
758
                # Meh, we need a *directory* to put something in it
712
759
                conflicts.append(('non-directory parent', parent_id))
713
760
        return conflicts
714
761
 
715
 
    def _any_contents(self, trans_ids):
716
 
        """Return true if any of the trans_ids, will have contents."""
717
 
        for trans_id in trans_ids:
718
 
            if self.final_kind(trans_id) is not None:
719
 
                return True
720
 
        return False
721
 
 
722
762
    def _set_executability(self, path, trans_id):
723
763
        """Set the executability of versioned files """
724
764
        if supports_executable():
736
776
                    to_mode |= 0010 & ~umask
737
777
            else:
738
778
                to_mode = current_mode & ~0111
739
 
            os.chmod(abspath, to_mode)
 
779
            osutils.chmod_if_possible(abspath, to_mode)
740
780
 
741
781
    def _new_entry(self, name, parent_id, file_id):
742
782
        """Helper function to create a new filesystem entry."""
746
786
        return trans_id
747
787
 
748
788
    def new_file(self, name, parent_id, contents, file_id=None,
749
 
                 executable=None):
 
789
                 executable=None, sha1=None):
750
790
        """Convenience method to create files.
751
791
 
752
792
        name is the name of the file to create.
759
799
        trans_id = self._new_entry(name, parent_id, file_id)
760
800
        # TODO: rather than scheduling a set_executable call,
761
801
        # have create_file create the file with the right mode.
762
 
        self.create_file(contents, trans_id)
 
802
        self.create_file(contents, trans_id, sha1=sha1)
763
803
        if executable is not None:
764
804
            self.set_executability(executable, trans_id)
765
805
        return trans_id
815
855
        """
816
856
        orphans = []
817
857
        # Find the potential orphans, stop if one item should be kept
818
 
        for c in self.by_parent()[dir_id]:
819
 
            if self.final_file_id(c) is None:
820
 
                orphans.append(c)
 
858
        for child_tid in self.by_parent()[dir_id]:
 
859
            if child_tid in self._removed_contents:
 
860
                # The child is removed as part of the transform. Since it was
 
861
                # versioned before, it's not an orphan
 
862
                continue
 
863
            elif self.final_file_id(child_tid) is None:
 
864
                # The child is not versioned
 
865
                orphans.append(child_tid)
821
866
            else:
822
867
                # We have a versioned file here, searching for orphans is
823
868
                # meaningless.
1142
1187
        self._deletiondir = None
1143
1188
        # A mapping of transform ids to their limbo filename
1144
1189
        self._limbo_files = {}
 
1190
        self._possibly_stale_limbo_files = set()
1145
1191
        # A mapping of transform ids to a set of the transform ids of children
1146
1192
        # that their limbo directory has
1147
1193
        self._limbo_children = {}
1160
1206
        if self._tree is None:
1161
1207
            return
1162
1208
        try:
1163
 
            entries = [(self._limbo_name(t), t, k) for t, k in
1164
 
                       self._new_contents.iteritems()]
1165
 
            entries.sort(reverse=True)
1166
 
            for path, trans_id, kind in entries:
1167
 
                delete_any(path)
 
1209
            limbo_paths = self._limbo_files.values() + list(
 
1210
                self._possibly_stale_limbo_files)
 
1211
            limbo_paths = sorted(limbo_paths, reverse=True)
 
1212
            for path in limbo_paths:
 
1213
                try:
 
1214
                    delete_any(path)
 
1215
                except OSError, e:
 
1216
                    if e.errno != errno.ENOENT:
 
1217
                        raise
 
1218
                    # XXX: warn? perhaps we just got interrupted at an
 
1219
                    # inconvenient moment, but perhaps files are disappearing
 
1220
                    # from under us?
1168
1221
            try:
1169
1222
                delete_any(self._limbodir)
1170
1223
            except OSError:
1219
1272
        entries from _limbo_files, because they are now stale.
1220
1273
        """
1221
1274
        for trans_id in trans_ids:
1222
 
            old_path = self._limbo_files.pop(trans_id)
 
1275
            old_path = self._limbo_files[trans_id]
 
1276
            self._possibly_stale_limbo_files.add(old_path)
 
1277
            del self._limbo_files[trans_id]
1223
1278
            if trans_id not in self._new_contents:
1224
1279
                continue
1225
1280
            new_path = self._limbo_name(trans_id)
1226
1281
            os.rename(old_path, new_path)
 
1282
            self._possibly_stale_limbo_files.remove(old_path)
1227
1283
            for descendant in self._limbo_descendants(trans_id):
1228
1284
                desc_path = self._limbo_files[descendant]
1229
1285
                desc_path = new_path + desc_path[len(old_path):]
1236
1292
            descendants.update(self._limbo_descendants(descendant))
1237
1293
        return descendants
1238
1294
 
1239
 
    def create_file(self, contents, trans_id, mode_id=None):
 
1295
    def create_file(self, contents, trans_id, mode_id=None, sha1=None):
1240
1296
        """Schedule creation of a new file.
1241
1297
 
1242
 
        See also new_file.
1243
 
 
1244
 
        Contents is an iterator of strings, all of which will be written
1245
 
        to the target destination.
1246
 
 
1247
 
        New file takes the permissions of any existing file with that id,
1248
 
        unless mode_id is specified.
 
1298
        :seealso: new_file.
 
1299
 
 
1300
        :param contents: an iterator of strings, all of which will be written
 
1301
            to the target destination.
 
1302
        :param trans_id: TreeTransform handle
 
1303
        :param mode_id: If not None, force the mode of the target file to match
 
1304
            the mode of the object referenced by mode_id.
 
1305
            Otherwise, we will try to preserve mode bits of an existing file.
 
1306
        :param sha1: If the sha1 of this content is already known, pass it in.
 
1307
            We can use it to prevent future sha1 computations.
1249
1308
        """
1250
1309
        name = self._limbo_name(trans_id)
1251
1310
        f = open(name, 'wb')
1252
1311
        try:
1253
 
            try:
1254
 
                unique_add(self._new_contents, trans_id, 'file')
1255
 
            except:
1256
 
                # Clean up the file, it never got registered so
1257
 
                # TreeTransform.finalize() won't clean it up.
1258
 
                f.close()
1259
 
                os.unlink(name)
1260
 
                raise
1261
 
 
 
1312
            unique_add(self._new_contents, trans_id, 'file')
1262
1313
            f.writelines(contents)
1263
1314
        finally:
1264
1315
            f.close()
1265
1316
        self._set_mtime(name)
1266
1317
        self._set_mode(trans_id, mode_id, S_ISREG)
 
1318
        # It is unfortunate we have to use lstat instead of fstat, but we just
 
1319
        # used utime and chmod on the file, so we need the accurate final
 
1320
        # details.
 
1321
        if sha1 is not None:
 
1322
            self._observed_sha1s[trans_id] = (sha1, osutils.lstat(name))
1267
1323
 
1268
1324
    def _read_file_chunks(self, trans_id):
1269
1325
        cur_file = open(self._limbo_name(trans_id), 'rb')
1328
1384
    def cancel_creation(self, trans_id):
1329
1385
        """Cancel the creation of new file contents."""
1330
1386
        del self._new_contents[trans_id]
 
1387
        if trans_id in self._observed_sha1s:
 
1388
            del self._observed_sha1s[trans_id]
1331
1389
        children = self._limbo_children.get(trans_id)
1332
1390
        # if this is a limbo directory with children, move them before removing
1333
1391
        # the directory
1349
1407
        if orphan_policy is None:
1350
1408
            orphan_policy = default_policy
1351
1409
        if orphan_policy not in orphaning_registry:
1352
 
            trace.warning('%s (from %s) is not a known policy, defaulting to %s'
1353
 
                          % (orphan_policy, conf_var_name, default_policy))
 
1410
            trace.warning('%s (from %s) is not a known policy, defaulting '
 
1411
                'to %s' % (orphan_policy, conf_var_name, default_policy))
1354
1412
            orphan_policy = default_policy
1355
1413
        handle_orphan = orphaning_registry.get(orphan_policy)
1356
1414
        handle_orphan(self, trans_id, parent_id)
1497
1555
        try:
1498
1556
            limbodir = urlutils.local_path_from_url(
1499
1557
                tree._transport.abspath('limbo'))
1500
 
            try:
1501
 
                os.mkdir(limbodir)
1502
 
            except OSError, e:
1503
 
                if e.errno == errno.EEXIST:
1504
 
                    raise ExistingLimbo(limbodir)
 
1558
            osutils.ensure_empty_directory_exists(
 
1559
                limbodir,
 
1560
                errors.ExistingLimbo)
1505
1561
            deletiondir = urlutils.local_path_from_url(
1506
1562
                tree._transport.abspath('pending-deletion'))
1507
 
            try:
1508
 
                os.mkdir(deletiondir)
1509
 
            except OSError, e:
1510
 
                if e.errno == errno.EEXIST:
1511
 
                    raise errors.ExistingPendingDeletion(deletiondir)
 
1563
            osutils.ensure_empty_directory_exists(
 
1564
                deletiondir,
 
1565
                errors.ExistingPendingDeletion)
1512
1566
        except:
1513
1567
            tree.unlock()
1514
1568
            raise
1577
1631
            else:
1578
1632
                raise
1579
1633
        if typefunc(mode):
1580
 
            os.chmod(self._limbo_name(trans_id), mode)
 
1634
            osutils.chmod_if_possible(self._limbo_name(trans_id), mode)
1581
1635
 
1582
1636
    def iter_tree_children(self, parent_id):
1583
1637
        """Iterate through the entry's tree children, if any"""
1663
1717
        """
1664
1718
        if not no_conflicts:
1665
1719
            self._check_malformed()
1666
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1720
        child_pb = ui.ui_factory.nested_progress_bar()
1667
1721
        try:
1668
1722
            if precomputed_delta is None:
1669
 
                child_pb.update('Apply phase', 0, 2)
 
1723
                child_pb.update(gettext('Apply phase'), 0, 2)
1670
1724
                inventory_delta = self._generate_inventory_delta()
1671
1725
                offset = 1
1672
1726
            else:
1677
1731
            else:
1678
1732
                mover = _mover
1679
1733
            try:
1680
 
                child_pb.update('Apply phase', 0 + offset, 2 + offset)
 
1734
                child_pb.update(gettext('Apply phase'), 0 + offset, 2 + offset)
1681
1735
                self._apply_removals(mover)
1682
 
                child_pb.update('Apply phase', 1 + offset, 2 + offset)
 
1736
                child_pb.update(gettext('Apply phase'), 1 + offset, 2 + offset)
1683
1737
                modified_paths = self._apply_insertions(mover)
1684
1738
            except:
1685
1739
                mover.rollback()
1688
1742
                mover.apply_deletions()
1689
1743
        finally:
1690
1744
            child_pb.finished()
 
1745
        if self.final_file_id(self.root) is None:
 
1746
            inventory_delta = [e for e in inventory_delta if e[0] != '']
1691
1747
        self._tree.apply_inventory_delta(inventory_delta)
 
1748
        self._apply_observed_sha1s()
1692
1749
        self._done = True
1693
1750
        self.finalize()
1694
1751
        return _TransformResults(modified_paths, self.rename_count)
1696
1753
    def _generate_inventory_delta(self):
1697
1754
        """Generate an inventory delta for the current transform."""
1698
1755
        inventory_delta = []
1699
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1756
        child_pb = ui.ui_factory.nested_progress_bar()
1700
1757
        new_paths = self._inventory_altered()
1701
1758
        total_entries = len(new_paths) + len(self._removed_id)
1702
1759
        try:
1703
1760
            for num, trans_id in enumerate(self._removed_id):
1704
1761
                if (num % 10) == 0:
1705
 
                    child_pb.update('removing file', num, total_entries)
 
1762
                    child_pb.update(gettext('removing file'), num, total_entries)
1706
1763
                if trans_id == self._new_root:
1707
1764
                    file_id = self._tree.get_root_id()
1708
1765
                else:
1720
1777
            final_kinds = {}
1721
1778
            for num, (path, trans_id) in enumerate(new_paths):
1722
1779
                if (num % 10) == 0:
1723
 
                    child_pb.update('adding file',
 
1780
                    child_pb.update(gettext('adding file'),
1724
1781
                                    num + len(self._removed_id), total_entries)
1725
1782
                file_id = new_path_file_ids[trans_id]
1726
1783
                if file_id is None:
1764
1821
        """
1765
1822
        tree_paths = list(self._tree_path_ids.iteritems())
1766
1823
        tree_paths.sort(reverse=True)
1767
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1824
        child_pb = ui.ui_factory.nested_progress_bar()
1768
1825
        try:
1769
 
            for num, data in enumerate(tree_paths):
1770
 
                path, trans_id = data
1771
 
                child_pb.update('removing file', num, len(tree_paths))
 
1826
            for num, (path, trans_id) in enumerate(tree_paths):
 
1827
                # do not attempt to move root into a subdirectory of itself.
 
1828
                if path == '':
 
1829
                    continue
 
1830
                child_pb.update(gettext('removing file'), num, len(tree_paths))
1772
1831
                full_path = self._tree.abspath(path)
1773
1832
                if trans_id in self._removed_contents:
1774
1833
                    delete_path = os.path.join(self._deletiondir, trans_id)
1799
1858
        modified_paths = []
1800
1859
        new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
1801
1860
                                 new_paths)
1802
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1861
        child_pb = ui.ui_factory.nested_progress_bar()
1803
1862
        try:
1804
1863
            for num, (path, trans_id) in enumerate(new_paths):
1805
1864
                if (num % 10) == 0:
1806
 
                    child_pb.update('adding file', num, len(new_paths))
 
1865
                    child_pb.update(gettext('adding file'), num, len(new_paths))
1807
1866
                full_path = self._tree.abspath(path)
1808
1867
                if trans_id in self._needs_rename:
1809
1868
                    try:
1814
1873
                            raise
1815
1874
                    else:
1816
1875
                        self.rename_count += 1
 
1876
                    # TODO: if trans_id in self._observed_sha1s, we should
 
1877
                    #       re-stat the final target, since ctime will be
 
1878
                    #       updated by the change.
1817
1879
                if (trans_id in self._new_contents or
1818
1880
                    self.path_changed(trans_id)):
1819
1881
                    if trans_id in self._new_contents:
1820
1882
                        modified_paths.append(full_path)
1821
1883
                if trans_id in self._new_executability:
1822
1884
                    self._set_executability(path, trans_id)
 
1885
                if trans_id in self._observed_sha1s:
 
1886
                    o_sha1, o_st_val = self._observed_sha1s[trans_id]
 
1887
                    st = osutils.lstat(full_path)
 
1888
                    self._observed_sha1s[trans_id] = (o_sha1, st)
1823
1889
        finally:
1824
1890
            child_pb.finished()
 
1891
        for path, trans_id in new_paths:
 
1892
            # new_paths includes stuff like workingtree conflicts. Only the
 
1893
            # stuff in new_contents actually comes from limbo.
 
1894
            if trans_id in self._limbo_files:
 
1895
                del self._limbo_files[trans_id]
1825
1896
        self._new_contents.clear()
1826
1897
        return modified_paths
1827
1898
 
 
1899
    def _apply_observed_sha1s(self):
 
1900
        """After we have finished renaming everything, update observed sha1s
 
1901
 
 
1902
        This has to be done after self._tree.apply_inventory_delta, otherwise
 
1903
        it doesn't know anything about the files we are updating. Also, we want
 
1904
        to do this as late as possible, so that most entries end up cached.
 
1905
        """
 
1906
        # TODO: this doesn't update the stat information for directories. So
 
1907
        #       the first 'bzr status' will still need to rewrite
 
1908
        #       .bzr/checkout/dirstate. However, we at least don't need to
 
1909
        #       re-read all of the files.
 
1910
        # TODO: If the operation took a while, we could do a time.sleep(3) here
 
1911
        #       to allow the clock to tick over and ensure we won't have any
 
1912
        #       problems. (we could observe start time, and finish time, and if
 
1913
        #       it is less than eg 10% overhead, add a sleep call.)
 
1914
        paths = FinalPaths(self)
 
1915
        for trans_id, observed in self._observed_sha1s.iteritems():
 
1916
            path = paths.get_path(trans_id)
 
1917
            # We could get the file_id, but dirstate prefers to use the path
 
1918
            # anyway, and it is 'cheaper' to determine.
 
1919
            # file_id = self._new_id[trans_id]
 
1920
            self._tree._observed_sha1(None, path, observed)
 
1921
 
1828
1922
 
1829
1923
class TransformPreview(DiskTreeTransform):
1830
1924
    """A TreeTransform for generating preview trees.
1846
1940
        path = self._tree_id_paths.get(trans_id)
1847
1941
        if path is None:
1848
1942
            return None
1849
 
        file_id = self._tree.path2id(path)
1850
 
        try:
1851
 
            return self._tree.kind(file_id)
1852
 
        except errors.NoSuchFile:
1853
 
            return None
 
1943
        kind = self._tree.path_content_summary(path)[0]
 
1944
        if kind == 'missing':
 
1945
            kind = None
 
1946
        return kind
1854
1947
 
1855
1948
    def _set_mode(self, trans_id, mode_id, typefunc):
1856
1949
        """Set the mode of new file contents.
1880
1973
        raise NotImplementedError(self.new_orphan)
1881
1974
 
1882
1975
 
1883
 
class _PreviewTree(tree.Tree):
 
1976
class _PreviewTree(tree.InventoryTree):
1884
1977
    """Partial implementation of Tree to support show_diff_trees"""
1885
1978
 
1886
1979
    def __init__(self, transform):
1915
2008
                yield self._get_repository().revision_tree(revision_id)
1916
2009
 
1917
2010
    def _get_file_revision(self, file_id, vf, tree_revision):
1918
 
        parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
 
2011
        parent_keys = [(file_id, t.get_file_revision(file_id)) for t in
1919
2012
                       self._iter_parent_trees()]
1920
2013
        vf.add_lines((file_id, tree_revision), parent_keys,
1921
2014
                     self.get_file_lines(file_id))
1925
2018
            vf.fallback_versionedfiles.append(base_vf)
1926
2019
        return tree_revision
1927
2020
 
1928
 
    def _stat_limbo_file(self, file_id):
1929
 
        trans_id = self._transform.trans_id_file_id(file_id)
 
2021
    def _stat_limbo_file(self, file_id=None, trans_id=None):
 
2022
        if trans_id is None:
 
2023
            trans_id = self._transform.trans_id_file_id(file_id)
1930
2024
        name = self._transform._limbo_name(trans_id)
1931
2025
        return os.lstat(name)
1932
2026
 
2147
2241
 
2148
2242
    def get_file_size(self, file_id):
2149
2243
        """See Tree.get_file_size"""
 
2244
        trans_id = self._transform.trans_id_file_id(file_id)
 
2245
        kind = self._transform.final_kind(trans_id)
 
2246
        if kind != 'file':
 
2247
            return None
 
2248
        if trans_id in self._transform._new_contents:
 
2249
            return self._stat_limbo_file(trans_id=trans_id).st_size
2150
2250
        if self.kind(file_id) == 'file':
2151
2251
            return self._transform._tree.get_file_size(file_id)
2152
2252
        else:
2153
2253
            return None
2154
2254
 
 
2255
    def get_file_verifier(self, file_id, path=None, stat_value=None):
 
2256
        trans_id = self._transform.trans_id_file_id(file_id)
 
2257
        kind = self._transform._new_contents.get(trans_id)
 
2258
        if kind is None:
 
2259
            return self._transform._tree.get_file_verifier(file_id)
 
2260
        if kind == 'file':
 
2261
            fileobj = self.get_file(file_id)
 
2262
            try:
 
2263
                return ("SHA1", sha_file(fileobj))
 
2264
            finally:
 
2265
                fileobj.close()
 
2266
 
2155
2267
    def get_file_sha1(self, file_id, path=None, stat_value=None):
2156
2268
        trans_id = self._transform.trans_id_file_id(file_id)
2157
2269
        kind = self._transform._new_contents.get(trans_id)
2180
2292
            except errors.NoSuchId:
2181
2293
                return False
2182
2294
 
 
2295
    def has_filename(self, path):
 
2296
        trans_id = self._path2trans_id(path)
 
2297
        if trans_id in self._transform._new_contents:
 
2298
            return True
 
2299
        elif trans_id in self._transform._removed_contents:
 
2300
            return False
 
2301
        else:
 
2302
            return self._transform._tree.has_filename(path)
 
2303
 
2183
2304
    def path_content_summary(self, path):
2184
2305
        trans_id = self._path2trans_id(path)
2185
2306
        tt = self._transform
2273
2394
                                   self.get_file(file_id).readlines(),
2274
2395
                                   default_revision)
2275
2396
 
2276
 
    def get_symlink_target(self, file_id):
 
2397
    def get_symlink_target(self, file_id, path=None):
2277
2398
        """See Tree.get_symlink_target"""
2278
2399
        if not self._content_change(file_id):
2279
2400
            return self._transform._tree.get_symlink_target(file_id)
2417
2538
        if num > 0:  # more than just a root
2418
2539
            raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
2419
2540
    file_trans_id = {}
2420
 
    top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
2541
    top_pb = ui.ui_factory.nested_progress_bar()
2421
2542
    pp = ProgressPhase("Build phase", 2, top_pb)
2422
 
    if tree.inventory.root is not None:
 
2543
    if tree.get_root_id() is not None:
2423
2544
        # This is kind of a hack: we should be altering the root
2424
2545
        # as part of the regular tree shape diff logic.
2425
2546
        # The conditional test here is to avoid doing an
2436
2557
        pp.next_phase()
2437
2558
        file_trans_id[wt.get_root_id()] = \
2438
2559
            tt.trans_id_tree_file_id(wt.get_root_id())
2439
 
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
2560
        pb = ui.ui_factory.nested_progress_bar()
2440
2561
        try:
2441
2562
            deferred_contents = []
2442
2563
            num = 0
2443
 
            total = len(tree.inventory)
 
2564
            total = len(tree.all_file_ids())
2444
2565
            if delta_from_tree:
2445
2566
                precomputed_delta = []
2446
2567
            else:
2455
2576
                for dir, files in wt.walkdirs():
2456
2577
                    existing_files.update(f[0] for f in files)
2457
2578
            for num, (tree_path, entry) in \
2458
 
                enumerate(tree.inventory.iter_entries_by_dir()):
2459
 
                pb.update("Building tree", num - len(deferred_contents), total)
 
2579
                enumerate(tree.iter_entries_by_dir()):
 
2580
                pb.update(gettext("Building tree"), num - len(deferred_contents), total)
2460
2581
                if entry.parent_id is None:
2461
2582
                    continue
2462
2583
                reparent = False
2468
2589
                    kind = file_kind(target_path)
2469
2590
                    if kind == "directory":
2470
2591
                        try:
2471
 
                            bzrdir.BzrDir.open(target_path)
 
2592
                            controldir.ControlDir.open(target_path)
2472
2593
                        except errors.NotBranchError:
2473
2594
                            pass
2474
2595
                        else:
2489
2610
                    executable = tree.is_executable(file_id, tree_path)
2490
2611
                    if executable:
2491
2612
                        tt.set_executability(executable, trans_id)
2492
 
                    trans_data = (trans_id, tree_path)
 
2613
                    trans_data = (trans_id, tree_path, entry.text_sha1)
2493
2614
                    deferred_contents.append((file_id, trans_data))
2494
2615
                else:
2495
2616
                    file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
2511
2632
            precomputed_delta = None
2512
2633
        conflicts = cook_conflicts(raw_conflicts, tt)
2513
2634
        for conflict in conflicts:
2514
 
            warning(conflict)
 
2635
            trace.warning(unicode(conflict))
2515
2636
        try:
2516
2637
            wt.add_conflicts(conflicts)
2517
2638
        except errors.UnsupportedOperation:
2540
2661
        unchanged = dict(unchanged)
2541
2662
        new_desired_files = []
2542
2663
        count = 0
2543
 
        for file_id, (trans_id, tree_path) in desired_files:
 
2664
        for file_id, (trans_id, tree_path, text_sha1) in desired_files:
2544
2665
            accelerator_path = unchanged.get(file_id)
2545
2666
            if accelerator_path is None:
2546
 
                new_desired_files.append((file_id, (trans_id, tree_path)))
 
2667
                new_desired_files.append((file_id,
 
2668
                    (trans_id, tree_path, text_sha1)))
2547
2669
                continue
2548
 
            pb.update('Adding file contents', count + offset, total)
 
2670
            pb.update(gettext('Adding file contents'), count + offset, total)
2549
2671
            if hardlink:
2550
2672
                tt.create_hardlink(accelerator_tree.abspath(accelerator_path),
2551
2673
                                   trans_id)
2556
2678
                    contents = filtered_output_bytes(contents, filters,
2557
2679
                        ContentFilterContext(tree_path, tree))
2558
2680
                try:
2559
 
                    tt.create_file(contents, trans_id)
 
2681
                    tt.create_file(contents, trans_id, sha1=text_sha1)
2560
2682
                finally:
2561
2683
                    try:
2562
2684
                        contents.close()
2565
2687
                        pass
2566
2688
            count += 1
2567
2689
        offset += count
2568
 
    for count, ((trans_id, tree_path), contents) in enumerate(
 
2690
    for count, ((trans_id, tree_path, text_sha1), contents) in enumerate(
2569
2691
            tree.iter_files_bytes(new_desired_files)):
2570
2692
        if wt.supports_content_filtering():
2571
2693
            filters = wt._content_filter_stack(tree_path)
2572
2694
            contents = filtered_output_bytes(contents, filters,
2573
2695
                ContentFilterContext(tree_path, tree))
2574
 
        tt.create_file(contents, trans_id)
2575
 
        pb.update('Adding file contents', count + offset, total)
 
2696
        tt.create_file(contents, trans_id, sha1=text_sha1)
 
2697
        pb.update(gettext('Adding file contents'), count + offset, total)
2576
2698
 
2577
2699
 
2578
2700
def _reparent_children(tt, old_parent, new_parent):
2710
2832
            return new_name
2711
2833
 
2712
2834
 
2713
 
def _entry_changes(file_id, entry, working_tree):
2714
 
    """Determine in which ways the inventory entry has changed.
2715
 
 
2716
 
    Returns booleans: has_contents, content_mod, meta_mod
2717
 
    has_contents means there are currently contents, but they differ
2718
 
    contents_mod means contents need to be modified
2719
 
    meta_mod means the metadata needs to be modified
2720
 
    """
2721
 
    cur_entry = working_tree.inventory[file_id]
2722
 
    try:
2723
 
        working_kind = working_tree.kind(file_id)
2724
 
        has_contents = True
2725
 
    except NoSuchFile:
2726
 
        has_contents = False
2727
 
        contents_mod = True
2728
 
        meta_mod = False
2729
 
    if has_contents is True:
2730
 
        if entry.kind != working_kind:
2731
 
            contents_mod, meta_mod = True, False
2732
 
        else:
2733
 
            cur_entry._read_tree_state(working_tree.id2path(file_id),
2734
 
                                       working_tree)
2735
 
            contents_mod, meta_mod = entry.detect_changes(cur_entry)
2736
 
            cur_entry._forget_tree_state()
2737
 
    return has_contents, contents_mod, meta_mod
2738
 
 
2739
 
 
2740
2835
def revert(working_tree, target_tree, filenames, backups=False,
2741
2836
           pb=None, change_reporter=None):
2742
2837
    """Revert a working tree's contents to those of a target tree."""
2752
2847
                unversioned_filter=working_tree.is_ignored)
2753
2848
            delta.report_changes(tt.iter_changes(), change_reporter)
2754
2849
        for conflict in conflicts:
2755
 
            warning(conflict)
 
2850
            trace.warning(unicode(conflict))
2756
2851
        pp.next_phase()
2757
2852
        tt.apply()
2758
2853
        working_tree.set_merge_modified(merge_modified)
2766
2861
def _prepare_revert_transform(working_tree, target_tree, tt, filenames,
2767
2862
                              backups, pp, basis_tree=None,
2768
2863
                              merge_modified=None):
2769
 
    child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
2864
    child_pb = ui.ui_factory.nested_progress_bar()
2770
2865
    try:
2771
2866
        if merge_modified is None:
2772
2867
            merge_modified = working_tree.merge_modified()
2775
2870
                                      merge_modified, basis_tree)
2776
2871
    finally:
2777
2872
        child_pb.finished()
2778
 
    child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
2873
    child_pb = ui.ui_factory.nested_progress_bar()
2779
2874
    try:
2780
2875
        raw_conflicts = resolve_conflicts(tt, child_pb,
2781
2876
            lambda t, c: conflict_pass(t, c, target_tree))
2789
2884
                 backups, merge_modified, basis_tree=None):
2790
2885
    if basis_tree is not None:
2791
2886
        basis_tree.lock_read()
2792
 
    change_list = target_tree.iter_changes(working_tree,
 
2887
    # We ask the working_tree for its changes relative to the target, rather
 
2888
    # than the target changes relative to the working tree. Because WT4 has an
 
2889
    # optimizer to compare itself to a target, but no optimizer for the
 
2890
    # reverse.
 
2891
    change_list = working_tree.iter_changes(target_tree,
2793
2892
        specific_files=specific_files, pb=pb)
2794
2893
    if target_tree.get_root_id() is None:
2795
2894
        skip_root = True
2799
2898
        deferred_files = []
2800
2899
        for id_num, (file_id, path, changed_content, versioned, parent, name,
2801
2900
                kind, executable) in enumerate(change_list):
2802
 
            if skip_root and file_id[0] is not None and parent[0] is None:
 
2901
            target_path, wt_path = path
 
2902
            target_versioned, wt_versioned = versioned
 
2903
            target_parent, wt_parent = parent
 
2904
            target_name, wt_name = name
 
2905
            target_kind, wt_kind = kind
 
2906
            target_executable, wt_executable = executable
 
2907
            if skip_root and wt_parent is None:
2803
2908
                continue
2804
2909
            trans_id = tt.trans_id_file_id(file_id)
2805
2910
            mode_id = None
2806
2911
            if changed_content:
2807
2912
                keep_content = False
2808
 
                if kind[0] == 'file' and (backups or kind[1] is None):
 
2913
                if wt_kind == 'file' and (backups or target_kind is None):
2809
2914
                    wt_sha1 = working_tree.get_file_sha1(file_id)
2810
2915
                    if merge_modified.get(file_id) != wt_sha1:
2811
2916
                        # acquire the basis tree lazily to prevent the
2814
2919
                        if basis_tree is None:
2815
2920
                            basis_tree = working_tree.basis_tree()
2816
2921
                            basis_tree.lock_read()
2817
 
                        if file_id in basis_tree:
 
2922
                        if basis_tree.has_id(file_id):
2818
2923
                            if wt_sha1 != basis_tree.get_file_sha1(file_id):
2819
2924
                                keep_content = True
2820
 
                        elif kind[1] is None and not versioned[1]:
 
2925
                        elif target_kind is None and not target_versioned:
2821
2926
                            keep_content = True
2822
 
                if kind[0] is not None:
 
2927
                if wt_kind is not None:
2823
2928
                    if not keep_content:
2824
2929
                        tt.delete_contents(trans_id)
2825
 
                    elif kind[1] is not None:
2826
 
                        parent_trans_id = tt.trans_id_file_id(parent[0])
 
2930
                    elif target_kind is not None:
 
2931
                        parent_trans_id = tt.trans_id_file_id(wt_parent)
2827
2932
                        backup_name = tt._available_backup_name(
2828
 
                            name[0], parent_trans_id)
 
2933
                            wt_name, parent_trans_id)
2829
2934
                        tt.adjust_path(backup_name, parent_trans_id, trans_id)
2830
 
                        new_trans_id = tt.create_path(name[0], parent_trans_id)
2831
 
                        if versioned == (True, True):
 
2935
                        new_trans_id = tt.create_path(wt_name, parent_trans_id)
 
2936
                        if wt_versioned and target_versioned:
2832
2937
                            tt.unversion_file(trans_id)
2833
2938
                            tt.version_file(file_id, new_trans_id)
2834
2939
                        # New contents should have the same unix perms as old
2835
2940
                        # contents
2836
2941
                        mode_id = trans_id
2837
2942
                        trans_id = new_trans_id
2838
 
                if kind[1] in ('directory', 'tree-reference'):
 
2943
                if target_kind in ('directory', 'tree-reference'):
2839
2944
                    tt.create_directory(trans_id)
2840
 
                    if kind[1] == 'tree-reference':
 
2945
                    if target_kind == 'tree-reference':
2841
2946
                        revision = target_tree.get_reference_revision(file_id,
2842
 
                                                                      path[1])
 
2947
                                                                      target_path)
2843
2948
                        tt.set_tree_reference(revision, trans_id)
2844
 
                elif kind[1] == 'symlink':
 
2949
                elif target_kind == 'symlink':
2845
2950
                    tt.create_symlink(target_tree.get_symlink_target(file_id),
2846
2951
                                      trans_id)
2847
 
                elif kind[1] == 'file':
 
2952
                elif target_kind == 'file':
2848
2953
                    deferred_files.append((file_id, (trans_id, mode_id)))
2849
2954
                    if basis_tree is None:
2850
2955
                        basis_tree = working_tree.basis_tree()
2851
2956
                        basis_tree.lock_read()
2852
2957
                    new_sha1 = target_tree.get_file_sha1(file_id)
2853
 
                    if (file_id in basis_tree and new_sha1 ==
2854
 
                        basis_tree.get_file_sha1(file_id)):
 
2958
                    if (basis_tree.has_id(file_id) and
 
2959
                        new_sha1 == basis_tree.get_file_sha1(file_id)):
2855
2960
                        if file_id in merge_modified:
2856
2961
                            del merge_modified[file_id]
2857
2962
                    else:
2858
2963
                        merge_modified[file_id] = new_sha1
2859
2964
 
2860
2965
                    # preserve the execute bit when backing up
2861
 
                    if keep_content and executable[0] == executable[1]:
2862
 
                        tt.set_executability(executable[1], trans_id)
2863
 
                elif kind[1] is not None:
2864
 
                    raise AssertionError(kind[1])
2865
 
            if versioned == (False, True):
 
2966
                    if keep_content and wt_executable == target_executable:
 
2967
                        tt.set_executability(target_executable, trans_id)
 
2968
                elif target_kind is not None:
 
2969
                    raise AssertionError(target_kind)
 
2970
            if not wt_versioned and target_versioned:
2866
2971
                tt.version_file(file_id, trans_id)
2867
 
            if versioned == (True, False):
 
2972
            if wt_versioned and not target_versioned:
2868
2973
                tt.unversion_file(trans_id)
2869
 
            if (name[1] is not None and
2870
 
                (name[0] != name[1] or parent[0] != parent[1])):
2871
 
                if name[1] == '' and parent[1] is None:
 
2974
            if (target_name is not None and
 
2975
                (wt_name != target_name or wt_parent != target_parent)):
 
2976
                if target_name == '' and target_parent is None:
2872
2977
                    parent_trans = ROOT_PARENT
2873
2978
                else:
2874
 
                    parent_trans = tt.trans_id_file_id(parent[1])
2875
 
                if parent[0] is None and versioned[0]:
2876
 
                    tt.adjust_root_path(name[1], parent_trans)
 
2979
                    parent_trans = tt.trans_id_file_id(target_parent)
 
2980
                if wt_parent is None and wt_versioned:
 
2981
                    tt.adjust_root_path(target_name, parent_trans)
2877
2982
                else:
2878
 
                    tt.adjust_path(name[1], parent_trans, trans_id)
2879
 
            if executable[0] != executable[1] and kind[1] == "file":
2880
 
                tt.set_executability(executable[1], trans_id)
 
2983
                    tt.adjust_path(target_name, parent_trans, trans_id)
 
2984
            if wt_executable != target_executable and target_kind == "file":
 
2985
                tt.set_executability(target_executable, trans_id)
2881
2986
        if working_tree.supports_content_filtering():
2882
2987
            for index, ((trans_id, mode_id), bytes) in enumerate(
2883
2988
                target_tree.iter_files_bytes(deferred_files)):
2909
3014
    pb = ui.ui_factory.nested_progress_bar()
2910
3015
    try:
2911
3016
        for n in range(10):
2912
 
            pb.update('Resolution pass', n+1, 10)
 
3017
            pb.update(gettext('Resolution pass'), n+1, 10)
2913
3018
            conflicts = tt.find_conflicts()
2914
3019
            if len(conflicts) == 0:
2915
3020
                return new_conflicts
2939
3044
                existing_file, new_file = conflict[2], conflict[1]
2940
3045
            else:
2941
3046
                existing_file, new_file = conflict[1], conflict[2]
2942
 
            new_name = tt.final_name(existing_file)+'.moved'
 
3047
            new_name = tt.final_name(existing_file) + '.moved'
2943
3048
            tt.adjust_path(new_name, final_parent, existing_file)
2944
3049
            new_conflicts.add((c_type, 'Moved existing file to',
2945
3050
                               existing_file, new_file))
2986
3091
                        file_id = tt.final_file_id(trans_id)
2987
3092
                        if file_id is None:
2988
3093
                            file_id = tt.inactive_file_id(trans_id)
2989
 
                        entry = path_tree.inventory[file_id]
 
3094
                        _, entry = path_tree.iter_entries_by_dir(
 
3095
                            [file_id]).next()
2990
3096
                        # special-case the other tree root (move its
2991
3097
                        # children to current root)
2992
3098
                        if entry.parent_id is None:
3007
3113
        elif c_type == 'unversioned parent':
3008
3114
            file_id = tt.inactive_file_id(conflict[1])
3009
3115
            # special-case the other tree root (move its children instead)
3010
 
            if path_tree and file_id in path_tree:
3011
 
                if path_tree.inventory[file_id].parent_id is None:
 
3116
            if path_tree and path_tree.has_id(file_id):
 
3117
                if path_tree.path2id('') == file_id:
 
3118
                    # This is the root entry, skip it
3012
3119
                    continue
3013
3120
            tt.version_file(file_id, conflict[1])
3014
3121
            new_conflicts.add((c_type, 'Versioned directory', conflict[1]))
3030
3137
 
3031
3138
def cook_conflicts(raw_conflicts, tt):
3032
3139
    """Generate a list of cooked conflicts, sorted by file path"""
3033
 
    from bzrlib.conflicts import Conflict
3034
3140
    conflict_iter = iter_cook_conflicts(raw_conflicts, tt)
3035
 
    return sorted(conflict_iter, key=Conflict.sort_key)
 
3141
    return sorted(conflict_iter, key=conflicts.Conflict.sort_key)
3036
3142
 
3037
3143
 
3038
3144
def iter_cook_conflicts(raw_conflicts, tt):
3039
 
    from bzrlib.conflicts import Conflict
3040
3145
    fp = FinalPaths(tt)
3041
3146
    for conflict in raw_conflicts:
3042
3147
        c_type = conflict[0]
3044
3149
        modified_path = fp.get_path(conflict[2])
3045
3150
        modified_id = tt.final_file_id(conflict[2])
3046
3151
        if len(conflict) == 3:
3047
 
            yield Conflict.factory(c_type, action=action, path=modified_path,
3048
 
                                     file_id=modified_id)
 
3152
            yield conflicts.Conflict.factory(
 
3153
                c_type, action=action, path=modified_path, file_id=modified_id)
3049
3154
 
3050
3155
        else:
3051
3156
            conflicting_path = fp.get_path(conflict[3])
3052
3157
            conflicting_id = tt.final_file_id(conflict[3])
3053
 
            yield Conflict.factory(c_type, action=action, path=modified_path,
3054
 
                                   file_id=modified_id,
3055
 
                                   conflict_path=conflicting_path,
3056
 
                                   conflict_file_id=conflicting_id)
 
3158
            yield conflicts.Conflict.factory(
 
3159
                c_type, action=action, path=modified_path,
 
3160
                file_id=modified_id,
 
3161
                conflict_path=conflicting_path,
 
3162
                conflict_file_id=conflicting_id)
3057
3163
 
3058
3164
 
3059
3165
class _FileMover(object):