~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

Use tweaks for subunit protocol client with --parallel=fork as well as --subunit

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
from __future__ import absolute_import
18
 
 
19
17
import os
20
18
import errno
21
19
from stat import S_ISREG, S_IEXEC
22
20
import time
23
21
 
24
22
from bzrlib import (
25
 
    config as _mod_config,
26
23
    errors,
27
24
    lazy_import,
28
25
    registry,
33
30
from bzrlib import (
34
31
    annotate,
35
32
    bencode,
36
 
    controldir,
 
33
    bzrdir,
37
34
    commit,
38
 
    conflicts,
39
35
    delta,
 
36
    errors,
40
37
    inventory,
41
38
    multiparent,
42
39
    osutils,
44
41
    ui,
45
42
    urlutils,
46
43
    )
47
 
from bzrlib.i18n import gettext
48
44
""")
49
 
from bzrlib.errors import (DuplicateKey, MalformedTransform,
 
45
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
50
46
                           ReusingTransform, CantMoveRoot,
51
 
                           ImmortalLimbo, NoFinalPath,
 
47
                           ExistingLimbo, ImmortalLimbo, NoFinalPath,
52
48
                           UnableCreateSymlink)
53
49
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
54
 
from bzrlib.mutabletree import MutableTree
55
50
from bzrlib.osutils import (
56
51
    delete_any,
57
52
    file_kind,
59
54
    pathjoin,
60
55
    sha_file,
61
56
    splitpath,
 
57
    supports_executable,
62
58
    )
63
59
from bzrlib.progress import ProgressPhase
64
60
from bzrlib.symbol_versioning import (
141
137
        # A counter of how many files have been renamed
142
138
        self.rename_count = 0
143
139
 
144
 
    def __enter__(self):
145
 
        """Support Context Manager API."""
146
 
        return self
147
 
 
148
 
    def __exit__(self, exc_type, exc_val, exc_tb):
149
 
        """Support Context Manager API."""
150
 
        self.finalize()
151
 
 
152
140
    def finalize(self):
153
141
        """Release the working tree lock, if held.
154
142
 
157
145
        """
158
146
        if self._tree is None:
159
147
            return
160
 
        for hook in MutableTree.hooks['post_transform']:
161
 
            hook(self._tree, self)
162
148
        self._tree.unlock()
163
149
        self._tree = None
164
150
 
231
217
        This means that the old root trans-id becomes obsolete, so it is
232
218
        recommended only to invoke this after the root trans-id has become
233
219
        irrelevant.
234
 
 
235
220
        """
236
 
        new_roots = [k for k, v in self._new_parent.iteritems() if v ==
 
221
        new_roots = [k for k, v in self._new_parent.iteritems() if v is
237
222
                     ROOT_PARENT]
238
223
        if len(new_roots) < 1:
239
224
            return
243
228
            self._new_root = new_roots[0]
244
229
            return
245
230
        old_new_root = new_roots[0]
 
231
        # TODO: What to do if a old_new_root is present, but self._new_root is
 
232
        #       not listed as being removed? This code explicitly unversions
 
233
        #       the old root and versions it with the new file_id. Though that
 
234
        #       seems like an incomplete delta
 
235
 
246
236
        # unversion the new root's directory.
247
 
        if self.final_kind(self._new_root) is None:
248
 
            file_id = self.final_file_id(old_new_root)
249
 
        else:
250
 
            file_id = self.final_file_id(self._new_root)
 
237
        file_id = self.final_file_id(old_new_root)
251
238
        if old_new_root in self._new_id:
252
239
            self.cancel_versioning(old_new_root)
253
240
        else:
257
244
        if (self.tree_file_id(self._new_root) is not None and
258
245
            self._new_root not in self._removed_id):
259
246
            self.unversion_file(self._new_root)
260
 
        if file_id is not None:
261
 
            self.version_file(file_id, self._new_root)
 
247
        self.version_file(file_id, self._new_root)
262
248
 
263
249
        # Now move children of new root into old root directory.
264
250
        # Ensure all children are registered with the transaction, but don't
398
384
        return sorted(FinalPaths(self).get_paths(new_ids))
399
385
 
400
386
    def _inventory_altered(self):
401
 
        """Determine which trans_ids need new Inventory entries.
402
 
 
403
 
        An new entry is needed when anything that would be reflected by an
404
 
        inventory entry changes, including file name, file_id, parent file_id,
405
 
        file kind, and the execute bit.
406
 
 
407
 
        Some care is taken to return entries with real changes, not cases
408
 
        where the value is deleted and then restored to its original value,
409
 
        but some actually unchanged values may be returned.
410
 
 
411
 
        :returns: A list of (path, trans_id) for all items requiring an
412
 
            inventory change. Ordered by path.
413
 
        """
414
 
        changed_ids = set()
415
 
        # Find entries whose file_ids are new (or changed).
416
 
        new_file_id = set(t for t in self._new_id
417
 
                          if self._new_id[t] != self.tree_file_id(t))
418
 
        for id_set in [self._new_name, self._new_parent, new_file_id,
 
387
        """Get the trans_ids and paths of files needing new inv entries."""
 
388
        new_ids = set()
 
389
        for id_set in [self._new_name, self._new_parent, self._new_id,
419
390
                       self._new_executability]:
420
 
            changed_ids.update(id_set)
421
 
        # removing implies a kind change
 
391
            new_ids.update(id_set)
422
392
        changed_kind = set(self._removed_contents)
423
 
        # so does adding
424
393
        changed_kind.intersection_update(self._new_contents)
425
 
        # Ignore entries that are already known to have changed.
426
 
        changed_kind.difference_update(changed_ids)
427
 
        #  to keep only the truly changed ones
 
394
        changed_kind.difference_update(new_ids)
428
395
        changed_kind = (t for t in changed_kind
429
396
                        if self.tree_kind(t) != self.final_kind(t))
430
 
        # all kind changes will alter the inventory
431
 
        changed_ids.update(changed_kind)
432
 
        # To find entries with changed parent_ids, find parents which existed,
433
 
        # but changed file_id.
434
 
        changed_file_id = set(t for t in new_file_id if t in self._removed_id)
435
 
        # Now add all their children to the set.
436
 
        for parent_trans_id in new_file_id:
437
 
            changed_ids.update(self.iter_tree_children(parent_trans_id))
438
 
        return sorted(FinalPaths(self).get_paths(changed_ids))
 
397
        new_ids.update(changed_kind)
 
398
        return sorted(FinalPaths(self).get_paths(new_ids))
439
399
 
440
400
    def final_kind(self, trans_id):
441
401
        """Determine the final file kind, after any changes applied.
566
526
        for trans_id in self._removed_id:
567
527
            file_id = self.tree_file_id(trans_id)
568
528
            if file_id is not None:
569
 
                if self._tree.stored_kind(file_id) == 'directory':
 
529
                # XXX: This seems like something that should go via a different
 
530
                #      indirection.
 
531
                if self._tree.inventory[file_id].kind == 'directory':
570
532
                    parents.append(trans_id)
571
533
            elif self.tree_kind(trans_id) == 'directory':
572
534
                parents.append(trans_id)
575
537
            # ensure that all children are registered with the transaction
576
538
            list(self.iter_tree_children(parent_id))
577
539
 
 
540
    @deprecated_method(deprecated_in((2, 3, 0)))
 
541
    def has_named_child(self, by_parent, parent_id, name):
 
542
        return self._has_named_child(
 
543
            name, parent_id, known_children=by_parent.get(parent_id, []))
 
544
 
578
545
    def _has_named_child(self, name, parent_id, known_children):
579
546
        """Does a parent already have a name child.
580
547
 
624
591
        for trans_id in self._new_parent:
625
592
            seen = set()
626
593
            parent_id = trans_id
627
 
            while parent_id != ROOT_PARENT:
 
594
            while parent_id is not ROOT_PARENT:
628
595
                seen.add(parent_id)
629
596
                try:
630
597
                    parent_id = self.final_parent(parent_id)
640
607
        """If parent directories are versioned, children must be versioned."""
641
608
        conflicts = []
642
609
        for parent_id, children in by_parent.iteritems():
643
 
            if parent_id == ROOT_PARENT:
 
610
            if parent_id is ROOT_PARENT:
644
611
                continue
645
612
            if self.final_file_id(parent_id) is not None:
646
613
                continue
739
706
        """Children must have a directory parent"""
740
707
        conflicts = []
741
708
        for parent_id, children in by_parent.iteritems():
742
 
            if parent_id == ROOT_PARENT:
 
709
            if parent_id is ROOT_PARENT:
743
710
                continue
744
711
            no_children = True
745
712
            for child_id in children:
761
728
 
762
729
    def _set_executability(self, path, trans_id):
763
730
        """Set the executability of versioned files """
764
 
        if self._tree._supports_executable():
 
731
        if supports_executable():
765
732
            new_executability = self._new_executability[trans_id]
766
733
            abspath = self._tree.abspath(path)
767
734
            current_mode = os.stat(abspath).st_mode
776
743
                    to_mode |= 0010 & ~umask
777
744
            else:
778
745
                to_mode = current_mode & ~0111
779
 
            osutils.chmod_if_possible(abspath, to_mode)
 
746
            os.chmod(abspath, to_mode)
780
747
 
781
748
    def _new_entry(self, name, parent_id, file_id):
782
749
        """Helper function to create a new filesystem entry."""
1187
1154
        self._deletiondir = None
1188
1155
        # A mapping of transform ids to their limbo filename
1189
1156
        self._limbo_files = {}
1190
 
        self._possibly_stale_limbo_files = set()
1191
1157
        # A mapping of transform ids to a set of the transform ids of children
1192
1158
        # that their limbo directory has
1193
1159
        self._limbo_children = {}
1206
1172
        if self._tree is None:
1207
1173
            return
1208
1174
        try:
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?
 
1175
            entries = [(self._limbo_name(t), t, k) for t, k in
 
1176
                       self._new_contents.iteritems()]
 
1177
            entries.sort(reverse=True)
 
1178
            for path, trans_id, kind in entries:
 
1179
                delete_any(path)
1221
1180
            try:
1222
1181
                delete_any(self._limbodir)
1223
1182
            except OSError:
1231
1190
        finally:
1232
1191
            TreeTransformBase.finalize(self)
1233
1192
 
1234
 
    def _limbo_supports_executable(self):
1235
 
        """Check if the limbo path supports the executable bit."""
1236
 
        # FIXME: Check actual file system capabilities of limbodir
1237
 
        return osutils.supports_executable()
1238
 
 
1239
1193
    def _limbo_name(self, trans_id):
1240
1194
        """Generate the limbo name of a file"""
1241
1195
        limbo_name = self._limbo_files.get(trans_id)
1277
1231
        entries from _limbo_files, because they are now stale.
1278
1232
        """
1279
1233
        for trans_id in trans_ids:
1280
 
            old_path = self._limbo_files[trans_id]
1281
 
            self._possibly_stale_limbo_files.add(old_path)
1282
 
            del self._limbo_files[trans_id]
 
1234
            old_path = self._limbo_files.pop(trans_id)
1283
1235
            if trans_id not in self._new_contents:
1284
1236
                continue
1285
1237
            new_path = self._limbo_name(trans_id)
1286
1238
            os.rename(old_path, new_path)
1287
 
            self._possibly_stale_limbo_files.remove(old_path)
1288
1239
            for descendant in self._limbo_descendants(trans_id):
1289
1240
                desc_path = self._limbo_files[descendant]
1290
1241
                desc_path = new_path + desc_path[len(old_path):]
1314
1265
        name = self._limbo_name(trans_id)
1315
1266
        f = open(name, 'wb')
1316
1267
        try:
1317
 
            unique_add(self._new_contents, trans_id, 'file')
 
1268
            try:
 
1269
                unique_add(self._new_contents, trans_id, 'file')
 
1270
            except:
 
1271
                # Clean up the file, it never got registered so
 
1272
                # TreeTransform.finalize() won't clean it up.
 
1273
                f.close()
 
1274
                os.unlink(name)
 
1275
                raise
1318
1276
            f.writelines(contents)
1319
1277
        finally:
1320
1278
            f.close()
1401
1359
        delete_any(self._limbo_name(trans_id))
1402
1360
 
1403
1361
    def new_orphan(self, trans_id, parent_id):
1404
 
        conf = self._tree.get_config_stack()
1405
 
        handle_orphan = conf.get('bzr.transform.orphan_policy')
 
1362
        # FIXME: There is no tree config, so we use the branch one (it's weird
 
1363
        # to define it this way as orphaning can only occur in a working tree,
 
1364
        # but that's all we have (for now). It will find the option in
 
1365
        # locations.conf or bazaar.conf though) -- vila 20100916
 
1366
        conf = self._tree.branch.get_config()
 
1367
        conf_var_name = 'bzr.transform.orphan_policy'
 
1368
        orphan_policy = conf.get_user_option(conf_var_name)
 
1369
        default_policy = orphaning_registry.default_key
 
1370
        if orphan_policy is None:
 
1371
            orphan_policy = default_policy
 
1372
        if orphan_policy not in orphaning_registry:
 
1373
            trace.warning('%s (from %s) is not a known policy, defaulting '
 
1374
                'to %s' % (orphan_policy, conf_var_name, default_policy))
 
1375
            orphan_policy = default_policy
 
1376
        handle_orphan = orphaning_registry.get(orphan_policy)
1406
1377
        handle_orphan(self, trans_id, parent_id)
1407
1378
 
1408
1379
 
1471
1442
orphaning_registry._set_default_key('conflict')
1472
1443
 
1473
1444
 
1474
 
opt_transform_orphan = _mod_config.RegistryOption(
1475
 
    'bzr.transform.orphan_policy', orphaning_registry,
1476
 
    help='Policy for orphaned files during transform operations.',
1477
 
    invalid='warning')
1478
 
 
1479
 
 
1480
1445
class TreeTransform(DiskTreeTransform):
1481
1446
    """Represent a tree transformation.
1482
1447
 
1553
1518
        try:
1554
1519
            limbodir = urlutils.local_path_from_url(
1555
1520
                tree._transport.abspath('limbo'))
1556
 
            osutils.ensure_empty_directory_exists(
1557
 
                limbodir,
1558
 
                errors.ExistingLimbo)
 
1521
            try:
 
1522
                os.mkdir(limbodir)
 
1523
            except OSError, e:
 
1524
                if e.errno == errno.EEXIST:
 
1525
                    raise ExistingLimbo(limbodir)
1559
1526
            deletiondir = urlutils.local_path_from_url(
1560
1527
                tree._transport.abspath('pending-deletion'))
1561
 
            osutils.ensure_empty_directory_exists(
1562
 
                deletiondir,
1563
 
                errors.ExistingPendingDeletion)
 
1528
            try:
 
1529
                os.mkdir(deletiondir)
 
1530
            except OSError, e:
 
1531
                if e.errno == errno.EEXIST:
 
1532
                    raise errors.ExistingPendingDeletion(deletiondir)
1564
1533
        except:
1565
1534
            tree.unlock()
1566
1535
            raise
1629
1598
            else:
1630
1599
                raise
1631
1600
        if typefunc(mode):
1632
 
            osutils.chmod_if_possible(self._limbo_name(trans_id), mode)
 
1601
            os.chmod(self._limbo_name(trans_id), mode)
1633
1602
 
1634
1603
    def iter_tree_children(self, parent_id):
1635
1604
        """Iterate through the entry's tree children, if any"""
1713
1682
            calculating one.
1714
1683
        :param _mover: Supply an alternate FileMover, for testing
1715
1684
        """
1716
 
        for hook in MutableTree.hooks['pre_transform']:
1717
 
            hook(self._tree, self)
1718
1685
        if not no_conflicts:
1719
1686
            self._check_malformed()
1720
1687
        child_pb = ui.ui_factory.nested_progress_bar()
1721
1688
        try:
1722
1689
            if precomputed_delta is None:
1723
 
                child_pb.update(gettext('Apply phase'), 0, 2)
 
1690
                child_pb.update('Apply phase', 0, 2)
1724
1691
                inventory_delta = self._generate_inventory_delta()
1725
1692
                offset = 1
1726
1693
            else:
1731
1698
            else:
1732
1699
                mover = _mover
1733
1700
            try:
1734
 
                child_pb.update(gettext('Apply phase'), 0 + offset, 2 + offset)
 
1701
                child_pb.update('Apply phase', 0 + offset, 2 + offset)
1735
1702
                self._apply_removals(mover)
1736
 
                child_pb.update(gettext('Apply phase'), 1 + offset, 2 + offset)
 
1703
                child_pb.update('Apply phase', 1 + offset, 2 + offset)
1737
1704
                modified_paths = self._apply_insertions(mover)
1738
1705
            except:
1739
1706
                mover.rollback()
1742
1709
                mover.apply_deletions()
1743
1710
        finally:
1744
1711
            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] != '']
1747
1712
        self._tree.apply_inventory_delta(inventory_delta)
1748
1713
        self._apply_observed_sha1s()
1749
1714
        self._done = True
1759
1724
        try:
1760
1725
            for num, trans_id in enumerate(self._removed_id):
1761
1726
                if (num % 10) == 0:
1762
 
                    child_pb.update(gettext('removing file'), num, total_entries)
 
1727
                    child_pb.update('removing file', num, total_entries)
1763
1728
                if trans_id == self._new_root:
1764
1729
                    file_id = self._tree.get_root_id()
1765
1730
                else:
1777
1742
            final_kinds = {}
1778
1743
            for num, (path, trans_id) in enumerate(new_paths):
1779
1744
                if (num % 10) == 0:
1780
 
                    child_pb.update(gettext('adding file'),
 
1745
                    child_pb.update('adding file',
1781
1746
                                    num + len(self._removed_id), total_entries)
1782
1747
                file_id = new_path_file_ids[trans_id]
1783
1748
                if file_id is None:
1823
1788
        tree_paths.sort(reverse=True)
1824
1789
        child_pb = ui.ui_factory.nested_progress_bar()
1825
1790
        try:
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))
 
1791
            for num, data in enumerate(tree_paths):
 
1792
                path, trans_id = data
 
1793
                child_pb.update('removing file', num, len(tree_paths))
1831
1794
                full_path = self._tree.abspath(path)
1832
1795
                if trans_id in self._removed_contents:
1833
1796
                    delete_path = os.path.join(self._deletiondir, trans_id)
1862
1825
        try:
1863
1826
            for num, (path, trans_id) in enumerate(new_paths):
1864
1827
                if (num % 10) == 0:
1865
 
                    child_pb.update(gettext('adding file'), num, len(new_paths))
 
1828
                    child_pb.update('adding file', num, len(new_paths))
1866
1829
                full_path = self._tree.abspath(path)
1867
1830
                if trans_id in self._needs_rename:
1868
1831
                    try:
1888
1851
                    self._observed_sha1s[trans_id] = (o_sha1, st)
1889
1852
        finally:
1890
1853
            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]
1896
1854
        self._new_contents.clear()
1897
1855
        return modified_paths
1898
1856
 
2051
2009
        pass
2052
2010
 
2053
2011
    @property
2054
 
    @deprecated_method(deprecated_in((2, 5, 0)))
2055
2012
    def inventory(self):
2056
2013
        """This Tree does not use inventory as its backing data."""
2057
2014
        raise NotImplementedError(_PreviewTree.inventory)
2058
2015
 
2059
 
    @property
2060
 
    def root_inventory(self):
2061
 
        """This Tree does not use inventory as its backing data."""
2062
 
        raise NotImplementedError(_PreviewTree.root_inventory)
2063
 
 
2064
2016
    def get_root_id(self):
2065
2017
        return self._transform.final_file_id(self._transform.root)
2066
2018
 
2112
2064
        return cur_parent
2113
2065
 
2114
2066
    def path2id(self, path):
2115
 
        if isinstance(path, list):
2116
 
            if path == []:
2117
 
                path = [""]
2118
 
            path = osutils.pathjoin(*path)
2119
2067
        return self._transform.final_file_id(self._path2trans_id(path))
2120
2068
 
2121
2069
    def id2path(self, file_id):
2182
2130
                ordered_ids.append((trans_id, parent_file_id))
2183
2131
        return ordered_ids
2184
2132
 
2185
 
    def iter_child_entries(self, file_id, path=None):
2186
 
        self.id2path(file_id)
2187
 
        trans_id = self._transform.trans_id_file_id(file_id)
2188
 
        todo = [(child_trans_id, trans_id) for child_trans_id in
2189
 
                self._all_children(trans_id)]
2190
 
        for entry, trans_id in self._make_inv_entries(todo):
2191
 
            yield entry
2192
 
 
2193
2133
    def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
2194
2134
        # This may not be a maximally efficient implementation, but it is
2195
2135
        # reasonably straightforward.  An implementation that grafts the
2270
2210
        else:
2271
2211
            return None
2272
2212
 
2273
 
    def get_file_verifier(self, file_id, path=None, stat_value=None):
2274
 
        trans_id = self._transform.trans_id_file_id(file_id)
2275
 
        kind = self._transform._new_contents.get(trans_id)
2276
 
        if kind is None:
2277
 
            return self._transform._tree.get_file_verifier(file_id)
2278
 
        if kind == 'file':
2279
 
            fileobj = self.get_file(file_id)
2280
 
            try:
2281
 
                return ("SHA1", sha_file(fileobj))
2282
 
            finally:
2283
 
                fileobj.close()
2284
 
 
2285
2213
    def get_file_sha1(self, file_id, path=None, stat_value=None):
2286
2214
        trans_id = self._transform.trans_id_file_id(file_id)
2287
2215
        kind = self._transform._new_contents.get(trans_id)
2337
2265
            if kind == 'file':
2338
2266
                statval = os.lstat(limbo_name)
2339
2267
                size = statval.st_size
2340
 
                if not tt._limbo_supports_executable():
 
2268
                if not supports_executable():
2341
2269
                    executable = False
2342
2270
                else:
2343
2271
                    executable = statval.st_mode & S_IEXEC
2558
2486
    file_trans_id = {}
2559
2487
    top_pb = ui.ui_factory.nested_progress_bar()
2560
2488
    pp = ProgressPhase("Build phase", 2, top_pb)
2561
 
    if tree.get_root_id() is not None:
 
2489
    if tree.inventory.root is not None:
2562
2490
        # This is kind of a hack: we should be altering the root
2563
2491
        # as part of the regular tree shape diff logic.
2564
2492
        # The conditional test here is to avoid doing an
2579
2507
        try:
2580
2508
            deferred_contents = []
2581
2509
            num = 0
2582
 
            total = len(tree.all_file_ids())
 
2510
            total = len(tree.inventory)
2583
2511
            if delta_from_tree:
2584
2512
                precomputed_delta = []
2585
2513
            else:
2594
2522
                for dir, files in wt.walkdirs():
2595
2523
                    existing_files.update(f[0] for f in files)
2596
2524
            for num, (tree_path, entry) in \
2597
 
                enumerate(tree.iter_entries_by_dir()):
2598
 
                pb.update(gettext("Building tree"), num - len(deferred_contents), total)
 
2525
                enumerate(tree.inventory.iter_entries_by_dir()):
 
2526
                pb.update("Building tree", num - len(deferred_contents), total)
2599
2527
                if entry.parent_id is None:
2600
2528
                    continue
2601
2529
                reparent = False
2607
2535
                    kind = file_kind(target_path)
2608
2536
                    if kind == "directory":
2609
2537
                        try:
2610
 
                            controldir.ControlDir.open(target_path)
 
2538
                            bzrdir.BzrDir.open(target_path)
2611
2539
                        except errors.NotBranchError:
2612
2540
                            pass
2613
2541
                        else:
2685
2613
                new_desired_files.append((file_id,
2686
2614
                    (trans_id, tree_path, text_sha1)))
2687
2615
                continue
2688
 
            pb.update(gettext('Adding file contents'), count + offset, total)
 
2616
            pb.update('Adding file contents', count + offset, total)
2689
2617
            if hardlink:
2690
2618
                tt.create_hardlink(accelerator_tree.abspath(accelerator_path),
2691
2619
                                   trans_id)
2712
2640
            contents = filtered_output_bytes(contents, filters,
2713
2641
                ContentFilterContext(tree_path, tree))
2714
2642
        tt.create_file(contents, trans_id, sha1=text_sha1)
2715
 
        pb.update(gettext('Adding file contents'), count + offset, total)
 
2643
        pb.update('Adding file contents', count + offset, total)
2716
2644
 
2717
2645
 
2718
2646
def _reparent_children(tt, old_parent, new_parent):
2832
2760
        tt.set_executability(entry.executable, trans_id)
2833
2761
 
2834
2762
 
 
2763
@deprecated_function(deprecated_in((2, 3, 0)))
 
2764
def get_backup_name(entry, by_parent, parent_trans_id, tt):
 
2765
    return _get_backup_name(entry.name, by_parent, parent_trans_id, tt)
 
2766
 
 
2767
 
 
2768
@deprecated_function(deprecated_in((2, 3, 0)))
 
2769
def _get_backup_name(name, by_parent, parent_trans_id, tt):
 
2770
    """Produce a backup-style name that appears to be available"""
 
2771
    def name_gen():
 
2772
        counter = 1
 
2773
        while True:
 
2774
            yield "%s.~%d~" % (name, counter)
 
2775
            counter += 1
 
2776
    for new_name in name_gen():
 
2777
        if not tt.has_named_child(by_parent, parent_trans_id, new_name):
 
2778
            return new_name
 
2779
 
 
2780
 
 
2781
def _entry_changes(file_id, entry, working_tree):
 
2782
    """Determine in which ways the inventory entry has changed.
 
2783
 
 
2784
    Returns booleans: has_contents, content_mod, meta_mod
 
2785
    has_contents means there are currently contents, but they differ
 
2786
    contents_mod means contents need to be modified
 
2787
    meta_mod means the metadata needs to be modified
 
2788
    """
 
2789
    cur_entry = working_tree.inventory[file_id]
 
2790
    try:
 
2791
        working_kind = working_tree.kind(file_id)
 
2792
        has_contents = True
 
2793
    except NoSuchFile:
 
2794
        has_contents = False
 
2795
        contents_mod = True
 
2796
        meta_mod = False
 
2797
    if has_contents is True:
 
2798
        if entry.kind != working_kind:
 
2799
            contents_mod, meta_mod = True, False
 
2800
        else:
 
2801
            cur_entry._read_tree_state(working_tree.id2path(file_id),
 
2802
                                       working_tree)
 
2803
            contents_mod, meta_mod = entry.detect_changes(cur_entry)
 
2804
            cur_entry._forget_tree_state()
 
2805
    return has_contents, contents_mod, meta_mod
 
2806
 
 
2807
 
2835
2808
def revert(working_tree, target_tree, filenames, backups=False,
2836
2809
           pb=None, change_reporter=None):
2837
2810
    """Revert a working tree's contents to those of a target tree."""
2919
2892
                        if basis_tree is None:
2920
2893
                            basis_tree = working_tree.basis_tree()
2921
2894
                            basis_tree.lock_read()
2922
 
                        if basis_tree.has_id(file_id):
 
2895
                        if file_id in basis_tree:
2923
2896
                            if wt_sha1 != basis_tree.get_file_sha1(file_id):
2924
2897
                                keep_content = True
2925
2898
                        elif target_kind is None and not target_versioned:
2955
2928
                        basis_tree = working_tree.basis_tree()
2956
2929
                        basis_tree.lock_read()
2957
2930
                    new_sha1 = target_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)):
 
2931
                    if (file_id in basis_tree and new_sha1 ==
 
2932
                        basis_tree.get_file_sha1(file_id)):
2960
2933
                        if file_id in merge_modified:
2961
2934
                            del merge_modified[file_id]
2962
2935
                    else:
3014
2987
    pb = ui.ui_factory.nested_progress_bar()
3015
2988
    try:
3016
2989
        for n in range(10):
3017
 
            pb.update(gettext('Resolution pass'), n+1, 10)
 
2990
            pb.update('Resolution pass', n+1, 10)
3018
2991
            conflicts = tt.find_conflicts()
3019
2992
            if len(conflicts) == 0:
3020
2993
                return new_conflicts
3044
3017
                existing_file, new_file = conflict[2], conflict[1]
3045
3018
            else:
3046
3019
                existing_file, new_file = conflict[1], conflict[2]
3047
 
            new_name = tt.final_name(existing_file) + '.moved'
 
3020
            new_name = tt.final_name(existing_file)+'.moved'
3048
3021
            tt.adjust_path(new_name, final_parent, existing_file)
3049
3022
            new_conflicts.add((c_type, 'Moved existing file to',
3050
3023
                               existing_file, new_file))
3113
3086
        elif c_type == 'unversioned parent':
3114
3087
            file_id = tt.inactive_file_id(conflict[1])
3115
3088
            # special-case the other tree root (move its children instead)
3116
 
            if path_tree and path_tree.has_id(file_id):
 
3089
            if path_tree and file_id in path_tree:
3117
3090
                if path_tree.path2id('') == file_id:
3118
3091
                    # This is the root entry, skip it
3119
3092
                    continue
3137
3110
 
3138
3111
def cook_conflicts(raw_conflicts, tt):
3139
3112
    """Generate a list of cooked conflicts, sorted by file path"""
 
3113
    from bzrlib.conflicts import Conflict
3140
3114
    conflict_iter = iter_cook_conflicts(raw_conflicts, tt)
3141
 
    return sorted(conflict_iter, key=conflicts.Conflict.sort_key)
 
3115
    return sorted(conflict_iter, key=Conflict.sort_key)
3142
3116
 
3143
3117
 
3144
3118
def iter_cook_conflicts(raw_conflicts, tt):
 
3119
    from bzrlib.conflicts import Conflict
3145
3120
    fp = FinalPaths(tt)
3146
3121
    for conflict in raw_conflicts:
3147
3122
        c_type = conflict[0]
3149
3124
        modified_path = fp.get_path(conflict[2])
3150
3125
        modified_id = tt.final_file_id(conflict[2])
3151
3126
        if len(conflict) == 3:
3152
 
            yield conflicts.Conflict.factory(
3153
 
                c_type, action=action, path=modified_path, file_id=modified_id)
 
3127
            yield Conflict.factory(c_type, action=action, path=modified_path,
 
3128
                                     file_id=modified_id)
3154
3129
 
3155
3130
        else:
3156
3131
            conflicting_path = fp.get_path(conflict[3])
3157
3132
            conflicting_id = tt.final_file_id(conflict[3])
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)
 
3133
            yield Conflict.factory(c_type, action=action, path=modified_path,
 
3134
                                   file_id=modified_id,
 
3135
                                   conflict_path=conflicting_path,
 
3136
                                   conflict_file_id=conflicting_id)
3163
3137
 
3164
3138
 
3165
3139
class _FileMover(object):