~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

merge bzr.dev and tweaks to lower threads

Show diffs side-by-side

added added

removed removed

Lines of Context:
450
450
        del self._new_id[trans_id]
451
451
        del self._r_new_id[file_id]
452
452
 
453
 
    def new_paths(self):
454
 
        """Determine the paths of all new and changed files"""
 
453
    def new_paths(self, filesystem_only=False):
 
454
        """Determine the paths of all new and changed files.
 
455
 
 
456
        :param filesystem_only: if True, only calculate values for files
 
457
            that require renames or execute bit changes.
 
458
        """
455
459
        new_ids = set()
456
 
        fp = FinalPaths(self)
457
 
        for id_set in (self._new_name, self._new_parent, self._new_contents,
458
 
                       self._new_id, self._new_executability):
 
460
        if filesystem_only:
 
461
            id_sets = (self._needs_rename, self._new_executability)
 
462
        else:
 
463
            id_sets = (self._new_name, self._new_parent, self._new_contents,
 
464
                       self._new_id, self._new_executability)
 
465
        for id_set in id_sets:
459
466
            new_ids.update(id_set)
460
 
        new_paths = [(fp.get_path(t), t) for t in new_ids]
461
 
        new_paths.sort()
462
 
        return new_paths
 
467
        return sorted(FinalPaths(self).get_paths(new_ids))
463
468
 
464
469
    def tree_kind(self, trans_id):
465
470
        """Determine the file kind in the working tree.
856
861
    def _set_executability(self, path, entry, trans_id):
857
862
        """Set the executability of versioned files """
858
863
        new_executability = self._new_executability[trans_id]
859
 
        entry.executable = new_executability
 
864
        if entry is not None:
 
865
            entry.executable = new_executability
860
866
        if supports_executable():
861
867
            abspath = self._tree.abspath(path)
862
868
            current_mode = os.stat(abspath).st_mode
1167
1173
                                   tree.case_sensitive)
1168
1174
        self._deletiondir = deletiondir
1169
1175
 
1170
 
    def apply(self, no_conflicts=False, _mover=None):
 
1176
    def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
1171
1177
        """Apply all changes to the inventory and filesystem.
1172
1178
 
1173
1179
        If filesystem or inventory conflicts are present, MalformedTransform
1177
1183
 
1178
1184
        :param no_conflicts: if True, the caller guarantees there are no
1179
1185
            conflicts, so no check is made.
 
1186
        :param precomputed_delta: An inventory delta to use instead of
 
1187
            calculating one.
1180
1188
        :param _mover: Supply an alternate FileMover, for testing
1181
1189
        """
1182
1190
        if not no_conflicts:
1183
1191
            conflicts = self.find_conflicts()
1184
1192
            if len(conflicts) != 0:
1185
1193
                raise MalformedTransform(conflicts=conflicts)
1186
 
        inventory_delta = []
 
1194
        if precomputed_delta is None:
 
1195
            new_inventory_delta = []
 
1196
            inventory_delta = new_inventory_delta
 
1197
        else:
 
1198
            new_inventory_delta = None
 
1199
            inventory_delta = precomputed_delta
1187
1200
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1188
1201
        try:
1189
1202
            if _mover is None:
1192
1205
                mover = _mover
1193
1206
            try:
1194
1207
                child_pb.update('Apply phase', 0, 2)
1195
 
                self._apply_removals(inventory_delta, mover)
 
1208
                self._apply_removals(new_inventory_delta, mover)
1196
1209
                child_pb.update('Apply phase', 1, 2)
1197
 
                modified_paths = self._apply_insertions(inventory_delta, mover)
 
1210
                modified_paths = self._apply_insertions(new_inventory_delta,
 
1211
                                                        mover)
1198
1212
            except:
1199
1213
                mover.rollback()
1200
1214
                raise
1213
1227
        That is, delete files that are to be deleted, and put any files that
1214
1228
        need renaming into limbo.  This must be done in strict child-to-parent
1215
1229
        order.
 
1230
 
 
1231
        If inventory_delta is None, no inventory delta generation is performed.
1216
1232
        """
1217
1233
        tree_paths = list(self._tree_path_ids.iteritems())
1218
1234
        tree_paths.sort(reverse=True)
1234
1250
                            raise
1235
1251
                    else:
1236
1252
                        self.rename_count += 1
1237
 
                if trans_id in self._removed_id:
 
1253
                if (trans_id in self._removed_id
 
1254
                    and inventory_delta is not None):
1238
1255
                    if trans_id == self._new_root:
1239
1256
                        file_id = self._tree.get_root_id()
1240
1257
                    else:
1252
1269
        That is, create any files that need to be created, and restore from
1253
1270
        limbo any files that needed renaming.  This must be done in strict
1254
1271
        parent-to-child order.
 
1272
 
 
1273
        If inventory_delta is None, no inventory delta is calculated, and
 
1274
        no list of modified paths is returned.
1255
1275
        """
1256
 
        new_paths = self.new_paths()
 
1276
        new_paths = self.new_paths(filesystem_only=(inventory_delta is None))
1257
1277
        modified_paths = []
1258
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1259
1278
        completed_new = []
 
1279
        new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
 
1280
                                 new_paths)
 
1281
        if inventory_delta is not None:
 
1282
            entries = self._tree.iter_entries_by_dir(
 
1283
                new_path_file_ids.values())
 
1284
            old_paths = dict((e.file_id, p) for p, e in entries)
 
1285
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1260
1286
        try:
1261
1287
            for num, (path, trans_id) in enumerate(new_paths):
1262
1288
                new_entry = None
1263
 
                child_pb.update('adding file', num, len(new_paths))
1264
 
                if trans_id in self._new_contents or \
1265
 
                    self.path_changed(trans_id):
1266
 
                    full_path = self._tree.abspath(path)
1267
 
                    if trans_id in self._needs_rename:
 
1289
                if (num % 10) == 0:
 
1290
                    child_pb.update('adding file', num, len(new_paths))
 
1291
                full_path = self._tree.abspath(path)
 
1292
                if trans_id in self._needs_rename:
 
1293
                    try:
 
1294
                        mover.rename(self._limbo_name(trans_id), full_path)
 
1295
                    except OSError, e:
 
1296
                        # We may be renaming a dangling inventory id
 
1297
                        if e.errno != errno.ENOENT:
 
1298
                            raise
 
1299
                    else:
 
1300
                        self.rename_count += 1
 
1301
                if inventory_delta is not None:
 
1302
                    if (trans_id in self._new_contents or
 
1303
                        self.path_changed(trans_id)):
 
1304
                        if trans_id in self._new_contents:
 
1305
                            modified_paths.append(full_path)
 
1306
                            completed_new.append(trans_id)
 
1307
                    file_id = new_path_file_ids[trans_id]
 
1308
                    if file_id is not None and (trans_id in self._new_id or
 
1309
                        trans_id in self._new_name or
 
1310
                        trans_id in self._new_parent
 
1311
                        or trans_id in self._new_executability):
1268
1312
                        try:
1269
 
                            mover.rename(self._limbo_name(trans_id), full_path)
1270
 
                        except OSError, e:
1271
 
                            # We may be renaming a dangling inventory id
1272
 
                            if e.errno != errno.ENOENT:
1273
 
                                raise
 
1313
                            kind = self.final_kind(trans_id)
 
1314
                        except NoSuchFile:
 
1315
                            kind = self._tree.stored_kind(file_id)
 
1316
                        parent_trans_id = self.final_parent(trans_id)
 
1317
                        parent_file_id = new_path_file_ids.get(parent_trans_id)
 
1318
                        if parent_file_id is None:
 
1319
                            parent_file_id = self.final_file_id(
 
1320
                                parent_trans_id)
 
1321
                        if trans_id in self._new_reference_revision:
 
1322
                            new_entry = inventory.TreeReference(
 
1323
                                file_id,
 
1324
                                self._new_name[trans_id],
 
1325
                                self.final_file_id(self._new_parent[trans_id]),
 
1326
                                None, self._new_reference_revision[trans_id])
1274
1327
                        else:
1275
 
                            self.rename_count += 1
1276
 
                    if trans_id in self._new_contents:
1277
 
                        modified_paths.append(full_path)
1278
 
                        completed_new.append(trans_id)
1279
 
                file_id = self.final_file_id(trans_id)
1280
 
                if file_id is not None and (trans_id in self._new_id or
1281
 
                    trans_id in self._new_name or trans_id in self._new_parent
1282
 
                    or trans_id in self._new_executability):
1283
 
                    try:
1284
 
                        kind = self.final_kind(trans_id)
1285
 
                    except NoSuchFile:
1286
 
                        kind = self._tree.stored_kind(file_id)
1287
 
                    if trans_id in self._new_reference_revision:
1288
 
                        new_entry = inventory.TreeReference(
1289
 
                            self.final_file_id(trans_id),
1290
 
                            self._new_name[trans_id],
1291
 
                            self.final_file_id(self._new_parent[trans_id]),
1292
 
                            None, self._new_reference_revision[trans_id])
1293
 
                    else:
1294
 
                        new_entry = inventory.make_entry(kind,
1295
 
                            self.final_name(trans_id),
1296
 
                            self.final_file_id(self.final_parent(trans_id)),
1297
 
                            self.final_file_id(trans_id))
1298
 
                    try:
1299
 
                        old_path = self._tree.id2path(new_entry.file_id)
1300
 
                    except errors.NoSuchId:
1301
 
                        old_path = None
1302
 
                    inventory_delta.append((old_path, path, new_entry.file_id,
1303
 
                                            new_entry))
 
1328
                            new_entry = inventory.make_entry(kind,
 
1329
                                self.final_name(trans_id),
 
1330
                                parent_file_id, file_id)
 
1331
                        old_path = old_paths.get(new_entry.file_id)
 
1332
                        inventory_delta.append(
 
1333
                            (old_path, path, new_entry.file_id, new_entry))
1304
1334
 
1305
1335
                if trans_id in self._new_executability:
1306
1336
                    self._set_executability(path, new_entry, trans_id)
1307
1337
        finally:
1308
1338
            child_pb.finished()
1309
 
        for trans_id in completed_new:
1310
 
            del self._new_contents[trans_id]
 
1339
        if inventory_delta is None:
 
1340
            self._new_contents.clear()
 
1341
        else:
 
1342
            for trans_id in completed_new:
 
1343
                del self._new_contents[trans_id]
1311
1344
        return modified_paths
1312
1345
 
1313
1346
 
1554
1587
            self._known_paths[trans_id] = self._determine_path(trans_id)
1555
1588
        return self._known_paths[trans_id]
1556
1589
 
 
1590
    def get_paths(self, trans_ids):
 
1591
        return [(self.get_path(t), t) for t in trans_ids]
 
1592
 
 
1593
 
1557
1594
 
1558
1595
def topology_sorted_ids(tree):
1559
1596
    """Determine the topological order of the ids in a tree"""
1562
1599
    return file_ids
1563
1600
 
1564
1601
 
1565
 
def build_tree(tree, wt, accelerator_tree=None, hardlink=False):
 
1602
def build_tree(tree, wt, accelerator_tree=None, hardlink=False,
 
1603
               delta_from_tree=False):
1566
1604
    """Create working tree for a branch, using a TreeTransform.
1567
1605
    
1568
1606
    This function should be used on empty trees, having a tree root at most.
1584
1622
    :param hardlink: If true, hard-link files to accelerator_tree, where
1585
1623
        possible.  accelerator_tree must implement abspath, i.e. be a
1586
1624
        working tree.
 
1625
    :param delta_from_tree: If true, build_tree may use the input Tree to
 
1626
        generate the inventory delta.
1587
1627
    """
1588
1628
    wt.lock_tree_write()
1589
1629
    try:
1592
1632
            if accelerator_tree is not None:
1593
1633
                accelerator_tree.lock_read()
1594
1634
            try:
1595
 
                return _build_tree(tree, wt, accelerator_tree, hardlink)
 
1635
                return _build_tree(tree, wt, accelerator_tree, hardlink,
 
1636
                                   delta_from_tree)
1596
1637
            finally:
1597
1638
                if accelerator_tree is not None:
1598
1639
                    accelerator_tree.unlock()
1602
1643
        wt.unlock()
1603
1644
 
1604
1645
 
1605
 
def _build_tree(tree, wt, accelerator_tree, hardlink):
 
1646
def _build_tree(tree, wt, accelerator_tree, hardlink, delta_from_tree):
1606
1647
    """See build_tree."""
1607
1648
    for num, _unused in enumerate(wt.all_file_ids()):
1608
1649
        if num > 0:  # more than just a root
1609
1650
            raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
 
1651
    existing_files = set()
 
1652
    for dir, files in wt.walkdirs():
 
1653
        existing_files.update(f[0] for f in files)
1610
1654
    file_trans_id = {}
1611
1655
    top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1612
1656
    pp = ProgressPhase("Build phase", 2, top_pb)
1631
1675
        try:
1632
1676
            deferred_contents = []
1633
1677
            num = 0
 
1678
            total = len(tree.inventory)
 
1679
            if delta_from_tree:
 
1680
                precomputed_delta = []
 
1681
            else:
 
1682
                precomputed_delta = None
1634
1683
            for num, (tree_path, entry) in \
1635
1684
                enumerate(tree.inventory.iter_entries_by_dir()):
1636
 
                pb.update("Building tree", num - len(deferred_contents),
1637
 
                          len(tree.inventory))
 
1685
                pb.update("Building tree", num - len(deferred_contents), total)
1638
1686
                if entry.parent_id is None:
1639
1687
                    continue
1640
1688
                reparent = False
1641
1689
                file_id = entry.file_id
1642
 
                target_path = wt.abspath(tree_path)
1643
 
                try:
 
1690
                if delta_from_tree:
 
1691
                    precomputed_delta.append((None, tree_path, file_id, entry))
 
1692
                if tree_path in existing_files:
 
1693
                    target_path = wt.abspath(tree_path)
1644
1694
                    kind = file_kind(target_path)
1645
 
                except NoSuchFile:
1646
 
                    pass
1647
 
                else:
1648
1695
                    if kind == "directory":
1649
1696
                        try:
1650
1697
                            bzrdir.BzrDir.open(target_path)
1658
1705
                        tt.delete_contents(tt.trans_id_tree_path(tree_path))
1659
1706
                        if kind == 'directory':
1660
1707
                            reparent = True
1661
 
                if entry.parent_id not in file_trans_id:
1662
 
                    raise AssertionError(
1663
 
                        'entry %s parent id %r is not in file_trans_id %r'
1664
 
                        % (entry, entry.parent_id, file_trans_id))
1665
1708
                parent_id = file_trans_id[entry.parent_id]
1666
1709
                if entry.kind == 'file':
1667
1710
                    # We *almost* replicate new_by_entry, so that we can defer
1668
1711
                    # getting the file text, and get them all at once.
1669
1712
                    trans_id = tt.create_path(entry.name, parent_id)
1670
1713
                    file_trans_id[file_id] = trans_id
1671
 
                    tt.version_file(entry.file_id, trans_id)
1672
 
                    executable = tree.is_executable(entry.file_id, tree_path)
1673
 
                    if executable is not None:
 
1714
                    tt.version_file(file_id, trans_id)
 
1715
                    executable = tree.is_executable(file_id, tree_path)
 
1716
                    if executable:
1674
1717
                        tt.set_executability(executable, trans_id)
1675
 
                    deferred_contents.append((entry.file_id, trans_id))
 
1718
                    deferred_contents.append((file_id, trans_id))
1676
1719
                else:
1677
1720
                    file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1678
1721
                                                          tree)
1689
1732
        divert_trans = set(file_trans_id[f] for f in divert)
1690
1733
        resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
1691
1734
        raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
 
1735
        if len(raw_conflicts) > 0:
 
1736
            precomputed_delta = None
1692
1737
        conflicts = cook_conflicts(raw_conflicts, tt)
1693
1738
        for conflict in conflicts:
1694
1739
            warning(conflict)
1696
1741
            wt.add_conflicts(conflicts)
1697
1742
        except errors.UnsupportedOperation:
1698
1743
            pass
1699
 
        result = tt.apply(no_conflicts=True)
 
1744
        result = tt.apply(no_conflicts=True,
 
1745
                          precomputed_delta=precomputed_delta)
1700
1746
    finally:
1701
1747
        tt.finalize()
1702
1748
        top_pb.finished()