/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
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
16
17
"""WorkingTree4 format and implementation.
18
19
WorkingTree4 provides the dirstate based working tree logic.
20
21
To get a WorkingTree, call bzrdir.open_workingtree() or
22
WorkingTree.open(dir).
23
"""
24
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
25
from cStringIO import StringIO
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
26
import os
27
28
from bzrlib.lazy_import import lazy_import
29
lazy_import(globals(), """
30
from bisect import bisect_left
31
import collections
32
from copy import deepcopy
33
import errno
34
import itertools
35
import operator
36
import stat
37
from time import time
38
import warnings
39
40
import bzrlib
41
from bzrlib import (
42
    bzrdir,
43
    conflicts as _mod_conflicts,
44
    dirstate,
45
    errors,
46
    generate_ids,
47
    globbing,
48
    hashcache,
49
    ignores,
50
    merge,
51
    osutils,
52
    textui,
53
    transform,
54
    urlutils,
55
    xml5,
56
    xml6,
57
    )
58
import bzrlib.branch
59
from bzrlib.transport import get_transport
60
import bzrlib.ui
61
""")
62
63
from bzrlib import symbol_versioning
64
from bzrlib.decorators import needs_read_lock, needs_write_lock
2255.2.74 by Robert Collins
Minor performance optimisation in _generate_inventory by avoiding normalisation checks and just using a factory to create the inventory entries.
65
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, entry_factory
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
66
from bzrlib.lockable_files import LockableFiles, TransportLock
67
from bzrlib.lockdir import LockDir
68
import bzrlib.mutabletree
69
from bzrlib.mutabletree import needs_tree_write_lock
70
from bzrlib.osutils import (
71
    compact_date,
72
    file_kind,
73
    isdir,
74
    normpath,
75
    pathjoin,
76
    rand_chars,
77
    realpath,
78
    safe_unicode,
79
    splitpath,
80
    supports_executable,
81
    )
82
from bzrlib.trace import mutter, note
83
from bzrlib.transport.local import LocalTransport
84
from bzrlib.progress import DummyProgress, ProgressPhase
85
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
86
from bzrlib.rio import RioReader, rio_file, Stanza
87
from bzrlib.symbol_versioning import (deprecated_passed,
88
        deprecated_method,
89
        deprecated_function,
90
        DEPRECATED_PARAMETER,
91
        zero_eight,
92
        zero_eleven,
93
        zero_thirteen,
94
        )
95
from bzrlib.tree import Tree
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
96
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
97
98
99
class WorkingTree4(WorkingTree3):
100
    """This is the Format 4 working tree.
101
102
    This differs from WorkingTree3 by:
103
     - having a consolidated internal dirstate.
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
104
     - not having a regular inventory attribute.
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
105
106
    This is new in bzr TODO FIXME SETMEBEFORE MERGE.
107
    """
108
109
    def __init__(self, basedir,
110
                 branch,
111
                 _control_files=None,
112
                 _format=None,
113
                 _bzrdir=None):
114
        """Construct a WorkingTree for basedir.
115
116
        If the branch is not supplied, it is opened automatically.
117
        If the branch is supplied, it must be the branch for this basedir.
118
        (branch.base is not cross checked, because for remote branches that
119
        would be meaningless).
120
        """
121
        self._format = _format
122
        self.bzrdir = _bzrdir
123
        from bzrlib.hashcache import HashCache
124
        from bzrlib.trace import note, mutter
125
        assert isinstance(basedir, basestring), \
126
            "base directory %r is not a string" % basedir
127
        basedir = safe_unicode(basedir)
128
        mutter("opening working tree %r", basedir)
129
        self._branch = branch
130
        assert isinstance(self.branch, bzrlib.branch.Branch), \
131
            "branch %r is not a Branch" % self.branch
132
        self.basedir = realpath(basedir)
133
        # if branch is at our basedir and is a format 6 or less
134
        # assume all other formats have their own control files.
135
        assert isinstance(_control_files, LockableFiles), \
136
            "_control_files must be a LockableFiles, not %r" % _control_files
137
        self._control_files = _control_files
138
        # update the whole cache up front and write to disk if anything changed;
139
        # in the future we might want to do this more selectively
140
        # two possible ways offer themselves : in self._unlock, write the cache
141
        # if needed, or, when the cache sees a change, append it to the hash
142
        # cache file, and have the parser take the most recent entry for a
143
        # given path only.
144
        cache_filename = self.bzrdir.get_workingtree_transport(None).local_abspath('stat-cache')
145
        hc = self._hashcache = HashCache(basedir, cache_filename, self._control_files._file_mode)
146
        hc.read()
147
        # is this scan needed ? it makes things kinda slow.
148
        #hc.scan()
149
150
        if hc.needs_write:
151
            mutter("write hc")
152
            hc.write()
153
154
        self._dirty = None
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
155
        #-------------
156
        # during a read or write lock these objects are set, and are
157
        # None the rest of the time.
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
158
        self._dirstate = None
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
159
        self._inventory = None
160
        #-------------
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
161
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
162
    @needs_tree_write_lock
2255.2.12 by Robert Collins
Partial implementation of WorkingTree4._add.
163
    def _add(self, files, ids, kinds):
164
        """See MutableTree._add."""
165
        state = self.current_dirstate()
166
        for f, file_id, kind in zip(files, ids, kinds):
2255.2.14 by Robert Collins
Dirstate: fix adding of directories to setup the next directories block, and test representation of symlinks. Also fix iter_rows to not reset the dirty bit.
167
            f = f.strip('/')
2255.2.12 by Robert Collins
Partial implementation of WorkingTree4._add.
168
            assert '//' not in f
169
            assert '..' not in f
170
            if file_id is None:
2255.2.20 by Robert Collins
Bypass irrelevant basis_inventory tests for dirstate.
171
                file_id = generate_ids.gen_file_id(f)
2255.2.43 by Robert Collins
WorkingTree4.add must not require a file to exist to add it when kind etc is given.
172
            # deliberately add the file with no cached stat or sha1
173
            # - on the first access it will be gathered, and we can
174
            # always change this once tests are all passing.
175
            state.add(f, file_id, kind, None, '')
2255.2.16 by Robert Collins
Implement WorkingTreeFormat4._write_inventory for better compatability with existing code, letting more test_test_trees pass, now up to test_tree_with_subdirs_and_all_content_types.
176
        self._dirty = True
2255.2.12 by Robert Collins
Partial implementation of WorkingTree4._add.
177
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
178
    def current_dirstate(self):
179
        """Return the current dirstate object. 
180
181
        This is not part of the tree interface and only exposed for ease of
182
        testing.
183
184
        :raises errors.NotWriteLocked: when not in a lock. 
185
            XXX: This should probably be errors.NotLocked.
186
        """
187
        if not self._control_files._lock_count:
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
188
            raise errors.ObjectNotLocked(self)
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
189
        if self._dirstate is not None:
190
            return self._dirstate
191
        local_path = self.bzrdir.get_workingtree_transport(None
192
            ).local_abspath('dirstate')
193
        self._dirstate = dirstate.DirState.on_file(local_path)
194
        return self._dirstate
195
2255.2.81 by Robert Collins
WorkingTree4: Implement filter_unversioned_files to use dirstate bisection.
196
    def filter_unversioned_files(self, paths):
197
        """Filter out paths that are not versioned.
198
199
        :return: set of paths.
200
        """
201
        # TODO: make a generic multi-bisect routine roughly that should list
202
        # the paths, then process one half at a time recursively, and feed the
203
        # results of each bisect in further still
204
        paths = sorted(paths)
205
        result = set()
206
        state = self.current_dirstate()
207
        # TODO we want a paths_to_dirblocks helper I think
208
        for path in paths:
209
            dirname, basename = os.path.split(path.encode('utf8'))
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
210
            _, _, _, path_is_versioned = state._get_block_entry_index(
211
                dirname, basename, 0)
2255.2.81 by Robert Collins
WorkingTree4: Implement filter_unversioned_files to use dirstate bisection.
212
            if path_is_versioned:
213
                result.add(path)
214
        return result
215
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
216
    def flush(self):
217
        """Write all cached data to disk."""
2255.2.39 by Robert Collins
WorkingTree4: flush can only be used during write locks.
218
        if self._control_files._lock_mode != 'w':
219
            raise errors.NotWriteLocked(self)
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
220
        self.current_dirstate().save()
221
        self._inventory = None
222
        self._dirty = False
223
2255.2.34 by Robert Collins
Fix WorkingTree4 parent_ids logic to use the dirstate to answer parent ids list queries.
224
    def _generate_inventory(self):
225
        """Create and set self.inventory from the dirstate object.
226
        
227
        This is relatively expensive: we have to walk the entire dirstate.
228
        Ideally we would not, and can deprecate this function.
229
        """
2255.2.82 by Robert Collins
various notes about find_ids_across_trees
230
        #: uncomment to trap on inventory requests.
231
        # import pdb;pdb.set_trace()
2255.2.75 by Robert Collins
Correct generation of revisiontree inventories to handle out of order parents.
232
        state = self.current_dirstate()
233
        state._read_dirblocks_if_needed()
2255.2.87 by Robert Collins
core dirstate tests passing with new structure.
234
        root_key, current_entry = self._get_entry(path='')
235
        current_id = root_key[2].decode('utf8')
236
        assert current_entry[0][0] == 'directory'
2255.2.75 by Robert Collins
Correct generation of revisiontree inventories to handle out of order parents.
237
        inv = Inventory(root_id=current_id)
2255.2.73 by Robert Collins
50% speedup in the dirstate->inventory conversion logic by caching the parent ids as we walk the tree. Some further work would be to maintain a stack of parents as we know we visit depth first.
238
        # we could do this straight out of the dirstate; it might be fast
239
        # and should be profiled - RBC 20070216
240
        parent_ids = {'' : inv.root.file_id}
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
241
        for block in state._dirblocks[1:]: # skip the root
2255.2.77 by Robert Collins
Tune working inventory generation more: walk the blocks, skipping deleted rows.
242
            dirname = block[0]
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
243
            try:
244
                parent_id = parent_ids[block[0]]
245
            except KeyError:
246
                # all the paths in this block are not versioned in this tree
247
                continue
2255.2.87 by Robert Collins
core dirstate tests passing with new structure.
248
            for key, entry in block[1]:
249
                if entry[0][0] in ('absent', 'relocated'):
250
                    # a parent tree only entry
251
                    continue
252
                name = key[1].decode('utf8')
253
                file_id = key[2].decode('utf8')
254
                kind, link_or_sha1, size, executable, stat = entry[0]
255
                inv_entry = entry_factory[kind](file_id, name, parent_id)
2255.2.77 by Robert Collins
Tune working inventory generation more: walk the blocks, skipping deleted rows.
256
                if kind == 'file':
2255.2.87 by Robert Collins
core dirstate tests passing with new structure.
257
                    # not strictly needed: working tree
2255.2.77 by Robert Collins
Tune working inventory generation more: walk the blocks, skipping deleted rows.
258
                    #entry.executable = executable
259
                    #entry.text_size = size
260
                    #entry.text_sha1 = sha1
261
                    pass
262
                elif kind == 'directory':
2255.2.87 by Robert Collins
core dirstate tests passing with new structure.
263
                    # add this entry to the parent map.
2255.2.77 by Robert Collins
Tune working inventory generation more: walk the blocks, skipping deleted rows.
264
                    parent_ids[(dirname + '/' + name).strip('/')] = file_id
2255.2.87 by Robert Collins
core dirstate tests passing with new structure.
265
                inv.add(inv_entry)
2255.2.34 by Robert Collins
Fix WorkingTree4 parent_ids logic to use the dirstate to answer parent ids list queries.
266
        self._inventory = inv
267
2255.2.87 by Robert Collins
core dirstate tests passing with new structure.
268
    def _get_entry(self, file_id=None, path=None):
269
        """Get the dirstate row for file_id or path.
270
271
        If either file_id or path is supplied, it is used as the key to lookup.
272
        If both are supplied, the fastest lookup is used, and an error is
273
        raised if they do not both point at the same row.
274
        
275
        :param file_id: An optional unicode file_id to be looked up.
276
        :param path: An optional unicode path to be looked up.
277
        :return: The dirstate row tuple for path/file_id, or (None, None)
278
        """
279
        if file_id is None and path is None:
280
            raise errors.BzrError('must supply file_id or path')
281
        state = self.current_dirstate()
282
        if file_id is not None:
283
            file_id = file_id.encode('utf8')
284
        if path is not None:
285
            path = path.encode('utf8')
286
        return state._get_entry(0, fileid_utf8=file_id, path_utf8=path)
287
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
288
    def get_file_sha1(self, file_id, path=None, stat_value=None):
2255.2.88 by Robert Collins
Significant steps back to operation.
289
        # check file id is valid unconditionally.
2255.2.95 by Robert Collins
Fix WorkingTree4.get_file_sha1 to actually work.
290
        key, details = self._get_entry(file_id=file_id, path=path)
291
        assert key is not None, 'what error should this raise'
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
292
        # TODO:
293
        # if row stat is valid, use cached sha1, else, get a new sha1.
2255.2.73 by Robert Collins
50% speedup in the dirstate->inventory conversion logic by caching the parent ids as we walk the tree. Some further work would be to maintain a stack of parents as we know we visit depth first.
294
        if path is None:
2255.2.95 by Robert Collins
Fix WorkingTree4.get_file_sha1 to actually work.
295
            path = os.path.join(*key[0:2]).decode('utf8')
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
296
        return self._hashcache.get_sha1(path, stat_value)
297
2255.2.17 by Robert Collins
tweaks - finishes off all the test_test_trees tests for dirstate.
298
    def _get_inventory(self):
299
        """Get the inventory for the tree. This is only valid within a lock."""
300
        if self._inventory is not None:
301
            return self._inventory
302
        self._generate_inventory()
303
        return self._inventory
304
305
    inventory = property(_get_inventory,
306
                         doc="Inventory of this Tree")
307
308
    @needs_read_lock
2255.2.34 by Robert Collins
Fix WorkingTree4 parent_ids logic to use the dirstate to answer parent ids list queries.
309
    def get_parent_ids(self):
310
        """See Tree.get_parent_ids.
311
        
312
        This implementation requests the ids list from the dirstate file.
313
        """
314
        return self.current_dirstate().get_parent_ids()
315
316
    @needs_read_lock
2255.2.17 by Robert Collins
tweaks - finishes off all the test_test_trees tests for dirstate.
317
    def get_root_id(self):
318
        """Return the id of this trees root"""
2255.2.87 by Robert Collins
core dirstate tests passing with new structure.
319
        return self._get_entry(path='')[0][2].decode('utf8')
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
320
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
321
    def has_id(self, file_id):
322
        state = self.current_dirstate()
323
        fileid_utf8 = file_id.encode('utf8')
2255.2.92 by James Westby
Make the WorkingTree4 has_id use the new _get_entry rather than _get_row.
324
        row, parents = self._get_entry(file_id=file_id)
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
325
        if row is None:
326
            return False
327
        return osutils.lexists(pathjoin(
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
328
                    self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
329
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
330
    @needs_read_lock
331
    def id2path(self, fileid):
332
        state = self.current_dirstate()
333
        fileid_utf8 = fileid.encode('utf8')
2255.2.88 by Robert Collins
Significant steps back to operation.
334
        key, tree_details = state._get_entry(0, fileid_utf8=fileid_utf8)
335
        return os.path.join(*key[0:2]).decode('utf8')
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
336
337
    @needs_read_lock
338
    def __iter__(self):
339
        """Iterate through file_ids for this tree.
340
341
        file_ids are in a WorkingTree if they are in the working inventory
342
        and the working file exists.
343
        """
344
        result = []
2255.2.88 by Robert Collins
Significant steps back to operation.
345
        for key, tree_details in self.current_dirstate()._iter_entries():
346
            if tree_details[0][0] in ('absent', 'relocated'):
347
                # not relevant to the working tree
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
348
                continue
2255.2.88 by Robert Collins
Significant steps back to operation.
349
            path = pathjoin(self.basedir, key[0].decode('utf8'), key[1].decode('utf8'))
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
350
            if osutils.lexists(path):
2255.2.88 by Robert Collins
Significant steps back to operation.
351
                result.append(key[2].decode('utf8'))
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
352
        return iter(result)
353
2255.2.21 by Robert Collins
Add WorkingTree4._last_revision, making workingtree_implementations.test_changes_from pass.
354
    @needs_read_lock
355
    def _last_revision(self):
356
        """See Mutable.last_revision."""
357
        parent_ids = self.current_dirstate().get_parent_ids()
358
        if parent_ids:
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
359
            return parent_ids[0]
2255.2.21 by Robert Collins
Add WorkingTree4._last_revision, making workingtree_implementations.test_changes_from pass.
360
        else:
361
            return None
362
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
363
    @needs_tree_write_lock
364
    def move(self, from_paths, to_dir=None, after=False, **kwargs):
365
        """See WorkingTree.move()."""
366
        if not from_paths:
367
            return ()
368
369
        state = self.current_dirstate()
370
371
        # check for deprecated use of signature
372
        if to_dir is None:
373
            to_dir = kwargs.get('to_name', None)
374
            if to_dir is None:
375
                raise TypeError('You must supply a target directory')
376
            else:
377
                symbol_versioning.warn('The parameter to_name was deprecated'
378
                                       ' in version 0.13. Use to_dir instead',
379
                                       DeprecationWarning)
380
381
        assert not isinstance(from_paths, basestring)
382
        to_dir_utf8 = to_dir.encode('utf8')
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
383
        to_entry_dirname, to_basename = os.path.split(to_dir_utf8)
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
384
        # check destination directory
385
        # get the details for it
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
386
        to_entry_block_index, to_entry_entry_index, dir_present, entry_present = \
387
            state._get_block_entry_index(to_entry_dirname, to_basename, 0)
388
        if not entry_present:
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
389
            raise errors.BzrMoveFailedError('', to_dir,
390
                errors.NotInWorkingDirectory(to_dir))
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
391
        to_entry = state._dirblocks[to_entry_block_index][1][to_entry_entry_index]
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
392
        # get a handle on the block itself.
393
        to_block_index = state._ensure_block(
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
394
            to_entry_block_index, to_entry_entry_index, to_dir_utf8)
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
395
        to_block = state._dirblocks[to_block_index]
396
        to_abs = self.abspath(to_dir)
397
        if not isdir(to_abs):
398
            raise errors.BzrMoveFailedError('',to_dir,
399
                errors.NotADirectory(to_abs))
400
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
401
        if to_entry[1][0][0] != 'directory':
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
402
            raise errors.BzrMoveFailedError('',to_dir,
403
                errors.NotADirectory(to_abs))
404
405
        if self._inventory is not None:
406
            update_inventory = True
407
            inv = self.inventory
408
            to_dir_ie = inv[to_dir_id]
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
409
            to_dir_id = to_entry[0][2].decode('utf8')
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
410
        else:
411
            update_inventory = False
412
413
        # create rename entries and tuples
414
        for from_rel in from_paths:
415
            # from_rel is 'pathinroot/foo/bar'
416
            from_dirname, from_tail = os.path.split(from_rel)
417
            from_dirname = from_dirname.encode('utf8')
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
418
            from_entry = self._get_entry(path=from_rel)
419
            if from_entry == (None, None):
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
420
                raise errors.BzrMoveFailedError(from_rel,to_dir,
421
                    errors.NotVersionedError(path=str(from_rel)))
422
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
423
            from_id = from_entry[0][2].decode('utf8')
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
424
            to_rel = pathjoin(to_dir, from_tail)
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
425
            item_to_entry = self._get_entry(path=to_rel)
426
            if item_to_entry != (None, None):
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
427
                raise errors.BzrMoveFailedError(from_rel, to_rel,
428
                    "Target is already versioned.")
429
430
            if from_rel == to_rel:
431
                raise errors.BzrMoveFailedError(from_rel, to_rel,
432
                    "Source and target are identical.")
433
434
            from_missing = not self.has_filename(from_rel)
435
            to_missing = not self.has_filename(to_rel)
436
            if after:
437
                move_file = False
438
            else:
439
                move_file = True
440
            if to_missing:
441
                if not move_file:
442
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
443
                        errors.NoSuchFile(path=to_rel,
444
                        extra="New file has not been created yet"))
445
                elif from_missing:
446
                    # neither path exists
447
                    raise errors.BzrRenameFailedError(from_rel, to_rel,
448
                        errors.PathsDoNotExist(paths=(from_rel, to_rel)))
449
            else:
450
                if from_missing: # implicitly just update our path mapping
451
                    move_file = False
452
                else:
453
                    raise errors.RenameFailedFilesExist(from_rel, to_rel,
454
                        extra="(Use --after to update the Bazaar id)")
455
456
            rollbacks = []
457
            def rollback_rename():
458
                """A single rename has failed, roll it back."""
459
                error = None
460
                for rollback in reversed(rollbacks):
461
                    try:
462
                        rollback()
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
463
                    except Exception, e:
464
                        import pdb;pdb.set_trace()
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
465
                        error = e
466
                if error:
467
                    raise error
468
469
            # perform the disk move first - its the most likely failure point.
470
            from_rel_abs = self.abspath(from_rel)
471
            to_rel_abs = self.abspath(to_rel)
472
            try:
473
                osutils.rename(from_rel_abs, to_rel_abs)
474
            except OSError, e:
475
                raise errors.BzrMoveFailedError(from_rel, to_rel, e[1])
476
            rollbacks.append(lambda: osutils.rename(to_rel_abs, from_rel_abs))
477
            try:
478
                # perform the rename in the inventory next if needed: its easy
479
                # to rollback
480
                if update_inventory:
481
                    # rename the entry
482
                    from_entry = inv[from_id]
483
                    current_parent = from_entry.parent_id
484
                    inv.rename(from_id, to_dir_id, from_tail)
485
                    rollbacks.append(
486
                        lambda: inv.rename(from_id, current_parent, from_tail))
487
                # finally do the rename in the dirstate, which is a little
488
                # tricky to rollback, but least likely to need it.
489
                basename = from_tail.encode('utf8')
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
490
                old_block_index, old_entry_index, dir_present, file_present = \
491
                    state._get_block_entry_index(from_dirname, basename, 0)
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
492
                old_block = state._dirblocks[old_block_index][1]
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
493
                old_entry_details = old_block[old_entry_index][1]
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
494
                # remove the old row
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
495
                from_key = old_block[old_entry_index][0]
496
                to_key = ((to_block[0],) + from_key[1:3])
497
                state._make_absent(old_block[old_entry_index])
498
                rollbacks.append(
499
                    lambda:state.update_minimal(from_key,
500
                        old_entry_details[0][0],
501
                        num_present_parents=len(old_entry_details) - 1,
502
                        executable=old_entry_details[0][3],
503
                        fingerprint=old_entry_details[0][1],
504
                        packed_stat=old_entry_details[0][4],
505
                        size=old_entry_details[0][2],
506
                        id_index=state._get_id_index(),
507
                        path_utf8=from_rel.encode('utf8')))
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
508
                # create new row in current block
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
509
                state.update_minimal(to_key,
510
                        old_entry_details[0][0],
511
                        num_present_parents=len(old_entry_details) - 1,
512
                        executable=old_entry_details[0][3],
513
                        fingerprint=old_entry_details[0][1],
514
                        packed_stat=old_entry_details[0][4],
515
                        size=old_entry_details[0][2],
516
                        id_index=state._get_id_index(),
517
                        path_utf8=to_rel.encode('utf8'))
518
                added_entry_index, _ = state._find_entry_index(to_key, to_block[1])
519
                new_entry = to_block[added_entry_index]
520
                rollbacks.append(lambda:state._make_absent(new_entry))
521
                if new_entry[1][0][0] == 'directory':
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
522
                    import pdb;pdb.set_trace()
523
                    # if a directory, rename all the contents of child blocks
524
                    # adding rollbacks as each is inserted to remove them and
525
                    # restore the original
526
                    # TODO: large scale slice assignment.
527
                    # setup new list
528
                    # save old list region
529
                    # move up or down the old region
530
                    # add rollback to move the region back
531
                    # assign new list to new region
532
                    # done
533
            except:
534
                rollback_rename()
535
                raise
536
            state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
537
            self._dirty = True
538
539
        return #rename_tuples
540
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
541
    def _new_tree(self):
542
        """Initialize the state in this tree to be a new tree."""
543
        self._dirty = True
544
545
    @needs_read_lock
2255.2.17 by Robert Collins
tweaks - finishes off all the test_test_trees tests for dirstate.
546
    def path2id(self, path):
547
        """Return the id for path in this tree."""
2255.2.88 by Robert Collins
Significant steps back to operation.
548
        entry = self._get_entry(path=path)
549
        if entry == (None, None):
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
550
            return None
2255.2.88 by Robert Collins
Significant steps back to operation.
551
        return entry[0][2].decode('utf8')
2255.2.17 by Robert Collins
tweaks - finishes off all the test_test_trees tests for dirstate.
552
2255.2.45 by Robert Collins
Dirstate - fix revision_tree() behaviour to match the interface contract.
553
    def read_working_inventory(self):
554
        """Read the working inventory.
555
        
556
        This is a meaningless operation for dirstate, but we obey it anyhow.
557
        """
558
        return self.inventory
559
2255.2.17 by Robert Collins
tweaks - finishes off all the test_test_trees tests for dirstate.
560
    @needs_read_lock
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
561
    def revision_tree(self, revision_id):
562
        """See Tree.revision_tree.
563
564
        WorkingTree4 supplies revision_trees for any basis tree.
565
        """
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
566
        revision_id = osutils.safe_revision_id(revision_id)
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
567
        dirstate = self.current_dirstate()
568
        parent_ids = dirstate.get_parent_ids()
569
        if revision_id not in parent_ids:
570
            raise errors.NoSuchRevisionInTree(self, revision_id)
2255.2.45 by Robert Collins
Dirstate - fix revision_tree() behaviour to match the interface contract.
571
        if revision_id in dirstate.get_ghosts():
572
            raise errors.NoSuchRevisionInTree(self, revision_id)
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
573
        return DirStateRevisionTree(dirstate, revision_id,
574
            self.branch.repository)
575
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
576
    @needs_tree_write_lock
2255.2.37 by Robert Collins
Get TestExecutable.test_06_pull working on DirState: fix cloning and the set_last_revision api on WorkingTree4.
577
    def set_last_revision(self, new_revision):
578
        """Change the last revision in the working tree."""
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
579
        new_revision = osutils.safe_revision_id(new_revision)
2255.2.37 by Robert Collins
Get TestExecutable.test_06_pull working on DirState: fix cloning and the set_last_revision api on WorkingTree4.
580
        parents = self.get_parent_ids()
581
        if new_revision in (NULL_REVISION, None):
2255.2.56 by Robert Collins
Dirstate: bring set_last_revision into line with the tested API.
582
            assert len(parents) < 2, (
2255.2.37 by Robert Collins
Get TestExecutable.test_06_pull working on DirState: fix cloning and the set_last_revision api on WorkingTree4.
583
                "setting the last parent to none with a pending merge is "
584
                "unsupported.")
585
            self.set_parent_ids([])
586
        else:
2255.2.56 by Robert Collins
Dirstate: bring set_last_revision into line with the tested API.
587
            self.set_parent_ids([new_revision] + parents[1:],
588
                allow_leftmost_as_ghost=True)
2255.2.37 by Robert Collins
Get TestExecutable.test_06_pull working on DirState: fix cloning and the set_last_revision api on WorkingTree4.
589
590
    @needs_tree_write_lock
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
591
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
592
        """Set the parent ids to revision_ids.
593
        
594
        See also set_parent_trees. This api will try to retrieve the tree data
595
        for each element of revision_ids from the trees repository. If you have
596
        tree data already available, it is more efficient to use
597
        set_parent_trees rather than set_parent_ids. set_parent_ids is however
598
        an easier API to use.
599
600
        :param revision_ids: The revision_ids to set as the parent ids of this
601
            working tree. Any of these may be ghosts.
602
        """
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
603
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
604
        trees = []
605
        for revision_id in revision_ids:
606
            try:
607
                revtree = self.branch.repository.revision_tree(revision_id)
2255.2.24 by John Arbash Meinel
When adding ghosts revision_tree() raises RevisionNotPresent because of Knit, not NoSuchRevision
608
                # TODO: jam 20070213 KnitVersionedFile raises
609
                #       RevisionNotPresent rather than NoSuchRevision if a
610
                #       given revision_id is not present. Should Repository be
611
                #       catching it and re-raising NoSuchRevision?
612
            except (errors.NoSuchRevision, errors.RevisionNotPresent):
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
613
                revtree = None
614
            trees.append((revision_id, revtree))
615
        self.set_parent_trees(trees,
616
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
617
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
618
    @needs_tree_write_lock
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
619
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
620
        """Set the parents of the working tree.
621
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
622
        :param parents_list: A list of (revision_id, tree) tuples.
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
623
            If tree is None, then that element is treated as an unreachable
624
            parent tree - i.e. a ghost.
625
        """
626
        dirstate = self.current_dirstate()
627
        if len(parents_list) > 0:
628
            if not allow_leftmost_as_ghost and parents_list[0][1] is None:
2255.2.42 by Robert Collins
Fix WorkingTree4.set_parent_trees.
629
                raise errors.GhostRevisionUnusableHere(parents_list[0][0])
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
630
        real_trees = []
631
        ghosts = []
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
632
        # convert absent trees to the null tree, which we convert back to
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
633
        # missing on access.
634
        for rev_id, tree in parents_list:
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
635
            rev_id = osutils.safe_revision_id(rev_id)
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
636
            if tree is not None:
637
                real_trees.append((rev_id, tree))
638
            else:
639
                real_trees.append((rev_id,
640
                    self.branch.repository.revision_tree(None)))
641
                ghosts.append(rev_id)
642
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
643
        self._dirty = True
644
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
645
    def _set_root_id(self, file_id):
646
        """See WorkingTree.set_root_id."""
2255.2.37 by Robert Collins
Get TestExecutable.test_06_pull working on DirState: fix cloning and the set_last_revision api on WorkingTree4.
647
        state = self.current_dirstate()
648
        state.set_path_id('', file_id)
649
        self._dirty = state._dirblock_state == dirstate.DirState.IN_MEMORY_MODIFIED
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
650
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
651
    def unlock(self):
652
        """Unlock in format 4 trees needs to write the entire dirstate."""
653
        if self._control_files._lock_count == 1:
2255.2.44 by Robert Collins
Fix tree unlock on readonly Format4 trees with dirty hashcache.
654
            self._write_hashcache_if_dirty()
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
655
            # eventually we should do signature checking during read locks for
656
            # dirstate updates.
657
            if self._control_files._lock_mode == 'w':
658
                if self._dirty:
659
                    self.flush()
660
            self._dirstate = None
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
661
            self._inventory = None
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
662
        # reverse order of locking.
663
        try:
664
            return self._control_files.unlock()
665
        finally:
666
            self.branch.unlock()
667
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
668
    @needs_tree_write_lock
669
    def unversion(self, file_ids):
670
        """Remove the file ids in file_ids from the current versioned set.
671
672
        When a file_id is unversioned, all of its children are automatically
673
        unversioned.
674
675
        :param file_ids: The file ids to stop versioning.
676
        :raises: NoSuchId if any fileid is not currently versioned.
677
        """
678
        if not file_ids:
679
            return
680
        state = self.current_dirstate()
681
        state._read_dirblocks_if_needed()
682
        ids_to_unversion = set()
683
        for fileid in file_ids:
684
            ids_to_unversion.add(fileid.encode('utf8'))
685
        paths_to_unversion = set()
686
        # sketch:
687
        # check if the root is to be unversioned, if so, assert for now.
2255.2.93 by Robert Collins
Dirstate - update WorkingTree4.unversion to the new layout, other tests still borked.
688
        # walk the state marking unversioned things as absent.
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
689
        # if there are any un-unversioned ids at the end, raise
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
690
        for key, details in state._dirblocks[0][1]:
2255.2.93 by Robert Collins
Dirstate - update WorkingTree4.unversion to the new layout, other tests still borked.
691
            if (details[0][0] not in ('absent', 'relocated') and
692
                key[2] in ids_to_unversion):
693
                # I haven't written the code to unversion / yet - it should be
694
                # supported.
695
                raise errors.BzrError('Unversioning the / is not currently supported')
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
696
        details_length = len(state._dirblocks[0][1][0][1])
2255.2.93 by Robert Collins
Dirstate - update WorkingTree4.unversion to the new layout, other tests still borked.
697
        block_index = 0
698
        while block_index < len(state._dirblocks):
699
            # process one directory at a time.
700
            block = state._dirblocks[block_index]
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
701
            # first check: is the path one to remove - it or its children
702
            delete_block = False
703
            for path in paths_to_unversion:
704
                if (block[0].startswith(path) and
705
                    (len(block[0]) == len(path) or
706
                     block[0][len(path)] == '/')):
2255.2.93 by Robert Collins
Dirstate - update WorkingTree4.unversion to the new layout, other tests still borked.
707
                    # this entire block should be deleted - its the block for a
708
                    # path to unversion; or the child of one
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
709
                    delete_block = True
710
                    break
711
            # TODO: trim paths_to_unversion as we pass by paths
712
            if delete_block:
2255.2.93 by Robert Collins
Dirstate - update WorkingTree4.unversion to the new layout, other tests still borked.
713
                # this block is to be deleted: process it.
714
                # TODO: we can special case the no-parents case and
715
                # just forget the whole block.
716
                entry_index = 0
717
                while entry_index < len(block[1]):
718
                    if not state._make_absent(block[1][entry_index]):
719
                        entry_index += 1
720
                # go to the next block. (At the moment we dont delete empty
721
                # dirblocks)
722
                block_index += 1
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
723
                continue
2255.2.93 by Robert Collins
Dirstate - update WorkingTree4.unversion to the new layout, other tests still borked.
724
            entry_index = 0
725
            while entry_index < len(block[1]):
726
                entry = block[1][entry_index]
727
                if (entry[1][0][0] in ('absent', 'relocated') or
728
                    # ^ some parent row.
729
                    entry[0][2] not in ids_to_unversion):
730
                    # ^ not an id to unversion
731
                    entry_index += 1
732
                    continue
733
                if entry[1][0][0] == 'directory':
734
                    paths_to_unversion.add(os.path.join(*entry[0][0:2]))
735
                if not state._make_absent(entry):
736
                    entry_index += 1
737
                # we have unversioned this id
738
                ids_to_unversion.remove(entry[0][2])
739
            block_index += 1
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
740
        if ids_to_unversion:
741
            raise errors.NoSuchId(self, iter(ids_to_unversion).next())
2255.2.46 by Robert Collins
Dirstate - unversion should set the tree state as dirty.
742
        self._dirty = True
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
743
        # have to change the legacy inventory too.
744
        if self._inventory is not None:
745
            for file_id in file_ids:
2255.2.33 by Robert Collins
Correct thunko in refactoring a few commits back.
746
                self._inventory.remove_recursive_id(file_id)
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
747
2255.2.16 by Robert Collins
Implement WorkingTreeFormat4._write_inventory for better compatability with existing code, letting more test_test_trees pass, now up to test_tree_with_subdirs_and_all_content_types.
748
    @needs_tree_write_lock
749
    def _write_inventory(self, inv):
750
        """Write inventory as the current inventory."""
751
        assert not self._dirty, "attempting to write an inventory when the dirstate is dirty will cause data loss"
752
        self.current_dirstate().set_state_from_inventory(inv)
753
        self._dirty = True
754
        self.flush()
755
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
756
757
class WorkingTreeFormat4(WorkingTreeFormat3):
758
    """The first consolidated dirstate working tree format.
759
760
    This format:
761
        - exists within a metadir controlling .bzr
762
        - includes an explicit version marker for the workingtree control
763
          files, separate from the BzrDir format
764
        - modifies the hash cache format
765
        - is new in bzr TODO FIXME SETBEFOREMERGE
766
        - uses a LockDir to guard access to it.
767
    """
768
769
    def get_format_string(self):
770
        """See WorkingTreeFormat.get_format_string()."""
771
        return "Bazaar Working Tree format 4\n"
772
773
    def get_format_description(self):
774
        """See WorkingTreeFormat.get_format_description()."""
775
        return "Working tree format 4"
776
777
    def initialize(self, a_bzrdir, revision_id=None):
778
        """See WorkingTreeFormat.initialize().
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
779
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
780
        revision_id allows creating a working tree at a different
781
        revision than the branch is at.
782
        """
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
783
        revision_id = osutils.safe_revision_id(revision_id)
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
784
        if not isinstance(a_bzrdir.transport, LocalTransport):
785
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
786
        transport = a_bzrdir.get_workingtree_transport(self)
787
        control_files = self._open_control_files(a_bzrdir)
788
        control_files.create_lock()
789
        control_files.lock_write()
790
        control_files.put_utf8('format', self.get_format_string())
791
        branch = a_bzrdir.open_branch()
792
        if revision_id is None:
793
            revision_id = branch.last_revision()
794
        local_path = transport.local_abspath('dirstate')
795
        dirstate.DirState.initialize(local_path)
796
        wt = WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
797
                         branch,
798
                         _format=self,
799
                         _bzrdir=a_bzrdir,
800
                         _control_files=control_files)
801
        wt._new_tree()
802
        wt.lock_write()
803
        try:
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
804
            #wt.current_dirstate().set_path_id('', NEWROOT)
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
805
            wt.set_last_revision(revision_id)
2255.2.16 by Robert Collins
Implement WorkingTreeFormat4._write_inventory for better compatability with existing code, letting more test_test_trees pass, now up to test_tree_with_subdirs_and_all_content_types.
806
            wt.flush()
2255.2.37 by Robert Collins
Get TestExecutable.test_06_pull working on DirState: fix cloning and the set_last_revision api on WorkingTree4.
807
            basis = wt.basis_tree()
808
            basis.lock_read()
809
            transform.build_tree(basis, wt)
810
            basis.unlock()
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
811
        finally:
812
            control_files.unlock()
813
            wt.unlock()
814
        return wt
815
816
817
    def _open(self, a_bzrdir, control_files):
818
        """Open the tree itself.
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
819
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
820
        :param a_bzrdir: the dir for the tree.
821
        :param control_files: the control files for the tree.
822
        """
823
        return WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
824
                           branch=a_bzrdir.open_branch(),
825
                           _format=self,
826
                           _bzrdir=a_bzrdir,
827
                           _control_files=control_files)
828
829
830
class DirStateRevisionTree(Tree):
831
    """A revision tree pulling the inventory from a dirstate."""
832
833
    def __init__(self, dirstate, revision_id, repository):
834
        self._dirstate = dirstate
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
835
        self._revision_id = osutils.safe_revision_id(revision_id)
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
836
        self._repository = repository
837
        self._inventory = None
2255.2.38 by Robert Collins
Fix WorkingTree4.pull to work.
838
        self._locked = 0
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
839
2255.2.69 by John Arbash Meinel
Implement annotate_iter, get_revision_id, and walkdirs so that all tree_implementations now pass
840
    def annotate_iter(self, file_id):
841
        """See Tree.annotate_iter"""
842
        w = self._repository.weave_store.get_weave(file_id,
843
                           self._repository.get_transaction())
844
        return w.annotate_iter(self.inventory[file_id].revision)
845
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
846
    def _comparison_data(self, entry, path):
847
        """See Tree._comparison_data."""
848
        if entry is None:
849
            return None, False, None
850
        # trust the entry as RevisionTree does, but this may not be
851
        # sensible: the entry might not have come from us?
852
        return entry.kind, entry.executable, None
853
2255.2.10 by Robert Collins
Now all tests matching dirstate pass - added generation of inventories for parent trees.
854
    def _file_size(self, entry, stat_value):
855
        return entry.text_size
856
2255.2.78 by Robert Collins
Really finish the prior commit.
857
    def filter_unversioned_files(self, paths):
858
        """Filter out paths that are not versioned.
859
860
        :return: set of paths.
861
        """
862
        pred = self.has_filename
863
        return set((p for p in paths if not pred(p)))
864
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
865
    def _generate_inventory(self):
866
        """Create and set self.inventory from the dirstate object.
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
867
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
868
        This is relatively expensive: we have to walk the entire dirstate.
869
        Ideally we would not, and instead would """
870
        assert self._locked, 'cannot generate inventory of an unlocked '\
871
            'dirstate revision tree'
2255.2.87 by Robert Collins
core dirstate tests passing with new structure.
872
        # separate call for profiling - makes it clear where the costs are.
873
        self._dirstate._read_dirblocks_if_needed()
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
874
        assert self._revision_id in self._dirstate.get_parent_ids(), \
875
            'parent %s has disappeared from %s' % (
876
            self._revision_id, self._dirstate.get_parent_ids())
2255.2.87 by Robert Collins
core dirstate tests passing with new structure.
877
        parent_index = self._dirstate.get_parent_ids().index(self._revision_id) + 1
878
        # This is identical now to the WorkingTree _generate_inventory except
879
        # for the tree index use.
880
        root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
881
        current_id = root_key[2].decode('utf8')
882
        assert current_entry[parent_index][0] == 'directory'
883
        inv = Inventory(root_id=current_id, revision_id=self._revision_id)
884
        inv.root.revision = current_entry[parent_index][4]
2255.2.73 by Robert Collins
50% speedup in the dirstate->inventory conversion logic by caching the parent ids as we walk the tree. Some further work would be to maintain a stack of parents as we know we visit depth first.
885
        # we could do this straight out of the dirstate; it might be fast
886
        # and should be profiled - RBC 20070216
887
        parent_ids = {'' : inv.root.file_id}
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
888
        for block in self._dirstate._dirblocks[1:]: #skip root
2255.2.87 by Robert Collins
core dirstate tests passing with new structure.
889
            dirname = block[0]
2255.2.96 by Robert Collins
Restore dirstate to all tests passing condition.
890
            try:
891
                parent_id = parent_ids[block[0]]
892
            except KeyError:
893
                # all the paths in this block are not versioned in this tree
894
                continue
2255.2.87 by Robert Collins
core dirstate tests passing with new structure.
895
            for key, entry in block[1]:
896
                if entry[parent_index][0] in ('absent', 'relocated'):
897
                    # not this tree
898
                    continue
899
                name = key[1].decode('utf8')
900
                file_id = key[2].decode('utf8')
901
                kind, link_or_sha1, size, executable, revid = entry[parent_index]
902
                inv_entry = entry_factory[kind](file_id, name, parent_id)
903
                inv_entry.revision = revid
904
                if kind == 'file':
905
                    inv_entry.executable = executable
906
                    inv_entry.text_size = size
907
                    inv_entry.text_sha1 = link_or_sha1
908
                elif kind == 'directory':
909
                    parent_ids[(dirname + '/' + name).strip('/')] = file_id
2255.2.93 by Robert Collins
Dirstate - update WorkingTree4.unversion to the new layout, other tests still borked.
910
                elif kind == 'symlink':
911
                    inv_entry.executable = False
912
                    inv_entry.text_size = size
913
                    inv_entry.symlink_target = link_or_sha1.decode('utf8')
2255.2.87 by Robert Collins
core dirstate tests passing with new structure.
914
                else:
915
                    raise Exception, kind
916
                inv.add(inv_entry)
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
917
        self._inventory = inv
918
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
919
    def get_file_sha1(self, file_id, path=None, stat_value=None):
920
        # TODO: if path is present, fast-path on that, as inventory
921
        # might not be present
922
        ie = self.inventory[file_id]
923
        if ie.kind == "file":
924
            return ie.text_sha1
925
        return None
926
927
    def get_file(self, file_id):
928
        return StringIO(self.get_file_text(file_id))
929
930
    def get_file_lines(self, file_id):
931
        ie = self.inventory[file_id]
932
        return self._repository.weave_store.get_weave(file_id,
933
                self._repository.get_transaction()).get_lines(ie.revision)
934
935
    def get_file_size(self, file_id):
936
        return self.inventory[file_id].text_size
937
938
    def get_file_text(self, file_id):
939
        return ''.join(self.get_file_lines(file_id))
940
2255.2.69 by John Arbash Meinel
Implement annotate_iter, get_revision_id, and walkdirs so that all tree_implementations now pass
941
    def get_revision_id(self):
942
        """Return the revision id for this tree."""
943
        return self._revision_id
944
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
945
    def _get_inventory(self):
946
        if self._inventory is not None:
947
            return self._inventory
948
        self._generate_inventory()
949
        return self._inventory
950
951
    inventory = property(_get_inventory,
952
                         doc="Inventory of this Tree")
953
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
954
    def get_parent_ids(self):
955
        """The parents of a tree in the dirstate are not cached."""
956
        return self._repository.get_revision(self._revision_id).parent_ids
957
2255.2.30 by Robert Collins
Some workingtree_implementations/test_workingtree.py test work - add DirStateRevisionTree.has_filename, locks around appropriate calls in tests.
958
    def has_filename(self, filename):
959
        return bool(self.inventory.path2id(filename))
960
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
961
    def kind(self, file_id):
962
        return self.inventory[file_id].kind
963
964
    def is_executable(self, file_id, path=None):
965
        ie = self.inventory[file_id]
966
        if ie.kind != "file":
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
967
            return None
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
968
        return ie.executable
969
2255.2.71 by John Arbash Meinel
Add a test for list_files, and implement it for DirStateRevisionTree
970
    def list_files(self, include_root=False):
971
        # We use a standard implementation, because DirStateRevisionTree is
972
        # dealing with one of the parents of the current state
973
        inv = self._get_inventory()
974
        entries = inv.iter_entries()
975
        if self.inventory.root is not None and not include_root:
976
            entries.next()
977
        for path, entry in entries:
978
            yield path, 'V', entry.kind, entry.file_id, entry
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
979
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
980
    def lock_read(self):
981
        """Lock the tree for a set of operations."""
2255.2.79 by Robert Collins
Take out repository locks from Dirstate revision trees, to improve file text access performance.
982
        if not self._locked:
983
            self._repository.lock_read()
2255.2.38 by Robert Collins
Fix WorkingTree4.pull to work.
984
        self._locked += 1
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
985
2255.2.65 by John Arbash Meinel
override path2id because it should be optimized anyway
986
    def path2id(self, path):
987
        """Return the id for path in this tree."""
2255.2.78 by Robert Collins
Really finish the prior commit.
988
        # TODO: if there is no inventory, do an optimistic lookup in the
989
        # dirstate by the path; commonly this will work.
990
        return self.inventory.path2id(path)
2255.2.65 by John Arbash Meinel
override path2id because it should be optimized anyway
991
2255.2.3 by Robert Collins
Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.
992
    def unlock(self):
993
        """Unlock, freeing any cache memory used during the lock."""
994
        # outside of a lock, the inventory is suspect: release it.
2255.2.38 by Robert Collins
Fix WorkingTree4.pull to work.
995
        self._locked -=1
996
        if not self._locked:
997
            self._inventory = None
998
            self._locked = False
2255.2.79 by Robert Collins
Take out repository locks from Dirstate revision trees, to improve file text access performance.
999
            self._repository.unlock()
2255.2.69 by John Arbash Meinel
Implement annotate_iter, get_revision_id, and walkdirs so that all tree_implementations now pass
1000
1001
    def walkdirs(self, prefix=""):
1002
        # TODO: jam 20070215 This is the cheap way by cheating and using the
1003
        #       RevisionTree implementation.
1004
        #       This should be cleaned up to use the much faster Dirstate code
2255.2.70 by John Arbash Meinel
Minor improvements to DirStateRevisionTree.walkdirs()
1005
        #       This is a little tricky, though, because the dirstate is
1006
        #       indexed by current path, not by parent path.
1007
        #       So for now, we just build up the parent inventory, and extract
1008
        #       it the same way RevisionTree does.
2255.2.69 by John Arbash Meinel
Implement annotate_iter, get_revision_id, and walkdirs so that all tree_implementations now pass
1009
        _directory = 'directory'
2255.2.70 by John Arbash Meinel
Minor improvements to DirStateRevisionTree.walkdirs()
1010
        inv = self._get_inventory()
2255.2.69 by John Arbash Meinel
Implement annotate_iter, get_revision_id, and walkdirs so that all tree_implementations now pass
1011
        top_id = inv.path2id(prefix)
1012
        if top_id is None:
1013
            pending = []
1014
        else:
2255.2.70 by John Arbash Meinel
Minor improvements to DirStateRevisionTree.walkdirs()
1015
            pending = [(prefix, top_id)]
2255.2.69 by John Arbash Meinel
Implement annotate_iter, get_revision_id, and walkdirs so that all tree_implementations now pass
1016
        while pending:
1017
            dirblock = []
2255.2.70 by John Arbash Meinel
Minor improvements to DirStateRevisionTree.walkdirs()
1018
            relpath, file_id = pending.pop()
1019
            # 0 - relpath, 1- file-id
1020
            if relpath:
1021
                relroot = relpath + '/'
2255.2.69 by John Arbash Meinel
Implement annotate_iter, get_revision_id, and walkdirs so that all tree_implementations now pass
1022
            else:
1023
                relroot = ""
1024
            # FIXME: stash the node in pending
2255.2.70 by John Arbash Meinel
Minor improvements to DirStateRevisionTree.walkdirs()
1025
            entry = inv[file_id]
2255.2.69 by John Arbash Meinel
Implement annotate_iter, get_revision_id, and walkdirs so that all tree_implementations now pass
1026
            for name, child in entry.sorted_children():
1027
                toppath = relroot + name
1028
                dirblock.append((toppath, name, child.kind, None,
1029
                    child.file_id, child.kind
1030
                    ))
2255.2.70 by John Arbash Meinel
Minor improvements to DirStateRevisionTree.walkdirs()
1031
            yield (relpath, entry.file_id), dirblock
2255.2.69 by John Arbash Meinel
Implement annotate_iter, get_revision_id, and walkdirs so that all tree_implementations now pass
1032
            # push the user specified dirs from dirblock
1033
            for dir in reversed(dirblock):
1034
                if dir[2] == _directory:
2255.2.70 by John Arbash Meinel
Minor improvements to DirStateRevisionTree.walkdirs()
1035
                    pending.append((dir[0], dir[4]))