~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

  • Committer: Andrew Bennetts
  • Date: 2007-03-26 06:24:01 UTC
  • mto: This revision was merged to the branch mainline in revision 2376.
  • Revision ID: andrew.bennetts@canonical.com-20070326062401-k3nbefzje5332jaf
Deal with review comments from Robert:

  * Add my name to the NEWS file
  * Move the test case to a new module in branch_implementations
  * Remove revision_history cruft from identitymap and test_identitymap
  * Improve some docstrings

Also, this fixes a bug where revision_history was not returning a copy of the
cached data, allowing the cache to be corrupted.

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
43
43
    bzrdir,
44
44
    cache_utf8,
45
45
    conflicts as _mod_conflicts,
46
 
    debug,
47
46
    delta,
48
47
    dirstate,
49
48
    errors,
50
49
    generate_ids,
51
50
    globbing,
 
51
    hashcache,
52
52
    ignores,
53
53
    merge,
54
54
    osutils,
55
 
    revision as _mod_revision,
56
55
    revisiontree,
57
56
    textui,
58
 
    trace,
59
57
    transform,
60
58
    urlutils,
61
59
    xml5,
101
99
# This is the Windows equivalent of ENOTDIR
102
100
# It is defined in pywin32.winerror, but we don't want a strong dependency for
103
101
# just an error code.
104
 
ERROR_PATH_NOT_FOUND = 3
105
102
ERROR_DIRECTORY = 267
106
103
 
107
104
 
131
128
        """
132
129
        self._format = _format
133
130
        self.bzrdir = _bzrdir
 
131
        from bzrlib.trace import note, mutter
 
132
        assert isinstance(basedir, basestring), \
 
133
            "base directory %r is not a string" % basedir
134
134
        basedir = safe_unicode(basedir)
135
135
        mutter("opening working tree %r", basedir)
136
136
        self._branch = branch
 
137
        assert isinstance(self.branch, bzrlib.branch.Branch), \
 
138
            "branch %r is not a Branch" % self.branch
137
139
        self.basedir = realpath(basedir)
138
140
        # if branch is at our basedir and is a format 6 or less
139
141
        # assume all other formats have their own control files.
 
142
        assert isinstance(_control_files, LockableFiles), \
 
143
            "_control_files must be a LockableFiles, not %r" % _control_files
140
144
        self._control_files = _control_files
141
 
        self._transport = self._control_files._transport
142
145
        self._dirty = None
143
146
        #-------------
144
147
        # during a read or write lock these objects are set, and are
146
149
        self._dirstate = None
147
150
        self._inventory = None
148
151
        #-------------
149
 
        self._setup_directory_is_tree_reference()
150
 
        self._detect_case_handling()
151
 
        self._rules_searcher = None
152
152
 
153
153
    @needs_tree_write_lock
154
154
    def _add(self, files, ids, kinds):
156
156
        state = self.current_dirstate()
157
157
        for f, file_id, kind in zip(files, ids, kinds):
158
158
            f = f.strip('/')
 
159
            assert '//' not in f
 
160
            assert '..' not in f
159
161
            if self.path2id(f):
160
162
                # special case tree root handling.
161
163
                if f == '' and self.path2id(f) == ROOT_ID:
266
268
        self._dirstate = dirstate.DirState.on_file(local_path)
267
269
        return self._dirstate
268
270
 
 
271
    def _directory_is_tree_reference(self, relpath):
 
272
        # as a special case, if a directory contains control files then 
 
273
        # it's a tree reference, except that the root of the tree is not
 
274
        return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
 
275
        # TODO: We could ask all the control formats whether they
 
276
        # recognize this directory, but at the moment there's no cheap api
 
277
        # to do that.  Since we probably can only nest bzr checkouts and
 
278
        # they always use this name it's ok for now.  -- mbp 20060306
 
279
        #
 
280
        # FIXME: There is an unhandled case here of a subdirectory
 
281
        # containing .bzr but not a branch; that will probably blow up
 
282
        # when you try to commit it.  It might happen if there is a
 
283
        # checkout in a subdirectory.  This can be avoided by not adding
 
284
        # it.  mbp 20070306
 
285
 
269
286
    def filter_unversioned_files(self, paths):
270
287
        """Filter out paths that are versioned.
271
288
 
313
330
        state._read_dirblocks_if_needed()
314
331
        root_key, current_entry = self._get_entry(path='')
315
332
        current_id = root_key[2]
316
 
        if not (current_entry[0][0] == 'd'): # directory
317
 
            raise AssertionError(current_entry)
 
333
        assert current_entry[0][0] == 'd' # directory
318
334
        inv = Inventory(root_id=current_id)
319
335
        # Turn some things into local variables
320
336
        minikind_to_kind = dirstate.DirState._minikind_to_kind
353
369
                    # add this entry to the parent map.
354
370
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
355
371
                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))
 
372
                    assert self._repo_supports_tree_reference, \
 
373
                        "repository of %r " \
 
374
                        "doesn't support tree references " \
 
375
                        "required by entry %r" \
 
376
                        % (self, name)
362
377
                    inv_entry.reference_revision = link_or_sha1 or None
363
378
                elif kind != 'symlink':
364
379
                    raise AssertionError("unknown kind %r" % kind)
365
380
                # 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,))
 
381
                assert file_id not in inv_byid, ('file_id %s already in'
 
382
                    ' inventory as %s' % (file_id, inv_byid[file_id]))
 
383
                assert name_unicode not in parent_ie.children
372
384
                inv_byid[file_id] = inv_entry
373
385
                parent_ie.children[name_unicode] = inv_entry
374
386
        self._inventory = inv
394
406
    def get_file_sha1(self, file_id, path=None, stat_value=None):
395
407
        # check file id is valid unconditionally.
396
408
        entry = self._get_entry(file_id=file_id, path=path)
397
 
        if entry[0] is None:
398
 
            raise errors.NoSuchId(self, file_id)
 
409
        assert entry[0] is not None, 'what error should this raise'
399
410
        if path is None:
400
411
            path = pathjoin(entry[0][0], entry[0][1]).decode('utf8')
401
412
 
402
413
        file_abspath = self.abspath(path)
403
414
        state = self.current_dirstate()
404
 
        if stat_value is None:
405
 
            try:
406
 
                stat_value = os.lstat(file_abspath)
407
 
            except OSError, e:
408
 
                if e.errno == errno.ENOENT:
409
 
                    return None
410
 
                else:
411
 
                    raise
412
415
        link_or_sha1 = state.update_entry(entry, file_abspath,
413
416
                                          stat_value=stat_value)
414
417
        if entry[1][0][0] == 'f':
417
420
 
418
421
    def _get_inventory(self):
419
422
        """Get the inventory for the tree. This is only valid within a lock."""
420
 
        if 'evil' in debug.debug_flags:
421
 
            trace.mutter_callsite(2,
422
 
                "accessing .inventory forces a size of tree translation.")
423
423
        if self._inventory is not None:
424
424
            return self._inventory
425
425
        self._must_be_locked()
454
454
 
455
455
    def has_id(self, file_id):
456
456
        state = self.current_dirstate()
 
457
        file_id = osutils.safe_file_id(file_id)
457
458
        row, parents = self._get_entry(file_id=file_id)
458
459
        if row is None:
459
460
            return False
462
463
 
463
464
    @needs_read_lock
464
465
    def id2path(self, file_id):
465
 
        "Convert a file-id to a path."
 
466
        file_id = osutils.safe_file_id(file_id)
466
467
        state = self.current_dirstate()
467
468
        entry = self._get_entry(file_id=file_id)
468
469
        if entry == (None, None):
470
471
        path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
471
472
        return path_utf8.decode('utf8')
472
473
 
473
 
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
474
 
        entry = self._get_entry(path=path)
475
 
        if entry == (None, None):
476
 
            return False # Missing entries are not executable
477
 
        return entry[1][0][3] # Executable?
478
 
 
479
474
    if not osutils.supports_executable():
 
475
        @needs_read_lock
480
476
        def is_executable(self, file_id, path=None):
481
 
            """Test if a file is executable or not.
482
 
 
483
 
            Note: The caller is expected to take a read-lock before calling this.
484
 
            """
 
477
            file_id = osutils.safe_file_id(file_id)
485
478
            entry = self._get_entry(file_id=file_id, path=path)
486
479
            if entry == (None, None):
487
480
                return False
488
481
            return entry[1][0][3]
489
 
 
490
 
        _is_executable_from_path_and_stat = \
491
 
            _is_executable_from_path_and_stat_from_basis
492
482
    else:
 
483
        @needs_read_lock
493
484
        def is_executable(self, file_id, path=None):
494
 
            """Test if a file is executable or not.
495
 
 
496
 
            Note: The caller is expected to take a read-lock before calling this.
497
 
            """
498
 
            self._must_be_locked()
499
485
            if not path:
 
486
                file_id = osutils.safe_file_id(file_id)
500
487
                path = self.id2path(file_id)
501
488
            mode = os.lstat(self.abspath(path)).st_mode
502
489
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
503
490
 
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
491
    @needs_read_lock
515
492
    def __iter__(self):
516
493
        """Iterate through file_ids for this tree.
529
506
        return iter(result)
530
507
 
531
508
    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
509
        for key, tree_details in self.current_dirstate()._iter_entries():
537
510
            if tree_details[0][0] in ('a', 'r'): # absent, relocated
538
511
                # not relevant to the working tree
540
513
            if not key[1]:
541
514
                # the root is not a reference.
542
515
                continue
543
 
            relpath = pathjoin(key[0].decode('utf8'), key[1].decode('utf8'))
 
516
            path = pathjoin(self.basedir, key[0].decode('utf8'), key[1].decode('utf8'))
544
517
            try:
545
 
                if self._kind(relpath) == 'tree-reference':
546
 
                    yield relpath, key[2]
 
518
                if self._kind(path) == 'tree-reference':
 
519
                    yield path, key[2]
547
520
            except errors.NoSuchFile:
548
521
                # path is missing on disk.
549
522
                continue
550
523
 
 
524
    @needs_read_lock
551
525
    def kind(self, file_id):
552
526
        """Return the kind of a file.
553
527
 
554
528
        This is always the actual kind that's on disk, regardless of what it
555
529
        was added as.
556
 
 
557
 
        Note: The caller is expected to take a read-lock before calling this.
558
530
        """
559
531
        relpath = self.id2path(file_id)
560
 
        if relpath is None:
561
 
            raise AssertionError(
562
 
                "path for id {%s} is None!" % file_id)
 
532
        assert relpath != None, \
 
533
            "path for id {%s} is None!" % file_id
563
534
        return self._kind(relpath)
564
535
 
565
536
    def _kind(self, relpath):
578
549
        if parent_ids:
579
550
            return parent_ids[0]
580
551
        else:
581
 
            return _mod_revision.NULL_REVISION
 
552
            return None
582
553
 
583
554
    def lock_read(self):
584
555
        """See Branch.lock_read, and WorkingTree.unlock."""
592
563
                # set our support for tree references from the repository in
593
564
                # use.
594
565
                self._repo_supports_tree_reference = getattr(
595
 
                    self.branch.repository._format, "supports_tree_reference",
 
566
                    self.branch.repository._format, "support_tree_reference",
596
567
                    False)
597
568
            except:
598
569
                self._control_files.unlock()
612
583
                # set our support for tree references from the repository in
613
584
                # use.
614
585
                self._repo_supports_tree_reference = getattr(
615
 
                    self.branch.repository._format, "supports_tree_reference",
 
586
                    self.branch.repository._format, "support_tree_reference",
616
587
                    False)
617
588
            except:
618
589
                self._control_files.unlock()
637
608
        result = []
638
609
        if not from_paths:
639
610
            return result
 
611
 
640
612
        state = self.current_dirstate()
641
 
        if isinstance(from_paths, basestring):
642
 
            raise ValueError()
 
613
 
 
614
        assert not isinstance(from_paths, basestring)
643
615
        to_dir_utf8 = to_dir.encode('utf8')
644
616
        to_entry_dirname, to_basename = os.path.split(to_dir_utf8)
645
617
        id_index = state._get_id_index()
667
639
        if self._inventory is not None:
668
640
            update_inventory = True
669
641
            inv = self.inventory
 
642
            to_dir_ie = inv[to_dir_id]
670
643
            to_dir_id = to_entry[0][2]
671
 
            to_dir_ie = inv[to_dir_id]
672
644
        else:
673
645
            update_inventory = False
674
646
 
697
669
            new_entry = to_block[1][added_entry_index]
698
670
            rollbacks.append(lambda:state._make_absent(new_entry))
699
671
 
 
672
        # create rename entries and tuples
700
673
        for from_rel in from_paths:
701
674
            # from_rel is 'pathinroot/foo/bar'
702
675
            from_rel_utf8 = from_rel.encode('utf8')
738
711
                if from_missing: # implicitly just update our path mapping
739
712
                    move_file = False
740
713
                elif not after:
741
 
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
 
714
                    raise errors.RenameFailedFilesExist(from_rel, to_rel,
 
715
                        extra="(Use --after to update the Bazaar id)")
742
716
 
743
717
            rollbacks = []
744
718
            def rollback_rename():
799
773
 
800
774
                if minikind == 'd':
801
775
                    def update_dirblock(from_dir, to_key, to_dir_utf8):
802
 
                        """Recursively update all entries in this dirblock."""
803
 
                        if from_dir == '':
804
 
                            raise AssertionError("renaming root not supported")
 
776
                        """all entries in this block need updating.
 
777
 
 
778
                        TODO: This is pretty ugly, and doesn't support
 
779
                        reverting, but it works.
 
780
                        """
 
781
                        assert from_dir != '', "renaming root not supported"
805
782
                        from_key = (from_dir, '')
806
783
                        from_block_idx, present = \
807
784
                            state._find_block_index_from_key(from_key)
817
794
                        to_block_index = state._ensure_block(
818
795
                            to_block_index, to_entry_index, to_dir_utf8)
819
796
                        to_block = state._dirblocks[to_block_index]
820
 
 
821
 
                        # Grab a copy since move_one may update the list.
822
 
                        for entry in from_block[1][:]:
823
 
                            if not (entry[0][0] == from_dir):
824
 
                                raise AssertionError()
 
797
                        for entry in from_block[1]:
 
798
                            assert entry[0][0] == from_dir
825
799
                            cur_details = entry[1][0]
826
800
                            to_key = (to_dir_utf8, entry[0][1], entry[0][2])
827
801
                            from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
828
802
                            to_path_utf8 = osutils.pathjoin(to_dir_utf8, entry[0][1])
829
803
                            minikind = cur_details[0]
830
 
                            if minikind in 'ar':
831
 
                                # Deleted children of a renamed directory
832
 
                                # Do not need to be updated.
833
 
                                # Children that have been renamed out of this
834
 
                                # directory should also not be updated
835
 
                                continue
836
804
                            move_one(entry, from_path_utf8=from_path_utf8,
837
805
                                     minikind=minikind,
838
806
                                     executable=cur_details[3],
841
809
                                     size=cur_details[2],
842
810
                                     to_block=to_block,
843
811
                                     to_key=to_key,
844
 
                                     to_path_utf8=to_path_utf8)
 
812
                                     to_path_utf8=to_rel_utf8)
845
813
                            if minikind == 'd':
846
814
                                # We need to move all the children of this
847
815
                                # entry
952
920
            if not all_versioned:
953
921
                raise errors.PathsNotVersionedError(paths)
954
922
        # -- remove redundancy in supplied paths to prevent over-scanning --
955
 
        search_paths = osutils.minimum_path_selection(paths)
 
923
        search_paths = set()
 
924
        for path in paths:
 
925
            other_paths = paths.difference(set([path]))
 
926
            if not osutils.is_inside_any(other_paths, path):
 
927
                # this is a top level path, we must check it.
 
928
                search_paths.add(path)
956
929
        # sketch: 
957
930
        # for all search_indexs in each path at or under each element of
958
931
        # search_paths, if the detail is relocated: add the id, and add the
1026
999
 
1027
1000
        WorkingTree4 supplies revision_trees for any basis tree.
1028
1001
        """
 
1002
        revision_id = osutils.safe_revision_id(revision_id)
1029
1003
        dirstate = self.current_dirstate()
1030
1004
        parent_ids = dirstate.get_parent_ids()
1031
1005
        if revision_id not in parent_ids:
1038
1012
    @needs_tree_write_lock
1039
1013
    def set_last_revision(self, new_revision):
1040
1014
        """Change the last revision in the working tree."""
 
1015
        new_revision = osutils.safe_revision_id(new_revision)
1041
1016
        parents = self.get_parent_ids()
1042
1017
        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.")
 
1018
            assert len(parents) < 2, (
 
1019
                "setting the last parent to none with a pending merge is "
 
1020
                "unsupported.")
1047
1021
            self.set_parent_ids([])
1048
1022
        else:
1049
1023
            self.set_parent_ids([new_revision] + parents[1:],
1062
1036
        :param revision_ids: The revision_ids to set as the parent ids of this
1063
1037
            working tree. Any of these may be ghosts.
1064
1038
        """
 
1039
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1065
1040
        trees = []
1066
1041
        for revision_id in revision_ids:
1067
1042
            try:
1073
1048
            except (errors.NoSuchRevision, errors.RevisionNotPresent):
1074
1049
                revtree = None
1075
1050
            trees.append((revision_id, revtree))
 
1051
        self.current_dirstate()._validate()
1076
1052
        self.set_parent_trees(trees,
1077
1053
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
1054
        self.current_dirstate()._validate()
1078
1055
 
1079
1056
    @needs_tree_write_lock
1080
1057
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1085
1062
            parent tree - i.e. a ghost.
1086
1063
        """
1087
1064
        dirstate = self.current_dirstate()
 
1065
        dirstate._validate()
1088
1066
        if len(parents_list) > 0:
1089
1067
            if not allow_leftmost_as_ghost and parents_list[0][1] is None:
1090
1068
                raise errors.GhostRevisionUnusableHere(parents_list[0][0])
1091
1069
        real_trees = []
1092
1070
        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
1071
        # convert absent trees to the null tree, which we convert back to
1100
1072
        # missing on access.
1101
1073
        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
 
            _mod_revision.check_not_reserved_id(rev_id)
 
1074
            rev_id = osutils.safe_revision_id(rev_id)
1109
1075
            if tree is not None:
1110
1076
                real_trees.append((rev_id, tree))
1111
1077
            else:
1112
1078
                real_trees.append((rev_id,
1113
1079
                    self.branch.repository.revision_tree(None)))
1114
1080
                ghosts.append(rev_id)
1115
 
            accepted_revisions.add(rev_id)
 
1081
        dirstate._validate()
1116
1082
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
 
1083
        dirstate._validate()
1117
1084
        self._make_dirty(reset_inventory=False)
 
1085
        dirstate._validate()
1118
1086
 
1119
1087
    def _set_root_id(self, file_id):
1120
1088
        """See WorkingTree.set_root_id."""
1123
1091
        if state._dirblock_state == dirstate.DirState.IN_MEMORY_MODIFIED:
1124
1092
            self._make_dirty(reset_inventory=True)
1125
1093
 
1126
 
    def _sha_from_stat(self, path, stat_result):
1127
 
        """Get a sha digest from the tree's stat cache.
1128
 
 
1129
 
        The default implementation assumes no stat cache is present.
1130
 
 
1131
 
        :param path: The path.
1132
 
        :param stat_result: The stat result being looked up.
1133
 
        """
1134
 
        return self.current_dirstate().sha1_from_stat(path, stat_result)
1135
 
 
1136
1094
    @needs_read_lock
1137
1095
    def supports_tree_reference(self):
1138
1096
        return self._repo_supports_tree_reference
1139
1097
 
1140
1098
    def unlock(self):
1141
1099
        """Unlock in format 4 trees needs to write the entire dirstate."""
1142
 
        # do non-implementation specific cleanup
1143
 
        self._cleanup()
1144
 
 
1145
1100
        if self._control_files._lock_count == 1:
1146
1101
            # eventually we should do signature checking during read locks for
1147
1102
            # dirstate updates.
1178
1133
            return
1179
1134
        state = self.current_dirstate()
1180
1135
        state._read_dirblocks_if_needed()
1181
 
        ids_to_unversion = set(file_ids)
 
1136
        ids_to_unversion = set()
 
1137
        for file_id in file_ids:
 
1138
            ids_to_unversion.add(osutils.safe_file_id(file_id))
1182
1139
        paths_to_unversion = set()
1183
1140
        # sketch:
1184
1141
        # check if the root is to be unversioned, if so, assert for now.
1214
1171
                    # Mark this file id as having been removed
1215
1172
                    entry = block[1][entry_index]
1216
1173
                    ids_to_unversion.discard(entry[0][2])
1217
 
                    if (entry[1][0][0] in 'ar' # don't remove absent or renamed
1218
 
                                               # entries
 
1174
                    if (entry[1][0][0] == 'a'
1219
1175
                        or not state._make_absent(entry)):
1220
1176
                        entry_index += 1
1221
1177
                # go to the next block. (At the moment we dont delete empty
1247
1203
                self._inventory.remove_recursive_id(file_id)
1248
1204
 
1249
1205
    @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
 
    def update_basis_by_delta(self, new_revid, delta):
1263
 
        """See MutableTree.update_basis_by_delta."""
1264
 
        if self.last_revision() == new_revid:
1265
 
            raise AssertionError()
1266
 
        self.current_dirstate().update_basis_by_delta(delta, new_revid)
1267
 
 
1268
 
    @needs_read_lock
1269
 
    def _validate(self):
1270
 
        self._dirstate._validate()
1271
 
 
1272
 
    @needs_tree_write_lock
1273
1206
    def _write_inventory(self, inv):
1274
1207
        """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")
 
1208
        assert not self._dirty, "attempting to write an inventory when the dirstate is dirty will cause data loss"
1278
1209
        self.current_dirstate().set_state_from_inventory(inv)
1279
1210
        self._make_dirty(reset_inventory=False)
1280
1211
        if self._inventory is not None:
1294
1225
        - uses a LockDir to guard access to it.
1295
1226
    """
1296
1227
 
1297
 
    upgrade_recommended = False
1298
 
 
1299
 
    _tree_class = WorkingTree4
1300
 
 
1301
1228
    def get_format_string(self):
1302
1229
        """See WorkingTreeFormat.get_format_string()."""
1303
1230
        return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1306
1233
        """See WorkingTreeFormat.get_format_description()."""
1307
1234
        return "Working tree format 4"
1308
1235
 
1309
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1310
 
                   accelerator_tree=None, hardlink=False):
 
1236
    def initialize(self, a_bzrdir, revision_id=None):
1311
1237
        """See WorkingTreeFormat.initialize().
1312
1238
 
1313
1239
        :param revision_id: allows creating a working tree at a different
1314
1240
        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
1241
 
1322
 
        These trees get an initial random root id, if their repository supports
1323
 
        rich root data, TREE_ROOT otherwise.
 
1242
        These trees get an initial random root id.
1324
1243
        """
 
1244
        revision_id = osutils.safe_revision_id(revision_id)
1325
1245
        if not isinstance(a_bzrdir.transport, LocalTransport):
1326
1246
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1327
1247
        transport = a_bzrdir.get_workingtree_transport(self)
1328
1248
        control_files = self._open_control_files(a_bzrdir)
1329
1249
        control_files.create_lock()
1330
1250
        control_files.lock_write()
1331
 
        transport.put_bytes('format', self.get_format_string(),
1332
 
            mode=a_bzrdir._get_file_mode())
1333
 
        if from_branch is not None:
1334
 
            branch = from_branch
1335
 
        else:
1336
 
            branch = a_bzrdir.open_branch()
 
1251
        control_files.put_utf8('format', self.get_format_string())
 
1252
        branch = a_bzrdir.open_branch()
1337
1253
        if revision_id is None:
1338
1254
            revision_id = branch.last_revision()
1339
1255
        local_path = transport.local_abspath('dirstate')
1340
1256
        # write out new dirstate (must exist when we create the tree)
1341
1257
        state = dirstate.DirState.initialize(local_path)
1342
1258
        state.unlock()
1343
 
        del state
1344
 
        wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
 
1259
        wt = WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
1345
1260
                         branch,
1346
1261
                         _format=self,
1347
1262
                         _bzrdir=a_bzrdir,
1348
1263
                         _control_files=control_files)
1349
1264
        wt._new_tree()
1350
1265
        wt.lock_tree_write()
 
1266
        state._validate()
1351
1267
        try:
1352
 
            self._init_custom_control_files(wt)
1353
1268
            if revision_id in (None, NULL_REVISION):
1354
 
                if branch.repository.supports_rich_root():
1355
 
                    wt._set_root_id(generate_ids.gen_root_id())
1356
 
                else:
1357
 
                    wt._set_root_id(ROOT_ID)
 
1269
                wt._set_root_id(generate_ids.gen_root_id())
1358
1270
                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)]
 
1271
                wt.current_dirstate()._validate()
 
1272
            wt.set_last_revision(revision_id)
 
1273
            wt.flush()
 
1274
            basis = wt.basis_tree()
1374
1275
            basis.lock_read()
1375
 
            try:
1376
 
                wt.set_parent_trees(parents_list, allow_leftmost_as_ghost=True)
 
1276
            # if the basis has a root id we have to use that; otherwise we use
 
1277
            # a new random one
 
1278
            basis_root_id = basis.get_root_id()
 
1279
            if basis_root_id is not None:
 
1280
                wt._set_root_id(basis_root_id)
1377
1281
                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()
 
1282
            transform.build_tree(basis, wt)
 
1283
            basis.unlock()
1391
1284
        finally:
1392
1285
            control_files.unlock()
1393
1286
            wt.unlock()
1394
1287
        return wt
1395
1288
 
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
1289
    def _open(self, a_bzrdir, control_files):
1406
1290
        """Open the tree itself.
1407
1291
 
1408
1292
        :param a_bzrdir: the dir for the tree.
1409
1293
        :param control_files: the control files for the tree.
1410
1294
        """
1411
 
        return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
 
1295
        return WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
1412
1296
                           branch=a_bzrdir.open_branch(),
1413
1297
                           _format=self,
1414
1298
                           _bzrdir=a_bzrdir,
1427
1311
 
1428
1312
    def __init__(self, dirstate, revision_id, repository):
1429
1313
        self._dirstate = dirstate
1430
 
        self._revision_id = revision_id
 
1314
        self._revision_id = osutils.safe_revision_id(revision_id)
1431
1315
        self._repository = repository
1432
1316
        self._inventory = None
1433
1317
        self._locked = 0
1434
1318
        self._dirstate_locked = False
1435
 
        self._repo_supports_tree_reference = getattr(
1436
 
            repository._format, "supports_tree_reference",
1437
 
            False)
1438
1319
 
1439
1320
    def __repr__(self):
1440
1321
        return "<%s of %s in %s>" % \
1441
1322
            (self.__class__.__name__, self._revision_id, self._dirstate)
1442
1323
 
1443
 
    def annotate_iter(self, file_id,
1444
 
                      default_revision=_mod_revision.CURRENT_REVISION):
 
1324
    def annotate_iter(self, file_id):
1445
1325
        """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]
 
1326
        w = self._repository.weave_store.get_weave(file_id,
 
1327
                           self._repository.get_transaction())
 
1328
        return w.annotate_iter(self.inventory[file_id].revision)
1449
1329
 
1450
 
    def _get_ancestors(self, default_revision):
1451
 
        return set(self._repository.get_ancestry(self._revision_id,
1452
 
                                                 topo_sorted=False))
1453
1330
    def _comparison_data(self, entry, path):
1454
1331
        """See Tree._comparison_data."""
1455
1332
        if entry is None:
1472
1349
    def get_root_id(self):
1473
1350
        return self.path2id('')
1474
1351
 
1475
 
    def id2path(self, file_id):
1476
 
        "Convert a file-id to a path."
1477
 
        entry = self._get_entry(file_id=file_id)
1478
 
        if entry == (None, None):
1479
 
            raise errors.NoSuchId(tree=self, file_id=file_id)
1480
 
        path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
1481
 
        return path_utf8.decode('utf8')
1482
 
 
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
1352
    def _get_parent_index(self):
1492
1353
        """Return the index in the dirstate referenced by this tree."""
1493
1354
        return self._dirstate.get_parent_ids().index(self._revision_id) + 1
1505
1366
        """
1506
1367
        if file_id is None and path is None:
1507
1368
            raise errors.BzrError('must supply file_id or path')
 
1369
        file_id = osutils.safe_file_id(file_id)
1508
1370
        if path is not None:
1509
1371
            path = path.encode('utf8')
1510
1372
        parent_index = self._get_parent_index()
1518
1380
 
1519
1381
        This is relatively expensive: we have to walk the entire dirstate.
1520
1382
        """
1521
 
        if not self._locked:
1522
 
            raise AssertionError(
1523
 
                'cannot generate inventory of an unlocked '
1524
 
                'dirstate revision tree')
 
1383
        assert self._locked, 'cannot generate inventory of an unlocked '\
 
1384
            'dirstate revision tree'
1525
1385
        # separate call for profiling - makes it clear where the costs are.
1526
1386
        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()))
 
1387
        assert self._revision_id in self._dirstate.get_parent_ids(), \
 
1388
            'parent %s has disappeared from %s' % (
 
1389
            self._revision_id, self._dirstate.get_parent_ids())
1531
1390
        parent_index = self._dirstate.get_parent_ids().index(self._revision_id) + 1
1532
1391
        # This is identical now to the WorkingTree _generate_inventory except
1533
1392
        # for the tree index use.
1534
1393
        root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
1535
1394
        current_id = root_key[2]
1536
 
        if current_entry[parent_index][0] != 'd':
1537
 
            raise AssertionError()
 
1395
        assert current_entry[parent_index][0] == 'd'
1538
1396
        inv = Inventory(root_id=current_id, revision_id=self._revision_id)
1539
1397
        inv.root.revision = current_entry[parent_index][4]
1540
1398
        # Turn some things into local variables
1580
1438
                    raise AssertionError("cannot convert entry %r into an InventoryEntry"
1581
1439
                            % entry)
1582
1440
                # 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,))
 
1441
                assert file_id not in inv_byid
 
1442
                assert name_unicode not in parent_ie.children
1589
1443
                inv_byid[file_id] = inv_entry
1590
1444
                parent_ie.children[name_unicode] = inv_entry
1591
1445
        self._inventory = inv
1611
1465
            return parent_details[1]
1612
1466
        return None
1613
1467
 
1614
 
    def get_file(self, file_id, path=None):
 
1468
    def get_file(self, file_id):
1615
1469
        return StringIO(self.get_file_text(file_id))
1616
1470
 
1617
1471
    def get_file_lines(self, file_id):
1618
 
        return osutils.split_lines(self.get_file_text(file_id))
 
1472
        ie = self.inventory[file_id]
 
1473
        return self._repository.weave_store.get_weave(file_id,
 
1474
                self._repository.get_transaction()).get_lines(ie.revision)
1619
1475
 
1620
1476
    def get_file_size(self, file_id):
1621
 
        """See Tree.get_file_size"""
1622
1477
        return self.inventory[file_id].text_size
1623
1478
 
1624
1479
    def get_file_text(self, file_id):
1625
 
        return list(self.iter_files_bytes([(file_id, None)]))[0][1]
 
1480
        return ''.join(self.get_file_lines(file_id))
1626
1481
 
1627
1482
    def get_reference_revision(self, file_id, path=None):
1628
1483
        return self.inventory[file_id].reference_revision
1629
1484
 
1630
 
    def iter_files_bytes(self, desired_files):
1631
 
        """See Tree.iter_files_bytes.
1632
 
 
1633
 
        This version is implemented on top of Repository.iter_files_bytes"""
1634
 
        parent_index = self._get_parent_index()
1635
 
        repo_desired_files = []
1636
 
        for file_id, identifier in desired_files:
1637
 
            entry = self._get_entry(file_id)
1638
 
            if entry == (None, None):
1639
 
                raise errors.NoSuchId(self, file_id)
1640
 
            repo_desired_files.append((file_id, entry[1][parent_index][4],
1641
 
                                       identifier))
1642
 
        return self._repository.iter_files_bytes(repo_desired_files)
1643
 
 
1644
1485
    def get_symlink_target(self, file_id):
1645
1486
        entry = self._get_entry(file_id=file_id)
1646
1487
        parent_index = self._get_parent_index()
1674
1515
        return bool(self.path2id(filename))
1675
1516
 
1676
1517
    def kind(self, file_id):
1677
 
        entry = self._get_entry(file_id=file_id)[1]
1678
 
        if entry is None:
1679
 
            raise errors.NoSuchId(tree=self, file_id=file_id)
1680
 
        return dirstate.DirState._minikind_to_kind[entry[1][0]]
1681
 
 
1682
 
    def stored_kind(self, file_id):
1683
 
        """See Tree.stored_kind"""
1684
 
        return self.kind(file_id)
1685
 
 
1686
 
    def path_content_summary(self, path):
1687
 
        """See Tree.path_content_summary."""
1688
 
        id = self.inventory.path2id(path)
1689
 
        if id is None:
1690
 
            return ('missing', None, None, None)
1691
 
        entry = self._inventory[id]
1692
 
        kind = entry.kind
1693
 
        if kind == 'file':
1694
 
            return (kind, entry.text_size, entry.executable, entry.text_sha1)
1695
 
        elif kind == 'symlink':
1696
 
            return (kind, None, None, entry.symlink_target)
1697
 
        else:
1698
 
            return (kind, None, None, None)
 
1518
        return self.inventory[file_id].kind
1699
1519
 
1700
1520
    def is_executable(self, file_id, path=None):
1701
1521
        ie = self.inventory[file_id]
1747
1567
                self._dirstate_locked = False
1748
1568
            self._repository.unlock()
1749
1569
 
1750
 
    @needs_read_lock
1751
 
    def supports_tree_reference(self):
1752
 
        return self._repo_supports_tree_reference
1753
 
 
1754
1570
    def walkdirs(self, prefix=""):
1755
1571
        # TODO: jam 20070215 This is the lazy way by using the RevisionTree
1756
1572
        # implementation based on an inventory.  
1813
1629
    _matching_to_tree_format = WorkingTreeFormat4()
1814
1630
    _test_mutable_trees_to_test_trees = make_source_parent_tree
1815
1631
 
1816
 
    def iter_changes(self, include_unchanged=False,
 
1632
    def _iter_changes(self, include_unchanged=False,
1817
1633
                      specific_files=None, pb=None, extra_trees=[],
1818
1634
                      require_versioned=True, want_unversioned=False):
1819
1635
        """Return the changes from source to target.
1820
1636
 
1821
 
        :return: An iterator that yields tuples. See InterTree.iter_changes
 
1637
        :return: An iterator that yields tuples. See InterTree._iter_changes
1822
1638
            for details.
1823
1639
        :param specific_files: An optional list of file paths to restrict the
1824
1640
            comparison to. When mapping filenames to ids, all matches in all
1835
1651
            output. An unversioned file is defined as one with (False, False)
1836
1652
            for the versioned pair.
1837
1653
        """
1838
 
        utf8_decode = cache_utf8._utf8_decode
 
1654
        utf8_decode_or_none = cache_utf8._utf8_decode_with_None
1839
1655
        _minikind_to_kind = dirstate.DirState._minikind_to_kind
1840
 
        cmp_by_dirs = dirstate.cmp_by_dirs
1841
1656
        # NB: show_status depends on being able to pass in non-versioned files
1842
1657
        # and report them as unknown
1843
1658
        # TODO: handle extra trees in the dirstate.
1844
 
        if (extra_trees or specific_files == []):
 
1659
        # TODO: handle comparisons as an empty tree as a different special
 
1660
        # case? mbp 20070226
 
1661
        if extra_trees or (self.source._revision_id == NULL_REVISION):
1845
1662
            # we can't fast-path these cases (yet)
1846
 
            for f in super(InterDirStateTree, self).iter_changes(
 
1663
            for f in super(InterDirStateTree, self)._iter_changes(
1847
1664
                include_unchanged, specific_files, pb, extra_trees,
1848
1665
                require_versioned, want_unversioned=want_unversioned):
1849
1666
                yield f
1850
1667
            return
1851
1668
        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))
 
1669
        assert (self.source._revision_id in parent_ids), \
 
1670
                "revision {%s} is not stored in {%s}, but %s " \
 
1671
                "can only be used for trees stored in the dirstate" \
 
1672
                % (self.source._revision_id, self.target, self._iter_changes)
1858
1673
        target_index = 0
1859
1674
        if self.source._revision_id == NULL_REVISION:
1860
1675
            source_index = None
1861
1676
            indices = (target_index,)
1862
1677
        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))
 
1678
            assert (self.source._revision_id in parent_ids), \
 
1679
                "Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
 
1680
                self.source._revision_id, parent_ids)
1867
1681
            source_index = 1 + parent_ids.index(self.source._revision_id)
1868
 
            indices = (source_index, target_index)
 
1682
            indices = (source_index,target_index)
1869
1683
        # -- make all specific_files utf8 --
1870
1684
        if specific_files:
1871
1685
            specific_files_utf8 = set()
1971
1785
        NULL_PARENT_DETAILS = dirstate.DirState.NULL_PARENT_DETAILS
1972
1786
        # Using a list so that we can access the values and change them in
1973
1787
        # nested scope. Each one is [path, file_id, entry]
1974
 
        last_source_parent = [None, None]
1975
 
        last_target_parent = [None, None]
 
1788
        last_source_parent = [None, None, None]
 
1789
        last_target_parent = [None, None, None]
1976
1790
 
1977
1791
        use_filesystem_for_exec = (sys.platform != 'win32')
1978
1792
 
1979
 
        # Just a sentry, so that _process_entry can say that this
1980
 
        # record is handled, but isn't interesting to process (unchanged)
1981
 
        uninteresting = object()
1982
 
 
1983
 
 
1984
 
        old_dirname_to_file_id = {}
1985
 
        new_dirname_to_file_id = {}
1986
 
        # TODO: jam 20070516 - Avoid the _get_entry lookup overhead by
1987
 
        #       keeping a cache of directories that we have seen.
1988
 
 
1989
1793
        def _process_entry(entry, path_info):
1990
1794
            """Compare an entry and real disk to generate delta information.
1991
1795
 
1995
1799
                Basename is returned as a utf8 string because we expect this
1996
1800
                tuple will be ignored, and don't want to take the time to
1997
1801
                decode.
1998
 
            :return: None if these don't match
1999
 
                     A tuple of information about the change, or
2000
 
                     the object 'uninteresting' if these match, but are
2001
 
                     basically identical.
2002
1802
            """
2003
1803
            if source_index is None:
2004
1804
                source_details = NULL_PARENT_DETAILS
2007
1807
            target_details = entry[1][target_index]
2008
1808
            target_minikind = target_details[0]
2009
1809
            if path_info is not None and target_minikind in 'fdlt':
2010
 
                if not (target_index == 0):
2011
 
                    raise AssertionError()
 
1810
                assert target_index == 0
2012
1811
                link_or_sha1 = state.update_entry(entry, abspath=path_info[4],
2013
1812
                                                  stat_value=path_info[3])
2014
1813
                # The entry may have been modified by update_entry
2016
1815
                target_minikind = target_details[0]
2017
1816
            else:
2018
1817
                link_or_sha1 = None
2019
 
            file_id = entry[0][2]
2020
1818
            source_minikind = source_details[0]
2021
1819
            if source_minikind in 'fdltr' and target_minikind in 'fdlt':
2022
1820
                # claimed content in both: diff
2039
1837
                                                 path_utf8=old_path)
2040
1838
                    # update the source details variable to be the real
2041
1839
                    # location.
2042
 
                    if old_entry == (None, None):
2043
 
                        raise errors.CorruptDirstate(state._filename,
2044
 
                            "entry '%s/%s' is considered renamed from %r"
2045
 
                            " but source does not exist\n"
2046
 
                            "entry: %s" % (entry[0][0], entry[0][1], old_path, entry))
2047
1840
                    source_details = old_entry[1][source_index]
2048
1841
                    source_minikind = source_details[0]
2049
1842
                else:
2050
1843
                    old_dirname = entry[0][0]
2051
1844
                    old_basename = entry[0][1]
2052
 
                    old_path = path = None
 
1845
                    old_path = path = pathjoin(old_dirname, old_basename)
2053
1846
                if path_info is None:
2054
1847
                    # the file is missing on disk, show as removed.
2055
1848
                    content_change = True
2059
1852
                    # source and target are both versioned and disk file is present.
2060
1853
                    target_kind = path_info[2]
2061
1854
                    if target_kind == 'directory':
2062
 
                        if path is None:
2063
 
                            old_path = path = pathjoin(old_dirname, old_basename)
2064
 
                        new_dirname_to_file_id[path] = file_id
2065
1855
                        if source_minikind != 'd':
2066
1856
                            content_change = True
2067
1857
                        else:
2096
1886
                        target_exec = False
2097
1887
                    else:
2098
1888
                        raise Exception, "unknown kind %s" % path_info[2]
2099
 
                if source_minikind == 'd':
2100
 
                    if path is None:
2101
 
                        old_path = path = pathjoin(old_dirname, old_basename)
2102
 
                    old_dirname_to_file_id[old_path] = file_id
2103
1889
                # parent id is the entry for the path in the target tree
2104
1890
                if old_dirname == last_source_parent[0]:
2105
1891
                    source_parent_id = last_source_parent[1]
2106
1892
                else:
2107
 
                    try:
2108
 
                        source_parent_id = old_dirname_to_file_id[old_dirname]
2109
 
                    except KeyError:
2110
 
                        source_parent_entry = state._get_entry(source_index,
2111
 
                                                               path_utf8=old_dirname)
2112
 
                        source_parent_id = source_parent_entry[0][2]
 
1893
                    source_parent_entry = state._get_entry(source_index,
 
1894
                                                           path_utf8=old_dirname)
 
1895
                    source_parent_id = source_parent_entry[0][2]
2113
1896
                    if source_parent_id == entry[0][2]:
2114
1897
                        # This is the root, so the parent is None
2115
1898
                        source_parent_id = None
2116
1899
                    else:
2117
1900
                        last_source_parent[0] = old_dirname
2118
1901
                        last_source_parent[1] = source_parent_id
 
1902
                        last_source_parent[2] = source_parent_entry
2119
1903
                new_dirname = entry[0][0]
2120
1904
                if new_dirname == last_target_parent[0]:
2121
1905
                    target_parent_id = last_target_parent[1]
2122
1906
                else:
2123
 
                    try:
2124
 
                        target_parent_id = new_dirname_to_file_id[new_dirname]
2125
 
                    except KeyError:
2126
 
                        # TODO: We don't always need to do the lookup, because the
2127
 
                        #       parent entry will be the same as the source entry.
2128
 
                        target_parent_entry = state._get_entry(target_index,
2129
 
                                                               path_utf8=new_dirname)
2130
 
                        if target_parent_entry == (None, None):
2131
 
                            raise AssertionError(
2132
 
                                "Could not find target parent in wt: %s\nparent of: %s"
2133
 
                                % (new_dirname, entry))
2134
 
                        target_parent_id = target_parent_entry[0][2]
 
1907
                    # TODO: We don't always need to do the lookup, because the
 
1908
                    #       parent entry will be the same as the source entry.
 
1909
                    target_parent_entry = state._get_entry(target_index,
 
1910
                                                           path_utf8=new_dirname)
 
1911
                    target_parent_id = target_parent_entry[0][2]
2135
1912
                    if target_parent_id == entry[0][2]:
2136
1913
                        # This is the root, so the parent is None
2137
1914
                        target_parent_id = None
2138
1915
                    else:
2139
1916
                        last_target_parent[0] = new_dirname
2140
1917
                        last_target_parent[1] = target_parent_id
 
1918
                        last_target_parent[2] = target_parent_entry
2141
1919
 
2142
1920
                source_exec = source_details[3]
2143
 
                if (include_unchanged
2144
 
                    or content_change
2145
 
                    or source_parent_id != target_parent_id
2146
 
                    or old_basename != entry[0][1]
2147
 
                    or source_exec != target_exec
2148
 
                    ):
2149
 
                    if old_path is None:
2150
 
                        old_path = path = pathjoin(old_dirname, old_basename)
2151
 
                        old_path_u = utf8_decode(old_path)[0]
2152
 
                        path_u = old_path_u
2153
 
                    else:
2154
 
                        old_path_u = utf8_decode(old_path)[0]
2155
 
                        if old_path == path:
2156
 
                            path_u = old_path_u
2157
 
                        else:
2158
 
                            path_u = utf8_decode(path)[0]
2159
 
                    source_kind = _minikind_to_kind[source_minikind]
2160
 
                    return (entry[0][2],
2161
 
                           (old_path_u, path_u),
2162
 
                           content_change,
2163
 
                           (True, True),
2164
 
                           (source_parent_id, target_parent_id),
2165
 
                           (utf8_decode(old_basename)[0], utf8_decode(entry[0][1])[0]),
2166
 
                           (source_kind, target_kind),
2167
 
                           (source_exec, target_exec))
2168
 
                else:
2169
 
                    return uninteresting
 
1921
                return ((entry[0][2], (old_path, path), content_change,
 
1922
                        (True, True),
 
1923
                        (source_parent_id, target_parent_id),
 
1924
                        (old_basename, entry[0][1]),
 
1925
                        (_minikind_to_kind[source_minikind], target_kind),
 
1926
                        (source_exec, target_exec)),)
2170
1927
            elif source_minikind in 'a' and target_minikind in 'fdlt':
2171
1928
                # looks like a new file
2172
1929
                if path_info is not None:
2185
1942
                            and stat.S_IEXEC & path_info[3].st_mode)
2186
1943
                    else:
2187
1944
                        target_exec = target_details[3]
2188
 
                    return (entry[0][2],
2189
 
                           (None, utf8_decode(path)[0]),
2190
 
                           True,
2191
 
                           (False, True),
2192
 
                           (None, parent_id),
2193
 
                           (None, utf8_decode(entry[0][1])[0]),
2194
 
                           (None, path_info[2]),
2195
 
                           (None, target_exec))
 
1945
                    return ((entry[0][2], (None, path), True,
 
1946
                            (False, True),
 
1947
                            (None, parent_id),
 
1948
                            (None, entry[0][1]),
 
1949
                            (None, path_info[2]),
 
1950
                            (None, target_exec)),)
2196
1951
                else:
2197
1952
                    # but its not on disk: we deliberately treat this as just
2198
1953
                    # never-present. (Why ?! - RBC 20070224)
2207
1962
                parent_id = state._get_entry(source_index, path_utf8=entry[0][0])[0][2]
2208
1963
                if parent_id == entry[0][2]:
2209
1964
                    parent_id = None
2210
 
                return (entry[0][2],
2211
 
                       (utf8_decode(old_path)[0], None),
2212
 
                       True,
2213
 
                       (True, False),
2214
 
                       (parent_id, None),
2215
 
                       (utf8_decode(entry[0][1])[0], None),
2216
 
                       (_minikind_to_kind[source_minikind], None),
2217
 
                       (source_details[3], None))
 
1965
                return ((entry[0][2], (old_path, None), True,
 
1966
                        (True, False),
 
1967
                        (parent_id, None),
 
1968
                        (entry[0][1], None),
 
1969
                        (_minikind_to_kind[source_minikind], None),
 
1970
                        (source_details[3], None)),)
2218
1971
            elif source_minikind in 'fdlt' and target_minikind in 'r':
2219
1972
                # a rename; could be a true rename, or a rename inherited from
2220
1973
                # a renamed parent. TODO: handle this efficiently. Its not
2232
1985
                    "source_minikind=%r, target_minikind=%r"
2233
1986
                    % (source_minikind, target_minikind))
2234
1987
                ## import pdb;pdb.set_trace()
2235
 
            return None
 
1988
            return ()
2236
1989
 
2237
1990
        while search_specific_files:
2238
1991
            # TODO: the pending list should be lexically sorted?  the
2268
2021
                continue
2269
2022
            path_handled = False
2270
2023
            for entry in root_entries:
2271
 
                result = _process_entry(entry, root_dir_info)
2272
 
                if result is not None:
 
2024
                for result in _process_entry(entry, root_dir_info):
 
2025
                    # this check should probably be outside the loop: one
 
2026
                    # 'iterate two trees' api, and then _iter_changes filters
 
2027
                    # unchanged pairs. - RBC 20070226
2273
2028
                    path_handled = True
2274
 
                    if result is not uninteresting:
2275
 
                        yield result
 
2029
                    if (include_unchanged
 
2030
                        or result[2]                    # content change
 
2031
                        or result[3][0] != result[3][1] # versioned status
 
2032
                        or result[4][0] != result[4][1] # parent id
 
2033
                        or result[5][0] != result[5][1] # name
 
2034
                        or result[6][0] != result[6][1] # kind
 
2035
                        or result[7][0] != result[7][1] # executable
 
2036
                        ):
 
2037
                        yield (result[0],
 
2038
                               (utf8_decode_or_none(result[1][0]),
 
2039
                                utf8_decode_or_none(result[1][1])),
 
2040
                               result[2],
 
2041
                               result[3],
 
2042
                               result[4],
 
2043
                               (utf8_decode_or_none(result[5][0]),
 
2044
                                utf8_decode_or_none(result[5][1])),
 
2045
                               result[6],
 
2046
                               result[7],
 
2047
                              )
2276
2048
            if want_unversioned and not path_handled and root_dir_info:
2277
2049
                new_executable = bool(
2278
2050
                    stat.S_ISREG(root_dir_info[3].st_mode)
2303
2075
                    # python 2.5 has e.errno == EINVAL,
2304
2076
                    #            and e.winerror == ERROR_DIRECTORY
2305
2077
                    e_winerror = getattr(e, 'winerror', None)
2306
 
                    win_errors = (ERROR_DIRECTORY, ERROR_PATH_NOT_FOUND)
2307
2078
                    # there may be directories in the inventory even though
2308
2079
                    # this path is not a file on disk: so mark it as end of
2309
2080
                    # iterator
2310
2081
                    if e.errno in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
2311
2082
                        current_dir_info = None
2312
2083
                    elif (sys.platform == 'win32'
2313
 
                          and (e.errno in win_errors
2314
 
                               or e_winerror in win_errors)):
 
2084
                          and ERROR_DIRECTORY in (e.errno, e_winerror)):
2315
2085
                        current_dir_info = None
2316
2086
                    else:
2317
2087
                        raise
2319
2089
                    if current_dir_info[0][0] == '':
2320
2090
                        # remove .bzr from iteration
2321
2091
                        bzr_index = bisect_left(current_dir_info[1], ('.bzr',))
2322
 
                        if current_dir_info[1][bzr_index][0] != '.bzr':
2323
 
                            raise AssertionError()
 
2092
                        assert current_dir_info[1][bzr_index][0] == '.bzr'
2324
2093
                        del current_dir_info[1][bzr_index]
2325
2094
            # walk until both the directory listing and the versioned metadata
2326
2095
            # are exhausted. 
2333
2102
                   current_block is not None):
2334
2103
                if (current_dir_info and current_block
2335
2104
                    and current_dir_info[0][0] != current_block[0]):
2336
 
                    if cmp_by_dirs(current_dir_info[0][0], current_block[0]) < 0:
 
2105
                    if current_dir_info[0][0] < current_block[0] :
2337
2106
                        # filesystem data refers to paths not covered by the dirblock.
2338
2107
                        # this has two possibilities:
2339
2108
                        # A) it is versioned but empty, so there is no block for it
2340
2109
                        # B) it is not versioned.
2341
 
 
2342
 
                        # if (A) then we need to recurse into it to check for
2343
 
                        # new unknown files or directories.
2344
 
                        # if (B) then we should ignore it, because we don't
2345
 
                        # recurse into unknown directories.
2346
 
                        path_index = 0
2347
 
                        while path_index < len(current_dir_info[1]):
2348
 
                                current_path_info = current_dir_info[1][path_index]
2349
 
                                if want_unversioned:
2350
 
                                    if current_path_info[2] == 'directory':
2351
 
                                        if self.target._directory_is_tree_reference(
2352
 
                                            current_path_info[0].decode('utf8')):
2353
 
                                            current_path_info = current_path_info[:2] + \
2354
 
                                                ('tree-reference',) + current_path_info[3:]
2355
 
                                    new_executable = bool(
2356
 
                                        stat.S_ISREG(current_path_info[3].st_mode)
2357
 
                                        and stat.S_IEXEC & current_path_info[3].st_mode)
2358
 
                                    yield (None,
2359
 
                                        (None, utf8_decode(current_path_info[0])[0]),
2360
 
                                        True,
2361
 
                                        (False, False),
2362
 
                                        (None, None),
2363
 
                                        (None, utf8_decode(current_path_info[1])[0]),
2364
 
                                        (None, current_path_info[2]),
2365
 
                                        (None, new_executable))
2366
 
                                # dont descend into this unversioned path if it is
2367
 
                                # a dir
2368
 
                                if current_path_info[2] in ('directory',
2369
 
                                                            'tree-reference'):
2370
 
                                    del current_dir_info[1][path_index]
2371
 
                                    path_index -= 1
2372
 
                                path_index += 1
2373
 
 
2374
 
                        # This dir info has been handled, go to the next
 
2110
                        # in either case it was processed by the containing directories walk:
 
2111
                        # if it is root/foo, when we walked root we emitted it,
 
2112
                        # or if we ere given root/foo to walk specifically, we
 
2113
                        # emitted it when checking the walk-root entries
 
2114
                        # advance the iterator and loop - we dont need to emit it.
2375
2115
                        try:
2376
2116
                            current_dir_info = dir_iterator.next()
2377
2117
                        except StopIteration:
2387
2127
                        for current_entry in current_block[1]:
2388
2128
                            # entry referring to file not present on disk.
2389
2129
                            # advance the entry only, after processing.
2390
 
                            result = _process_entry(current_entry, None)
2391
 
                            if result is not None:
2392
 
                                if result is not uninteresting:
2393
 
                                    yield result
 
2130
                            for result in _process_entry(current_entry, None):
 
2131
                                # this check should probably be outside the loop: one
 
2132
                                # 'iterate two trees' api, and then _iter_changes filters
 
2133
                                # unchanged pairs. - RBC 20070226
 
2134
                                if (include_unchanged
 
2135
                                    or result[2]                    # content change
 
2136
                                    or result[3][0] != result[3][1] # versioned status
 
2137
                                    or result[4][0] != result[4][1] # parent id
 
2138
                                    or result[5][0] != result[5][1] # name
 
2139
                                    or result[6][0] != result[6][1] # kind
 
2140
                                    or result[7][0] != result[7][1] # executable
 
2141
                                    ):
 
2142
                                    yield (result[0],
 
2143
                                           (utf8_decode_or_none(result[1][0]),
 
2144
                                            utf8_decode_or_none(result[1][1])),
 
2145
                                           result[2],
 
2146
                                           result[3],
 
2147
                                           result[4],
 
2148
                                           (utf8_decode_or_none(result[5][0]),
 
2149
                                            utf8_decode_or_none(result[5][1])),
 
2150
                                           result[6],
 
2151
                                           result[7],
 
2152
                                          )
2394
2153
                        block_index +=1
2395
2154
                        if (block_index < len(state._dirblocks) and
2396
2155
                            osutils.is_inside(current_root,
2425
2184
                        pass
2426
2185
                    elif current_path_info is None:
2427
2186
                        # no path is fine: the per entry code will handle it.
2428
 
                        result = _process_entry(current_entry, current_path_info)
2429
 
                        if result is not None:
2430
 
                            if result is not uninteresting:
2431
 
                                yield result
2432
 
                    elif (current_entry[0][1] != current_path_info[1]
2433
 
                          or current_entry[1][target_index][0] in 'ar'):
2434
 
                        # The current path on disk doesn't match the dirblock
2435
 
                        # record. Either the dirblock is marked as absent, or
2436
 
                        # the file on disk is not present at all in the
2437
 
                        # dirblock. Either way, report about the dirblock
2438
 
                        # entry, and let other code handle the filesystem one.
2439
 
 
2440
 
                        # Compare the basename for these files to determine
2441
 
                        # which comes first
 
2187
                        for result in _process_entry(current_entry, current_path_info):
 
2188
                            # this check should probably be outside the loop: one
 
2189
                            # 'iterate two trees' api, and then _iter_changes filters
 
2190
                            # unchanged pairs. - RBC 20070226
 
2191
                            if (include_unchanged
 
2192
                                or result[2]                    # content change
 
2193
                                or result[3][0] != result[3][1] # versioned status
 
2194
                                or result[4][0] != result[4][1] # parent id
 
2195
                                or result[5][0] != result[5][1] # name
 
2196
                                or result[6][0] != result[6][1] # kind
 
2197
                                or result[7][0] != result[7][1] # executable
 
2198
                                ):
 
2199
                                yield (result[0],
 
2200
                                       (utf8_decode_or_none(result[1][0]),
 
2201
                                        utf8_decode_or_none(result[1][1])),
 
2202
                                       result[2],
 
2203
                                       result[3],
 
2204
                                       result[4],
 
2205
                                       (utf8_decode_or_none(result[5][0]),
 
2206
                                        utf8_decode_or_none(result[5][1])),
 
2207
                                       result[6],
 
2208
                                       result[7],
 
2209
                                      )
 
2210
                    elif current_entry[0][1] != current_path_info[1]:
2442
2211
                        if current_path_info[1] < current_entry[0][1]:
2443
2212
                            # extra file on disk: pass for now, but only
2444
2213
                            # increment the path, not the entry
2446
2215
                        else:
2447
2216
                            # entry referring to file not present on disk.
2448
2217
                            # advance the entry only, after processing.
2449
 
                            result = _process_entry(current_entry, None)
2450
 
                            if result is not None:
2451
 
                                if result is not uninteresting:
2452
 
                                    yield result
 
2218
                            for result in _process_entry(current_entry, None):
 
2219
                                # this check should probably be outside the loop: one
 
2220
                                # 'iterate two trees' api, and then _iter_changes filters
 
2221
                                # unchanged pairs. - RBC 20070226
 
2222
                                path_handled = True
 
2223
                                if (include_unchanged
 
2224
                                    or result[2]                    # content change
 
2225
                                    or result[3][0] != result[3][1] # versioned status
 
2226
                                    or result[4][0] != result[4][1] # parent id
 
2227
                                    or result[5][0] != result[5][1] # name
 
2228
                                    or result[6][0] != result[6][1] # kind
 
2229
                                    or result[7][0] != result[7][1] # executable
 
2230
                                    ):
 
2231
                                    yield (result[0],
 
2232
                                           (utf8_decode_or_none(result[1][0]),
 
2233
                                            utf8_decode_or_none(result[1][1])),
 
2234
                                           result[2],
 
2235
                                           result[3],
 
2236
                                           result[4],
 
2237
                                           (utf8_decode_or_none(result[5][0]),
 
2238
                                            utf8_decode_or_none(result[5][1])),
 
2239
                                           result[6],
 
2240
                                           result[7],
 
2241
                                          )
2453
2242
                            advance_path = False
2454
2243
                    else:
2455
 
                        result = _process_entry(current_entry, current_path_info)
2456
 
                        if result is not None:
 
2244
                        for result in _process_entry(current_entry, current_path_info):
 
2245
                            # this check should probably be outside the loop: one
 
2246
                            # 'iterate two trees' api, and then _iter_changes filters
 
2247
                            # unchanged pairs. - RBC 20070226
2457
2248
                            path_handled = True
2458
 
                            if result is not uninteresting:
2459
 
                                yield result
 
2249
                            if (include_unchanged
 
2250
                                or result[2]                    # content change
 
2251
                                or result[3][0] != result[3][1] # versioned status
 
2252
                                or result[4][0] != result[4][1] # parent id
 
2253
                                or result[5][0] != result[5][1] # name
 
2254
                                or result[6][0] != result[6][1] # kind
 
2255
                                or result[7][0] != result[7][1] # executable
 
2256
                                ):
 
2257
                                yield (result[0],
 
2258
                                       (utf8_decode_or_none(result[1][0]),
 
2259
                                        utf8_decode_or_none(result[1][1])),
 
2260
                                       result[2],
 
2261
                                       result[3],
 
2262
                                       result[4],
 
2263
                                       (utf8_decode_or_none(result[5][0]),
 
2264
                                        utf8_decode_or_none(result[5][1])),
 
2265
                                       result[6],
 
2266
                                       result[7],
 
2267
                                      )
2460
2268
                    if advance_entry and current_entry is not None:
2461
2269
                        entry_index += 1
2462
2270
                        if entry_index < len(current_block[1]):
2472
2280
                                new_executable = bool(
2473
2281
                                    stat.S_ISREG(current_path_info[3].st_mode)
2474
2282
                                    and stat.S_IEXEC & current_path_info[3].st_mode)
2475
 
                                try:
2476
 
                                    relpath_unicode = utf8_decode(current_path_info[0])[0]
2477
 
                                except UnicodeDecodeError:
2478
 
                                    raise errors.BadFilenameEncoding(
2479
 
                                        current_path_info[0], osutils._fs_enc)
2480
 
                                yield (None,
2481
 
                                    (None, relpath_unicode),
2482
 
                                    True,
2483
 
                                    (False, False),
2484
 
                                    (None, None),
2485
 
                                    (None, utf8_decode(current_path_info[1])[0]),
2486
 
                                    (None, current_path_info[2]),
2487
 
                                    (None, new_executable))
 
2283
                                if want_unversioned:
 
2284
                                    yield (None,
 
2285
                                        (None, utf8_decode_or_none(current_path_info[0])),
 
2286
                                        True,
 
2287
                                        (False, False),
 
2288
                                        (None, None),
 
2289
                                        (None, utf8_decode_or_none(current_path_info[1])),
 
2290
                                        (None, current_path_info[2]),
 
2291
                                        (None, new_executable))
2488
2292
                            # dont descend into this unversioned path if it is
2489
2293
                            # a dir
2490
2294
                            if current_path_info[2] in ('directory'):
2582
2386
 
2583
2387
    def update_format(self, tree):
2584
2388
        """Change the format marker."""
2585
 
        tree._transport.put_bytes('format',
2586
 
            self.target_format.get_format_string(),
2587
 
            mode=tree.bzrdir._get_file_mode())
 
2389
        tree._control_files.put_utf8('format',
 
2390
            self.target_format.get_format_string())