/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
25
import os
26
27
from bzrlib.lazy_import import lazy_import
28
lazy_import(globals(), """
29
from bisect import bisect_left
30
import collections
31
from copy import deepcopy
32
import errno
33
import itertools
34
import operator
35
import stat
36
from time import time
37
import warnings
38
39
import bzrlib
40
from bzrlib import (
41
    bzrdir,
42
    conflicts as _mod_conflicts,
43
    dirstate,
44
    errors,
45
    generate_ids,
46
    globbing,
47
    hashcache,
48
    ignores,
49
    merge,
50
    osutils,
51
    textui,
52
    transform,
53
    urlutils,
54
    xml5,
55
    xml6,
56
    )
57
import bzrlib.branch
58
from bzrlib.transport import get_transport
59
import bzrlib.ui
60
""")
61
62
from bzrlib import symbol_versioning
63
from bzrlib.decorators import needs_read_lock, needs_write_lock
2255.2.10 by Robert Collins
Now all tests matching dirstate pass - added generation of inventories for parent trees.
64
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, make_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.
65
from bzrlib.lockable_files import LockableFiles, TransportLock
66
from bzrlib.lockdir import LockDir
67
import bzrlib.mutabletree
68
from bzrlib.mutabletree import needs_tree_write_lock
69
from bzrlib.osutils import (
70
    compact_date,
71
    file_kind,
72
    isdir,
73
    normpath,
74
    pathjoin,
75
    rand_chars,
76
    realpath,
77
    safe_unicode,
78
    splitpath,
79
    supports_executable,
80
    )
81
from bzrlib.trace import mutter, note
82
from bzrlib.transport.local import LocalTransport
83
from bzrlib.progress import DummyProgress, ProgressPhase
84
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
85
from bzrlib.rio import RioReader, rio_file, Stanza
86
from bzrlib.symbol_versioning import (deprecated_passed,
87
        deprecated_method,
88
        deprecated_function,
89
        DEPRECATED_PARAMETER,
90
        zero_eight,
91
        zero_eleven,
92
        zero_thirteen,
93
        )
94
from bzrlib.tree import Tree
95
from bzrlib.workingtree import WorkingTree3, WorkingTreeFormat3
96
97
98
class WorkingTree4(WorkingTree3):
99
    """This is the Format 4 working tree.
100
101
    This differs from WorkingTree3 by:
102
     - 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.
103
     - 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.
104
105
    This is new in bzr TODO FIXME SETMEBEFORE MERGE.
106
    """
107
108
    def __init__(self, basedir,
109
                 branch,
110
                 _control_files=None,
111
                 _format=None,
112
                 _bzrdir=None):
113
        """Construct a WorkingTree for basedir.
114
115
        If the branch is not supplied, it is opened automatically.
116
        If the branch is supplied, it must be the branch for this basedir.
117
        (branch.base is not cross checked, because for remote branches that
118
        would be meaningless).
119
        """
120
        self._format = _format
121
        self.bzrdir = _bzrdir
122
        from bzrlib.hashcache import HashCache
123
        from bzrlib.trace import note, mutter
124
        assert isinstance(basedir, basestring), \
125
            "base directory %r is not a string" % basedir
126
        basedir = safe_unicode(basedir)
127
        mutter("opening working tree %r", basedir)
128
        self._branch = branch
129
        assert isinstance(self.branch, bzrlib.branch.Branch), \
130
            "branch %r is not a Branch" % self.branch
131
        self.basedir = realpath(basedir)
132
        # if branch is at our basedir and is a format 6 or less
133
        # assume all other formats have their own control files.
134
        assert isinstance(_control_files, LockableFiles), \
135
            "_control_files must be a LockableFiles, not %r" % _control_files
136
        self._control_files = _control_files
137
        # update the whole cache up front and write to disk if anything changed;
138
        # in the future we might want to do this more selectively
139
        # two possible ways offer themselves : in self._unlock, write the cache
140
        # if needed, or, when the cache sees a change, append it to the hash
141
        # cache file, and have the parser take the most recent entry for a
142
        # given path only.
143
        cache_filename = self.bzrdir.get_workingtree_transport(None).local_abspath('stat-cache')
144
        hc = self._hashcache = HashCache(basedir, cache_filename, self._control_files._file_mode)
145
        hc.read()
146
        # is this scan needed ? it makes things kinda slow.
147
        #hc.scan()
148
149
        if hc.needs_write:
150
            mutter("write hc")
151
            hc.write()
152
153
        self._dirty = None
154
        self._parent_revisions = 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.12 by Robert Collins
Partial implementation of WorkingTree4._add.
172
            stat = os.lstat(self.abspath(f))
173
            sha1 = '1' * 20 # FIXME: DIRSTATE MERGE BLOCKER
174
            state.add(f, file_id, kind, stat, sha1)
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.
175
        self._dirty = True
2255.2.12 by Robert Collins
Partial implementation of WorkingTree4._add.
176
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.
177
    def current_dirstate(self):
178
        """Return the current dirstate object. 
179
180
        This is not part of the tree interface and only exposed for ease of
181
        testing.
182
183
        :raises errors.NotWriteLocked: when not in a lock. 
184
            XXX: This should probably be errors.NotLocked.
185
        """
186
        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.
187
            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.
188
        if self._dirstate is not None:
189
            return self._dirstate
190
        local_path = self.bzrdir.get_workingtree_transport(None
191
            ).local_abspath('dirstate')
192
        self._dirstate = dirstate.DirState.on_file(local_path)
193
        return self._dirstate
194
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
195
    def flush(self):
196
        """Write all cached data to disk."""
197
        self.current_dirstate().save()
198
        self._inventory = None
199
        self._dirty = False
200
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
201
    def _generate_inventory(self):
202
        """Create and set self.inventory from the dirstate object.
203
        
204
        This is relatively expensive: we have to walk the entire dirstate.
205
        Ideally we would not, and can deprecate this function.
206
        """
207
        dirstate = self.current_dirstate()
208
        rows = self._dirstate._iter_rows()
209
        root_row = rows.next()
210
        inv = Inventory(root_id=root_row[0][3].decode('utf8'))
211
        for line in rows:
212
            dirname, name, kind, fileid_utf8, size, stat, link_or_sha1 = line[0]
213
            if dirname == '/':
214
                # not in this revision tree.
215
                continue
216
            parent_id = inv[inv.path2id(dirname.decode('utf8'))].file_id
217
            file_id = fileid_utf8.decode('utf8')
218
            entry = make_entry(kind, name.decode('utf8'), parent_id, file_id)
219
            if kind == 'file':
220
                #entry.executable = executable
221
                #entry.text_size = size
222
                #entry.text_sha1 = sha1
223
                pass
224
            inv.add(entry)
225
        self._inventory = inv
226
2255.2.17 by Robert Collins
tweaks - finishes off all the test_test_trees tests for dirstate.
227
    def _get_inventory(self):
228
        """Get the inventory for the tree. This is only valid within a lock."""
229
        if self._inventory is not None:
230
            return self._inventory
231
        self._generate_inventory()
232
        return self._inventory
233
234
    inventory = property(_get_inventory,
235
                         doc="Inventory of this Tree")
236
237
    @needs_read_lock
238
    def get_root_id(self):
239
        """Return the id of this trees root"""
240
        return self.current_dirstate()._iter_rows().next()[0][3].decode('utf8')
241
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
242
    def has_id(self, file_id):
243
        state = self.current_dirstate()
244
        fileid_utf8 = file_id.encode('utf8')
245
        for row, parents in state._iter_rows():
246
            if row[3] == fileid_utf8:
247
                return osutils.lexists(pathjoin(
248
                    self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
249
        return False
250
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
251
    @needs_read_lock
252
    def id2path(self, fileid):
253
        state = self.current_dirstate()
254
        fileid_utf8 = fileid.encode('utf8')
255
        for row, parents in state._iter_rows():
256
            if row[3] == fileid_utf8:
257
                return (row[0] + '/' + row[1]).decode('utf8').strip('/')
258
259
    @needs_read_lock
260
    def __iter__(self):
261
        """Iterate through file_ids for this tree.
262
263
        file_ids are in a WorkingTree if they are in the working inventory
264
        and the working file exists.
265
        """
266
        result = []
267
        for row, parents in self.current_dirstate()._iter_rows():
268
            if row[0] == '/':
269
                continue
270
            path = pathjoin(self.basedir, row[0].decode('utf8'), row[1].decode('utf8'))
271
            if osutils.lexists(path):
272
                result.append(row[3].decode('utf8'))
273
        return iter(result)
274
2255.2.21 by Robert Collins
Add WorkingTree4._last_revision, making workingtree_implementations.test_changes_from pass.
275
    @needs_read_lock
276
    def _last_revision(self):
277
        """See Mutable.last_revision."""
278
        parent_ids = self.current_dirstate().get_parent_ids()
279
        if parent_ids:
280
            return parent_ids[0].decode('utf8')
281
        else:
282
            return None
283
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.
284
    def _new_tree(self):
285
        """Initialize the state in this tree to be a new tree."""
286
        self._parent_revisions = [NULL_REVISION]
287
        self._dirty = True
288
289
    @needs_read_lock
2255.2.17 by Robert Collins
tweaks - finishes off all the test_test_trees tests for dirstate.
290
    def path2id(self, path):
291
        """Return the id for path in this tree."""
292
        state = self.current_dirstate()
293
        path_utf8 = os.path.split(path.encode('utf8'))
294
        for row, parents in state._iter_rows():
295
            if row[0:2] == path_utf8:
296
                return row[3].decode('utf8')
297
        return None
298
299
    @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.
300
    def revision_tree(self, revision_id):
301
        """See Tree.revision_tree.
302
303
        WorkingTree4 supplies revision_trees for any basis tree.
304
        """
305
        dirstate = self.current_dirstate()
306
        parent_ids = dirstate.get_parent_ids()
307
        if revision_id not in parent_ids:
308
            raise errors.NoSuchRevisionInTree(self, revision_id)
309
        return DirStateRevisionTree(dirstate, revision_id,
310
            self.branch.repository)
311
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
312
    @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.
313
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
314
        """Set the parent ids to revision_ids.
315
        
316
        See also set_parent_trees. This api will try to retrieve the tree data
317
        for each element of revision_ids from the trees repository. If you have
318
        tree data already available, it is more efficient to use
319
        set_parent_trees rather than set_parent_ids. set_parent_ids is however
320
        an easier API to use.
321
322
        :param revision_ids: The revision_ids to set as the parent ids of this
323
            working tree. Any of these may be ghosts.
324
        """
325
        trees = []
326
        for revision_id in revision_ids:
327
            try:
328
                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
329
                # TODO: jam 20070213 KnitVersionedFile raises
330
                #       RevisionNotPresent rather than NoSuchRevision if a
331
                #       given revision_id is not present. Should Repository be
332
                #       catching it and re-raising NoSuchRevision?
333
            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.
334
                revtree = None
335
            trees.append((revision_id, revtree))
336
        self.set_parent_trees(trees,
337
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
338
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
339
    @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.
340
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
341
        """Set the parents of the working tree.
342
343
        :param parents_list: A list of (revision_id, tree) tuples. 
344
            If tree is None, then that element is treated as an unreachable
345
            parent tree - i.e. a ghost.
346
        """
347
        dirstate = self.current_dirstate()
348
        if len(parents_list) > 0:
349
            if not allow_leftmost_as_ghost and parents_list[0][1] is None:
350
                raise errors.GhostRevisionUnusableHere(leftmost_id)
351
        real_trees = []
352
        ghosts = []
353
        # convert absent trees to the null tree, which we convert back to 
354
        # missing on access.
355
        for rev_id, tree in parents_list:
356
            if tree is not None:
357
                real_trees.append((rev_id, tree))
358
            else:
359
                real_trees.append((rev_id,
360
                    self.branch.repository.revision_tree(None)))
361
                ghosts.append(rev_id)
362
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
363
        self._dirty = True
364
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
365
    def _set_root_id(self, file_id):
366
        """See WorkingTree.set_root_id."""
367
        self.current_dirstate().set_path_id('', file_id)
368
        self._dirty = True
369
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.
370
    def unlock(self):
371
        """Unlock in format 4 trees needs to write the entire dirstate."""
372
        if self._control_files._lock_count == 1:
373
            if self._hashcache.needs_write:
374
                self._hashcache.write()
375
            # eventually we should do signature checking during read locks for
376
            # dirstate updates.
377
            if self._control_files._lock_mode == 'w':
378
                if self._dirty:
379
                    self.flush()
380
            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.
381
            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.
382
        # reverse order of locking.
383
        try:
384
            return self._control_files.unlock()
385
        finally:
386
            self.branch.unlock()
387
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
388
    @needs_tree_write_lock
389
    def unversion(self, file_ids):
390
        """Remove the file ids in file_ids from the current versioned set.
391
392
        When a file_id is unversioned, all of its children are automatically
393
        unversioned.
394
395
        :param file_ids: The file ids to stop versioning.
396
        :raises: NoSuchId if any fileid is not currently versioned.
397
        """
398
        if not file_ids:
399
            return
400
        state = self.current_dirstate()
401
        state._read_dirblocks_if_needed()
402
        ids_to_unversion = set()
403
        for fileid in file_ids:
404
            ids_to_unversion.add(fileid.encode('utf8'))
405
        paths_to_unversion = set()
406
        # sketch:
407
        # check if the root is to be unversioned, if so, assert for now.
408
        # make a copy of the _dirblocks data 
409
        # during the copy,
410
        #  skip paths in paths_to_unversion
411
        #  skip ids in ids_to_unversion, and add their paths to
412
        #  paths_to_unversion if they are a directory
413
        # if there are any un-unversioned ids at the end, raise
414
        if state._root_row[0][3] in ids_to_unversion:
415
            # I haven't written the code to unversion / yet - it should be 
416
            # supported.
417
            raise errors.BzrError('Unversioning the / is not currently supported')
418
        new_blocks = []
419
        for block in state._dirblocks:
420
            # first check: is the path one to remove - it or its children
421
            delete_block = False
422
            for path in paths_to_unversion:
423
                if (block[0].startswith(path) and
424
                    (len(block[0]) == len(path) or
425
                     block[0][len(path)] == '/')):
426
                    # this path should be deleted
427
                    delete_block = True
428
                    break
429
            # TODO: trim paths_to_unversion as we pass by paths
430
            if delete_block:
431
                # this block is to be deleted. skip it.
432
                continue
433
            # copy undeleted rows from within the the block
434
            new_blocks.append((block[0], []))
435
            new_row = new_blocks[-1][1]
436
            for row, row_parents in block[1]:
437
                if row[3] not in ids_to_unversion:
438
                    new_row.append((row, row_parents))
439
                else:
440
                    # skip the row, and if its a dir mark its path to be removed
441
                    if row[2] == 'directory':
442
                        paths_to_unversion.add((row[0] + '/' + row[1]).strip('/'))
443
                    assert not row_parents, "not ready to preserve parents."
444
                    ids_to_unversion.remove(row[3])
445
        if ids_to_unversion:
446
            raise errors.NoSuchId(self, iter(ids_to_unversion).next())
447
        state._dirblocks = new_blocks
448
        state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
449
        # have to change the legacy inventory too.
450
        if self._inventory is not None:
451
            for file_id in file_ids:
452
                if self._inventory.has_id(file_id):
453
                    self._inventory.remove_recursive_id(file_id)
454
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.
455
    @needs_tree_write_lock
456
    def _write_inventory(self, inv):
457
        """Write inventory as the current inventory."""
458
        assert not self._dirty, "attempting to write an inventory when the dirstate is dirty will cause data loss"
459
        self.current_dirstate().set_state_from_inventory(inv)
460
        self._dirty = True
461
        self.flush()
462
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.
463
464
class WorkingTreeFormat4(WorkingTreeFormat3):
465
    """The first consolidated dirstate working tree format.
466
467
    This format:
468
        - exists within a metadir controlling .bzr
469
        - includes an explicit version marker for the workingtree control
470
          files, separate from the BzrDir format
471
        - modifies the hash cache format
472
        - is new in bzr TODO FIXME SETBEFOREMERGE
473
        - uses a LockDir to guard access to it.
474
    """
475
476
    def get_format_string(self):
477
        """See WorkingTreeFormat.get_format_string()."""
478
        return "Bazaar Working Tree format 4\n"
479
480
    def get_format_description(self):
481
        """See WorkingTreeFormat.get_format_description()."""
482
        return "Working tree format 4"
483
484
    def initialize(self, a_bzrdir, revision_id=None):
485
        """See WorkingTreeFormat.initialize().
486
        
487
        revision_id allows creating a working tree at a different
488
        revision than the branch is at.
489
        """
490
        if not isinstance(a_bzrdir.transport, LocalTransport):
491
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
492
        transport = a_bzrdir.get_workingtree_transport(self)
493
        control_files = self._open_control_files(a_bzrdir)
494
        control_files.create_lock()
495
        control_files.lock_write()
496
        control_files.put_utf8('format', self.get_format_string())
497
        branch = a_bzrdir.open_branch()
498
        if revision_id is None:
499
            revision_id = branch.last_revision()
500
        local_path = transport.local_abspath('dirstate')
501
        dirstate.DirState.initialize(local_path)
502
        wt = WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
503
                         branch,
504
                         _format=self,
505
                         _bzrdir=a_bzrdir,
506
                         _control_files=control_files)
507
        wt._new_tree()
508
        wt.lock_write()
509
        try:
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
510
            #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.
511
            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.
512
            wt.flush()
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.
513
            transform.build_tree(wt.basis_tree(), wt)
514
        finally:
515
            control_files.unlock()
516
            wt.unlock()
517
        return wt
518
519
520
    def _open(self, a_bzrdir, control_files):
521
        """Open the tree itself.
522
        
523
        :param a_bzrdir: the dir for the tree.
524
        :param control_files: the control files for the tree.
525
        """
526
        return WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
527
                           branch=a_bzrdir.open_branch(),
528
                           _format=self,
529
                           _bzrdir=a_bzrdir,
530
                           _control_files=control_files)
531
532
533
class DirStateRevisionTree(Tree):
534
    """A revision tree pulling the inventory from a dirstate."""
535
536
    def __init__(self, dirstate, revision_id, repository):
537
        self._dirstate = dirstate
538
        self._revision_id = revision_id
539
        self._repository = repository
540
        self._inventory = None
541
        self._locked = False
542
543
    def _comparison_data(self, entry, path):
544
        """See Tree._comparison_data."""
545
        if entry is None:
546
            return None, False, None
547
        # trust the entry as RevisionTree does, but this may not be
548
        # sensible: the entry might not have come from us?
549
        return entry.kind, entry.executable, None
550
2255.2.10 by Robert Collins
Now all tests matching dirstate pass - added generation of inventories for parent trees.
551
    def _file_size(self, entry, stat_value):
552
        return entry.text_size
553
554
    def get_file_sha1(self, file_id, path=None, stat_value=None):
555
        # TODO: if path is present, fast-path on that, as inventory
556
        # might not be present
557
        ie = self.inventory[file_id]
558
        if ie.kind == "file":
559
            return ie.text_sha1
560
        return None
561
562
    def get_file_size(self, file_id):
563
        return self.inventory[file_id].text_size
564
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.
565
    def _get_inventory(self):
566
        if self._inventory is not None:
567
            return self._inventory
568
        self._generate_inventory()
569
        return self._inventory
570
571
    inventory = property(_get_inventory,
572
                         doc="Inventory of this Tree")
573
574
    def _generate_inventory(self):
575
        """Create and set self.inventory from the dirstate object.
576
        
577
        This is relatively expensive: we have to walk the entire dirstate.
578
        Ideally we would not, and instead would """
579
        assert self._locked, 'cannot generate inventory of an unlocked '\
580
            'dirstate revision tree'
581
        assert self._revision_id in self._dirstate.get_parent_ids(), \
582
            'parent %s has disappeared from %s' % (
583
            self._revision_id, self._dirstate.get_parent_ids())
2255.2.10 by Robert Collins
Now all tests matching dirstate pass - added generation of inventories for parent trees.
584
        parent_index = self._dirstate.get_parent_ids().index(self._revision_id)
585
        rows = self._dirstate._iter_rows()
586
        root_row = rows.next()
587
        inv = Inventory(root_id=root_row[0][3].decode('utf8'),
588
            revision_id=self._revision_id)
589
        for line in rows:
590
            revid, kind, dirname, name, size, executable, sha1 = line[1][parent_index]
591
            if not revid:
592
                # not in this revision tree.
593
                continue
594
            parent_id = inv[inv.path2id(dirname.decode('utf8'))].file_id
595
            file_id = line[0][3].decode('utf8')
596
            entry = make_entry(kind, name.decode('utf8'), parent_id, file_id)
597
            entry.revision = revid.decode('utf8')
598
            if kind == 'file':
599
                entry.executable = executable
600
                entry.text_size = size
601
                entry.text_sha1 = sha1
602
            inv.add(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.
603
        self._inventory = inv
604
605
    def get_parent_ids(self):
606
        """The parents of a tree in the dirstate are not cached."""
607
        return self._repository.get_revision(self._revision_id).parent_ids
608
609
    def lock_read(self):
610
        """Lock the tree for a set of operations."""
611
        self._locked = True
612
613
    def unlock(self):
614
        """Unlock, freeing any cache memory used during the lock."""
615
        # outside of a lock, the inventory is suspect: release it.
616
        self._inventory = None
617
        self._locked = False