~lifeless/bzr/index.range_map

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

  • Committer: Robert Collins
  • Date: 2008-06-19 01:17:19 UTC
  • mfrom: (3218.1.277 +trunk)
  • Revision ID: robertc@robertcollins.net-20080619011719-1c4g4uxzzhdls2wf
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
131
131
        """
132
132
        self._format = _format
133
133
        self.bzrdir = _bzrdir
134
 
        assert isinstance(basedir, basestring), \
135
 
            "base directory %r is not a string" % basedir
136
134
        basedir = safe_unicode(basedir)
137
135
        mutter("opening working tree %r", basedir)
138
136
        self._branch = branch
139
 
        assert isinstance(self.branch, bzrlib.branch.Branch), \
140
 
            "branch %r is not a Branch" % self.branch
141
137
        self.basedir = realpath(basedir)
142
138
        # if branch is at our basedir and is a format 6 or less
143
139
        # assume all other formats have their own control files.
144
 
        assert isinstance(_control_files, LockableFiles), \
145
 
            "_control_files must be a LockableFiles, not %r" % _control_files
146
140
        self._control_files = _control_files
 
141
        self._transport = self._control_files._transport
147
142
        self._dirty = None
148
143
        #-------------
149
144
        # during a read or write lock these objects are set, and are
317
312
        state._read_dirblocks_if_needed()
318
313
        root_key, current_entry = self._get_entry(path='')
319
314
        current_id = root_key[2]
320
 
        assert current_entry[0][0] == 'd' # directory
 
315
        if not (current_entry[0][0] == 'd'): # directory
 
316
            raise AssertionError(current_entry)
321
317
        inv = Inventory(root_id=current_id)
322
318
        # Turn some things into local variables
323
319
        minikind_to_kind = dirstate.DirState._minikind_to_kind
356
352
                    # add this entry to the parent map.
357
353
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
358
354
                elif kind == 'tree-reference':
359
 
                    assert self._repo_supports_tree_reference, \
360
 
                        "repository of %r " \
361
 
                        "doesn't support tree references " \
362
 
                        "required by entry %r" \
363
 
                        % (self, name)
 
355
                    if not self._repo_supports_tree_reference:
 
356
                        raise AssertionError(
 
357
                            "repository of %r "
 
358
                            "doesn't support tree references "
 
359
                            "required by entry %r"
 
360
                            % (self, name))
364
361
                    inv_entry.reference_revision = link_or_sha1 or None
365
362
                elif kind != 'symlink':
366
363
                    raise AssertionError("unknown kind %r" % kind)
367
364
                # These checks cost us around 40ms on a 55k entry tree
368
 
                assert file_id not in inv_byid, ('file_id %s already in'
369
 
                    ' inventory as %s' % (file_id, inv_byid[file_id]))
370
 
                assert name_unicode not in parent_ie.children
 
365
                if file_id in inv_byid:
 
366
                    raise AssertionError('file_id %s already in'
 
367
                        ' inventory as %s' % (file_id, inv_byid[file_id]))
 
368
                if name_unicode in parent_ie.children:
 
369
                    raise AssertionError('name %r already in parent'
 
370
                        % (name_unicode,))
371
371
                inv_byid[file_id] = inv_entry
372
372
                parent_ie.children[name_unicode] = inv_entry
373
373
        self._inventory = inv
500
500
            mode = os.lstat(self.abspath(path)).st_mode
501
501
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
502
502
 
 
503
    def all_file_ids(self):
 
504
        """See Tree.iter_all_file_ids"""
 
505
        self._must_be_locked()
 
506
        result = set()
 
507
        for key, tree_details in self.current_dirstate()._iter_entries():
 
508
            if tree_details[0][0] in ('a', 'r'): # relocated
 
509
                continue
 
510
            result.add(key[2])
 
511
        return result
 
512
 
503
513
    @needs_read_lock
504
514
    def __iter__(self):
505
515
        """Iterate through file_ids for this tree.
542
552
        Note: The caller is expected to take a read-lock before calling this.
543
553
        """
544
554
        relpath = self.id2path(file_id)
545
 
        assert relpath != None, \
546
 
            "path for id {%s} is None!" % file_id
 
555
        if relpath is None:
 
556
            raise AssertionError(
 
557
                "path for id {%s} is None!" % file_id)
547
558
        return self._kind(relpath)
548
559
 
549
560
    def _kind(self, relpath):
621
632
        result = []
622
633
        if not from_paths:
623
634
            return result
624
 
 
625
635
        state = self.current_dirstate()
626
 
 
627
 
        assert not isinstance(from_paths, basestring)
 
636
        if isinstance(from_paths, basestring):
 
637
            raise ValueError()
628
638
        to_dir_utf8 = to_dir.encode('utf8')
629
639
        to_entry_dirname, to_basename = os.path.split(to_dir_utf8)
630
640
        id_index = state._get_id_index()
652
662
        if self._inventory is not None:
653
663
            update_inventory = True
654
664
            inv = self.inventory
 
665
            to_dir_id = to_entry[0][2]
655
666
            to_dir_ie = inv[to_dir_id]
656
 
            to_dir_id = to_entry[0][2]
657
667
        else:
658
668
            update_inventory = False
659
669
 
785
795
                if minikind == 'd':
786
796
                    def update_dirblock(from_dir, to_key, to_dir_utf8):
787
797
                        """Recursively update all entries in this dirblock."""
788
 
                        assert from_dir != '', "renaming root not supported"
 
798
                        if from_dir == '':
 
799
                            raise AssertionError("renaming root not supported")
789
800
                        from_key = (from_dir, '')
790
801
                        from_block_idx, present = \
791
802
                            state._find_block_index_from_key(from_key)
804
815
 
805
816
                        # Grab a copy since move_one may update the list.
806
817
                        for entry in from_block[1][:]:
807
 
                            assert entry[0][0] == from_dir
 
818
                            if not (entry[0][0] == from_dir):
 
819
                                raise AssertionError()
808
820
                            cur_details = entry[1][0]
809
821
                            to_key = (to_dir_utf8, entry[0][1], entry[0][2])
810
822
                            from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
1023
1035
        """Change the last revision in the working tree."""
1024
1036
        parents = self.get_parent_ids()
1025
1037
        if new_revision in (NULL_REVISION, None):
1026
 
            assert len(parents) < 2, (
1027
 
                "setting the last parent to none with a pending merge is "
1028
 
                "unsupported.")
 
1038
            if len(parents) >= 2:
 
1039
                raise AssertionError(
 
1040
                    "setting the last parent to none with a pending merge is "
 
1041
                    "unsupported.")
1029
1042
            self.set_parent_ids([])
1030
1043
        else:
1031
1044
            self.set_parent_ids([new_revision] + parents[1:],
1072
1085
                raise errors.GhostRevisionUnusableHere(parents_list[0][0])
1073
1086
        real_trees = []
1074
1087
        ghosts = []
 
1088
 
 
1089
        parent_ids = [rev_id for rev_id, tree in parents_list]
 
1090
        graph = self.branch.repository.get_graph()
 
1091
        heads = graph.heads(parent_ids)
 
1092
        accepted_revisions = set()
 
1093
 
1075
1094
        # convert absent trees to the null tree, which we convert back to
1076
1095
        # missing on access.
1077
1096
        for rev_id, tree in parents_list:
 
1097
            if len(accepted_revisions) > 0:
 
1098
                # we always accept the first tree
 
1099
                if rev_id in accepted_revisions or rev_id not in heads:
 
1100
                    # We have already included either this tree, or its
 
1101
                    # descendent, so we skip it.
 
1102
                    continue
1078
1103
            _mod_revision.check_not_reserved_id(rev_id)
1079
1104
            if tree is not None:
1080
1105
                real_trees.append((rev_id, tree))
1082
1107
                real_trees.append((rev_id,
1083
1108
                    self.branch.repository.revision_tree(None)))
1084
1109
                ghosts.append(rev_id)
 
1110
            accepted_revisions.add(rev_id)
1085
1111
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1086
1112
        self._make_dirty(reset_inventory=False)
1087
1113
 
1215
1241
            for file_id in file_ids:
1216
1242
                self._inventory.remove_recursive_id(file_id)
1217
1243
 
 
1244
    @needs_tree_write_lock
 
1245
    def rename_one(self, from_rel, to_rel, after=False):
 
1246
        """See WorkingTree.rename_one"""
 
1247
        self.flush()
 
1248
        WorkingTree.rename_one(self, from_rel, to_rel, after)
 
1249
 
 
1250
    @needs_tree_write_lock
 
1251
    def apply_inventory_delta(self, changes):
 
1252
        """See MutableTree.apply_inventory_delta"""
 
1253
        state = self.current_dirstate()
 
1254
        state.update_by_delta(changes)
 
1255
        self._make_dirty(reset_inventory=True)
 
1256
 
1218
1257
    def update_basis_by_delta(self, new_revid, delta):
1219
1258
        """See MutableTree.update_basis_by_delta."""
1220
 
        assert self.last_revision() != new_revid
 
1259
        if self.last_revision() == new_revid:
 
1260
            raise AssertionError()
1221
1261
        self.current_dirstate().update_basis_by_delta(delta, new_revid)
1222
1262
 
1223
1263
    @needs_read_lock
1227
1267
    @needs_tree_write_lock
1228
1268
    def _write_inventory(self, inv):
1229
1269
        """Write inventory as the current inventory."""
1230
 
        assert not self._dirty, ("attempting to write an inventory when the "
1231
 
            "dirstate is dirty will cause data loss")
 
1270
        if self._dirty:
 
1271
            raise AssertionError("attempting to write an inventory when the "
 
1272
                "dirstate is dirty will lose pending changes")
1232
1273
        self.current_dirstate().set_state_from_inventory(inv)
1233
1274
        self._make_dirty(reset_inventory=False)
1234
1275
        if self._inventory is not None:
1259
1300
        return "Working tree format 4"
1260
1301
 
1261
1302
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1262
 
                   accelerator_tree=None):
 
1303
                   accelerator_tree=None, hardlink=False):
1263
1304
        """See WorkingTreeFormat.initialize().
1264
1305
 
1265
1306
        :param revision_id: allows creating a working tree at a different
1268
1309
            contents more quickly than the revision tree, i.e. a workingtree.
1269
1310
            The revision tree will be used for cases where accelerator_tree's
1270
1311
            content is different.
 
1312
        :param hardlink: If true, hard-link files from accelerator_tree,
 
1313
            where possible.
1271
1314
 
1272
1315
        These trees get an initial random root id, if their repository supports
1273
1316
        rich root data, TREE_ROOT otherwise.
1278
1321
        control_files = self._open_control_files(a_bzrdir)
1279
1322
        control_files.create_lock()
1280
1323
        control_files.lock_write()
1281
 
        control_files.put_utf8('format', self.get_format_string())
 
1324
        transport.put_bytes('format', self.get_format_string(),
 
1325
            mode=a_bzrdir._get_file_mode())
1282
1326
        if from_branch is not None:
1283
1327
            branch = from_branch
1284
1328
        else:
1320
1364
            else:
1321
1365
                parents_list = [(revision_id, basis)]
1322
1366
            basis.lock_read()
1323
 
            wt.set_parent_trees(parents_list, allow_leftmost_as_ghost=True)
1324
 
            wt.flush()
1325
 
            # if the basis has a root id we have to use that; otherwise we use
1326
 
            # a new random one
1327
 
            basis_root_id = basis.get_root_id()
1328
 
            if basis_root_id is not None:
1329
 
                wt._set_root_id(basis_root_id)
 
1367
            try:
 
1368
                wt.set_parent_trees(parents_list, allow_leftmost_as_ghost=True)
1330
1369
                wt.flush()
1331
 
            transform.build_tree(basis, wt, accelerator_tree)
1332
 
            basis.unlock()
 
1370
                # if the basis has a root id we have to use that; otherwise we
 
1371
                # use a new random one
 
1372
                basis_root_id = basis.get_root_id()
 
1373
                if basis_root_id is not None:
 
1374
                    wt._set_root_id(basis_root_id)
 
1375
                    wt.flush()
 
1376
                # delta_from_tree is safe even for DirStateRevisionTrees,
 
1377
                # because wt4.apply_inventory_delta does not mutate the input
 
1378
                # inventory entries.
 
1379
                transform.build_tree(basis, wt, accelerator_tree,
 
1380
                                     hardlink=hardlink, delta_from_tree=True)
 
1381
            finally:
 
1382
                basis.unlock()
1333
1383
        finally:
1334
1384
            control_files.unlock()
1335
1385
            wt.unlock()
1374
1424
                      default_revision=_mod_revision.CURRENT_REVISION):
1375
1425
        """See Tree.annotate_iter"""
1376
1426
        w = self._get_weave(file_id)
1377
 
        return w.annotate_iter(self.inventory[file_id].revision)
 
1427
        return w.annotate(self.inventory[file_id].revision)
1378
1428
 
1379
1429
    def _get_ancestors(self, default_revision):
1380
1430
        return set(self._repository.get_ancestry(self._revision_id,
1439
1489
 
1440
1490
        This is relatively expensive: we have to walk the entire dirstate.
1441
1491
        """
1442
 
        assert self._locked, 'cannot generate inventory of an unlocked '\
1443
 
            'dirstate revision tree'
 
1492
        if not self._locked:
 
1493
            raise AssertionError(
 
1494
                'cannot generate inventory of an unlocked '
 
1495
                'dirstate revision tree')
1444
1496
        # separate call for profiling - makes it clear where the costs are.
1445
1497
        self._dirstate._read_dirblocks_if_needed()
1446
 
        assert self._revision_id in self._dirstate.get_parent_ids(), \
1447
 
            'parent %s has disappeared from %s' % (
1448
 
            self._revision_id, self._dirstate.get_parent_ids())
 
1498
        if self._revision_id not in self._dirstate.get_parent_ids():
 
1499
            raise AssertionError(
 
1500
                'parent %s has disappeared from %s' % (
 
1501
                self._revision_id, self._dirstate.get_parent_ids()))
1449
1502
        parent_index = self._dirstate.get_parent_ids().index(self._revision_id) + 1
1450
1503
        # This is identical now to the WorkingTree _generate_inventory except
1451
1504
        # for the tree index use.
1452
1505
        root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
1453
1506
        current_id = root_key[2]
1454
 
        assert current_entry[parent_index][0] == 'd'
 
1507
        if current_entry[parent_index][0] != 'd':
 
1508
            raise AssertionError()
1455
1509
        inv = Inventory(root_id=current_id, revision_id=self._revision_id)
1456
1510
        inv.root.revision = current_entry[parent_index][4]
1457
1511
        # Turn some things into local variables
1497
1551
                    raise AssertionError("cannot convert entry %r into an InventoryEntry"
1498
1552
                            % entry)
1499
1553
                # These checks cost us around 40ms on a 55k entry tree
1500
 
                assert file_id not in inv_byid
1501
 
                assert name_unicode not in parent_ie.children
 
1554
                if file_id in inv_byid:
 
1555
                    raise AssertionError('file_id %s already in'
 
1556
                        ' inventory as %s' % (file_id, inv_byid[file_id]))
 
1557
                if name_unicode in parent_ie.children:
 
1558
                    raise AssertionError('name %r already in parent'
 
1559
                        % (name_unicode,))
1502
1560
                inv_byid[file_id] = inv_entry
1503
1561
                parent_ie.children[name_unicode] = inv_entry
1504
1562
        self._inventory = inv
1524
1582
            return parent_details[1]
1525
1583
        return None
1526
1584
 
1527
 
    @symbol_versioning.deprecated_method(symbol_versioning.zero_ninety)
1528
 
    def get_weave(self, file_id):
1529
 
        return self._get_weave(file_id)
1530
 
 
1531
1585
    def _get_weave(self, file_id):
1532
1586
        return self._repository.weave_store.get_weave(file_id,
1533
1587
                self._repository.get_transaction())
1537
1591
 
1538
1592
    def get_file_lines(self, file_id):
1539
1593
        entry = self._get_entry(file_id=file_id)[1]
1540
 
        if entry == None:
 
1594
        if entry is None:
1541
1595
            raise errors.NoSuchId(tree=self, file_id=file_id)
1542
1596
        return self._get_weave(file_id).get_lines(entry[1][4])
1543
1597
 
1544
1598
    def get_file_size(self, file_id):
 
1599
        """See Tree.get_file_size"""
1545
1600
        return self.inventory[file_id].text_size
1546
1601
 
1547
1602
    def get_file_text(self, file_id):
1598
1653
 
1599
1654
    def kind(self, file_id):
1600
1655
        entry = self._get_entry(file_id=file_id)[1]
1601
 
        if entry == None:
 
1656
        if entry is None:
1602
1657
            raise errors.NoSuchId(tree=self, file_id=file_id)
1603
1658
        return dirstate.DirState._minikind_to_kind[entry[1][0]]
1604
1659
 
 
1660
    def stored_kind(self, file_id):
 
1661
        """See Tree.stored_kind"""
 
1662
        return self.kind(file_id)
 
1663
 
1605
1664
    def path_content_summary(self, path):
1606
1665
        """See Tree.path_content_summary."""
1607
1666
        id = self.inventory.path2id(path)
1728
1787
    _matching_to_tree_format = WorkingTreeFormat4()
1729
1788
    _test_mutable_trees_to_test_trees = make_source_parent_tree
1730
1789
 
1731
 
    def _iter_changes(self, include_unchanged=False,
 
1790
    def iter_changes(self, include_unchanged=False,
1732
1791
                      specific_files=None, pb=None, extra_trees=[],
1733
1792
                      require_versioned=True, want_unversioned=False):
1734
1793
        """Return the changes from source to target.
1735
1794
 
1736
 
        :return: An iterator that yields tuples. See InterTree._iter_changes
 
1795
        :return: An iterator that yields tuples. See InterTree.iter_changes
1737
1796
            for details.
1738
1797
        :param specific_files: An optional list of file paths to restrict the
1739
1798
            comparison to. When mapping filenames to ids, all matches in all
1758
1817
        # TODO: handle extra trees in the dirstate.
1759
1818
        if (extra_trees or specific_files == []):
1760
1819
            # we can't fast-path these cases (yet)
1761
 
            for f in super(InterDirStateTree, self)._iter_changes(
 
1820
            for f in super(InterDirStateTree, self).iter_changes(
1762
1821
                include_unchanged, specific_files, pb, extra_trees,
1763
1822
                require_versioned, want_unversioned=want_unversioned):
1764
1823
                yield f
1765
1824
            return
1766
1825
        parent_ids = self.target.get_parent_ids()
1767
 
        assert (self.source._revision_id in parent_ids
1768
 
                or self.source._revision_id == NULL_REVISION), \
1769
 
                "revision {%s} is not stored in {%s}, but %s " \
1770
 
                "can only be used for trees stored in the dirstate" \
1771
 
                % (self.source._revision_id, self.target, self._iter_changes)
 
1826
        if not (self.source._revision_id in parent_ids
 
1827
                or self.source._revision_id == NULL_REVISION):
 
1828
            raise AssertionError(
 
1829
                "revision {%s} is not stored in {%s}, but %s "
 
1830
                "can only be used for trees stored in the dirstate"
 
1831
                % (self.source._revision_id, self.target, self.iter_changes))
1772
1832
        target_index = 0
1773
1833
        if self.source._revision_id == NULL_REVISION:
1774
1834
            source_index = None
1775
1835
            indices = (target_index,)
1776
1836
        else:
1777
 
            assert (self.source._revision_id in parent_ids), \
1778
 
                "Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
1779
 
                self.source._revision_id, parent_ids)
 
1837
            if not (self.source._revision_id in parent_ids):
 
1838
                raise AssertionError(
 
1839
                    "Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
 
1840
                    self.source._revision_id, parent_ids))
1780
1841
            source_index = 1 + parent_ids.index(self.source._revision_id)
1781
1842
            indices = (source_index, target_index)
1782
1843
        # -- make all specific_files utf8 --
1920
1981
            target_details = entry[1][target_index]
1921
1982
            target_minikind = target_details[0]
1922
1983
            if path_info is not None and target_minikind in 'fdlt':
1923
 
                assert target_index == 0
 
1984
                if not (target_index == 0):
 
1985
                    raise AssertionError()
1924
1986
                link_or_sha1 = state.update_entry(entry, abspath=path_info[4],
1925
1987
                                                  stat_value=path_info[3])
1926
1988
                # The entry may have been modified by update_entry
1951
2013
                                                 path_utf8=old_path)
1952
2014
                    # update the source details variable to be the real
1953
2015
                    # location.
 
2016
                    if old_entry == (None, None):
 
2017
                        raise errors.CorruptDirstate(state._filename,
 
2018
                            "entry '%s/%s' is considered renamed from %r"
 
2019
                            " but source does not exist\n"
 
2020
                            "entry: %s" % (entry[0][0], entry[0][1], old_path, entry))
1954
2021
                    source_details = old_entry[1][source_index]
1955
2022
                    source_minikind = source_details[0]
1956
2023
                else:
2034
2101
                        #       parent entry will be the same as the source entry.
2035
2102
                        target_parent_entry = state._get_entry(target_index,
2036
2103
                                                               path_utf8=new_dirname)
2037
 
                        assert target_parent_entry != (None, None), (
2038
 
                            "Could not find target parent in wt: %s\nparent of: %s"
2039
 
                            % (new_dirname, entry))
 
2104
                        if target_parent_entry == (None, None):
 
2105
                            raise AssertionError(
 
2106
                                "Could not find target parent in wt: %s\nparent of: %s"
 
2107
                                % (new_dirname, entry))
2040
2108
                        target_parent_id = target_parent_entry[0][2]
2041
2109
                    if target_parent_id == entry[0][2]:
2042
2110
                        # This is the root, so the parent is None
2225
2293
                    if current_dir_info[0][0] == '':
2226
2294
                        # remove .bzr from iteration
2227
2295
                        bzr_index = bisect_left(current_dir_info[1], ('.bzr',))
2228
 
                        assert current_dir_info[1][bzr_index][0] == '.bzr'
 
2296
                        if current_dir_info[1][bzr_index][0] != '.bzr':
 
2297
                            raise AssertionError()
2229
2298
                        del current_dir_info[1][bzr_index]
2230
2299
            # walk until both the directory listing and the versioned metadata
2231
2300
            # are exhausted. 
2482
2551
 
2483
2552
    def update_format(self, tree):
2484
2553
        """Change the format marker."""
2485
 
        tree._control_files.put_utf8('format',
2486
 
            self.target_format.get_format_string())
 
2554
        tree._transport.put_bytes('format',
 
2555
            self.target_format.get_format_string(),
 
2556
            mode=tree.bzrdir._get_file_mode())