~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

Update to bzr.dev.

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
1145
1151
        tree.lock_tree_write()
1146
1152
 
1147
1153
        try:
1148
 
            control_files = tree._control_files
1149
1154
            limbodir = urlutils.local_path_from_url(
1150
 
                control_files.controlfilename('limbo'))
 
1155
                tree._transport.abspath('limbo'))
1151
1156
            try:
1152
1157
                os.mkdir(limbodir)
1153
1158
            except OSError, e:
1154
1159
                if e.errno == errno.EEXIST:
1155
1160
                    raise ExistingLimbo(limbodir)
1156
1161
            deletiondir = urlutils.local_path_from_url(
1157
 
                control_files.controlfilename('pending-deletion'))
 
1162
                tree._transport.abspath('pending-deletion'))
1158
1163
            try:
1159
1164
                os.mkdir(deletiondir)
1160
1165
            except OSError, e:
1168
1173
                                   tree.case_sensitive)
1169
1174
        self._deletiondir = deletiondir
1170
1175
 
1171
 
    def apply(self, no_conflicts=False, _mover=None):
 
1176
    def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
1172
1177
        """Apply all changes to the inventory and filesystem.
1173
1178
 
1174
1179
        If filesystem or inventory conflicts are present, MalformedTransform
1178
1183
 
1179
1184
        :param no_conflicts: if True, the caller guarantees there are no
1180
1185
            conflicts, so no check is made.
 
1186
        :param precomputed_delta: An inventory delta to use instead of
 
1187
            calculating one.
1181
1188
        :param _mover: Supply an alternate FileMover, for testing
1182
1189
        """
1183
1190
        if not no_conflicts:
1184
1191
            conflicts = self.find_conflicts()
1185
1192
            if len(conflicts) != 0:
1186
1193
                raise MalformedTransform(conflicts=conflicts)
1187
 
        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
1188
1200
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1189
1201
        try:
1190
1202
            if _mover is None:
1193
1205
                mover = _mover
1194
1206
            try:
1195
1207
                child_pb.update('Apply phase', 0, 2)
1196
 
                self._apply_removals(inventory_delta, mover)
 
1208
                self._apply_removals(new_inventory_delta, mover)
1197
1209
                child_pb.update('Apply phase', 1, 2)
1198
 
                modified_paths = self._apply_insertions(inventory_delta, mover)
 
1210
                modified_paths = self._apply_insertions(new_inventory_delta,
 
1211
                                                        mover)
1199
1212
            except:
1200
1213
                mover.rollback()
1201
1214
                raise
1214
1227
        That is, delete files that are to be deleted, and put any files that
1215
1228
        need renaming into limbo.  This must be done in strict child-to-parent
1216
1229
        order.
 
1230
 
 
1231
        If inventory_delta is None, no inventory delta generation is performed.
1217
1232
        """
1218
1233
        tree_paths = list(self._tree_path_ids.iteritems())
1219
1234
        tree_paths.sort(reverse=True)
1235
1250
                            raise
1236
1251
                    else:
1237
1252
                        self.rename_count += 1
1238
 
                if trans_id in self._removed_id:
 
1253
                if (trans_id in self._removed_id
 
1254
                    and inventory_delta is not None):
1239
1255
                    if trans_id == self._new_root:
1240
1256
                        file_id = self._tree.get_root_id()
1241
1257
                    else:
1253
1269
        That is, create any files that need to be created, and restore from
1254
1270
        limbo any files that needed renaming.  This must be done in strict
1255
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.
1256
1275
        """
1257
 
        new_paths = self.new_paths()
 
1276
        new_paths = self.new_paths(filesystem_only=(inventory_delta is None))
1258
1277
        modified_paths = []
1259
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1260
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()
1261
1286
        try:
1262
1287
            for num, (path, trans_id) in enumerate(new_paths):
1263
1288
                new_entry = None
1264
 
                child_pb.update('adding file', num, len(new_paths))
1265
 
                if trans_id in self._new_contents or \
1266
 
                    self.path_changed(trans_id):
1267
 
                    full_path = self._tree.abspath(path)
1268
 
                    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):
1269
1312
                        try:
1270
 
                            mover.rename(self._limbo_name(trans_id), full_path)
1271
 
                        except OSError, e:
1272
 
                            # We may be renaming a dangling inventory id
1273
 
                            if e.errno != errno.ENOENT:
1274
 
                                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])
1275
1327
                        else:
1276
 
                            self.rename_count += 1
1277
 
                    if trans_id in self._new_contents:
1278
 
                        modified_paths.append(full_path)
1279
 
                        completed_new.append(trans_id)
1280
 
                file_id = self.final_file_id(trans_id)
1281
 
                if file_id is not None and (trans_id in self._new_id or
1282
 
                    trans_id in self._new_name or trans_id in self._new_parent
1283
 
                    or trans_id in self._new_executability):
1284
 
                    try:
1285
 
                        kind = self.final_kind(trans_id)
1286
 
                    except NoSuchFile:
1287
 
                        kind = self._tree.stored_kind(file_id)
1288
 
                    if trans_id in self._new_reference_revision:
1289
 
                        new_entry = inventory.TreeReference(
1290
 
                            self.final_file_id(trans_id),
1291
 
                            self._new_name[trans_id],
1292
 
                            self.final_file_id(self._new_parent[trans_id]),
1293
 
                            None, self._new_reference_revision[trans_id])
1294
 
                    else:
1295
 
                        new_entry = inventory.make_entry(kind,
1296
 
                            self.final_name(trans_id),
1297
 
                            self.final_file_id(self.final_parent(trans_id)),
1298
 
                            self.final_file_id(trans_id))
1299
 
                    try:
1300
 
                        old_path = self._tree.id2path(new_entry.file_id)
1301
 
                    except errors.NoSuchId:
1302
 
                        old_path = None
1303
 
                    inventory_delta.append((old_path, path, new_entry.file_id,
1304
 
                                            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))
1305
1334
 
1306
1335
                if trans_id in self._new_executability:
1307
1336
                    self._set_executability(path, new_entry, trans_id)
1308
1337
        finally:
1309
1338
            child_pb.finished()
1310
 
        for trans_id in completed_new:
1311
 
            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]
1312
1344
        return modified_paths
1313
1345
 
1314
1346
 
1555
1587
            self._known_paths[trans_id] = self._determine_path(trans_id)
1556
1588
        return self._known_paths[trans_id]
1557
1589
 
 
1590
    def get_paths(self, trans_ids):
 
1591
        return [(self.get_path(t), t) for t in trans_ids]
 
1592
 
 
1593
 
1558
1594
 
1559
1595
def topology_sorted_ids(tree):
1560
1596
    """Determine the topological order of the ids in a tree"""
1563
1599
    return file_ids
1564
1600
 
1565
1601
 
1566
 
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):
1567
1604
    """Create working tree for a branch, using a TreeTransform.
1568
1605
    
1569
1606
    This function should be used on empty trees, having a tree root at most.
1585
1622
    :param hardlink: If true, hard-link files to accelerator_tree, where
1586
1623
        possible.  accelerator_tree must implement abspath, i.e. be a
1587
1624
        working tree.
 
1625
    :param delta_from_tree: If true, build_tree may use the input Tree to
 
1626
        generate the inventory delta.
1588
1627
    """
1589
1628
    wt.lock_tree_write()
1590
1629
    try:
1593
1632
            if accelerator_tree is not None:
1594
1633
                accelerator_tree.lock_read()
1595
1634
            try:
1596
 
                return _build_tree(tree, wt, accelerator_tree, hardlink)
 
1635
                return _build_tree(tree, wt, accelerator_tree, hardlink,
 
1636
                                   delta_from_tree)
1597
1637
            finally:
1598
1638
                if accelerator_tree is not None:
1599
1639
                    accelerator_tree.unlock()
1603
1643
        wt.unlock()
1604
1644
 
1605
1645
 
1606
 
def _build_tree(tree, wt, accelerator_tree, hardlink):
 
1646
def _build_tree(tree, wt, accelerator_tree, hardlink, delta_from_tree):
1607
1647
    """See build_tree."""
1608
1648
    for num, _unused in enumerate(wt.all_file_ids()):
1609
1649
        if num > 0:  # more than just a root
1610
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)
1611
1654
    file_trans_id = {}
1612
1655
    top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1613
1656
    pp = ProgressPhase("Build phase", 2, top_pb)
1632
1675
        try:
1633
1676
            deferred_contents = []
1634
1677
            num = 0
 
1678
            total = len(tree.inventory)
 
1679
            if delta_from_tree:
 
1680
                precomputed_delta = []
 
1681
            else:
 
1682
                precomputed_delta = None
1635
1683
            for num, (tree_path, entry) in \
1636
1684
                enumerate(tree.inventory.iter_entries_by_dir()):
1637
 
                pb.update("Building tree", num - len(deferred_contents),
1638
 
                          len(tree.inventory))
 
1685
                pb.update("Building tree", num - len(deferred_contents), total)
1639
1686
                if entry.parent_id is None:
1640
1687
                    continue
1641
1688
                reparent = False
1642
1689
                file_id = entry.file_id
1643
 
                target_path = wt.abspath(tree_path)
1644
 
                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)
1645
1694
                    kind = file_kind(target_path)
1646
 
                except NoSuchFile:
1647
 
                    pass
1648
 
                else:
1649
1695
                    if kind == "directory":
1650
1696
                        try:
1651
1697
                            bzrdir.BzrDir.open(target_path)
1659
1705
                        tt.delete_contents(tt.trans_id_tree_path(tree_path))
1660
1706
                        if kind == 'directory':
1661
1707
                            reparent = True
1662
 
                if entry.parent_id not in file_trans_id:
1663
 
                    raise AssertionError(
1664
 
                        'entry %s parent id %r is not in file_trans_id %r'
1665
 
                        % (entry, entry.parent_id, file_trans_id))
1666
1708
                parent_id = file_trans_id[entry.parent_id]
1667
1709
                if entry.kind == 'file':
1668
1710
                    # We *almost* replicate new_by_entry, so that we can defer
1669
1711
                    # getting the file text, and get them all at once.
1670
1712
                    trans_id = tt.create_path(entry.name, parent_id)
1671
1713
                    file_trans_id[file_id] = trans_id
1672
 
                    tt.version_file(entry.file_id, trans_id)
1673
 
                    executable = tree.is_executable(entry.file_id, tree_path)
1674
 
                    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:
1675
1717
                        tt.set_executability(executable, trans_id)
1676
 
                    deferred_contents.append((entry.file_id, trans_id))
 
1718
                    deferred_contents.append((file_id, trans_id))
1677
1719
                else:
1678
1720
                    file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1679
1721
                                                          tree)
1690
1732
        divert_trans = set(file_trans_id[f] for f in divert)
1691
1733
        resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
1692
1734
        raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
 
1735
        if len(raw_conflicts) > 0:
 
1736
            precomputed_delta = None
1693
1737
        conflicts = cook_conflicts(raw_conflicts, tt)
1694
1738
        for conflict in conflicts:
1695
1739
            warning(conflict)
1697
1741
            wt.add_conflicts(conflicts)
1698
1742
        except errors.UnsupportedOperation:
1699
1743
            pass
1700
 
        result = tt.apply(no_conflicts=True)
 
1744
        result = tt.apply(no_conflicts=True,
 
1745
                          precomputed_delta=precomputed_delta)
1701
1746
    finally:
1702
1747
        tt.finalize()
1703
1748
        top_pb.finished()