/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

  • Committer: Martin Pool
  • Date: 2008-06-11 02:36:40 UTC
  • mfrom: (3490 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3492.
  • Revision ID: mbp@sourcefrog.net-20080611023640-db0lqd75yueksdw7
Merge news

Show diffs side-by-side

added added

removed removed

Lines of Context:
39
39
from bzrlib.progress import DummyProgress, ProgressPhase
40
40
from bzrlib.symbol_versioning import (
41
41
        deprecated_function,
42
 
        zero_fifteen,
43
 
        zero_ninety,
44
42
        )
45
43
from bzrlib.trace import mutter, warning
46
44
from bzrlib import tree
441
439
 
442
440
    def version_file(self, file_id, trans_id):
443
441
        """Schedule a file to become versioned."""
444
 
        assert file_id is not None
 
442
        if file_id is None:
 
443
            raise ValueError()
445
444
        unique_add(self._new_id, trans_id, file_id)
446
445
        unique_add(self._r_new_id, file_id, trans_id)
447
446
 
451
450
        del self._new_id[trans_id]
452
451
        del self._r_new_id[file_id]
453
452
 
454
 
    def new_paths(self):
455
 
        """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
        """
456
459
        new_ids = set()
457
 
        fp = FinalPaths(self)
458
 
        for id_set in (self._new_name, self._new_parent, self._new_contents,
459
 
                       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:
460
466
            new_ids.update(id_set)
461
 
        new_paths = [(fp.get_path(t), t) for t in new_ids]
462
 
        new_paths.sort()
463
 
        return new_paths
 
467
        return sorted(FinalPaths(self).get_paths(new_ids))
464
468
 
465
469
    def tree_kind(self, trans_id):
466
470
        """Determine the file kind in the working tree.
511
515
        applied.
512
516
        """
513
517
        try:
514
 
            # there is a new id for this file
515
 
            assert self._new_id[trans_id] is not None
516
518
            return self._new_id[trans_id]
517
519
        except KeyError:
518
520
            if trans_id in self._removed_id:
859
861
    def _set_executability(self, path, entry, trans_id):
860
862
        """Set the executability of versioned files """
861
863
        new_executability = self._new_executability[trans_id]
862
 
        entry.executable = new_executability
 
864
        if entry is not None:
 
865
            entry.executable = new_executability
863
866
        if supports_executable():
864
867
            abspath = self._tree.abspath(path)
865
868
            current_mode = os.stat(abspath).st_mode
1148
1151
        tree.lock_tree_write()
1149
1152
 
1150
1153
        try:
1151
 
            control_files = tree._control_files
1152
1154
            limbodir = urlutils.local_path_from_url(
1153
 
                control_files.controlfilename('limbo'))
 
1155
                tree._transport.abspath('limbo'))
1154
1156
            try:
1155
1157
                os.mkdir(limbodir)
1156
1158
            except OSError, e:
1157
1159
                if e.errno == errno.EEXIST:
1158
1160
                    raise ExistingLimbo(limbodir)
1159
1161
            deletiondir = urlutils.local_path_from_url(
1160
 
                control_files.controlfilename('pending-deletion'))
 
1162
                tree._transport.abspath('pending-deletion'))
1161
1163
            try:
1162
1164
                os.mkdir(deletiondir)
1163
1165
            except OSError, e:
1171
1173
                                   tree.case_sensitive)
1172
1174
        self._deletiondir = deletiondir
1173
1175
 
1174
 
    def apply(self, no_conflicts=False, _mover=None):
 
1176
    def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
1175
1177
        """Apply all changes to the inventory and filesystem.
1176
1178
 
1177
1179
        If filesystem or inventory conflicts are present, MalformedTransform
1181
1183
 
1182
1184
        :param no_conflicts: if True, the caller guarantees there are no
1183
1185
            conflicts, so no check is made.
 
1186
        :param precomputed_delta: An inventory delta to use instead of
 
1187
            calculating one.
1184
1188
        :param _mover: Supply an alternate FileMover, for testing
1185
1189
        """
1186
1190
        if not no_conflicts:
1187
1191
            conflicts = self.find_conflicts()
1188
1192
            if len(conflicts) != 0:
1189
1193
                raise MalformedTransform(conflicts=conflicts)
1190
 
        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
1191
1200
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1192
1201
        try:
1193
1202
            if _mover is None:
1196
1205
                mover = _mover
1197
1206
            try:
1198
1207
                child_pb.update('Apply phase', 0, 2)
1199
 
                self._apply_removals(inventory_delta, mover)
 
1208
                self._apply_removals(new_inventory_delta, mover)
1200
1209
                child_pb.update('Apply phase', 1, 2)
1201
 
                modified_paths = self._apply_insertions(inventory_delta, mover)
 
1210
                modified_paths = self._apply_insertions(new_inventory_delta,
 
1211
                                                        mover)
1202
1212
            except:
1203
1213
                mover.rollback()
1204
1214
                raise
1217
1227
        That is, delete files that are to be deleted, and put any files that
1218
1228
        need renaming into limbo.  This must be done in strict child-to-parent
1219
1229
        order.
 
1230
 
 
1231
        If inventory_delta is None, no inventory delta generation is performed.
1220
1232
        """
1221
1233
        tree_paths = list(self._tree_path_ids.iteritems())
1222
1234
        tree_paths.sort(reverse=True)
1238
1250
                            raise
1239
1251
                    else:
1240
1252
                        self.rename_count += 1
1241
 
                if trans_id in self._removed_id:
 
1253
                if (trans_id in self._removed_id
 
1254
                    and inventory_delta is not None):
1242
1255
                    if trans_id == self._new_root:
1243
1256
                        file_id = self._tree.get_root_id()
1244
1257
                    else:
1245
1258
                        file_id = self.tree_file_id(trans_id)
1246
 
                    assert file_id is not None
1247
1259
                    # File-id isn't really being deleted, just moved
1248
1260
                    if file_id in self._r_new_id:
1249
1261
                        continue
1257
1269
        That is, create any files that need to be created, and restore from
1258
1270
        limbo any files that needed renaming.  This must be done in strict
1259
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.
1260
1275
        """
1261
 
        new_paths = self.new_paths()
 
1276
        new_paths = self.new_paths(filesystem_only=(inventory_delta is None))
1262
1277
        modified_paths = []
1263
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1264
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()
1265
1286
        try:
1266
1287
            for num, (path, trans_id) in enumerate(new_paths):
1267
1288
                new_entry = None
1268
 
                child_pb.update('adding file', num, len(new_paths))
1269
 
                if trans_id in self._new_contents or \
1270
 
                    self.path_changed(trans_id):
1271
 
                    full_path = self._tree.abspath(path)
1272
 
                    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):
1273
1312
                        try:
1274
 
                            mover.rename(self._limbo_name(trans_id), full_path)
1275
 
                        except OSError, e:
1276
 
                            # We may be renaming a dangling inventory id
1277
 
                            if e.errno != errno.ENOENT:
1278
 
                                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])
1279
1327
                        else:
1280
 
                            self.rename_count += 1
1281
 
                    if trans_id in self._new_contents:
1282
 
                        modified_paths.append(full_path)
1283
 
                        completed_new.append(trans_id)
1284
 
                file_id = self.final_file_id(trans_id)
1285
 
                if file_id is not None and (trans_id in self._new_id or
1286
 
                    trans_id in self._new_name or trans_id in self._new_parent
1287
 
                    or trans_id in self._new_executability):
1288
 
                    try:
1289
 
                        kind = self.final_kind(trans_id)
1290
 
                    except NoSuchFile:
1291
 
                        kind = self._tree.stored_kind(file_id)
1292
 
                    if trans_id in self._new_reference_revision:
1293
 
                        new_entry = inventory.TreeReference(
1294
 
                            self.final_file_id(trans_id),
1295
 
                            self._new_name[trans_id],
1296
 
                            self.final_file_id(self._new_parent[trans_id]),
1297
 
                            None, self._new_reference_revision[trans_id])
1298
 
                    else:
1299
 
                        new_entry = inventory.make_entry(kind,
1300
 
                            self.final_name(trans_id),
1301
 
                            self.final_file_id(self.final_parent(trans_id)),
1302
 
                            self.final_file_id(trans_id))
1303
 
                    try:
1304
 
                        old_path = self._tree.id2path(new_entry.file_id)
1305
 
                    except errors.NoSuchId:
1306
 
                        old_path = None
1307
 
                    inventory_delta.append((old_path, path, new_entry.file_id,
1308
 
                                            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))
1309
1334
 
1310
1335
                if trans_id in self._new_executability:
1311
1336
                    self._set_executability(path, new_entry, trans_id)
1312
1337
        finally:
1313
1338
            child_pb.finished()
1314
 
        for trans_id in completed_new:
1315
 
            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]
1316
1344
        return modified_paths
1317
1345
 
1318
1346
 
1559
1587
            self._known_paths[trans_id] = self._determine_path(trans_id)
1560
1588
        return self._known_paths[trans_id]
1561
1589
 
 
1590
    def get_paths(self, trans_ids):
 
1591
        return [(self.get_path(t), t) for t in trans_ids]
 
1592
 
 
1593
 
1562
1594
 
1563
1595
def topology_sorted_ids(tree):
1564
1596
    """Determine the topological order of the ids in a tree"""
1567
1599
    return file_ids
1568
1600
 
1569
1601
 
1570
 
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):
1571
1604
    """Create working tree for a branch, using a TreeTransform.
1572
1605
    
1573
1606
    This function should be used on empty trees, having a tree root at most.
1589
1622
    :param hardlink: If true, hard-link files to accelerator_tree, where
1590
1623
        possible.  accelerator_tree must implement abspath, i.e. be a
1591
1624
        working tree.
 
1625
    :param delta_from_tree: If true, build_tree may use the input Tree to
 
1626
        generate the inventory delta.
1592
1627
    """
1593
1628
    wt.lock_tree_write()
1594
1629
    try:
1597
1632
            if accelerator_tree is not None:
1598
1633
                accelerator_tree.lock_read()
1599
1634
            try:
1600
 
                return _build_tree(tree, wt, accelerator_tree, hardlink)
 
1635
                return _build_tree(tree, wt, accelerator_tree, hardlink,
 
1636
                                   delta_from_tree)
1601
1637
            finally:
1602
1638
                if accelerator_tree is not None:
1603
1639
                    accelerator_tree.unlock()
1607
1643
        wt.unlock()
1608
1644
 
1609
1645
 
1610
 
def _build_tree(tree, wt, accelerator_tree, hardlink):
 
1646
def _build_tree(tree, wt, accelerator_tree, hardlink, delta_from_tree):
1611
1647
    """See build_tree."""
1612
1648
    for num, _unused in enumerate(wt.all_file_ids()):
1613
1649
        if num > 0:  # more than just a root
1614
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)
1615
1654
    file_trans_id = {}
1616
1655
    top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1617
1656
    pp = ProgressPhase("Build phase", 2, top_pb)
1636
1675
        try:
1637
1676
            deferred_contents = []
1638
1677
            num = 0
 
1678
            total = len(tree.inventory)
 
1679
            if delta_from_tree:
 
1680
                precomputed_delta = []
 
1681
            else:
 
1682
                precomputed_delta = None
1639
1683
            for num, (tree_path, entry) in \
1640
1684
                enumerate(tree.inventory.iter_entries_by_dir()):
1641
 
                pb.update("Building tree", num - len(deferred_contents),
1642
 
                          len(tree.inventory))
 
1685
                pb.update("Building tree", num - len(deferred_contents), total)
1643
1686
                if entry.parent_id is None:
1644
1687
                    continue
1645
1688
                reparent = False
1646
1689
                file_id = entry.file_id
1647
 
                target_path = wt.abspath(tree_path)
1648
 
                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)
1649
1694
                    kind = file_kind(target_path)
1650
 
                except NoSuchFile:
1651
 
                    pass
1652
 
                else:
1653
1695
                    if kind == "directory":
1654
1696
                        try:
1655
1697
                            bzrdir.BzrDir.open(target_path)
1663
1705
                        tt.delete_contents(tt.trans_id_tree_path(tree_path))
1664
1706
                        if kind == 'directory':
1665
1707
                            reparent = True
1666
 
                if entry.parent_id not in file_trans_id:
1667
 
                    raise AssertionError(
1668
 
                        'entry %s parent id %r is not in file_trans_id %r'
1669
 
                        % (entry, entry.parent_id, file_trans_id))
1670
1708
                parent_id = file_trans_id[entry.parent_id]
1671
1709
                if entry.kind == 'file':
1672
1710
                    # We *almost* replicate new_by_entry, so that we can defer
1673
1711
                    # getting the file text, and get them all at once.
1674
1712
                    trans_id = tt.create_path(entry.name, parent_id)
1675
1713
                    file_trans_id[file_id] = trans_id
1676
 
                    tt.version_file(entry.file_id, trans_id)
1677
 
                    executable = tree.is_executable(entry.file_id, tree_path)
1678
 
                    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:
1679
1717
                        tt.set_executability(executable, trans_id)
1680
 
                    deferred_contents.append((entry.file_id, trans_id))
 
1718
                    deferred_contents.append((file_id, trans_id))
1681
1719
                else:
1682
1720
                    file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1683
1721
                                                          tree)
1694
1732
        divert_trans = set(file_trans_id[f] for f in divert)
1695
1733
        resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
1696
1734
        raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
 
1735
        if len(raw_conflicts) > 0:
 
1736
            precomputed_delta = None
1697
1737
        conflicts = cook_conflicts(raw_conflicts, tt)
1698
1738
        for conflict in conflicts:
1699
1739
            warning(conflict)
1701
1741
            wt.add_conflicts(conflicts)
1702
1742
        except errors.UnsupportedOperation:
1703
1743
            pass
1704
 
        result = tt.apply(no_conflicts=True)
 
1744
        result = tt.apply(no_conflicts=True,
 
1745
                          precomputed_delta=precomputed_delta)
1705
1746
    finally:
1706
1747
        tt.finalize()
1707
1748
        top_pb.finished()
1770
1811
    new_conflicts = set()
1771
1812
    for c_type, conflict in ((c[0], c) for c in conflicts):
1772
1813
        # Anything but a 'duplicate' would indicate programmer error
1773
 
        assert c_type == 'duplicate', c_type
 
1814
        if c_type != 'duplicate':
 
1815
            raise AssertionError(c_type)
1774
1816
        # Now figure out which is new and which is old
1775
1817
        if tt.new_contents(conflict[1]):
1776
1818
            new_file = conflict[1]
1834
1876
        tt.set_executability(entry.executable, trans_id)
1835
1877
 
1836
1878
 
1837
 
@deprecated_function(zero_fifteen)
1838
 
def find_interesting(working_tree, target_tree, filenames):
1839
 
    """Find the ids corresponding to specified filenames.
1840
 
    
1841
 
    Deprecated: Please use tree1.paths2ids(filenames, [tree2]).
1842
 
    """
1843
 
    working_tree.lock_read()
1844
 
    try:
1845
 
        target_tree.lock_read()
1846
 
        try:
1847
 
            return working_tree.paths2ids(filenames, [target_tree])
1848
 
        finally:
1849
 
            target_tree.unlock()
1850
 
    finally:
1851
 
        working_tree.unlock()
1852
 
 
1853
 
 
1854
 
@deprecated_function(zero_ninety)
1855
 
def change_entry(tt, file_id, working_tree, target_tree, 
1856
 
                 trans_id_file_id, backups, trans_id, by_parent):
1857
 
    """Replace a file_id's contents with those from a target tree."""
1858
 
    if file_id is None and target_tree is None:
1859
 
        # skip the logic altogether in the deprecation test
1860
 
        return
1861
 
    e_trans_id = trans_id_file_id(file_id)
1862
 
    entry = target_tree.inventory[file_id]
1863
 
    has_contents, contents_mod, meta_mod, = _entry_changes(file_id, entry, 
1864
 
                                                           working_tree)
1865
 
    if contents_mod:
1866
 
        mode_id = e_trans_id
1867
 
        if has_contents:
1868
 
            if not backups:
1869
 
                tt.delete_contents(e_trans_id)
1870
 
            else:
1871
 
                parent_trans_id = trans_id_file_id(entry.parent_id)
1872
 
                backup_name = get_backup_name(entry, by_parent,
1873
 
                                              parent_trans_id, tt)
1874
 
                tt.adjust_path(backup_name, parent_trans_id, e_trans_id)
1875
 
                tt.unversion_file(e_trans_id)
1876
 
                e_trans_id = tt.create_path(entry.name, parent_trans_id)
1877
 
                tt.version_file(file_id, e_trans_id)
1878
 
                trans_id[file_id] = e_trans_id
1879
 
        create_by_entry(tt, entry, target_tree, e_trans_id, mode_id=mode_id)
1880
 
        create_entry_executability(tt, entry, e_trans_id)
1881
 
 
1882
 
    elif meta_mod:
1883
 
        tt.set_executability(entry.executable, e_trans_id)
1884
 
    if tt.final_name(e_trans_id) != entry.name:
1885
 
        adjust_path  = True
1886
 
    else:
1887
 
        parent_id = tt.final_parent(e_trans_id)
1888
 
        parent_file_id = tt.final_file_id(parent_id)
1889
 
        if parent_file_id != entry.parent_id:
1890
 
            adjust_path = True
1891
 
        else:
1892
 
            adjust_path = False
1893
 
    if adjust_path:
1894
 
        parent_trans_id = trans_id_file_id(entry.parent_id)
1895
 
        tt.adjust_path(entry.name, parent_trans_id, e_trans_id)
1896
 
 
1897
 
 
1898
1879
def get_backup_name(entry, by_parent, parent_trans_id, tt):
1899
1880
    return _get_backup_name(entry.name, by_parent, parent_trans_id, tt)
1900
1881
 
2048
2029
                    # preserve the execute bit when backing up
2049
2030
                    if keep_content and executable[0] == executable[1]:
2050
2031
                        tt.set_executability(executable[1], trans_id)
2051
 
                else:
2052
 
                    assert kind[1] is None
 
2032
                elif kind[1] is not None:
 
2033
                    raise AssertionError(kind[1])
2053
2034
            if versioned == (False, True):
2054
2035
                tt.version_file(file_id, trans_id)
2055
2036
            if versioned == (True, False):