~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

  • Committer: Robert Collins
  • Date: 2009-07-07 04:32:13 UTC
  • mto: This revision was merged to the branch mainline in revision 4524.
  • Revision ID: robertc@robertcollins.net-20090707043213-4hjjhgr40iq7gk2d
More informative assertions in xml serialisation.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 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
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""WorkingTree4 format and implementation.
18
18
 
28
28
 
29
29
from bzrlib.lazy_import import lazy_import
30
30
lazy_import(globals(), """
31
 
from bisect import bisect_left
32
 
import collections
33
 
from copy import deepcopy
34
31
import errno
35
 
import itertools
36
 
import operator
37
32
import stat
38
 
from time import time
39
 
import warnings
40
33
 
41
34
import bzrlib
42
35
from bzrlib import (
43
36
    bzrdir,
44
37
    cache_utf8,
45
 
    conflicts as _mod_conflicts,
46
 
    delta,
 
38
    debug,
47
39
    dirstate,
48
40
    errors,
49
41
    generate_ids,
50
 
    globbing,
51
 
    hashcache,
52
 
    ignores,
53
 
    merge,
54
42
    osutils,
55
43
    revision as _mod_revision,
56
44
    revisiontree,
57
 
    textui,
 
45
    trace,
58
46
    transform,
59
 
    urlutils,
60
 
    xml5,
61
 
    xml6,
 
47
    views,
62
48
    )
63
49
import bzrlib.branch
64
 
from bzrlib.transport import get_transport
65
50
import bzrlib.ui
66
51
""")
67
52
 
68
 
from bzrlib import symbol_versioning
69
53
from bzrlib.decorators import needs_read_lock, needs_write_lock
70
 
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, entry_factory
71
 
from bzrlib.lockable_files import LockableFiles, TransportLock
72
 
from bzrlib.lockdir import LockDir
 
54
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
 
55
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
73
56
import bzrlib.mutabletree
74
57
from bzrlib.mutabletree import needs_tree_write_lock
75
58
from bzrlib.osutils import (
76
59
    file_kind,
77
60
    isdir,
78
 
    normpath,
79
61
    pathjoin,
80
 
    rand_chars,
81
62
    realpath,
82
63
    safe_unicode,
83
 
    splitpath,
84
64
    )
85
 
from bzrlib.trace import mutter, note
 
65
from bzrlib.trace import mutter
86
66
from bzrlib.transport.local import LocalTransport
87
67
from bzrlib.tree import InterTree
88
 
from bzrlib.progress import DummyProgress, ProgressPhase
89
 
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
90
 
from bzrlib.rio import RioReader, rio_file, Stanza
91
 
from bzrlib.symbol_versioning import (deprecated_passed,
92
 
        deprecated_method,
93
 
        deprecated_function,
94
 
        DEPRECATED_PARAMETER,
95
 
        )
96
68
from bzrlib.tree import Tree
97
69
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
98
70
 
99
71
 
100
 
# This is the Windows equivalent of ENOTDIR
101
 
# It is defined in pywin32.winerror, but we don't want a strong dependency for
102
 
# just an error code.
103
 
ERROR_PATH_NOT_FOUND = 3
104
 
ERROR_DIRECTORY = 267
105
 
 
106
 
 
107
 
class WorkingTree4(WorkingTree3):
108
 
    """This is the Format 4 working tree.
109
 
 
110
 
    This differs from WorkingTree3 by:
111
 
     - Having a consolidated internal dirstate, stored in a
112
 
       randomly-accessible sorted file on disk.
113
 
     - Not having a regular inventory attribute.  One can be synthesized 
114
 
       on demand but this is expensive and should be avoided.
115
 
 
116
 
    This is new in bzr 0.15.
117
 
    """
118
 
 
 
72
class DirStateWorkingTree(WorkingTree3):
119
73
    def __init__(self, basedir,
120
74
                 branch,
121
75
                 _control_files=None,
130
84
        """
131
85
        self._format = _format
132
86
        self.bzrdir = _bzrdir
133
 
        from bzrlib.trace import note, mutter
134
 
        assert isinstance(basedir, basestring), \
135
 
            "base directory %r is not a string" % basedir
136
87
        basedir = safe_unicode(basedir)
137
88
        mutter("opening working tree %r", basedir)
138
89
        self._branch = branch
139
 
        assert isinstance(self.branch, bzrlib.branch.Branch), \
140
 
            "branch %r is not a Branch" % self.branch
141
90
        self.basedir = realpath(basedir)
142
91
        # if branch is at our basedir and is a format 6 or less
143
92
        # assume all other formats have their own control files.
144
 
        assert isinstance(_control_files, LockableFiles), \
145
 
            "_control_files must be a LockableFiles, not %r" % _control_files
146
93
        self._control_files = _control_files
 
94
        self._transport = self._control_files._transport
147
95
        self._dirty = None
148
96
        #-------------
149
97
        # during a read or write lock these objects are set, and are
151
99
        self._dirstate = None
152
100
        self._inventory = None
153
101
        #-------------
 
102
        self._setup_directory_is_tree_reference()
 
103
        self._detect_case_handling()
 
104
        self._rules_searcher = None
 
105
        self.views = self._make_views()
 
106
        #--- allow tests to select the dirstate iter_changes implementation
 
107
        self._iter_changes = dirstate._process_entry
154
108
 
155
109
    @needs_tree_write_lock
156
110
    def _add(self, files, ids, kinds):
158
112
        state = self.current_dirstate()
159
113
        for f, file_id, kind in zip(files, ids, kinds):
160
114
            f = f.strip('/')
161
 
            assert '//' not in f
162
 
            assert '..' not in f
163
115
            if self.path2id(f):
164
116
                # special case tree root handling.
165
117
                if f == '' and self.path2id(f) == ROOT_ID:
186
138
    @needs_tree_write_lock
187
139
    def add_reference(self, sub_tree):
188
140
        # use standard implementation, which calls back to self._add
189
 
        # 
 
141
        #
190
142
        # So we don't store the reference_revision in the working dirstate,
191
 
        # it's just recorded at the moment of commit. 
 
143
        # it's just recorded at the moment of commit.
192
144
        self._add_reference(sub_tree)
193
145
 
194
146
    def break_lock(self):
233
185
            WorkingTree3._comparison_data(self, entry, path)
234
186
        # it looks like a plain directory, but it's really a reference -- see
235
187
        # also kind()
236
 
        if (self._repo_supports_tree_reference and
237
 
            kind == 'directory' and
238
 
            self._directory_is_tree_reference(path)):
 
188
        if (self._repo_supports_tree_reference and kind == 'directory'
 
189
            and entry is not None and entry.kind == 'tree-reference'):
239
190
            kind = 'tree-reference'
240
191
        return kind, executable, stat_value
241
192
 
267
218
            return self._dirstate
268
219
        local_path = self.bzrdir.get_workingtree_transport(None
269
220
            ).local_abspath('dirstate')
270
 
        self._dirstate = dirstate.DirState.on_file(local_path)
 
221
        self._dirstate = dirstate.DirState.on_file(local_path,
 
222
            self._sha1_provider())
271
223
        return self._dirstate
272
224
 
273
 
    def _directory_is_tree_reference(self, relpath):
274
 
        # as a special case, if a directory contains control files then 
275
 
        # it's a tree reference, except that the root of the tree is not
276
 
        return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
277
 
        # TODO: We could ask all the control formats whether they
278
 
        # recognize this directory, but at the moment there's no cheap api
279
 
        # to do that.  Since we probably can only nest bzr checkouts and
280
 
        # they always use this name it's ok for now.  -- mbp 20060306
281
 
        #
282
 
        # FIXME: There is an unhandled case here of a subdirectory
283
 
        # containing .bzr but not a branch; that will probably blow up
284
 
        # when you try to commit it.  It might happen if there is a
285
 
        # checkout in a subdirectory.  This can be avoided by not adding
286
 
        # it.  mbp 20070306
 
225
    def _sha1_provider(self):
 
226
        """A function that returns a SHA1Provider suitable for this tree.
 
227
 
 
228
        :return: None if content filtering is not supported by this tree.
 
229
          Otherwise, a SHA1Provider is returned that sha's the canonical
 
230
          form of files, i.e. after read filters are applied.
 
231
        """
 
232
        if self.supports_content_filtering():
 
233
            return ContentFilterAwareSHA1Provider(self)
 
234
        else:
 
235
            return None
287
236
 
288
237
    def filter_unversioned_files(self, paths):
289
238
        """Filter out paths that are versioned.
322
271
 
323
272
    def _generate_inventory(self):
324
273
        """Create and set self.inventory from the dirstate object.
325
 
        
 
274
 
326
275
        This is relatively expensive: we have to walk the entire dirstate.
327
276
        Ideally we would not, and can deprecate this function.
328
277
        """
332
281
        state._read_dirblocks_if_needed()
333
282
        root_key, current_entry = self._get_entry(path='')
334
283
        current_id = root_key[2]
335
 
        assert current_entry[0][0] == 'd' # directory
 
284
        if not (current_entry[0][0] == 'd'): # directory
 
285
            raise AssertionError(current_entry)
336
286
        inv = Inventory(root_id=current_id)
337
287
        # Turn some things into local variables
338
288
        minikind_to_kind = dirstate.DirState._minikind_to_kind
371
321
                    # add this entry to the parent map.
372
322
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
373
323
                elif kind == 'tree-reference':
374
 
                    assert self._repo_supports_tree_reference, \
375
 
                        "repository of %r " \
376
 
                        "doesn't support tree references " \
377
 
                        "required by entry %r" \
378
 
                        % (self, name)
 
324
                    if not self._repo_supports_tree_reference:
 
325
                        raise errors.UnsupportedOperation(
 
326
                            self._generate_inventory,
 
327
                            self.branch.repository)
379
328
                    inv_entry.reference_revision = link_or_sha1 or None
380
329
                elif kind != 'symlink':
381
330
                    raise AssertionError("unknown kind %r" % kind)
382
331
                # These checks cost us around 40ms on a 55k entry tree
383
 
                assert file_id not in inv_byid, ('file_id %s already in'
384
 
                    ' inventory as %s' % (file_id, inv_byid[file_id]))
385
 
                assert name_unicode not in parent_ie.children
 
332
                if file_id in inv_byid:
 
333
                    raise AssertionError('file_id %s already in'
 
334
                        ' inventory as %s' % (file_id, inv_byid[file_id]))
 
335
                if name_unicode in parent_ie.children:
 
336
                    raise AssertionError('name %r already in parent'
 
337
                        % (name_unicode,))
386
338
                inv_byid[file_id] = inv_entry
387
339
                parent_ie.children[name_unicode] = inv_entry
388
340
        self._inventory = inv
393
345
        If either file_id or path is supplied, it is used as the key to lookup.
394
346
        If both are supplied, the fastest lookup is used, and an error is
395
347
        raised if they do not both point at the same row.
396
 
        
 
348
 
397
349
        :param file_id: An optional unicode file_id to be looked up.
398
350
        :param path: An optional unicode path to be looked up.
399
351
        :return: The dirstate row tuple for path/file_id, or (None, None)
408
360
    def get_file_sha1(self, file_id, path=None, stat_value=None):
409
361
        # check file id is valid unconditionally.
410
362
        entry = self._get_entry(file_id=file_id, path=path)
411
 
        assert entry[0] is not None, 'what error should this raise'
 
363
        if entry[0] is None:
 
364
            raise errors.NoSuchId(self, file_id)
412
365
        if path is None:
413
366
            path = pathjoin(entry[0][0], entry[0][1]).decode('utf8')
414
367
 
422
375
                    return None
423
376
                else:
424
377
                    raise
425
 
        link_or_sha1 = state.update_entry(entry, file_abspath,
426
 
                                          stat_value=stat_value)
 
378
        link_or_sha1 = dirstate.update_entry(state, entry, file_abspath,
 
379
            stat_value=stat_value)
427
380
        if entry[1][0][0] == 'f':
428
 
            return link_or_sha1
 
381
            if link_or_sha1 is None:
 
382
                file_obj, statvalue = self.get_file_with_stat(file_id, path)
 
383
                try:
 
384
                    sha1 = osutils.sha_file(file_obj)
 
385
                finally:
 
386
                    file_obj.close()
 
387
                self._observed_sha1(file_id, path, (sha1, statvalue))
 
388
                return sha1
 
389
            else:
 
390
                return link_or_sha1
429
391
        return None
430
392
 
431
393
    def _get_inventory(self):
432
394
        """Get the inventory for the tree. This is only valid within a lock."""
 
395
        if 'evil' in debug.debug_flags:
 
396
            trace.mutter_callsite(2,
 
397
                "accessing .inventory forces a size of tree translation.")
433
398
        if self._inventory is not None:
434
399
            return self._inventory
435
400
        self._must_be_locked()
442
407
    @needs_read_lock
443
408
    def get_parent_ids(self):
444
409
        """See Tree.get_parent_ids.
445
 
        
 
410
 
446
411
        This implementation requests the ids list from the dirstate file.
447
412
        """
448
413
        return self.current_dirstate().get_parent_ids()
464
429
 
465
430
    def has_id(self, file_id):
466
431
        state = self.current_dirstate()
467
 
        file_id = osutils.safe_file_id(file_id)
468
432
        row, parents = self._get_entry(file_id=file_id)
469
433
        if row is None:
470
434
            return False
474
438
    @needs_read_lock
475
439
    def id2path(self, file_id):
476
440
        "Convert a file-id to a path."
477
 
        file_id = osutils.safe_file_id(file_id)
478
441
        state = self.current_dirstate()
479
442
        entry = self._get_entry(file_id=file_id)
480
443
        if entry == (None, None):
482
445
        path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
483
446
        return path_utf8.decode('utf8')
484
447
 
 
448
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
 
449
        entry = self._get_entry(path=path)
 
450
        if entry == (None, None):
 
451
            return False # Missing entries are not executable
 
452
        return entry[1][0][3] # Executable?
 
453
 
485
454
    if not osutils.supports_executable():
486
455
        def is_executable(self, file_id, path=None):
487
456
            """Test if a file is executable or not.
488
457
 
489
458
            Note: The caller is expected to take a read-lock before calling this.
490
459
            """
491
 
            file_id = osutils.safe_file_id(file_id)
492
460
            entry = self._get_entry(file_id=file_id, path=path)
493
461
            if entry == (None, None):
494
462
                return False
495
463
            return entry[1][0][3]
 
464
 
 
465
        _is_executable_from_path_and_stat = \
 
466
            _is_executable_from_path_and_stat_from_basis
496
467
    else:
497
468
        def is_executable(self, file_id, path=None):
498
469
            """Test if a file is executable or not.
499
470
 
500
471
            Note: The caller is expected to take a read-lock before calling this.
501
472
            """
 
473
            self._must_be_locked()
502
474
            if not path:
503
 
                file_id = osutils.safe_file_id(file_id)
504
475
                path = self.id2path(file_id)
505
476
            mode = os.lstat(self.abspath(path)).st_mode
506
477
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
507
478
 
 
479
    def all_file_ids(self):
 
480
        """See Tree.iter_all_file_ids"""
 
481
        self._must_be_locked()
 
482
        result = set()
 
483
        for key, tree_details in self.current_dirstate()._iter_entries():
 
484
            if tree_details[0][0] in ('a', 'r'): # relocated
 
485
                continue
 
486
            result.add(key[2])
 
487
        return result
 
488
 
508
489
    @needs_read_lock
509
490
    def __iter__(self):
510
491
        """Iterate through file_ids for this tree.
523
504
        return iter(result)
524
505
 
525
506
    def iter_references(self):
 
507
        if not self._repo_supports_tree_reference:
 
508
            # When the repo doesn't support references, we will have nothing to
 
509
            # return
 
510
            return
526
511
        for key, tree_details in self.current_dirstate()._iter_entries():
527
512
            if tree_details[0][0] in ('a', 'r'): # absent, relocated
528
513
                # not relevant to the working tree
530
515
            if not key[1]:
531
516
                # the root is not a reference.
532
517
                continue
533
 
            path = pathjoin(self.basedir, key[0].decode('utf8'), key[1].decode('utf8'))
 
518
            relpath = pathjoin(key[0].decode('utf8'), key[1].decode('utf8'))
534
519
            try:
535
 
                if self._kind(path) == 'tree-reference':
536
 
                    yield path, key[2]
 
520
                if self._kind(relpath) == 'tree-reference':
 
521
                    yield relpath, key[2]
537
522
            except errors.NoSuchFile:
538
523
                # path is missing on disk.
539
524
                continue
540
525
 
 
526
    def _observed_sha1(self, file_id, path, (sha1, statvalue)):
 
527
        """See MutableTree._observed_sha1."""
 
528
        state = self.current_dirstate()
 
529
        entry = self._get_entry(file_id=file_id, path=path)
 
530
        state._observed_sha1(entry, sha1, statvalue)
 
531
 
541
532
    def kind(self, file_id):
542
533
        """Return the kind of a file.
543
534
 
547
538
        Note: The caller is expected to take a read-lock before calling this.
548
539
        """
549
540
        relpath = self.id2path(file_id)
550
 
        assert relpath != None, \
551
 
            "path for id {%s} is None!" % file_id
 
541
        if relpath is None:
 
542
            raise AssertionError(
 
543
                "path for id {%s} is None!" % file_id)
552
544
        return self._kind(relpath)
553
545
 
554
546
    def _kind(self, relpath):
555
547
        abspath = self.abspath(relpath)
556
548
        kind = file_kind(abspath)
557
 
        if (self._repo_supports_tree_reference and
558
 
            kind == 'directory' and
559
 
            self._directory_is_tree_reference(relpath)):
560
 
            kind = 'tree-reference'
 
549
        if (self._repo_supports_tree_reference and kind == 'directory'):
 
550
            entry = self._get_entry(path=relpath)
 
551
            if entry[1] is not None:
 
552
                if entry[1][0][0] == 't':
 
553
                    kind = 'tree-reference'
561
554
        return kind
562
555
 
563
556
    @needs_read_lock
567
560
        if parent_ids:
568
561
            return parent_ids[0]
569
562
        else:
570
 
            return None
 
563
            return _mod_revision.NULL_REVISION
571
564
 
572
565
    def lock_read(self):
573
566
        """See Branch.lock_read, and WorkingTree.unlock."""
626
619
        result = []
627
620
        if not from_paths:
628
621
            return result
629
 
 
630
622
        state = self.current_dirstate()
631
 
 
632
 
        assert not isinstance(from_paths, basestring)
 
623
        if isinstance(from_paths, basestring):
 
624
            raise ValueError()
633
625
        to_dir_utf8 = to_dir.encode('utf8')
634
626
        to_entry_dirname, to_basename = os.path.split(to_dir_utf8)
635
627
        id_index = state._get_id_index()
657
649
        if self._inventory is not None:
658
650
            update_inventory = True
659
651
            inv = self.inventory
 
652
            to_dir_id = to_entry[0][2]
660
653
            to_dir_ie = inv[to_dir_id]
661
 
            to_dir_id = to_entry[0][2]
662
654
        else:
663
655
            update_inventory = False
664
656
 
728
720
                if from_missing: # implicitly just update our path mapping
729
721
                    move_file = False
730
722
                elif not after:
731
 
                    raise errors.RenameFailedFilesExist(from_rel, to_rel,
732
 
                        extra="(Use --after to update the Bazaar id)")
 
723
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
733
724
 
734
725
            rollbacks = []
735
726
            def rollback_rename():
791
782
                if minikind == 'd':
792
783
                    def update_dirblock(from_dir, to_key, to_dir_utf8):
793
784
                        """Recursively update all entries in this dirblock."""
794
 
                        assert from_dir != '', "renaming root not supported"
 
785
                        if from_dir == '':
 
786
                            raise AssertionError("renaming root not supported")
795
787
                        from_key = (from_dir, '')
796
788
                        from_block_idx, present = \
797
789
                            state._find_block_index_from_key(from_key)
810
802
 
811
803
                        # Grab a copy since move_one may update the list.
812
804
                        for entry in from_block[1][:]:
813
 
                            assert entry[0][0] == from_dir
 
805
                            if not (entry[0][0] == from_dir):
 
806
                                raise AssertionError()
814
807
                            cur_details = entry[1][0]
815
808
                            to_key = (to_dir_utf8, entry[0][1], entry[0][2])
816
809
                            from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
875
868
        for tree in trees:
876
869
            if not (isinstance(tree, DirStateRevisionTree) and tree._revision_id in
877
870
                parents):
878
 
                return super(WorkingTree4, self).paths2ids(paths, trees, require_versioned)
 
871
                return super(DirStateWorkingTree, self).paths2ids(paths,
 
872
                    trees, require_versioned)
879
873
        search_indexes = [0] + [1 + parents.index(tree._revision_id) for tree in trees]
880
874
        # -- make all paths utf8 --
881
875
        paths_utf8 = set()
941
935
            if not all_versioned:
942
936
                raise errors.PathsNotVersionedError(paths)
943
937
        # -- remove redundancy in supplied paths to prevent over-scanning --
944
 
        search_paths = set()
945
 
        for path in paths:
946
 
            other_paths = paths.difference(set([path]))
947
 
            if not osutils.is_inside_any(other_paths, path):
948
 
                # this is a top level path, we must check it.
949
 
                search_paths.add(path)
950
 
        # sketch: 
 
938
        search_paths = osutils.minimum_path_selection(paths)
 
939
        # sketch:
951
940
        # for all search_indexs in each path at or under each element of
952
941
        # search_paths, if the detail is relocated: add the id, and add the
953
942
        # relocated path as one to search if its not searched already. If the
1009
998
 
1010
999
    def read_working_inventory(self):
1011
1000
        """Read the working inventory.
1012
 
        
 
1001
 
1013
1002
        This is a meaningless operation for dirstate, but we obey it anyhow.
1014
1003
        """
1015
1004
        return self.inventory
1020
1009
 
1021
1010
        WorkingTree4 supplies revision_trees for any basis tree.
1022
1011
        """
1023
 
        revision_id = osutils.safe_revision_id(revision_id)
1024
1012
        dirstate = self.current_dirstate()
1025
1013
        parent_ids = dirstate.get_parent_ids()
1026
1014
        if revision_id not in parent_ids:
1033
1021
    @needs_tree_write_lock
1034
1022
    def set_last_revision(self, new_revision):
1035
1023
        """Change the last revision in the working tree."""
1036
 
        new_revision = osutils.safe_revision_id(new_revision)
1037
1024
        parents = self.get_parent_ids()
1038
 
        if new_revision in (NULL_REVISION, None):
1039
 
            assert len(parents) < 2, (
1040
 
                "setting the last parent to none with a pending merge is "
1041
 
                "unsupported.")
 
1025
        if new_revision in (_mod_revision.NULL_REVISION, None):
 
1026
            if len(parents) >= 2:
 
1027
                raise AssertionError(
 
1028
                    "setting the last parent to none with a pending merge is "
 
1029
                    "unsupported.")
1042
1030
            self.set_parent_ids([])
1043
1031
        else:
1044
1032
            self.set_parent_ids([new_revision] + parents[1:],
1047
1035
    @needs_tree_write_lock
1048
1036
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
1049
1037
        """Set the parent ids to revision_ids.
1050
 
        
 
1038
 
1051
1039
        See also set_parent_trees. This api will try to retrieve the tree data
1052
1040
        for each element of revision_ids from the trees repository. If you have
1053
1041
        tree data already available, it is more efficient to use
1057
1045
        :param revision_ids: The revision_ids to set as the parent ids of this
1058
1046
            working tree. Any of these may be ghosts.
1059
1047
        """
1060
 
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1061
1048
        trees = []
1062
1049
        for revision_id in revision_ids:
1063
1050
            try:
1086
1073
                raise errors.GhostRevisionUnusableHere(parents_list[0][0])
1087
1074
        real_trees = []
1088
1075
        ghosts = []
 
1076
 
 
1077
        parent_ids = [rev_id for rev_id, tree in parents_list]
 
1078
        graph = self.branch.repository.get_graph()
 
1079
        heads = graph.heads(parent_ids)
 
1080
        accepted_revisions = set()
 
1081
 
1089
1082
        # convert absent trees to the null tree, which we convert back to
1090
1083
        # missing on access.
1091
1084
        for rev_id, tree in parents_list:
1092
 
            rev_id = osutils.safe_revision_id(rev_id)
 
1085
            if len(accepted_revisions) > 0:
 
1086
                # we always accept the first tree
 
1087
                if rev_id in accepted_revisions or rev_id not in heads:
 
1088
                    # We have already included either this tree, or its
 
1089
                    # descendent, so we skip it.
 
1090
                    continue
1093
1091
            _mod_revision.check_not_reserved_id(rev_id)
1094
1092
            if tree is not None:
1095
1093
                real_trees.append((rev_id, tree))
1096
1094
            else:
1097
1095
                real_trees.append((rev_id,
1098
 
                    self.branch.repository.revision_tree(None)))
 
1096
                    self.branch.repository.revision_tree(
 
1097
                        _mod_revision.NULL_REVISION)))
1099
1098
                ghosts.append(rev_id)
 
1099
            accepted_revisions.add(rev_id)
1100
1100
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1101
1101
        self._make_dirty(reset_inventory=False)
1102
1102
 
1107
1107
        if state._dirblock_state == dirstate.DirState.IN_MEMORY_MODIFIED:
1108
1108
            self._make_dirty(reset_inventory=True)
1109
1109
 
 
1110
    def _sha_from_stat(self, path, stat_result):
 
1111
        """Get a sha digest from the tree's stat cache.
 
1112
 
 
1113
        The default implementation assumes no stat cache is present.
 
1114
 
 
1115
        :param path: The path.
 
1116
        :param stat_result: The stat result being looked up.
 
1117
        """
 
1118
        return self.current_dirstate().sha1_from_stat(path, stat_result)
 
1119
 
1110
1120
    @needs_read_lock
1111
1121
    def supports_tree_reference(self):
1112
1122
        return self._repo_supports_tree_reference
1113
1123
 
1114
1124
    def unlock(self):
1115
1125
        """Unlock in format 4 trees needs to write the entire dirstate."""
 
1126
        # do non-implementation specific cleanup
 
1127
        self._cleanup()
 
1128
 
1116
1129
        if self._control_files._lock_count == 1:
1117
1130
            # eventually we should do signature checking during read locks for
1118
1131
            # dirstate updates.
1149
1162
            return
1150
1163
        state = self.current_dirstate()
1151
1164
        state._read_dirblocks_if_needed()
1152
 
        ids_to_unversion = set()
1153
 
        for file_id in file_ids:
1154
 
            ids_to_unversion.add(osutils.safe_file_id(file_id))
 
1165
        ids_to_unversion = set(file_ids)
1155
1166
        paths_to_unversion = set()
1156
1167
        # sketch:
1157
1168
        # check if the root is to be unversioned, if so, assert for now.
1187
1198
                    # Mark this file id as having been removed
1188
1199
                    entry = block[1][entry_index]
1189
1200
                    ids_to_unversion.discard(entry[0][2])
1190
 
                    if (entry[1][0][0] == 'a'
 
1201
                    if (entry[1][0][0] in 'ar' # don't remove absent or renamed
 
1202
                                               # entries
1191
1203
                        or not state._make_absent(entry)):
1192
1204
                        entry_index += 1
1193
1205
                # go to the next block. (At the moment we dont delete empty
1218
1230
            for file_id in file_ids:
1219
1231
                self._inventory.remove_recursive_id(file_id)
1220
1232
 
 
1233
    @needs_tree_write_lock
 
1234
    def rename_one(self, from_rel, to_rel, after=False):
 
1235
        """See WorkingTree.rename_one"""
 
1236
        self.flush()
 
1237
        WorkingTree.rename_one(self, from_rel, to_rel, after)
 
1238
 
 
1239
    @needs_tree_write_lock
 
1240
    def apply_inventory_delta(self, changes):
 
1241
        """See MutableTree.apply_inventory_delta"""
 
1242
        state = self.current_dirstate()
 
1243
        state.update_by_delta(changes)
 
1244
        self._make_dirty(reset_inventory=True)
 
1245
 
 
1246
    def update_basis_by_delta(self, new_revid, delta):
 
1247
        """See MutableTree.update_basis_by_delta."""
 
1248
        if self.last_revision() == new_revid:
 
1249
            raise AssertionError()
 
1250
        self.current_dirstate().update_basis_by_delta(delta, new_revid)
 
1251
 
1221
1252
    @needs_read_lock
1222
1253
    def _validate(self):
1223
1254
        self._dirstate._validate()
1225
1256
    @needs_tree_write_lock
1226
1257
    def _write_inventory(self, inv):
1227
1258
        """Write inventory as the current inventory."""
1228
 
        assert not self._dirty, "attempting to write an inventory when the dirstate is dirty will cause data loss"
 
1259
        if self._dirty:
 
1260
            raise AssertionError("attempting to write an inventory when the "
 
1261
                "dirstate is dirty will lose pending changes")
1229
1262
        self.current_dirstate().set_state_from_inventory(inv)
1230
1263
        self._make_dirty(reset_inventory=False)
1231
1264
        if self._inventory is not None:
1233
1266
        self.flush()
1234
1267
 
1235
1268
 
1236
 
class WorkingTreeFormat4(WorkingTreeFormat3):
1237
 
    """The first consolidated dirstate working tree format.
1238
 
 
1239
 
    This format:
1240
 
        - exists within a metadir controlling .bzr
1241
 
        - includes an explicit version marker for the workingtree control
1242
 
          files, separate from the BzrDir format
1243
 
        - modifies the hash cache format
1244
 
        - is new in bzr 0.15
1245
 
        - uses a LockDir to guard access to it.
1246
 
    """
1247
 
 
1248
 
    upgrade_recommended = False
1249
 
 
1250
 
    def get_format_string(self):
1251
 
        """See WorkingTreeFormat.get_format_string()."""
1252
 
        return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1253
 
 
1254
 
    def get_format_description(self):
1255
 
        """See WorkingTreeFormat.get_format_description()."""
1256
 
        return "Working tree format 4"
1257
 
 
1258
 
    def initialize(self, a_bzrdir, revision_id=None):
 
1269
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
 
1270
 
 
1271
    def __init__(self, tree):
 
1272
        self.tree = tree
 
1273
 
 
1274
    def sha1(self, abspath):
 
1275
        """See dirstate.SHA1Provider.sha1()."""
 
1276
        filters = self.tree._content_filter_stack(
 
1277
            self.tree.relpath(osutils.safe_unicode(abspath)))
 
1278
        return internal_size_sha_file_byname(abspath, filters)[1]
 
1279
 
 
1280
    def stat_and_sha1(self, abspath):
 
1281
        """See dirstate.SHA1Provider.stat_and_sha1()."""
 
1282
        filters = self.tree._content_filter_stack(
 
1283
            self.tree.relpath(osutils.safe_unicode(abspath)))
 
1284
        file_obj = file(abspath, 'rb', 65000)
 
1285
        try:
 
1286
            statvalue = os.fstat(file_obj.fileno())
 
1287
            if filters:
 
1288
                file_obj = filtered_input_file(file_obj, filters)
 
1289
            sha1 = osutils.size_sha_file(file_obj)[1]
 
1290
        finally:
 
1291
            file_obj.close()
 
1292
        return statvalue, sha1
 
1293
 
 
1294
 
 
1295
class WorkingTree4(DirStateWorkingTree):
 
1296
    """This is the Format 4 working tree.
 
1297
 
 
1298
    This differs from WorkingTree3 by:
 
1299
     - Having a consolidated internal dirstate, stored in a
 
1300
       randomly-accessible sorted file on disk.
 
1301
     - Not having a regular inventory attribute.  One can be synthesized
 
1302
       on demand but this is expensive and should be avoided.
 
1303
 
 
1304
    This is new in bzr 0.15.
 
1305
    """
 
1306
 
 
1307
 
 
1308
class WorkingTree5(DirStateWorkingTree):
 
1309
    """This is the Format 5 working tree.
 
1310
 
 
1311
    This differs from WorkingTree4 by:
 
1312
     - Supporting content filtering.
 
1313
 
 
1314
    This is new in bzr 1.11.
 
1315
    """
 
1316
 
 
1317
 
 
1318
class WorkingTree6(DirStateWorkingTree):
 
1319
    """This is the Format 6 working tree.
 
1320
 
 
1321
    This differs from WorkingTree5 by:
 
1322
     - Supporting a current view that may mask the set of files in a tree
 
1323
       impacted by most user operations.
 
1324
 
 
1325
    This is new in bzr 1.14.
 
1326
    """
 
1327
 
 
1328
    def _make_views(self):
 
1329
        return views.PathBasedViews(self)
 
1330
 
 
1331
 
 
1332
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
 
1333
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
1334
                   accelerator_tree=None, hardlink=False):
1259
1335
        """See WorkingTreeFormat.initialize().
1260
1336
 
1261
1337
        :param revision_id: allows creating a working tree at a different
1262
1338
        revision than the branch is at.
 
1339
        :param accelerator_tree: A tree which can be used for retrieving file
 
1340
            contents more quickly than the revision tree, i.e. a workingtree.
 
1341
            The revision tree will be used for cases where accelerator_tree's
 
1342
            content is different.
 
1343
        :param hardlink: If true, hard-link files from accelerator_tree,
 
1344
            where possible.
1263
1345
 
1264
1346
        These trees get an initial random root id, if their repository supports
1265
1347
        rich root data, TREE_ROOT otherwise.
1266
1348
        """
1267
 
        revision_id = osutils.safe_revision_id(revision_id)
1268
1349
        if not isinstance(a_bzrdir.transport, LocalTransport):
1269
1350
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1270
1351
        transport = a_bzrdir.get_workingtree_transport(self)
1271
1352
        control_files = self._open_control_files(a_bzrdir)
1272
1353
        control_files.create_lock()
1273
1354
        control_files.lock_write()
1274
 
        control_files.put_utf8('format', self.get_format_string())
1275
 
        branch = a_bzrdir.open_branch()
 
1355
        transport.put_bytes('format', self.get_format_string(),
 
1356
            mode=a_bzrdir._get_file_mode())
 
1357
        if from_branch is not None:
 
1358
            branch = from_branch
 
1359
        else:
 
1360
            branch = a_bzrdir.open_branch()
1276
1361
        if revision_id is None:
1277
1362
            revision_id = branch.last_revision()
1278
1363
        local_path = transport.local_abspath('dirstate')
1280
1365
        state = dirstate.DirState.initialize(local_path)
1281
1366
        state.unlock()
1282
1367
        del state
1283
 
        wt = WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
 
1368
        wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1284
1369
                         branch,
1285
1370
                         _format=self,
1286
1371
                         _bzrdir=a_bzrdir,
1288
1373
        wt._new_tree()
1289
1374
        wt.lock_tree_write()
1290
1375
        try:
1291
 
            if revision_id in (None, NULL_REVISION):
 
1376
            self._init_custom_control_files(wt)
 
1377
            if revision_id in (None, _mod_revision.NULL_REVISION):
1292
1378
                if branch.repository.supports_rich_root():
1293
1379
                    wt._set_root_id(generate_ids.gen_root_id())
1294
1380
                else:
1295
1381
                    wt._set_root_id(ROOT_ID)
1296
1382
                wt.flush()
1297
 
            wt.set_last_revision(revision_id)
1298
 
            wt.flush()
1299
 
            basis = wt.basis_tree()
 
1383
            basis = None
 
1384
            # frequently, we will get here due to branching.  The accelerator
 
1385
            # tree will be the tree from the branch, so the desired basis
 
1386
            # tree will often be a parent of the accelerator tree.
 
1387
            if accelerator_tree is not None:
 
1388
                try:
 
1389
                    basis = accelerator_tree.revision_tree(revision_id)
 
1390
                except errors.NoSuchRevision:
 
1391
                    pass
 
1392
            if basis is None:
 
1393
                basis = branch.repository.revision_tree(revision_id)
 
1394
            if revision_id == _mod_revision.NULL_REVISION:
 
1395
                parents_list = []
 
1396
            else:
 
1397
                parents_list = [(revision_id, basis)]
1300
1398
            basis.lock_read()
1301
 
            # if the basis has a root id we have to use that; otherwise we use
1302
 
            # a new random one
1303
 
            basis_root_id = basis.get_root_id()
1304
 
            if basis_root_id is not None:
1305
 
                wt._set_root_id(basis_root_id)
 
1399
            try:
 
1400
                wt.set_parent_trees(parents_list, allow_leftmost_as_ghost=True)
1306
1401
                wt.flush()
1307
 
            transform.build_tree(basis, wt)
1308
 
            basis.unlock()
 
1402
                # if the basis has a root id we have to use that; otherwise we
 
1403
                # use a new random one
 
1404
                basis_root_id = basis.get_root_id()
 
1405
                if basis_root_id is not None:
 
1406
                    wt._set_root_id(basis_root_id)
 
1407
                    wt.flush()
 
1408
                # If content filtering is supported, do not use the accelerator
 
1409
                # tree - the cost of transforming the content both ways and
 
1410
                # checking for changed content can outweight the gains it gives.
 
1411
                # Note: do NOT move this logic up higher - using the basis from
 
1412
                # the accelerator tree is still desirable because that can save
 
1413
                # a minute or more of processing on large trees!
 
1414
                # The original tree may not have the same content filters
 
1415
                # applied so we can't safely build the inventory delta from
 
1416
                # the source tree.
 
1417
                if wt.supports_content_filtering():
 
1418
                    accelerator_tree = None
 
1419
                    delta_from_tree = False
 
1420
                else:
 
1421
                    delta_from_tree = True
 
1422
                # delta_from_tree is safe even for DirStateRevisionTrees,
 
1423
                # because wt4.apply_inventory_delta does not mutate the input
 
1424
                # inventory entries.
 
1425
                transform.build_tree(basis, wt, accelerator_tree,
 
1426
                                     hardlink=hardlink,
 
1427
                                     delta_from_tree=delta_from_tree)
 
1428
            finally:
 
1429
                basis.unlock()
1309
1430
        finally:
1310
1431
            control_files.unlock()
1311
1432
            wt.unlock()
1312
1433
        return wt
1313
1434
 
 
1435
    def _init_custom_control_files(self, wt):
 
1436
        """Subclasses with custom control files should override this method.
 
1437
 
 
1438
        The working tree and control files are locked for writing when this
 
1439
        method is called.
 
1440
 
 
1441
        :param wt: the WorkingTree object
 
1442
        """
 
1443
 
1314
1444
    def _open(self, a_bzrdir, control_files):
1315
1445
        """Open the tree itself.
1316
1446
 
1317
1447
        :param a_bzrdir: the dir for the tree.
1318
1448
        :param control_files: the control files for the tree.
1319
1449
        """
1320
 
        return WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
 
1450
        return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1321
1451
                           branch=a_bzrdir.open_branch(),
1322
1452
                           _format=self,
1323
1453
                           _bzrdir=a_bzrdir,
1324
1454
                           _control_files=control_files)
1325
1455
 
1326
1456
    def __get_matchingbzrdir(self):
 
1457
        return self._get_matchingbzrdir()
 
1458
 
 
1459
    def _get_matchingbzrdir(self):
 
1460
        """Overrideable method to get a bzrdir for testing."""
1327
1461
        # please test against something that will let us do tree references
1328
1462
        return bzrdir.format_registry.make_bzrdir(
1329
1463
            'dirstate-with-subtree')
1331
1465
    _matchingbzrdir = property(__get_matchingbzrdir)
1332
1466
 
1333
1467
 
 
1468
class WorkingTreeFormat4(DirStateWorkingTreeFormat):
 
1469
    """The first consolidated dirstate working tree format.
 
1470
 
 
1471
    This format:
 
1472
        - exists within a metadir controlling .bzr
 
1473
        - includes an explicit version marker for the workingtree control
 
1474
          files, separate from the BzrDir format
 
1475
        - modifies the hash cache format
 
1476
        - is new in bzr 0.15
 
1477
        - uses a LockDir to guard access to it.
 
1478
    """
 
1479
 
 
1480
    upgrade_recommended = False
 
1481
 
 
1482
    _tree_class = WorkingTree4
 
1483
 
 
1484
    def get_format_string(self):
 
1485
        """See WorkingTreeFormat.get_format_string()."""
 
1486
        return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
 
1487
 
 
1488
    def get_format_description(self):
 
1489
        """See WorkingTreeFormat.get_format_description()."""
 
1490
        return "Working tree format 4"
 
1491
 
 
1492
 
 
1493
class WorkingTreeFormat5(DirStateWorkingTreeFormat):
 
1494
    """WorkingTree format supporting content filtering.
 
1495
    """
 
1496
 
 
1497
    upgrade_recommended = False
 
1498
 
 
1499
    _tree_class = WorkingTree5
 
1500
 
 
1501
    def get_format_string(self):
 
1502
        """See WorkingTreeFormat.get_format_string()."""
 
1503
        return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
 
1504
 
 
1505
    def get_format_description(self):
 
1506
        """See WorkingTreeFormat.get_format_description()."""
 
1507
        return "Working tree format 5"
 
1508
 
 
1509
    def supports_content_filtering(self):
 
1510
        return True
 
1511
 
 
1512
 
 
1513
class WorkingTreeFormat6(DirStateWorkingTreeFormat):
 
1514
    """WorkingTree format supporting views.
 
1515
    """
 
1516
 
 
1517
    upgrade_recommended = False
 
1518
 
 
1519
    _tree_class = WorkingTree6
 
1520
 
 
1521
    def get_format_string(self):
 
1522
        """See WorkingTreeFormat.get_format_string()."""
 
1523
        return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
 
1524
 
 
1525
    def get_format_description(self):
 
1526
        """See WorkingTreeFormat.get_format_description()."""
 
1527
        return "Working tree format 6"
 
1528
 
 
1529
    def _init_custom_control_files(self, wt):
 
1530
        """Subclasses with custom control files should override this method."""
 
1531
        wt._transport.put_bytes('views', '', mode=wt.bzrdir._get_file_mode())
 
1532
 
 
1533
    def supports_content_filtering(self):
 
1534
        return True
 
1535
 
 
1536
    def supports_views(self):
 
1537
        return True
 
1538
 
 
1539
 
1334
1540
class DirStateRevisionTree(Tree):
1335
1541
    """A revision tree pulling the inventory from a dirstate."""
1336
1542
 
1337
1543
    def __init__(self, dirstate, revision_id, repository):
1338
1544
        self._dirstate = dirstate
1339
 
        self._revision_id = osutils.safe_revision_id(revision_id)
 
1545
        self._revision_id = revision_id
1340
1546
        self._repository = repository
1341
1547
        self._inventory = None
1342
1548
        self._locked = 0
1343
1549
        self._dirstate_locked = False
 
1550
        self._repo_supports_tree_reference = getattr(
 
1551
            repository._format, "supports_tree_reference",
 
1552
            False)
1344
1553
 
1345
1554
    def __repr__(self):
1346
1555
        return "<%s of %s in %s>" % \
1347
1556
            (self.__class__.__name__, self._revision_id, self._dirstate)
1348
1557
 
1349
 
    def annotate_iter(self, file_id):
 
1558
    def annotate_iter(self, file_id,
 
1559
                      default_revision=_mod_revision.CURRENT_REVISION):
1350
1560
        """See Tree.annotate_iter"""
1351
 
        w = self._repository.weave_store.get_weave(file_id,
1352
 
                           self._repository.get_transaction())
1353
 
        return w.annotate_iter(self.inventory[file_id].revision)
 
1561
        text_key = (file_id, self.inventory[file_id].revision)
 
1562
        annotations = self._repository.texts.annotate(text_key)
 
1563
        return [(key[-1], line) for (key, line) in annotations]
1354
1564
 
 
1565
    def _get_ancestors(self, default_revision):
 
1566
        return set(self._repository.get_ancestry(self._revision_id,
 
1567
                                                 topo_sorted=False))
1355
1568
    def _comparison_data(self, entry, path):
1356
1569
        """See Tree._comparison_data."""
1357
1570
        if entry is None:
1374
1587
    def get_root_id(self):
1375
1588
        return self.path2id('')
1376
1589
 
 
1590
    def id2path(self, file_id):
 
1591
        "Convert a file-id to a path."
 
1592
        entry = self._get_entry(file_id=file_id)
 
1593
        if entry == (None, None):
 
1594
            raise errors.NoSuchId(tree=self, file_id=file_id)
 
1595
        path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
 
1596
        return path_utf8.decode('utf8')
 
1597
 
 
1598
    def iter_references(self):
 
1599
        if not self._repo_supports_tree_reference:
 
1600
            # When the repo doesn't support references, we will have nothing to
 
1601
            # return
 
1602
            return iter([])
 
1603
        # Otherwise, fall back to the default implementation
 
1604
        return super(DirStateRevisionTree, self).iter_references()
 
1605
 
1377
1606
    def _get_parent_index(self):
1378
1607
        """Return the index in the dirstate referenced by this tree."""
1379
1608
        return self._dirstate.get_parent_ids().index(self._revision_id) + 1
1384
1613
        If either file_id or path is supplied, it is used as the key to lookup.
1385
1614
        If both are supplied, the fastest lookup is used, and an error is
1386
1615
        raised if they do not both point at the same row.
1387
 
        
 
1616
 
1388
1617
        :param file_id: An optional unicode file_id to be looked up.
1389
1618
        :param path: An optional unicode path to be looked up.
1390
1619
        :return: The dirstate row tuple for path/file_id, or (None, None)
1391
1620
        """
1392
1621
        if file_id is None and path is None:
1393
1622
            raise errors.BzrError('must supply file_id or path')
1394
 
        file_id = osutils.safe_file_id(file_id)
1395
1623
        if path is not None:
1396
1624
            path = path.encode('utf8')
1397
1625
        parent_index = self._get_parent_index()
1405
1633
 
1406
1634
        This is relatively expensive: we have to walk the entire dirstate.
1407
1635
        """
1408
 
        assert self._locked, 'cannot generate inventory of an unlocked '\
1409
 
            'dirstate revision tree'
 
1636
        if not self._locked:
 
1637
            raise AssertionError(
 
1638
                'cannot generate inventory of an unlocked '
 
1639
                'dirstate revision tree')
1410
1640
        # separate call for profiling - makes it clear where the costs are.
1411
1641
        self._dirstate._read_dirblocks_if_needed()
1412
 
        assert self._revision_id in self._dirstate.get_parent_ids(), \
1413
 
            'parent %s has disappeared from %s' % (
1414
 
            self._revision_id, self._dirstate.get_parent_ids())
 
1642
        if self._revision_id not in self._dirstate.get_parent_ids():
 
1643
            raise AssertionError(
 
1644
                'parent %s has disappeared from %s' % (
 
1645
                self._revision_id, self._dirstate.get_parent_ids()))
1415
1646
        parent_index = self._dirstate.get_parent_ids().index(self._revision_id) + 1
1416
1647
        # This is identical now to the WorkingTree _generate_inventory except
1417
1648
        # for the tree index use.
1418
1649
        root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
1419
1650
        current_id = root_key[2]
1420
 
        assert current_entry[parent_index][0] == 'd'
 
1651
        if current_entry[parent_index][0] != 'd':
 
1652
            raise AssertionError()
1421
1653
        inv = Inventory(root_id=current_id, revision_id=self._revision_id)
1422
1654
        inv.root.revision = current_entry[parent_index][4]
1423
1655
        # Turn some things into local variables
1463
1695
                    raise AssertionError("cannot convert entry %r into an InventoryEntry"
1464
1696
                            % entry)
1465
1697
                # These checks cost us around 40ms on a 55k entry tree
1466
 
                assert file_id not in inv_byid
1467
 
                assert name_unicode not in parent_ie.children
 
1698
                if file_id in inv_byid:
 
1699
                    raise AssertionError('file_id %s already in'
 
1700
                        ' inventory as %s' % (file_id, inv_byid[file_id]))
 
1701
                if name_unicode in parent_ie.children:
 
1702
                    raise AssertionError('name %r already in parent'
 
1703
                        % (name_unicode,))
1468
1704
                inv_byid[file_id] = inv_entry
1469
1705
                parent_ie.children[name_unicode] = inv_entry
1470
1706
        self._inventory = inv
1490
1726
            return parent_details[1]
1491
1727
        return None
1492
1728
 
1493
 
    def get_weave(self, file_id):
1494
 
        return self._repository.weave_store.get_weave(file_id,
1495
 
                self._repository.get_transaction())
1496
 
 
1497
 
    def get_file(self, file_id):
 
1729
    def get_file(self, file_id, path=None):
1498
1730
        return StringIO(self.get_file_text(file_id))
1499
1731
 
1500
 
    def get_file_lines(self, file_id):
1501
 
        ie = self.inventory[file_id]
1502
 
        return self._repository.weave_store.get_weave(file_id,
1503
 
                self._repository.get_transaction()).get_lines(ie.revision)
1504
 
 
1505
1732
    def get_file_size(self, file_id):
 
1733
        """See Tree.get_file_size"""
1506
1734
        return self.inventory[file_id].text_size
1507
1735
 
1508
 
    def get_file_text(self, file_id):
1509
 
        return ''.join(self.get_file_lines(file_id))
 
1736
    def get_file_text(self, file_id, path=None):
 
1737
        _, content = list(self.iter_files_bytes([(file_id, None)]))[0]
 
1738
        return ''.join(content)
1510
1739
 
1511
1740
    def get_reference_revision(self, file_id, path=None):
1512
1741
        return self.inventory[file_id].reference_revision
1513
1742
 
 
1743
    def iter_files_bytes(self, desired_files):
 
1744
        """See Tree.iter_files_bytes.
 
1745
 
 
1746
        This version is implemented on top of Repository.iter_files_bytes"""
 
1747
        parent_index = self._get_parent_index()
 
1748
        repo_desired_files = []
 
1749
        for file_id, identifier in desired_files:
 
1750
            entry = self._get_entry(file_id)
 
1751
            if entry == (None, None):
 
1752
                raise errors.NoSuchId(self, file_id)
 
1753
            repo_desired_files.append((file_id, entry[1][parent_index][4],
 
1754
                                       identifier))
 
1755
        return self._repository.iter_files_bytes(repo_desired_files)
 
1756
 
1514
1757
    def get_symlink_target(self, file_id):
1515
1758
        entry = self._get_entry(file_id=file_id)
1516
1759
        parent_index = self._get_parent_index()
1517
1760
        if entry[1][parent_index][0] != 'l':
1518
1761
            return None
1519
1762
        else:
1520
 
            # At present, none of the tree implementations supports non-ascii
1521
 
            # symlink targets. So we will just assume that the dirstate path is
1522
 
            # correct.
1523
 
            return entry[1][parent_index][1]
 
1763
            target = entry[1][parent_index][1]
 
1764
            target = target.decode('utf8')
 
1765
            return target
1524
1766
 
1525
1767
    def get_revision_id(self):
1526
1768
        """Return the revision id for this tree."""
1544
1786
        return bool(self.path2id(filename))
1545
1787
 
1546
1788
    def kind(self, file_id):
1547
 
        return self.inventory[file_id].kind
 
1789
        entry = self._get_entry(file_id=file_id)[1]
 
1790
        if entry is None:
 
1791
            raise errors.NoSuchId(tree=self, file_id=file_id)
 
1792
        return dirstate.DirState._minikind_to_kind[entry[1][0]]
 
1793
 
 
1794
    def stored_kind(self, file_id):
 
1795
        """See Tree.stored_kind"""
 
1796
        return self.kind(file_id)
 
1797
 
 
1798
    def path_content_summary(self, path):
 
1799
        """See Tree.path_content_summary."""
 
1800
        id = self.inventory.path2id(path)
 
1801
        if id is None:
 
1802
            return ('missing', None, None, None)
 
1803
        entry = self._inventory[id]
 
1804
        kind = entry.kind
 
1805
        if kind == 'file':
 
1806
            return (kind, entry.text_size, entry.executable, entry.text_sha1)
 
1807
        elif kind == 'symlink':
 
1808
            return (kind, None, None, entry.symlink_target)
 
1809
        else:
 
1810
            return (kind, None, None, None)
1548
1811
 
1549
1812
    def is_executable(self, file_id, path=None):
1550
1813
        ie = self.inventory[file_id]
1552
1815
            return None
1553
1816
        return ie.executable
1554
1817
 
1555
 
    def list_files(self, include_root=False):
 
1818
    def list_files(self, include_root=False, from_dir=None, recursive=True):
1556
1819
        # We use a standard implementation, because DirStateRevisionTree is
1557
1820
        # dealing with one of the parents of the current state
1558
1821
        inv = self._get_inventory()
1559
 
        entries = inv.iter_entries()
1560
 
        if self.inventory.root is not None and not include_root:
 
1822
        if from_dir is None:
 
1823
            from_dir_id = None
 
1824
        else:
 
1825
            from_dir_id = inv.path2id(from_dir)
 
1826
            if from_dir_id is None:
 
1827
                # Directory not versioned
 
1828
                return
 
1829
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
 
1830
        if inv.root is not None and not include_root and from_dir is None:
1561
1831
            entries.next()
1562
1832
        for path, entry in entries:
1563
1833
            yield path, 'V', entry.kind, entry.file_id, entry
1596
1866
                self._dirstate_locked = False
1597
1867
            self._repository.unlock()
1598
1868
 
 
1869
    @needs_read_lock
 
1870
    def supports_tree_reference(self):
 
1871
        return self._repo_supports_tree_reference
 
1872
 
1599
1873
    def walkdirs(self, prefix=""):
1600
1874
        # TODO: jam 20070215 This is the lazy way by using the RevisionTree
1601
 
        # implementation based on an inventory.  
 
1875
        # implementation based on an inventory.
1602
1876
        # This should be cleaned up to use the much faster Dirstate code
1603
1877
        # So for now, we just build up the parent inventory, and extract
1604
1878
        # it the same way RevisionTree does.
1633
1907
 
1634
1908
class InterDirStateTree(InterTree):
1635
1909
    """Fast path optimiser for changes_from with dirstate trees.
1636
 
    
1637
 
    This is used only when both trees are in the dirstate working file, and 
1638
 
    the source is any parent within the dirstate, and the destination is 
 
1910
 
 
1911
    This is used only when both trees are in the dirstate working file, and
 
1912
    the source is any parent within the dirstate, and the destination is
1639
1913
    the current working tree of the same dirstate.
1640
1914
    """
1641
1915
    # this could be generalized to allow comparisons between any trees in the
1654
1928
        target.set_parent_ids([revid])
1655
1929
        return target.basis_tree(), target
1656
1930
 
 
1931
    @classmethod
 
1932
    def make_source_parent_tree_python_dirstate(klass, test_case, source, target):
 
1933
        result = klass.make_source_parent_tree(source, target)
 
1934
        result[1]._iter_changes = dirstate.ProcessEntryPython
 
1935
        return result
 
1936
 
 
1937
    @classmethod
 
1938
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
 
1939
        from bzrlib.tests.test__dirstate_helpers import \
 
1940
            CompiledDirstateHelpersFeature
 
1941
        if not CompiledDirstateHelpersFeature.available():
 
1942
            from bzrlib.tests import UnavailableFeature
 
1943
            raise UnavailableFeature(CompiledDirstateHelpersFeature)
 
1944
        from bzrlib._dirstate_helpers_pyx import ProcessEntryC
 
1945
        result = klass.make_source_parent_tree(source, target)
 
1946
        result[1]._iter_changes = ProcessEntryC
 
1947
        return result
 
1948
 
1657
1949
    _matching_from_tree_format = WorkingTreeFormat4()
1658
1950
    _matching_to_tree_format = WorkingTreeFormat4()
1659
 
    _test_mutable_trees_to_test_trees = make_source_parent_tree
1660
 
 
1661
 
    def _iter_changes(self, include_unchanged=False,
 
1951
 
 
1952
    @classmethod
 
1953
    def _test_mutable_trees_to_test_trees(klass, test_case, source, target):
 
1954
        # This method shouldn't be called, because we have python and C
 
1955
        # specific flavours.
 
1956
        raise NotImplementedError
 
1957
 
 
1958
    def iter_changes(self, include_unchanged=False,
1662
1959
                      specific_files=None, pb=None, extra_trees=[],
1663
1960
                      require_versioned=True, want_unversioned=False):
1664
1961
        """Return the changes from source to target.
1665
1962
 
1666
 
        :return: An iterator that yields tuples. See InterTree._iter_changes
 
1963
        :return: An iterator that yields tuples. See InterTree.iter_changes
1667
1964
            for details.
1668
1965
        :param specific_files: An optional list of file paths to restrict the
1669
1966
            comparison to. When mapping filenames to ids, all matches in all
1680
1977
            output. An unversioned file is defined as one with (False, False)
1681
1978
            for the versioned pair.
1682
1979
        """
1683
 
        utf8_decode = cache_utf8._utf8_decode
1684
 
        _minikind_to_kind = dirstate.DirState._minikind_to_kind
1685
1980
        # NB: show_status depends on being able to pass in non-versioned files
1686
1981
        # and report them as unknown
1687
1982
        # TODO: handle extra trees in the dirstate.
1688
 
        # TODO: handle comparisons as an empty tree as a different special
1689
 
        # case? mbp 20070226
1690
 
        if extra_trees or (self.source._revision_id == NULL_REVISION):
 
1983
        if (extra_trees or specific_files == []):
1691
1984
            # we can't fast-path these cases (yet)
1692
 
            for f in super(InterDirStateTree, self)._iter_changes(
 
1985
            return super(InterDirStateTree, self).iter_changes(
1693
1986
                include_unchanged, specific_files, pb, extra_trees,
1694
 
                require_versioned, want_unversioned=want_unversioned):
1695
 
                yield f
1696
 
            return
 
1987
                require_versioned, want_unversioned=want_unversioned)
1697
1988
        parent_ids = self.target.get_parent_ids()
1698
 
        assert (self.source._revision_id in parent_ids), \
1699
 
                "revision {%s} is not stored in {%s}, but %s " \
1700
 
                "can only be used for trees stored in the dirstate" \
1701
 
                % (self.source._revision_id, self.target, self._iter_changes)
 
1989
        if not (self.source._revision_id in parent_ids
 
1990
                or self.source._revision_id == _mod_revision.NULL_REVISION):
 
1991
            raise AssertionError(
 
1992
                "revision {%s} is not stored in {%s}, but %s "
 
1993
                "can only be used for trees stored in the dirstate"
 
1994
                % (self.source._revision_id, self.target, self.iter_changes))
1702
1995
        target_index = 0
1703
 
        if self.source._revision_id == NULL_REVISION:
 
1996
        if self.source._revision_id == _mod_revision.NULL_REVISION:
1704
1997
            source_index = None
1705
1998
            indices = (target_index,)
1706
1999
        else:
1707
 
            assert (self.source._revision_id in parent_ids), \
1708
 
                "Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
1709
 
                self.source._revision_id, parent_ids)
 
2000
            if not (self.source._revision_id in parent_ids):
 
2001
                raise AssertionError(
 
2002
                    "Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
 
2003
                    self.source._revision_id, parent_ids))
1710
2004
            source_index = 1 + parent_ids.index(self.source._revision_id)
1711
 
            indices = (source_index,target_index)
 
2005
            indices = (source_index, target_index)
1712
2006
        # -- make all specific_files utf8 --
1713
2007
        if specific_files:
1714
2008
            specific_files_utf8 = set()
1715
2009
            for path in specific_files:
 
2010
                # Note, if there are many specific files, using cache_utf8
 
2011
                # would be good here.
1716
2012
                specific_files_utf8.add(path.encode('utf8'))
1717
2013
            specific_files = specific_files_utf8
1718
2014
        else:
1719
2015
            specific_files = set([''])
1720
2016
        # -- specific_files is now a utf8 path set --
 
2017
 
1721
2018
        # -- get the state object and prepare it.
1722
2019
        state = self.target.current_dirstate()
1723
2020
        state._read_dirblocks_if_needed()
1724
 
        def _entries_for_path(path):
1725
 
            """Return a list with all the entries that match path for all ids.
1726
 
            """
1727
 
            dirname, basename = os.path.split(path)
1728
 
            key = (dirname, basename, '')
1729
 
            block_index, present = state._find_block_index_from_key(key)
1730
 
            if not present:
1731
 
                # the block which should contain path is absent.
1732
 
                return []
1733
 
            result = []
1734
 
            block = state._dirblocks[block_index][1]
1735
 
            entry_index, _ = state._find_entry_index(key, block)
1736
 
            # we may need to look at multiple entries at this path: walk while the specific_files match.
1737
 
            while (entry_index < len(block) and
1738
 
                block[entry_index][0][0:2] == key[0:2]):
1739
 
                result.append(block[entry_index])
1740
 
                entry_index += 1
1741
 
            return result
1742
2021
        if require_versioned:
1743
2022
            # -- check all supplied paths are versioned in a search tree. --
1744
 
            all_versioned = True
 
2023
            not_versioned = []
1745
2024
            for path in specific_files:
1746
 
                path_entries = _entries_for_path(path)
 
2025
                path_entries = state._entries_for_path(path)
1747
2026
                if not path_entries:
1748
2027
                    # this specified path is not present at all: error
1749
 
                    all_versioned = False
1750
 
                    break
 
2028
                    not_versioned.append(path)
 
2029
                    continue
1751
2030
                found_versioned = False
1752
2031
                # for each id at this path
1753
2032
                for entry in path_entries:
1760
2039
                if not found_versioned:
1761
2040
                    # none of the indexes was not 'absent' at all ids for this
1762
2041
                    # path.
1763
 
                    all_versioned = False
1764
 
                    break
1765
 
            if not all_versioned:
1766
 
                raise errors.PathsNotVersionedError(specific_files)
 
2042
                    not_versioned.append(path)
 
2043
            if len(not_versioned) > 0:
 
2044
                raise errors.PathsNotVersionedError(not_versioned)
1767
2045
        # -- remove redundancy in supplied specific_files to prevent over-scanning --
1768
 
        search_specific_files = set()
1769
 
        for path in specific_files:
1770
 
            other_specific_files = specific_files.difference(set([path]))
1771
 
            if not osutils.is_inside_any(other_specific_files, path):
1772
 
                # this is a top level path, we must check it.
1773
 
                search_specific_files.add(path)
1774
 
        # sketch: 
1775
 
        # compare source_index and target_index at or under each element of search_specific_files.
1776
 
        # follow the following comparison table. Note that we only want to do diff operations when
1777
 
        # the target is fdl because thats when the walkdirs logic will have exposed the pathinfo 
1778
 
        # for the target.
1779
 
        # cases:
1780
 
        # 
1781
 
        # Source | Target | disk | action
1782
 
        #   r    | fdlt   |      | add source to search, add id path move and perform
1783
 
        #        |        |      | diff check on source-target
1784
 
        #   r    | fdlt   |  a   | dangling file that was present in the basis. 
1785
 
        #        |        |      | ???
1786
 
        #   r    |  a     |      | add source to search
1787
 
        #   r    |  a     |  a   | 
1788
 
        #   r    |  r     |      | this path is present in a non-examined tree, skip.
1789
 
        #   r    |  r     |  a   | this path is present in a non-examined tree, skip.
1790
 
        #   a    | fdlt   |      | add new id
1791
 
        #   a    | fdlt   |  a   | dangling locally added file, skip
1792
 
        #   a    |  a     |      | not present in either tree, skip
1793
 
        #   a    |  a     |  a   | not present in any tree, skip
1794
 
        #   a    |  r     |      | not present in either tree at this path, skip as it
1795
 
        #        |        |      | may not be selected by the users list of paths.
1796
 
        #   a    |  r     |  a   | not present in either tree at this path, skip as it
1797
 
        #        |        |      | may not be selected by the users list of paths.
1798
 
        #  fdlt  | fdlt   |      | content in both: diff them
1799
 
        #  fdlt  | fdlt   |  a   | deleted locally, but not unversioned - show as deleted ?
1800
 
        #  fdlt  |  a     |      | unversioned: output deleted id for now
1801
 
        #  fdlt  |  a     |  a   | unversioned and deleted: output deleted id
1802
 
        #  fdlt  |  r     |      | relocated in this tree, so add target to search.
1803
 
        #        |        |      | Dont diff, we will see an r,fd; pair when we reach
1804
 
        #        |        |      | this id at the other path.
1805
 
        #  fdlt  |  r     |  a   | relocated in this tree, so add target to search.
1806
 
        #        |        |      | Dont diff, we will see an r,fd; pair when we reach
1807
 
        #        |        |      | this id at the other path.
1808
 
 
1809
 
        # for all search_indexs in each path at or under each element of
1810
 
        # search_specific_files, if the detail is relocated: add the id, and add the
1811
 
        # relocated path as one to search if its not searched already. If the
1812
 
        # detail is not relocated, add the id.
1813
 
        searched_specific_files = set()
1814
 
        NULL_PARENT_DETAILS = dirstate.DirState.NULL_PARENT_DETAILS
1815
 
        # Using a list so that we can access the values and change them in
1816
 
        # nested scope. Each one is [path, file_id, entry]
1817
 
        last_source_parent = [None, None]
1818
 
        last_target_parent = [None, None]
 
2046
        search_specific_files = osutils.minimum_path_selection(specific_files)
1819
2047
 
1820
2048
        use_filesystem_for_exec = (sys.platform != 'win32')
1821
 
 
1822
 
        # Just a sentry, so that _process_entry can say that this
1823
 
        # record is handled, but isn't interesting to process (unchanged)
1824
 
        uninteresting = object()
1825
 
 
1826
 
 
1827
 
        old_dirname_to_file_id = {}
1828
 
        new_dirname_to_file_id = {}
1829
 
        # TODO: jam 20070516 - Avoid the _get_entry lookup overhead by
1830
 
        #       keeping a cache of directories that we have seen.
1831
 
 
1832
 
        def _process_entry(entry, path_info):
1833
 
            """Compare an entry and real disk to generate delta information.
1834
 
 
1835
 
            :param path_info: top_relpath, basename, kind, lstat, abspath for
1836
 
                the path of entry. If None, then the path is considered absent.
1837
 
                (Perhaps we should pass in a concrete entry for this ?)
1838
 
                Basename is returned as a utf8 string because we expect this
1839
 
                tuple will be ignored, and don't want to take the time to
1840
 
                decode.
1841
 
            :return: None if these don't match
1842
 
                     A tuple of information about the change, or
1843
 
                     the object 'uninteresting' if these match, but are
1844
 
                     basically identical.
1845
 
            """
1846
 
            if source_index is None:
1847
 
                source_details = NULL_PARENT_DETAILS
1848
 
            else:
1849
 
                source_details = entry[1][source_index]
1850
 
            target_details = entry[1][target_index]
1851
 
            target_minikind = target_details[0]
1852
 
            if path_info is not None and target_minikind in 'fdlt':
1853
 
                assert target_index == 0
1854
 
                link_or_sha1 = state.update_entry(entry, abspath=path_info[4],
1855
 
                                                  stat_value=path_info[3])
1856
 
                # The entry may have been modified by update_entry
1857
 
                target_details = entry[1][target_index]
1858
 
                target_minikind = target_details[0]
1859
 
            else:
1860
 
                link_or_sha1 = None
1861
 
            file_id = entry[0][2]
1862
 
            source_minikind = source_details[0]
1863
 
            if source_minikind in 'fdltr' and target_minikind in 'fdlt':
1864
 
                # claimed content in both: diff
1865
 
                #   r    | fdlt   |      | add source to search, add id path move and perform
1866
 
                #        |        |      | diff check on source-target
1867
 
                #   r    | fdlt   |  a   | dangling file that was present in the basis.
1868
 
                #        |        |      | ???
1869
 
                if source_minikind in 'r':
1870
 
                    # add the source to the search path to find any children it
1871
 
                    # has.  TODO ? : only add if it is a container ?
1872
 
                    if not osutils.is_inside_any(searched_specific_files,
1873
 
                                                 source_details[1]):
1874
 
                        search_specific_files.add(source_details[1])
1875
 
                    # generate the old path; this is needed for stating later
1876
 
                    # as well.
1877
 
                    old_path = source_details[1]
1878
 
                    old_dirname, old_basename = os.path.split(old_path)
1879
 
                    path = pathjoin(entry[0][0], entry[0][1])
1880
 
                    old_entry = state._get_entry(source_index,
1881
 
                                                 path_utf8=old_path)
1882
 
                    # update the source details variable to be the real
1883
 
                    # location.
1884
 
                    source_details = old_entry[1][source_index]
1885
 
                    source_minikind = source_details[0]
1886
 
                else:
1887
 
                    old_dirname = entry[0][0]
1888
 
                    old_basename = entry[0][1]
1889
 
                    old_path = path = None
1890
 
                if path_info is None:
1891
 
                    # the file is missing on disk, show as removed.
1892
 
                    content_change = True
1893
 
                    target_kind = None
1894
 
                    target_exec = False
1895
 
                else:
1896
 
                    # source and target are both versioned and disk file is present.
1897
 
                    target_kind = path_info[2]
1898
 
                    if target_kind == 'directory':
1899
 
                        if path is None:
1900
 
                            old_path = path = pathjoin(old_dirname, old_basename)
1901
 
                        new_dirname_to_file_id[path] = file_id
1902
 
                        if source_minikind != 'd':
1903
 
                            content_change = True
1904
 
                        else:
1905
 
                            # directories have no fingerprint
1906
 
                            content_change = False
1907
 
                        target_exec = False
1908
 
                    elif target_kind == 'file':
1909
 
                        if source_minikind != 'f':
1910
 
                            content_change = True
1911
 
                        else:
1912
 
                            # We could check the size, but we already have the
1913
 
                            # sha1 hash.
1914
 
                            content_change = (link_or_sha1 != source_details[1])
1915
 
                        # Target details is updated at update_entry time
1916
 
                        if use_filesystem_for_exec:
1917
 
                            # We don't need S_ISREG here, because we are sure
1918
 
                            # we are dealing with a file.
1919
 
                            target_exec = bool(stat.S_IEXEC & path_info[3].st_mode)
1920
 
                        else:
1921
 
                            target_exec = target_details[3]
1922
 
                    elif target_kind == 'symlink':
1923
 
                        if source_minikind != 'l':
1924
 
                            content_change = True
1925
 
                        else:
1926
 
                            content_change = (link_or_sha1 != source_details[1])
1927
 
                        target_exec = False
1928
 
                    elif target_kind == 'tree-reference':
1929
 
                        if source_minikind != 't':
1930
 
                            content_change = True
1931
 
                        else:
1932
 
                            content_change = False
1933
 
                        target_exec = False
1934
 
                    else:
1935
 
                        raise Exception, "unknown kind %s" % path_info[2]
1936
 
                if source_minikind == 'd':
1937
 
                    if path is None:
1938
 
                        old_path = path = pathjoin(old_dirname, old_basename)
1939
 
                    old_dirname_to_file_id[old_path] = file_id
1940
 
                # parent id is the entry for the path in the target tree
1941
 
                if old_dirname == last_source_parent[0]:
1942
 
                    source_parent_id = last_source_parent[1]
1943
 
                else:
1944
 
                    try:
1945
 
                        source_parent_id = old_dirname_to_file_id[old_dirname]
1946
 
                    except KeyError:
1947
 
                        source_parent_entry = state._get_entry(source_index,
1948
 
                                                               path_utf8=old_dirname)
1949
 
                        source_parent_id = source_parent_entry[0][2]
1950
 
                    if source_parent_id == entry[0][2]:
1951
 
                        # This is the root, so the parent is None
1952
 
                        source_parent_id = None
1953
 
                    else:
1954
 
                        last_source_parent[0] = old_dirname
1955
 
                        last_source_parent[1] = source_parent_id
1956
 
                new_dirname = entry[0][0]
1957
 
                if new_dirname == last_target_parent[0]:
1958
 
                    target_parent_id = last_target_parent[1]
1959
 
                else:
1960
 
                    try:
1961
 
                        target_parent_id = new_dirname_to_file_id[new_dirname]
1962
 
                    except KeyError:
1963
 
                        # TODO: We don't always need to do the lookup, because the
1964
 
                        #       parent entry will be the same as the source entry.
1965
 
                        target_parent_entry = state._get_entry(target_index,
1966
 
                                                               path_utf8=new_dirname)
1967
 
                        assert target_parent_entry != (None, None), (
1968
 
                            "Could not find target parent in wt: %s\nparent of: %s"
1969
 
                            % (new_dirname, entry))
1970
 
                        target_parent_id = target_parent_entry[0][2]
1971
 
                    if target_parent_id == entry[0][2]:
1972
 
                        # This is the root, so the parent is None
1973
 
                        target_parent_id = None
1974
 
                    else:
1975
 
                        last_target_parent[0] = new_dirname
1976
 
                        last_target_parent[1] = target_parent_id
1977
 
 
1978
 
                source_exec = source_details[3]
1979
 
                if (include_unchanged
1980
 
                    or content_change
1981
 
                    or source_parent_id != target_parent_id
1982
 
                    or old_basename != entry[0][1]
1983
 
                    or source_exec != target_exec
1984
 
                    ):
1985
 
                    if old_path is None:
1986
 
                        old_path = path = pathjoin(old_dirname, old_basename)
1987
 
                        old_path_u = utf8_decode(old_path)[0]
1988
 
                        path_u = old_path_u
1989
 
                    else:
1990
 
                        old_path_u = utf8_decode(old_path)[0]
1991
 
                        if old_path == path:
1992
 
                            path_u = old_path_u
1993
 
                        else:
1994
 
                            path_u = utf8_decode(path)[0]
1995
 
                    source_kind = _minikind_to_kind[source_minikind]
1996
 
                    return (entry[0][2],
1997
 
                           (old_path_u, path_u),
1998
 
                           content_change,
1999
 
                           (True, True),
2000
 
                           (source_parent_id, target_parent_id),
2001
 
                           (utf8_decode(old_basename)[0], utf8_decode(entry[0][1])[0]),
2002
 
                           (source_kind, target_kind),
2003
 
                           (source_exec, target_exec))
2004
 
                else:
2005
 
                    return uninteresting
2006
 
            elif source_minikind in 'a' and target_minikind in 'fdlt':
2007
 
                # looks like a new file
2008
 
                if path_info is not None:
2009
 
                    path = pathjoin(entry[0][0], entry[0][1])
2010
 
                    # parent id is the entry for the path in the target tree
2011
 
                    # TODO: these are the same for an entire directory: cache em.
2012
 
                    parent_id = state._get_entry(target_index,
2013
 
                                                 path_utf8=entry[0][0])[0][2]
2014
 
                    if parent_id == entry[0][2]:
2015
 
                        parent_id = None
2016
 
                    if use_filesystem_for_exec:
2017
 
                        # We need S_ISREG here, because we aren't sure if this
2018
 
                        # is a file or not.
2019
 
                        target_exec = bool(
2020
 
                            stat.S_ISREG(path_info[3].st_mode)
2021
 
                            and stat.S_IEXEC & path_info[3].st_mode)
2022
 
                    else:
2023
 
                        target_exec = target_details[3]
2024
 
                    return (entry[0][2],
2025
 
                           (None, utf8_decode(path)[0]),
2026
 
                           True,
2027
 
                           (False, True),
2028
 
                           (None, parent_id),
2029
 
                           (None, utf8_decode(entry[0][1])[0]),
2030
 
                           (None, path_info[2]),
2031
 
                           (None, target_exec))
2032
 
                else:
2033
 
                    # but its not on disk: we deliberately treat this as just
2034
 
                    # never-present. (Why ?! - RBC 20070224)
2035
 
                    pass
2036
 
            elif source_minikind in 'fdlt' and target_minikind in 'a':
2037
 
                # unversioned, possibly, or possibly not deleted: we dont care.
2038
 
                # if its still on disk, *and* theres no other entry at this
2039
 
                # path [we dont know this in this routine at the moment -
2040
 
                # perhaps we should change this - then it would be an unknown.
2041
 
                old_path = pathjoin(entry[0][0], entry[0][1])
2042
 
                # parent id is the entry for the path in the target tree
2043
 
                parent_id = state._get_entry(source_index, path_utf8=entry[0][0])[0][2]
2044
 
                if parent_id == entry[0][2]:
2045
 
                    parent_id = None
2046
 
                return (entry[0][2],
2047
 
                       (utf8_decode(old_path)[0], None),
2048
 
                       True,
2049
 
                       (True, False),
2050
 
                       (parent_id, None),
2051
 
                       (utf8_decode(entry[0][1])[0], None),
2052
 
                       (_minikind_to_kind[source_minikind], None),
2053
 
                       (source_details[3], None))
2054
 
            elif source_minikind in 'fdlt' and target_minikind in 'r':
2055
 
                # a rename; could be a true rename, or a rename inherited from
2056
 
                # a renamed parent. TODO: handle this efficiently. Its not
2057
 
                # common case to rename dirs though, so a correct but slow
2058
 
                # implementation will do.
2059
 
                if not osutils.is_inside_any(searched_specific_files, target_details[1]):
2060
 
                    search_specific_files.add(target_details[1])
2061
 
            elif source_minikind in 'ra' and target_minikind in 'ra':
2062
 
                # neither of the selected trees contain this file,
2063
 
                # so skip over it. This is not currently directly tested, but
2064
 
                # is indirectly via test_too_much.TestCommands.test_conflicts.
2065
 
                pass
2066
 
            else:
2067
 
                raise AssertionError("don't know how to compare "
2068
 
                    "source_minikind=%r, target_minikind=%r"
2069
 
                    % (source_minikind, target_minikind))
2070
 
                ## import pdb;pdb.set_trace()
2071
 
            return None
2072
 
 
2073
 
        while search_specific_files:
2074
 
            # TODO: the pending list should be lexically sorted?  the
2075
 
            # interface doesn't require it.
2076
 
            current_root = search_specific_files.pop()
2077
 
            current_root_unicode = current_root.decode('utf8')
2078
 
            searched_specific_files.add(current_root)
2079
 
            # process the entries for this containing directory: the rest will be
2080
 
            # found by their parents recursively.
2081
 
            root_entries = _entries_for_path(current_root)
2082
 
            root_abspath = self.target.abspath(current_root_unicode)
2083
 
            try:
2084
 
                root_stat = os.lstat(root_abspath)
2085
 
            except OSError, e:
2086
 
                if e.errno == errno.ENOENT:
2087
 
                    # the path does not exist: let _process_entry know that.
2088
 
                    root_dir_info = None
2089
 
                else:
2090
 
                    # some other random error: hand it up.
2091
 
                    raise
2092
 
            else:
2093
 
                root_dir_info = ('', current_root,
2094
 
                    osutils.file_kind_from_stat_mode(root_stat.st_mode), root_stat,
2095
 
                    root_abspath)
2096
 
                if root_dir_info[2] == 'directory':
2097
 
                    if self.target._directory_is_tree_reference(
2098
 
                        current_root.decode('utf8')):
2099
 
                        root_dir_info = root_dir_info[:2] + \
2100
 
                            ('tree-reference',) + root_dir_info[3:]
2101
 
 
2102
 
            if not root_entries and not root_dir_info:
2103
 
                # this specified path is not present at all, skip it.
2104
 
                continue
2105
 
            path_handled = False
2106
 
            for entry in root_entries:
2107
 
                result = _process_entry(entry, root_dir_info)
2108
 
                if result is not None:
2109
 
                    path_handled = True
2110
 
                    if result is not uninteresting:
2111
 
                        yield result
2112
 
            if want_unversioned and not path_handled and root_dir_info:
2113
 
                new_executable = bool(
2114
 
                    stat.S_ISREG(root_dir_info[3].st_mode)
2115
 
                    and stat.S_IEXEC & root_dir_info[3].st_mode)
2116
 
                yield (None,
2117
 
                       (None, current_root_unicode),
2118
 
                       True,
2119
 
                       (False, False),
2120
 
                       (None, None),
2121
 
                       (None, splitpath(current_root_unicode)[-1]),
2122
 
                       (None, root_dir_info[2]),
2123
 
                       (None, new_executable)
2124
 
                      )
2125
 
            initial_key = (current_root, '', '')
2126
 
            block_index, _ = state._find_block_index_from_key(initial_key)
2127
 
            if block_index == 0:
2128
 
                # we have processed the total root already, but because the
2129
 
                # initial key matched it we should skip it here.
2130
 
                block_index +=1
2131
 
            if root_dir_info and root_dir_info[2] == 'tree-reference':
2132
 
                current_dir_info = None
2133
 
            else:
2134
 
                dir_iterator = osutils._walkdirs_utf8(root_abspath, prefix=current_root)
2135
 
                try:
2136
 
                    current_dir_info = dir_iterator.next()
2137
 
                except OSError, e:
2138
 
                    # on win32, python2.4 has e.errno == ERROR_DIRECTORY, but
2139
 
                    # python 2.5 has e.errno == EINVAL,
2140
 
                    #            and e.winerror == ERROR_DIRECTORY
2141
 
                    e_winerror = getattr(e, 'winerror', None)
2142
 
                    win_errors = (ERROR_DIRECTORY, ERROR_PATH_NOT_FOUND)
2143
 
                    # there may be directories in the inventory even though
2144
 
                    # this path is not a file on disk: so mark it as end of
2145
 
                    # iterator
2146
 
                    if e.errno in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
2147
 
                        current_dir_info = None
2148
 
                    elif (sys.platform == 'win32'
2149
 
                          and (e.errno in win_errors
2150
 
                               or e_winerror in win_errors)):
2151
 
                        current_dir_info = None
2152
 
                    else:
2153
 
                        raise
2154
 
                else:
2155
 
                    if current_dir_info[0][0] == '':
2156
 
                        # remove .bzr from iteration
2157
 
                        bzr_index = bisect_left(current_dir_info[1], ('.bzr',))
2158
 
                        assert current_dir_info[1][bzr_index][0] == '.bzr'
2159
 
                        del current_dir_info[1][bzr_index]
2160
 
            # walk until both the directory listing and the versioned metadata
2161
 
            # are exhausted. 
2162
 
            if (block_index < len(state._dirblocks) and
2163
 
                osutils.is_inside(current_root, state._dirblocks[block_index][0])):
2164
 
                current_block = state._dirblocks[block_index]
2165
 
            else:
2166
 
                current_block = None
2167
 
            while (current_dir_info is not None or
2168
 
                   current_block is not None):
2169
 
                if (current_dir_info and current_block
2170
 
                    and current_dir_info[0][0] != current_block[0]):
2171
 
                    if current_dir_info[0][0].split('/') < current_block[0].split('/'):
2172
 
                        # filesystem data refers to paths not covered by the dirblock.
2173
 
                        # this has two possibilities:
2174
 
                        # A) it is versioned but empty, so there is no block for it
2175
 
                        # B) it is not versioned.
2176
 
 
2177
 
                        # if (A) then we need to recurse into it to check for
2178
 
                        # new unknown files or directories.
2179
 
                        # if (B) then we should ignore it, because we don't
2180
 
                        # recurse into unknown directories.
2181
 
                        path_index = 0
2182
 
                        while path_index < len(current_dir_info[1]):
2183
 
                                current_path_info = current_dir_info[1][path_index]
2184
 
                                if want_unversioned:
2185
 
                                    if current_path_info[2] == 'directory':
2186
 
                                        if self.target._directory_is_tree_reference(
2187
 
                                            current_path_info[0].decode('utf8')):
2188
 
                                            current_path_info = current_path_info[:2] + \
2189
 
                                                ('tree-reference',) + current_path_info[3:]
2190
 
                                    new_executable = bool(
2191
 
                                        stat.S_ISREG(current_path_info[3].st_mode)
2192
 
                                        and stat.S_IEXEC & current_path_info[3].st_mode)
2193
 
                                    yield (None,
2194
 
                                        (None, utf8_decode(current_path_info[0])[0]),
2195
 
                                        True,
2196
 
                                        (False, False),
2197
 
                                        (None, None),
2198
 
                                        (None, utf8_decode(current_path_info[1])[0]),
2199
 
                                        (None, current_path_info[2]),
2200
 
                                        (None, new_executable))
2201
 
                                # dont descend into this unversioned path if it is
2202
 
                                # a dir
2203
 
                                if current_path_info[2] in ('directory',
2204
 
                                                            'tree-reference'):
2205
 
                                    del current_dir_info[1][path_index]
2206
 
                                    path_index -= 1
2207
 
                                path_index += 1
2208
 
 
2209
 
                        # This dir info has been handled, go to the next
2210
 
                        try:
2211
 
                            current_dir_info = dir_iterator.next()
2212
 
                        except StopIteration:
2213
 
                            current_dir_info = None
2214
 
                    else:
2215
 
                        # We have a dirblock entry for this location, but there
2216
 
                        # is no filesystem path for this. This is most likely
2217
 
                        # because a directory was removed from the disk.
2218
 
                        # We don't have to report the missing directory,
2219
 
                        # because that should have already been handled, but we
2220
 
                        # need to handle all of the files that are contained
2221
 
                        # within.
2222
 
                        for current_entry in current_block[1]:
2223
 
                            # entry referring to file not present on disk.
2224
 
                            # advance the entry only, after processing.
2225
 
                            result = _process_entry(current_entry, None)
2226
 
                            if result is not None:
2227
 
                                if result is not uninteresting:
2228
 
                                    yield result
2229
 
                        block_index +=1
2230
 
                        if (block_index < len(state._dirblocks) and
2231
 
                            osutils.is_inside(current_root,
2232
 
                                              state._dirblocks[block_index][0])):
2233
 
                            current_block = state._dirblocks[block_index]
2234
 
                        else:
2235
 
                            current_block = None
2236
 
                    continue
2237
 
                entry_index = 0
2238
 
                if current_block and entry_index < len(current_block[1]):
2239
 
                    current_entry = current_block[1][entry_index]
2240
 
                else:
2241
 
                    current_entry = None
2242
 
                advance_entry = True
2243
 
                path_index = 0
2244
 
                if current_dir_info and path_index < len(current_dir_info[1]):
2245
 
                    current_path_info = current_dir_info[1][path_index]
2246
 
                    if current_path_info[2] == 'directory':
2247
 
                        if self.target._directory_is_tree_reference(
2248
 
                            current_path_info[0].decode('utf8')):
2249
 
                            current_path_info = current_path_info[:2] + \
2250
 
                                ('tree-reference',) + current_path_info[3:]
2251
 
                else:
2252
 
                    current_path_info = None
2253
 
                advance_path = True
2254
 
                path_handled = False
2255
 
                while (current_entry is not None or
2256
 
                    current_path_info is not None):
2257
 
                    if current_entry is None:
2258
 
                        # the check for path_handled when the path is adnvaced
2259
 
                        # will yield this path if needed.
2260
 
                        pass
2261
 
                    elif current_path_info is None:
2262
 
                        # no path is fine: the per entry code will handle it.
2263
 
                        result = _process_entry(current_entry, current_path_info)
2264
 
                        if result is not None:
2265
 
                            if result is not uninteresting:
2266
 
                                yield result
2267
 
                    elif (current_entry[0][1] != current_path_info[1]
2268
 
                          or current_entry[1][target_index][0] in 'ar'):
2269
 
                        # The current path on disk doesn't match the dirblock
2270
 
                        # record. Either the dirblock is marked as absent, or
2271
 
                        # the file on disk is not present at all in the
2272
 
                        # dirblock. Either way, report about the dirblock
2273
 
                        # entry, and let other code handle the filesystem one.
2274
 
 
2275
 
                        # Compare the basename for these files to determine
2276
 
                        # which comes first
2277
 
                        if current_path_info[1] < current_entry[0][1]:
2278
 
                            # extra file on disk: pass for now, but only
2279
 
                            # increment the path, not the entry
2280
 
                            advance_entry = False
2281
 
                        else:
2282
 
                            # entry referring to file not present on disk.
2283
 
                            # advance the entry only, after processing.
2284
 
                            result = _process_entry(current_entry, None)
2285
 
                            if result is not None:
2286
 
                                if result is not uninteresting:
2287
 
                                    yield result
2288
 
                            advance_path = False
2289
 
                    else:
2290
 
                        result = _process_entry(current_entry, current_path_info)
2291
 
                        if result is not None:
2292
 
                            path_handled = True
2293
 
                            if result is not uninteresting:
2294
 
                                yield result
2295
 
                    if advance_entry and current_entry is not None:
2296
 
                        entry_index += 1
2297
 
                        if entry_index < len(current_block[1]):
2298
 
                            current_entry = current_block[1][entry_index]
2299
 
                        else:
2300
 
                            current_entry = None
2301
 
                    else:
2302
 
                        advance_entry = True # reset the advance flaga
2303
 
                    if advance_path and current_path_info is not None:
2304
 
                        if not path_handled:
2305
 
                            # unversioned in all regards
2306
 
                            if want_unversioned:
2307
 
                                new_executable = bool(
2308
 
                                    stat.S_ISREG(current_path_info[3].st_mode)
2309
 
                                    and stat.S_IEXEC & current_path_info[3].st_mode)
2310
 
                                yield (None,
2311
 
                                    (None, utf8_decode(current_path_info[0])[0]),
2312
 
                                    True,
2313
 
                                    (False, False),
2314
 
                                    (None, None),
2315
 
                                    (None, utf8_decode(current_path_info[1])[0]),
2316
 
                                    (None, current_path_info[2]),
2317
 
                                    (None, new_executable))
2318
 
                            # dont descend into this unversioned path if it is
2319
 
                            # a dir
2320
 
                            if current_path_info[2] in ('directory'):
2321
 
                                del current_dir_info[1][path_index]
2322
 
                                path_index -= 1
2323
 
                        # dont descend the disk iterator into any tree 
2324
 
                        # paths.
2325
 
                        if current_path_info[2] == 'tree-reference':
2326
 
                            del current_dir_info[1][path_index]
2327
 
                            path_index -= 1
2328
 
                        path_index += 1
2329
 
                        if path_index < len(current_dir_info[1]):
2330
 
                            current_path_info = current_dir_info[1][path_index]
2331
 
                            if current_path_info[2] == 'directory':
2332
 
                                if self.target._directory_is_tree_reference(
2333
 
                                    current_path_info[0].decode('utf8')):
2334
 
                                    current_path_info = current_path_info[:2] + \
2335
 
                                        ('tree-reference',) + current_path_info[3:]
2336
 
                        else:
2337
 
                            current_path_info = None
2338
 
                        path_handled = False
2339
 
                    else:
2340
 
                        advance_path = True # reset the advance flagg.
2341
 
                if current_block is not None:
2342
 
                    block_index += 1
2343
 
                    if (block_index < len(state._dirblocks) and
2344
 
                        osutils.is_inside(current_root, state._dirblocks[block_index][0])):
2345
 
                        current_block = state._dirblocks[block_index]
2346
 
                    else:
2347
 
                        current_block = None
2348
 
                if current_dir_info is not None:
2349
 
                    try:
2350
 
                        current_dir_info = dir_iterator.next()
2351
 
                    except StopIteration:
2352
 
                        current_dir_info = None
2353
 
 
 
2049
        iter_changes = self.target._iter_changes(include_unchanged,
 
2050
            use_filesystem_for_exec, search_specific_files, state,
 
2051
            source_index, target_index, want_unversioned, self.target)
 
2052
        return iter_changes.iter_changes()
2354
2053
 
2355
2054
    @staticmethod
2356
2055
    def is_compatible(source, target):
2357
2056
        # the target must be a dirstate working tree
2358
 
        if not isinstance(target, WorkingTree4):
 
2057
        if not isinstance(target, DirStateWorkingTree):
2359
2058
            return False
2360
 
        # the source must be a revtreee or dirstate rev tree.
 
2059
        # the source must be a revtree or dirstate rev tree.
2361
2060
        if not isinstance(source,
2362
2061
            (revisiontree.RevisionTree, DirStateRevisionTree)):
2363
2062
            return False
2364
2063
        # the source revid must be in the target dirstate
2365
 
        if not (source._revision_id == NULL_REVISION or
 
2064
        if not (source._revision_id == _mod_revision.NULL_REVISION or
2366
2065
            source._revision_id in target.get_parent_ids()):
2367
 
            # TODO: what about ghosts? it may well need to 
 
2066
            # TODO: what about ghosts? it may well need to
2368
2067
            # check for them explicitly.
2369
2068
            return False
2370
2069
        return True
2380
2079
 
2381
2080
    def convert(self, tree):
2382
2081
        # lock the control files not the tree, so that we dont get tree
2383
 
        # on-unlock behaviours, and so that noone else diddles with the 
 
2082
        # on-unlock behaviours, and so that noone else diddles with the
2384
2083
        # tree during upgrade.
2385
2084
        tree._control_files.lock_write()
2386
2085
        try:
2412
2111
 
2413
2112
    def update_format(self, tree):
2414
2113
        """Change the format marker."""
2415
 
        tree._control_files.put_utf8('format',
2416
 
            self.target_format.get_format_string())
 
2114
        tree._transport.put_bytes('format',
 
2115
            self.target_format.get_format_string(),
 
2116
            mode=tree.bzrdir._get_file_mode())
 
2117
 
 
2118
 
 
2119
class Converter4to5(object):
 
2120
    """Perform an in-place upgrade of format 4 to format 5 trees."""
 
2121
 
 
2122
    def __init__(self):
 
2123
        self.target_format = WorkingTreeFormat5()
 
2124
 
 
2125
    def convert(self, tree):
 
2126
        # lock the control files not the tree, so that we don't get tree
 
2127
        # on-unlock behaviours, and so that no-one else diddles with the
 
2128
        # tree during upgrade.
 
2129
        tree._control_files.lock_write()
 
2130
        try:
 
2131
            self.update_format(tree)
 
2132
        finally:
 
2133
            tree._control_files.unlock()
 
2134
 
 
2135
    def update_format(self, tree):
 
2136
        """Change the format marker."""
 
2137
        tree._transport.put_bytes('format',
 
2138
            self.target_format.get_format_string(),
 
2139
            mode=tree.bzrdir._get_file_mode())
 
2140
 
 
2141
 
 
2142
class Converter4or5to6(object):
 
2143
    """Perform an in-place upgrade of format 4 or 5 to format 6 trees."""
 
2144
 
 
2145
    def __init__(self):
 
2146
        self.target_format = WorkingTreeFormat6()
 
2147
 
 
2148
    def convert(self, tree):
 
2149
        # lock the control files not the tree, so that we don't get tree
 
2150
        # on-unlock behaviours, and so that no-one else diddles with the
 
2151
        # tree during upgrade.
 
2152
        tree._control_files.lock_write()
 
2153
        try:
 
2154
            self.init_custom_control_files(tree)
 
2155
            self.update_format(tree)
 
2156
        finally:
 
2157
            tree._control_files.unlock()
 
2158
 
 
2159
    def init_custom_control_files(self, tree):
 
2160
        """Initialize custom control files."""
 
2161
        tree._transport.put_bytes('views', '',
 
2162
            mode=tree.bzrdir._get_file_mode())
 
2163
 
 
2164
    def update_format(self, tree):
 
2165
        """Change the format marker."""
 
2166
        tree._transport.put_bytes('format',
 
2167
            self.target_format.get_format_string(),
 
2168
            mode=tree.bzrdir._get_file_mode())