~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-12-20 04:20:19 UTC
  • mfrom: (3062.2.13 fast-plan-merge2)
  • Revision ID: pqm@pqm.ubuntu.com-20071220042019-wsij5vgvhgw4qhdt
Annotate merge can do cherrypicks (abentley)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
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
134
136
        basedir = safe_unicode(basedir)
135
137
        mutter("opening working tree %r", basedir)
136
138
        self._branch = branch
 
139
        assert isinstance(self.branch, bzrlib.branch.Branch), \
 
140
            "branch %r is not a Branch" % self.branch
137
141
        self.basedir = realpath(basedir)
138
142
        # if branch is at our basedir and is a format 6 or less
139
143
        # 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
140
146
        self._control_files = _control_files
141
 
        self._transport = self._control_files._transport
142
147
        self._dirty = None
143
148
        #-------------
144
149
        # during a read or write lock these objects are set, and are
146
151
        self._dirstate = None
147
152
        self._inventory = None
148
153
        #-------------
149
 
        self._setup_directory_is_tree_reference()
150
154
        self._detect_case_handling()
151
 
        self._rules_searcher = None
152
155
 
153
156
    @needs_tree_write_lock
154
157
    def _add(self, files, ids, kinds):
313
316
        state._read_dirblocks_if_needed()
314
317
        root_key, current_entry = self._get_entry(path='')
315
318
        current_id = root_key[2]
316
 
        if not (current_entry[0][0] == 'd'): # directory
317
 
            raise AssertionError(current_entry)
 
319
        assert current_entry[0][0] == 'd' # directory
318
320
        inv = Inventory(root_id=current_id)
319
321
        # Turn some things into local variables
320
322
        minikind_to_kind = dirstate.DirState._minikind_to_kind
353
355
                    # add this entry to the parent map.
354
356
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
355
357
                elif kind == 'tree-reference':
356
 
                    if not self._repo_supports_tree_reference:
357
 
                        raise AssertionError(
358
 
                            "repository of %r "
359
 
                            "doesn't support tree references "
360
 
                            "required by entry %r"
361
 
                            % (self, name))
 
358
                    assert self._repo_supports_tree_reference, \
 
359
                        "repository of %r " \
 
360
                        "doesn't support tree references " \
 
361
                        "required by entry %r" \
 
362
                        % (self, name)
362
363
                    inv_entry.reference_revision = link_or_sha1 or None
363
364
                elif kind != 'symlink':
364
365
                    raise AssertionError("unknown kind %r" % kind)
365
366
                # These checks cost us around 40ms on a 55k entry tree
366
 
                if file_id in inv_byid:
367
 
                    raise AssertionError('file_id %s already in'
368
 
                        ' inventory as %s' % (file_id, inv_byid[file_id]))
369
 
                if name_unicode in parent_ie.children:
370
 
                    raise AssertionError('name %r already in parent'
371
 
                        % (name_unicode,))
 
367
                assert file_id not in inv_byid, ('file_id %s already in'
 
368
                    ' inventory as %s' % (file_id, inv_byid[file_id]))
 
369
                assert name_unicode not in parent_ie.children
372
370
                inv_byid[file_id] = inv_entry
373
371
                parent_ie.children[name_unicode] = inv_entry
374
372
        self._inventory = inv
495
493
 
496
494
            Note: The caller is expected to take a read-lock before calling this.
497
495
            """
498
 
            self._must_be_locked()
499
496
            if not path:
500
497
                path = self.id2path(file_id)
501
498
            mode = os.lstat(self.abspath(path)).st_mode
502
499
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
503
500
 
504
 
    def all_file_ids(self):
505
 
        """See Tree.iter_all_file_ids"""
506
 
        self._must_be_locked()
507
 
        result = set()
508
 
        for key, tree_details in self.current_dirstate()._iter_entries():
509
 
            if tree_details[0][0] in ('a', 'r'): # relocated
510
 
                continue
511
 
            result.add(key[2])
512
 
        return result
513
 
 
514
501
    @needs_read_lock
515
502
    def __iter__(self):
516
503
        """Iterate through file_ids for this tree.
529
516
        return iter(result)
530
517
 
531
518
    def iter_references(self):
532
 
        if not self._repo_supports_tree_reference:
533
 
            # When the repo doesn't support references, we will have nothing to
534
 
            # return
535
 
            return
536
519
        for key, tree_details in self.current_dirstate()._iter_entries():
537
520
            if tree_details[0][0] in ('a', 'r'): # absent, relocated
538
521
                # not relevant to the working tree
540
523
            if not key[1]:
541
524
                # the root is not a reference.
542
525
                continue
543
 
            relpath = pathjoin(key[0].decode('utf8'), key[1].decode('utf8'))
 
526
            path = pathjoin(self.basedir, key[0].decode('utf8'), key[1].decode('utf8'))
544
527
            try:
545
 
                if self._kind(relpath) == 'tree-reference':
546
 
                    yield relpath, key[2]
 
528
                if self._kind(path) == 'tree-reference':
 
529
                    yield path, key[2]
547
530
            except errors.NoSuchFile:
548
531
                # path is missing on disk.
549
532
                continue
557
540
        Note: The caller is expected to take a read-lock before calling this.
558
541
        """
559
542
        relpath = self.id2path(file_id)
560
 
        if relpath is None:
561
 
            raise AssertionError(
562
 
                "path for id {%s} is None!" % file_id)
 
543
        assert relpath != None, \
 
544
            "path for id {%s} is None!" % file_id
563
545
        return self._kind(relpath)
564
546
 
565
547
    def _kind(self, relpath):
637
619
        result = []
638
620
        if not from_paths:
639
621
            return result
 
622
 
640
623
        state = self.current_dirstate()
641
 
        if isinstance(from_paths, basestring):
642
 
            raise ValueError()
 
624
 
 
625
        assert not isinstance(from_paths, basestring)
643
626
        to_dir_utf8 = to_dir.encode('utf8')
644
627
        to_entry_dirname, to_basename = os.path.split(to_dir_utf8)
645
628
        id_index = state._get_id_index()
667
650
        if self._inventory is not None:
668
651
            update_inventory = True
669
652
            inv = self.inventory
 
653
            to_dir_ie = inv[to_dir_id]
670
654
            to_dir_id = to_entry[0][2]
671
 
            to_dir_ie = inv[to_dir_id]
672
655
        else:
673
656
            update_inventory = False
674
657
 
800
783
                if minikind == 'd':
801
784
                    def update_dirblock(from_dir, to_key, to_dir_utf8):
802
785
                        """Recursively update all entries in this dirblock."""
803
 
                        if from_dir == '':
804
 
                            raise AssertionError("renaming root not supported")
 
786
                        assert from_dir != '', "renaming root not supported"
805
787
                        from_key = (from_dir, '')
806
788
                        from_block_idx, present = \
807
789
                            state._find_block_index_from_key(from_key)
820
802
 
821
803
                        # Grab a copy since move_one may update the list.
822
804
                        for entry in from_block[1][:]:
823
 
                            if not (entry[0][0] == from_dir):
824
 
                                raise AssertionError()
 
805
                            assert entry[0][0] == from_dir
825
806
                            cur_details = entry[1][0]
826
807
                            to_key = (to_dir_utf8, entry[0][1], entry[0][2])
827
808
                            from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
1040
1021
        """Change the last revision in the working tree."""
1041
1022
        parents = self.get_parent_ids()
1042
1023
        if new_revision in (NULL_REVISION, None):
1043
 
            if len(parents) >= 2:
1044
 
                raise AssertionError(
1045
 
                    "setting the last parent to none with a pending merge is "
1046
 
                    "unsupported.")
 
1024
            assert len(parents) < 2, (
 
1025
                "setting the last parent to none with a pending merge is "
 
1026
                "unsupported.")
1047
1027
            self.set_parent_ids([])
1048
1028
        else:
1049
1029
            self.set_parent_ids([new_revision] + parents[1:],
1090
1070
                raise errors.GhostRevisionUnusableHere(parents_list[0][0])
1091
1071
        real_trees = []
1092
1072
        ghosts = []
1093
 
 
1094
 
        parent_ids = [rev_id for rev_id, tree in parents_list]
1095
 
        graph = self.branch.repository.get_graph()
1096
 
        heads = graph.heads(parent_ids)
1097
 
        accepted_revisions = set()
1098
 
 
1099
1073
        # convert absent trees to the null tree, which we convert back to
1100
1074
        # missing on access.
1101
1075
        for rev_id, tree in parents_list:
1102
 
            if len(accepted_revisions) > 0:
1103
 
                # we always accept the first tree
1104
 
                if rev_id in accepted_revisions or rev_id not in heads:
1105
 
                    # We have already included either this tree, or its
1106
 
                    # descendent, so we skip it.
1107
 
                    continue
1108
1076
            _mod_revision.check_not_reserved_id(rev_id)
1109
1077
            if tree is not None:
1110
1078
                real_trees.append((rev_id, tree))
1112
1080
                real_trees.append((rev_id,
1113
1081
                    self.branch.repository.revision_tree(None)))
1114
1082
                ghosts.append(rev_id)
1115
 
            accepted_revisions.add(rev_id)
1116
1083
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1117
1084
        self._make_dirty(reset_inventory=False)
1118
1085
 
1246
1213
            for file_id in file_ids:
1247
1214
                self._inventory.remove_recursive_id(file_id)
1248
1215
 
1249
 
    @needs_tree_write_lock
1250
 
    def rename_one(self, from_rel, to_rel, after=False):
1251
 
        """See WorkingTree.rename_one"""
1252
 
        self.flush()
1253
 
        WorkingTree.rename_one(self, from_rel, to_rel, after)
1254
 
 
1255
 
    @needs_tree_write_lock
1256
 
    def apply_inventory_delta(self, changes):
1257
 
        """See MutableTree.apply_inventory_delta"""
1258
 
        state = self.current_dirstate()
1259
 
        state.update_by_delta(changes)
1260
 
        self._make_dirty(reset_inventory=True)
1261
 
 
1262
1216
    def update_basis_by_delta(self, new_revid, delta):
1263
1217
        """See MutableTree.update_basis_by_delta."""
1264
 
        if self.last_revision() == new_revid:
1265
 
            raise AssertionError()
 
1218
        assert self.last_revision() != new_revid
1266
1219
        self.current_dirstate().update_basis_by_delta(delta, new_revid)
1267
1220
 
1268
1221
    @needs_read_lock
1272
1225
    @needs_tree_write_lock
1273
1226
    def _write_inventory(self, inv):
1274
1227
        """Write inventory as the current inventory."""
1275
 
        if self._dirty:
1276
 
            raise AssertionError("attempting to write an inventory when the "
1277
 
                "dirstate is dirty will lose pending changes")
 
1228
        assert not self._dirty, ("attempting to write an inventory when the "
 
1229
            "dirstate is dirty will cause data loss")
1278
1230
        self.current_dirstate().set_state_from_inventory(inv)
1279
1231
        self._make_dirty(reset_inventory=False)
1280
1232
        if self._inventory is not None:
1296
1248
 
1297
1249
    upgrade_recommended = False
1298
1250
 
1299
 
    _tree_class = WorkingTree4
1300
 
 
1301
1251
    def get_format_string(self):
1302
1252
        """See WorkingTreeFormat.get_format_string()."""
1303
1253
        return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1306
1256
        """See WorkingTreeFormat.get_format_description()."""
1307
1257
        return "Working tree format 4"
1308
1258
 
1309
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1310
 
                   accelerator_tree=None, hardlink=False):
 
1259
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None):
1311
1260
        """See WorkingTreeFormat.initialize().
1312
1261
 
1313
1262
        :param revision_id: allows creating a working tree at a different
1314
1263
        revision than the branch is at.
1315
 
        :param accelerator_tree: A tree which can be used for retrieving file
1316
 
            contents more quickly than the revision tree, i.e. a workingtree.
1317
 
            The revision tree will be used for cases where accelerator_tree's
1318
 
            content is different.
1319
 
        :param hardlink: If true, hard-link files from accelerator_tree,
1320
 
            where possible.
1321
1264
 
1322
1265
        These trees get an initial random root id, if their repository supports
1323
1266
        rich root data, TREE_ROOT otherwise.
1328
1271
        control_files = self._open_control_files(a_bzrdir)
1329
1272
        control_files.create_lock()
1330
1273
        control_files.lock_write()
1331
 
        transport.put_bytes('format', self.get_format_string(),
1332
 
            mode=a_bzrdir._get_file_mode())
 
1274
        control_files.put_utf8('format', self.get_format_string())
1333
1275
        if from_branch is not None:
1334
1276
            branch = from_branch
1335
1277
        else:
1341
1283
        state = dirstate.DirState.initialize(local_path)
1342
1284
        state.unlock()
1343
1285
        del state
1344
 
        wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
 
1286
        wt = WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
1345
1287
                         branch,
1346
1288
                         _format=self,
1347
1289
                         _bzrdir=a_bzrdir,
1349
1291
        wt._new_tree()
1350
1292
        wt.lock_tree_write()
1351
1293
        try:
1352
 
            self._init_custom_control_files(wt)
1353
1294
            if revision_id in (None, NULL_REVISION):
1354
1295
                if branch.repository.supports_rich_root():
1355
1296
                    wt._set_root_id(generate_ids.gen_root_id())
1356
1297
                else:
1357
1298
                    wt._set_root_id(ROOT_ID)
1358
1299
                wt.flush()
1359
 
            basis = None
1360
 
            # frequently, we will get here due to branching.  The accelerator
1361
 
            # tree will be the tree from the branch, so the desired basis
1362
 
            # tree will often be a parent of the accelerator tree.
1363
 
            if accelerator_tree is not None:
1364
 
                try:
1365
 
                    basis = accelerator_tree.revision_tree(revision_id)
1366
 
                except errors.NoSuchRevision:
1367
 
                    pass
1368
 
            if basis is None:
1369
 
                basis = branch.repository.revision_tree(revision_id)
1370
 
            if revision_id == NULL_REVISION:
1371
 
                parents_list = []
1372
 
            else:
1373
 
                parents_list = [(revision_id, basis)]
 
1300
            wt.set_last_revision(revision_id)
 
1301
            wt.flush()
 
1302
            basis = wt.basis_tree()
1374
1303
            basis.lock_read()
1375
 
            try:
1376
 
                wt.set_parent_trees(parents_list, allow_leftmost_as_ghost=True)
 
1304
            # if the basis has a root id we have to use that; otherwise we use
 
1305
            # a new random one
 
1306
            basis_root_id = basis.get_root_id()
 
1307
            if basis_root_id is not None:
 
1308
                wt._set_root_id(basis_root_id)
1377
1309
                wt.flush()
1378
 
                # if the basis has a root id we have to use that; otherwise we
1379
 
                # use a new random one
1380
 
                basis_root_id = basis.get_root_id()
1381
 
                if basis_root_id is not None:
1382
 
                    wt._set_root_id(basis_root_id)
1383
 
                    wt.flush()
1384
 
                # delta_from_tree is safe even for DirStateRevisionTrees,
1385
 
                # because wt4.apply_inventory_delta does not mutate the input
1386
 
                # inventory entries.
1387
 
                transform.build_tree(basis, wt, accelerator_tree,
1388
 
                                     hardlink=hardlink, delta_from_tree=True)
1389
 
            finally:
1390
 
                basis.unlock()
 
1310
            transform.build_tree(basis, wt)
 
1311
            basis.unlock()
1391
1312
        finally:
1392
1313
            control_files.unlock()
1393
1314
            wt.unlock()
1394
1315
        return wt
1395
1316
 
1396
 
    def _init_custom_control_files(self, wt):
1397
 
        """Subclasses with custom control files should override this method.
1398
 
        
1399
 
        The working tree and control files are locked for writing when this
1400
 
        method is called.
1401
 
        
1402
 
        :param wt: the WorkingTree object
1403
 
        """
1404
 
 
1405
1317
    def _open(self, a_bzrdir, control_files):
1406
1318
        """Open the tree itself.
1407
1319
 
1408
1320
        :param a_bzrdir: the dir for the tree.
1409
1321
        :param control_files: the control files for the tree.
1410
1322
        """
1411
 
        return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
 
1323
        return WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
1412
1324
                           branch=a_bzrdir.open_branch(),
1413
1325
                           _format=self,
1414
1326
                           _bzrdir=a_bzrdir,
1432
1344
        self._inventory = None
1433
1345
        self._locked = 0
1434
1346
        self._dirstate_locked = False
1435
 
        self._repo_supports_tree_reference = getattr(
1436
 
            repository._format, "supports_tree_reference",
1437
 
            False)
1438
1347
 
1439
1348
    def __repr__(self):
1440
1349
        return "<%s of %s in %s>" % \
1443
1352
    def annotate_iter(self, file_id,
1444
1353
                      default_revision=_mod_revision.CURRENT_REVISION):
1445
1354
        """See Tree.annotate_iter"""
1446
 
        text_key = (file_id, self.inventory[file_id].revision)
1447
 
        annotations = self._repository.texts.annotate(text_key)
1448
 
        return [(key[-1], line) for (key, line) in annotations]
 
1355
        w = self._get_weave(file_id)
 
1356
        return w.annotate_iter(self.inventory[file_id].revision)
1449
1357
 
1450
1358
    def _get_ancestors(self, default_revision):
1451
1359
        return set(self._repository.get_ancestry(self._revision_id,
1480
1388
        path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
1481
1389
        return path_utf8.decode('utf8')
1482
1390
 
1483
 
    def iter_references(self):
1484
 
        if not self._repo_supports_tree_reference:
1485
 
            # When the repo doesn't support references, we will have nothing to
1486
 
            # return
1487
 
            return iter([])
1488
 
        # Otherwise, fall back to the default implementation
1489
 
        return super(DirStateRevisionTree, self).iter_references()
1490
 
 
1491
1391
    def _get_parent_index(self):
1492
1392
        """Return the index in the dirstate referenced by this tree."""
1493
1393
        return self._dirstate.get_parent_ids().index(self._revision_id) + 1
1518
1418
 
1519
1419
        This is relatively expensive: we have to walk the entire dirstate.
1520
1420
        """
1521
 
        if not self._locked:
1522
 
            raise AssertionError(
1523
 
                'cannot generate inventory of an unlocked '
1524
 
                'dirstate revision tree')
 
1421
        assert self._locked, 'cannot generate inventory of an unlocked '\
 
1422
            'dirstate revision tree'
1525
1423
        # separate call for profiling - makes it clear where the costs are.
1526
1424
        self._dirstate._read_dirblocks_if_needed()
1527
 
        if self._revision_id not in self._dirstate.get_parent_ids():
1528
 
            raise AssertionError(
1529
 
                'parent %s has disappeared from %s' % (
1530
 
                self._revision_id, self._dirstate.get_parent_ids()))
 
1425
        assert self._revision_id in self._dirstate.get_parent_ids(), \
 
1426
            'parent %s has disappeared from %s' % (
 
1427
            self._revision_id, self._dirstate.get_parent_ids())
1531
1428
        parent_index = self._dirstate.get_parent_ids().index(self._revision_id) + 1
1532
1429
        # This is identical now to the WorkingTree _generate_inventory except
1533
1430
        # for the tree index use.
1534
1431
        root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
1535
1432
        current_id = root_key[2]
1536
 
        if current_entry[parent_index][0] != 'd':
1537
 
            raise AssertionError()
 
1433
        assert current_entry[parent_index][0] == 'd'
1538
1434
        inv = Inventory(root_id=current_id, revision_id=self._revision_id)
1539
1435
        inv.root.revision = current_entry[parent_index][4]
1540
1436
        # Turn some things into local variables
1580
1476
                    raise AssertionError("cannot convert entry %r into an InventoryEntry"
1581
1477
                            % entry)
1582
1478
                # These checks cost us around 40ms on a 55k entry tree
1583
 
                if file_id in inv_byid:
1584
 
                    raise AssertionError('file_id %s already in'
1585
 
                        ' inventory as %s' % (file_id, inv_byid[file_id]))
1586
 
                if name_unicode in parent_ie.children:
1587
 
                    raise AssertionError('name %r already in parent'
1588
 
                        % (name_unicode,))
 
1479
                assert file_id not in inv_byid
 
1480
                assert name_unicode not in parent_ie.children
1589
1481
                inv_byid[file_id] = inv_entry
1590
1482
                parent_ie.children[name_unicode] = inv_entry
1591
1483
        self._inventory = inv
1611
1503
            return parent_details[1]
1612
1504
        return None
1613
1505
 
 
1506
    @symbol_versioning.deprecated_method(symbol_versioning.zero_ninety)
 
1507
    def get_weave(self, file_id):
 
1508
        return self._get_weave(file_id)
 
1509
 
 
1510
    def _get_weave(self, file_id):
 
1511
        return self._repository.weave_store.get_weave(file_id,
 
1512
                self._repository.get_transaction())
 
1513
 
1614
1514
    def get_file(self, file_id, path=None):
1615
1515
        return StringIO(self.get_file_text(file_id))
1616
1516
 
1617
1517
    def get_file_lines(self, file_id):
1618
 
        return osutils.split_lines(self.get_file_text(file_id))
 
1518
        entry = self._get_entry(file_id=file_id)[1]
 
1519
        if entry == None:
 
1520
            raise errors.NoSuchId(tree=self, file_id=file_id)
 
1521
        return self._get_weave(file_id).get_lines(entry[1][4])
1619
1522
 
1620
1523
    def get_file_size(self, file_id):
1621
 
        """See Tree.get_file_size"""
1622
1524
        return self.inventory[file_id].text_size
1623
1525
 
1624
1526
    def get_file_text(self, file_id):
1625
 
        return list(self.iter_files_bytes([(file_id, None)]))[0][1]
 
1527
        return ''.join(self.get_file_lines(file_id))
1626
1528
 
1627
1529
    def get_reference_revision(self, file_id, path=None):
1628
1530
        return self.inventory[file_id].reference_revision
1675
1577
 
1676
1578
    def kind(self, file_id):
1677
1579
        entry = self._get_entry(file_id=file_id)[1]
1678
 
        if entry is None:
 
1580
        if entry == None:
1679
1581
            raise errors.NoSuchId(tree=self, file_id=file_id)
1680
1582
        return dirstate.DirState._minikind_to_kind[entry[1][0]]
1681
1583
 
1682
 
    def stored_kind(self, file_id):
1683
 
        """See Tree.stored_kind"""
1684
 
        return self.kind(file_id)
1685
 
 
1686
1584
    def path_content_summary(self, path):
1687
1585
        """See Tree.path_content_summary."""
1688
1586
        id = self.inventory.path2id(path)
1747
1645
                self._dirstate_locked = False
1748
1646
            self._repository.unlock()
1749
1647
 
1750
 
    @needs_read_lock
1751
 
    def supports_tree_reference(self):
1752
 
        return self._repo_supports_tree_reference
1753
 
 
1754
1648
    def walkdirs(self, prefix=""):
1755
1649
        # TODO: jam 20070215 This is the lazy way by using the RevisionTree
1756
1650
        # implementation based on an inventory.  
1813
1707
    _matching_to_tree_format = WorkingTreeFormat4()
1814
1708
    _test_mutable_trees_to_test_trees = make_source_parent_tree
1815
1709
 
1816
 
    def iter_changes(self, include_unchanged=False,
 
1710
    def _iter_changes(self, include_unchanged=False,
1817
1711
                      specific_files=None, pb=None, extra_trees=[],
1818
1712
                      require_versioned=True, want_unversioned=False):
1819
1713
        """Return the changes from source to target.
1820
1714
 
1821
 
        :return: An iterator that yields tuples. See InterTree.iter_changes
 
1715
        :return: An iterator that yields tuples. See InterTree._iter_changes
1822
1716
            for details.
1823
1717
        :param specific_files: An optional list of file paths to restrict the
1824
1718
            comparison to. When mapping filenames to ids, all matches in all
1841
1735
        # NB: show_status depends on being able to pass in non-versioned files
1842
1736
        # and report them as unknown
1843
1737
        # TODO: handle extra trees in the dirstate.
1844
 
        if (extra_trees or specific_files == []):
 
1738
        # TODO: handle comparisons as an empty tree as a different special
 
1739
        # case? mbp 20070226
 
1740
        if (extra_trees or (self.source._revision_id == NULL_REVISION)
 
1741
            or specific_files == []):
1845
1742
            # we can't fast-path these cases (yet)
1846
 
            for f in super(InterDirStateTree, self).iter_changes(
 
1743
            for f in super(InterDirStateTree, self)._iter_changes(
1847
1744
                include_unchanged, specific_files, pb, extra_trees,
1848
1745
                require_versioned, want_unversioned=want_unversioned):
1849
1746
                yield f
1850
1747
            return
1851
1748
        parent_ids = self.target.get_parent_ids()
1852
 
        if not (self.source._revision_id in parent_ids
1853
 
                or self.source._revision_id == NULL_REVISION):
1854
 
            raise AssertionError(
1855
 
                "revision {%s} is not stored in {%s}, but %s "
1856
 
                "can only be used for trees stored in the dirstate"
1857
 
                % (self.source._revision_id, self.target, self.iter_changes))
 
1749
        assert (self.source._revision_id in parent_ids), \
 
1750
                "revision {%s} is not stored in {%s}, but %s " \
 
1751
                "can only be used for trees stored in the dirstate" \
 
1752
                % (self.source._revision_id, self.target, self._iter_changes)
1858
1753
        target_index = 0
1859
1754
        if self.source._revision_id == NULL_REVISION:
1860
1755
            source_index = None
1861
1756
            indices = (target_index,)
1862
1757
        else:
1863
 
            if not (self.source._revision_id in parent_ids):
1864
 
                raise AssertionError(
1865
 
                    "Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
1866
 
                    self.source._revision_id, parent_ids))
 
1758
            assert (self.source._revision_id in parent_ids), \
 
1759
                "Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
 
1760
                self.source._revision_id, parent_ids)
1867
1761
            source_index = 1 + parent_ids.index(self.source._revision_id)
1868
 
            indices = (source_index, target_index)
 
1762
            indices = (source_index,target_index)
1869
1763
        # -- make all specific_files utf8 --
1870
1764
        if specific_files:
1871
1765
            specific_files_utf8 = set()
1980
1874
        # record is handled, but isn't interesting to process (unchanged)
1981
1875
        uninteresting = object()
1982
1876
 
 
1877
 
1983
1878
        old_dirname_to_file_id = {}
1984
1879
        new_dirname_to_file_id = {}
1985
1880
        # TODO: jam 20070516 - Avoid the _get_entry lookup overhead by
2006
1901
            target_details = entry[1][target_index]
2007
1902
            target_minikind = target_details[0]
2008
1903
            if path_info is not None and target_minikind in 'fdlt':
2009
 
                if not (target_index == 0):
2010
 
                    raise AssertionError()
 
1904
                assert target_index == 0
2011
1905
                link_or_sha1 = state.update_entry(entry, abspath=path_info[4],
2012
1906
                                                  stat_value=path_info[3])
2013
1907
                # The entry may have been modified by update_entry
2038
1932
                                                 path_utf8=old_path)
2039
1933
                    # update the source details variable to be the real
2040
1934
                    # location.
2041
 
                    if old_entry == (None, None):
2042
 
                        raise errors.CorruptDirstate(state._filename,
2043
 
                            "entry '%s/%s' is considered renamed from %r"
2044
 
                            " but source does not exist\n"
2045
 
                            "entry: %s" % (entry[0][0], entry[0][1], old_path, entry))
2046
1935
                    source_details = old_entry[1][source_index]
2047
1936
                    source_minikind = source_details[0]
2048
1937
                else:
2126
2015
                        #       parent entry will be the same as the source entry.
2127
2016
                        target_parent_entry = state._get_entry(target_index,
2128
2017
                                                               path_utf8=new_dirname)
2129
 
                        if target_parent_entry == (None, None):
2130
 
                            raise AssertionError(
2131
 
                                "Could not find target parent in wt: %s\nparent of: %s"
2132
 
                                % (new_dirname, entry))
 
2018
                        assert target_parent_entry != (None, None), (
 
2019
                            "Could not find target parent in wt: %s\nparent of: %s"
 
2020
                            % (new_dirname, entry))
2133
2021
                        target_parent_id = target_parent_entry[0][2]
2134
2022
                    if target_parent_id == entry[0][2]:
2135
2023
                        # This is the root, so the parent is None
2168
2056
                    return uninteresting
2169
2057
            elif source_minikind in 'a' and target_minikind in 'fdlt':
2170
2058
                # looks like a new file
2171
 
                path = pathjoin(entry[0][0], entry[0][1])
2172
 
                # parent id is the entry for the path in the target tree
2173
 
                # TODO: these are the same for an entire directory: cache em.
2174
 
                parent_id = state._get_entry(target_index,
2175
 
                                             path_utf8=entry[0][0])[0][2]
2176
 
                if parent_id == entry[0][2]:
2177
 
                    parent_id = None
2178
2059
                if path_info is not None:
2179
 
                    # Present on disk:
 
2060
                    path = pathjoin(entry[0][0], entry[0][1])
 
2061
                    # parent id is the entry for the path in the target tree
 
2062
                    # TODO: these are the same for an entire directory: cache em.
 
2063
                    parent_id = state._get_entry(target_index,
 
2064
                                                 path_utf8=entry[0][0])[0][2]
 
2065
                    if parent_id == entry[0][2]:
 
2066
                        parent_id = None
2180
2067
                    if use_filesystem_for_exec:
2181
2068
                        # We need S_ISREG here, because we aren't sure if this
2182
2069
                        # is a file or not.
2194
2081
                           (None, path_info[2]),
2195
2082
                           (None, target_exec))
2196
2083
                else:
2197
 
                    # Its a missing file, report it as such.
2198
 
                    return (entry[0][2],
2199
 
                           (None, utf8_decode(path)[0]),
2200
 
                           False,
2201
 
                           (False, True),
2202
 
                           (None, parent_id),
2203
 
                           (None, utf8_decode(entry[0][1])[0]),
2204
 
                           (None, None),
2205
 
                           (None, False))
 
2084
                    # but its not on disk: we deliberately treat this as just
 
2085
                    # never-present. (Why ?! - RBC 20070224)
 
2086
                    pass
2206
2087
            elif source_minikind in 'fdlt' and target_minikind in 'a':
2207
2088
                # unversioned, possibly, or possibly not deleted: we dont care.
2208
2089
                # if its still on disk, *and* theres no other entry at this
2325
2206
                    if current_dir_info[0][0] == '':
2326
2207
                        # remove .bzr from iteration
2327
2208
                        bzr_index = bisect_left(current_dir_info[1], ('.bzr',))
2328
 
                        if current_dir_info[1][bzr_index][0] != '.bzr':
2329
 
                            raise AssertionError()
 
2209
                        assert current_dir_info[1][bzr_index][0] == '.bzr'
2330
2210
                        del current_dir_info[1][bzr_index]
2331
2211
            # walk until both the directory listing and the versioned metadata
2332
2212
            # are exhausted. 
2478
2358
                                new_executable = bool(
2479
2359
                                    stat.S_ISREG(current_path_info[3].st_mode)
2480
2360
                                    and stat.S_IEXEC & current_path_info[3].st_mode)
2481
 
                                try:
2482
 
                                    relpath_unicode = utf8_decode(current_path_info[0])[0]
2483
 
                                except UnicodeDecodeError:
2484
 
                                    raise errors.BadFilenameEncoding(
2485
 
                                        current_path_info[0], osutils._fs_enc)
2486
2361
                                yield (None,
2487
 
                                    (None, relpath_unicode),
 
2362
                                    (None, utf8_decode(current_path_info[0])[0]),
2488
2363
                                    True,
2489
2364
                                    (False, False),
2490
2365
                                    (None, None),
2527
2402
                    except StopIteration:
2528
2403
                        current_dir_info = None
2529
2404
 
 
2405
 
2530
2406
    @staticmethod
2531
2407
    def is_compatible(source, target):
2532
2408
        # the target must be a dirstate working tree
2587
2463
 
2588
2464
    def update_format(self, tree):
2589
2465
        """Change the format marker."""
2590
 
        tree._transport.put_bytes('format',
2591
 
            self.target_format.get_format_string(),
2592
 
            mode=tree.bzrdir._get_file_mode())
 
2466
        tree._control_files.put_utf8('format',
 
2467
            self.target_format.get_format_string())