/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.10 by Robert Collins
Now all tests matching dirstate pass - added generation of inventories for parent trees.
65
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.
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
96
from bzrlib.workingtree import WorkingTree3, WorkingTreeFormat3
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
155
        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.
156
        #-------------
157
        # during a read or write lock these objects are set, and are
158
        # 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.
159
        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.
160
        self._inventory = None
161
        #-------------
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.
162
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
163
    @needs_tree_write_lock
2255.2.12 by Robert Collins
Partial implementation of WorkingTree4._add.
164
    def _add(self, files, ids, kinds):
165
        """See MutableTree._add."""
166
        state = self.current_dirstate()
167
        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.
168
            f = f.strip('/')
2255.2.12 by Robert Collins
Partial implementation of WorkingTree4._add.
169
            assert '//' not in f
170
            assert '..' not in f
171
            if file_id is None:
2255.2.20 by Robert Collins
Bypass irrelevant basis_inventory tests for dirstate.
172
                file_id = generate_ids.gen_file_id(f)
2255.2.12 by Robert Collins
Partial implementation of WorkingTree4._add.
173
            stat = os.lstat(self.abspath(f))
174
            sha1 = '1' * 20 # FIXME: DIRSTATE MERGE BLOCKER
175
            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.
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.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
196
    def flush(self):
197
        """Write all cached data to disk."""
198
        self.current_dirstate().save()
199
        self._inventory = None
200
        self._dirty = False
201
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
202
    def get_file_sha1(self, file_id, path=None, stat_value=None):
203
        #if not path:
204
        #    path = self.inventory.id2path(file_id)
205
        #    # now lookup row by path
206
        row, parents = self._get_row(file_id=file_id)
207
        assert row is not None, 'what error should this raise'
208
        # TODO:
209
        # if row stat is valid, use cached sha1, else, get a new sha1.
210
        path = (row[0] + '/' + row[1]).strip('/').decode('utf8')
211
        return self._hashcache.get_sha1(path, stat_value)
212
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
213
    def _generate_inventory(self):
214
        """Create and set self.inventory from the dirstate object.
215
        
216
        This is relatively expensive: we have to walk the entire dirstate.
217
        Ideally we would not, and can deprecate this function.
218
        """
219
        dirstate = self.current_dirstate()
220
        rows = self._dirstate._iter_rows()
221
        root_row = rows.next()
222
        inv = Inventory(root_id=root_row[0][3].decode('utf8'))
223
        for line in rows:
224
            dirname, name, kind, fileid_utf8, size, stat, link_or_sha1 = line[0]
225
            if dirname == '/':
226
                # not in this revision tree.
227
                continue
228
            parent_id = inv[inv.path2id(dirname.decode('utf8'))].file_id
229
            file_id = fileid_utf8.decode('utf8')
230
            entry = make_entry(kind, name.decode('utf8'), parent_id, file_id)
231
            if kind == 'file':
232
                #entry.executable = executable
233
                #entry.text_size = size
234
                #entry.text_sha1 = sha1
235
                pass
236
            inv.add(entry)
237
        self._inventory = inv
238
2255.2.17 by Robert Collins
tweaks - finishes off all the test_test_trees tests for dirstate.
239
    def _get_inventory(self):
240
        """Get the inventory for the tree. This is only valid within a lock."""
241
        if self._inventory is not None:
242
            return self._inventory
243
        self._generate_inventory()
244
        return self._inventory
245
246
    inventory = property(_get_inventory,
247
                         doc="Inventory of this Tree")
248
249
    @needs_read_lock
250
    def get_root_id(self):
251
        """Return the id of this trees root"""
252
        return self.current_dirstate()._iter_rows().next()[0][3].decode('utf8')
253
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
254
    def _get_row(self, file_id):
255
        """Get the dirstate row for file_id."""
256
        state = self.current_dirstate()
257
        fileid_utf8 = file_id.encode('utf8')
258
        for row in state._iter_rows():
259
            if row[0][3] == fileid_utf8:
260
                return row
261
        return None, None
262
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
263
    def has_id(self, file_id):
264
        state = self.current_dirstate()
265
        fileid_utf8 = file_id.encode('utf8')
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
266
        row, parents = self._get_row(file_id)
267
        if row is None:
268
            return False
269
        return osutils.lexists(pathjoin(
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
270
                    self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
271
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
272
    @needs_read_lock
273
    def id2path(self, fileid):
274
        state = self.current_dirstate()
275
        fileid_utf8 = fileid.encode('utf8')
276
        for row, parents in state._iter_rows():
277
            if row[3] == fileid_utf8:
278
                return (row[0] + '/' + row[1]).decode('utf8').strip('/')
279
280
    @needs_read_lock
281
    def __iter__(self):
282
        """Iterate through file_ids for this tree.
283
284
        file_ids are in a WorkingTree if they are in the working inventory
285
        and the working file exists.
286
        """
287
        result = []
288
        for row, parents in self.current_dirstate()._iter_rows():
289
            if row[0] == '/':
290
                continue
291
            path = pathjoin(self.basedir, row[0].decode('utf8'), row[1].decode('utf8'))
292
            if osutils.lexists(path):
293
                result.append(row[3].decode('utf8'))
294
        return iter(result)
295
2255.2.21 by Robert Collins
Add WorkingTree4._last_revision, making workingtree_implementations.test_changes_from pass.
296
    @needs_read_lock
297
    def _last_revision(self):
298
        """See Mutable.last_revision."""
299
        parent_ids = self.current_dirstate().get_parent_ids()
300
        if parent_ids:
301
            return parent_ids[0].decode('utf8')
302
        else:
303
            return None
304
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.
305
    def _new_tree(self):
306
        """Initialize the state in this tree to be a new tree."""
307
        self._parent_revisions = [NULL_REVISION]
308
        self._dirty = True
309
310
    @needs_read_lock
2255.2.17 by Robert Collins
tweaks - finishes off all the test_test_trees tests for dirstate.
311
    def path2id(self, path):
312
        """Return the id for path in this tree."""
313
        state = self.current_dirstate()
314
        path_utf8 = os.path.split(path.encode('utf8'))
315
        for row, parents in state._iter_rows():
316
            if row[0:2] == path_utf8:
317
                return row[3].decode('utf8')
318
        return None
319
320
    @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.
321
    def revision_tree(self, revision_id):
322
        """See Tree.revision_tree.
323
324
        WorkingTree4 supplies revision_trees for any basis tree.
325
        """
326
        dirstate = self.current_dirstate()
327
        parent_ids = dirstate.get_parent_ids()
328
        if revision_id not in parent_ids:
329
            raise errors.NoSuchRevisionInTree(self, revision_id)
330
        return DirStateRevisionTree(dirstate, revision_id,
331
            self.branch.repository)
332
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
333
    @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.
334
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
335
        """Set the parent ids to revision_ids.
336
        
337
        See also set_parent_trees. This api will try to retrieve the tree data
338
        for each element of revision_ids from the trees repository. If you have
339
        tree data already available, it is more efficient to use
340
        set_parent_trees rather than set_parent_ids. set_parent_ids is however
341
        an easier API to use.
342
343
        :param revision_ids: The revision_ids to set as the parent ids of this
344
            working tree. Any of these may be ghosts.
345
        """
346
        trees = []
347
        for revision_id in revision_ids:
348
            try:
349
                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
350
                # TODO: jam 20070213 KnitVersionedFile raises
351
                #       RevisionNotPresent rather than NoSuchRevision if a
352
                #       given revision_id is not present. Should Repository be
353
                #       catching it and re-raising NoSuchRevision?
354
            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.
355
                revtree = None
356
            trees.append((revision_id, revtree))
357
        self.set_parent_trees(trees,
358
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
359
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
360
    @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.
361
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
362
        """Set the parents of the working tree.
363
364
        :param parents_list: A list of (revision_id, tree) tuples. 
365
            If tree is None, then that element is treated as an unreachable
366
            parent tree - i.e. a ghost.
367
        """
368
        dirstate = self.current_dirstate()
369
        if len(parents_list) > 0:
370
            if not allow_leftmost_as_ghost and parents_list[0][1] is None:
371
                raise errors.GhostRevisionUnusableHere(leftmost_id)
372
        real_trees = []
373
        ghosts = []
374
        # convert absent trees to the null tree, which we convert back to 
375
        # missing on access.
376
        for rev_id, tree in parents_list:
377
            if tree is not None:
378
                real_trees.append((rev_id, tree))
379
            else:
380
                real_trees.append((rev_id,
381
                    self.branch.repository.revision_tree(None)))
382
                ghosts.append(rev_id)
383
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
384
        self._dirty = True
385
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
386
    def _set_root_id(self, file_id):
387
        """See WorkingTree.set_root_id."""
388
        self.current_dirstate().set_path_id('', file_id)
389
        self._dirty = True
390
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.
391
    def unlock(self):
392
        """Unlock in format 4 trees needs to write the entire dirstate."""
393
        if self._control_files._lock_count == 1:
394
            if self._hashcache.needs_write:
395
                self._hashcache.write()
396
            # eventually we should do signature checking during read locks for
397
            # dirstate updates.
398
            if self._control_files._lock_mode == 'w':
399
                if self._dirty:
400
                    self.flush()
401
            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.
402
            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.
403
        # reverse order of locking.
404
        try:
405
            return self._control_files.unlock()
406
        finally:
407
            self.branch.unlock()
408
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
409
    @needs_tree_write_lock
410
    def unversion(self, file_ids):
411
        """Remove the file ids in file_ids from the current versioned set.
412
413
        When a file_id is unversioned, all of its children are automatically
414
        unversioned.
415
416
        :param file_ids: The file ids to stop versioning.
417
        :raises: NoSuchId if any fileid is not currently versioned.
418
        """
419
        if not file_ids:
420
            return
421
        state = self.current_dirstate()
422
        state._read_dirblocks_if_needed()
423
        ids_to_unversion = set()
424
        for fileid in file_ids:
425
            ids_to_unversion.add(fileid.encode('utf8'))
426
        paths_to_unversion = set()
427
        # sketch:
428
        # check if the root is to be unversioned, if so, assert for now.
429
        # make a copy of the _dirblocks data 
430
        # during the copy,
431
        #  skip paths in paths_to_unversion
432
        #  skip ids in ids_to_unversion, and add their paths to
433
        #  paths_to_unversion if they are a directory
434
        # if there are any un-unversioned ids at the end, raise
435
        if state._root_row[0][3] in ids_to_unversion:
436
            # I haven't written the code to unversion / yet - it should be 
437
            # supported.
438
            raise errors.BzrError('Unversioning the / is not currently supported')
439
        new_blocks = []
440
        for block in state._dirblocks:
441
            # first check: is the path one to remove - it or its children
442
            delete_block = False
443
            for path in paths_to_unversion:
444
                if (block[0].startswith(path) and
445
                    (len(block[0]) == len(path) or
446
                     block[0][len(path)] == '/')):
447
                    # this path should be deleted
448
                    delete_block = True
449
                    break
450
            # TODO: trim paths_to_unversion as we pass by paths
451
            if delete_block:
452
                # this block is to be deleted. skip it.
453
                continue
454
            # copy undeleted rows from within the the block
455
            new_blocks.append((block[0], []))
456
            new_row = new_blocks[-1][1]
457
            for row, row_parents in block[1]:
458
                if row[3] not in ids_to_unversion:
459
                    new_row.append((row, row_parents))
460
                else:
461
                    # skip the row, and if its a dir mark its path to be removed
462
                    if row[2] == 'directory':
463
                        paths_to_unversion.add((row[0] + '/' + row[1]).strip('/'))
464
                    assert not row_parents, "not ready to preserve parents."
465
                    ids_to_unversion.remove(row[3])
466
        if ids_to_unversion:
467
            raise errors.NoSuchId(self, iter(ids_to_unversion).next())
468
        state._dirblocks = new_blocks
469
        state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
470
        # have to change the legacy inventory too.
471
        if self._inventory is not None:
472
            for file_id in file_ids:
473
                if self._inventory.has_id(file_id):
474
                    self._inventory.remove_recursive_id(file_id)
475
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.
476
    @needs_tree_write_lock
477
    def _write_inventory(self, inv):
478
        """Write inventory as the current inventory."""
479
        assert not self._dirty, "attempting to write an inventory when the dirstate is dirty will cause data loss"
480
        self.current_dirstate().set_state_from_inventory(inv)
481
        self._dirty = True
482
        self.flush()
483
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.
484
485
class WorkingTreeFormat4(WorkingTreeFormat3):
486
    """The first consolidated dirstate working tree format.
487
488
    This format:
489
        - exists within a metadir controlling .bzr
490
        - includes an explicit version marker for the workingtree control
491
          files, separate from the BzrDir format
492
        - modifies the hash cache format
493
        - is new in bzr TODO FIXME SETBEFOREMERGE
494
        - uses a LockDir to guard access to it.
495
    """
496
497
    def get_format_string(self):
498
        """See WorkingTreeFormat.get_format_string()."""
499
        return "Bazaar Working Tree format 4\n"
500
501
    def get_format_description(self):
502
        """See WorkingTreeFormat.get_format_description()."""
503
        return "Working tree format 4"
504
505
    def initialize(self, a_bzrdir, revision_id=None):
506
        """See WorkingTreeFormat.initialize().
507
        
508
        revision_id allows creating a working tree at a different
509
        revision than the branch is at.
510
        """
511
        if not isinstance(a_bzrdir.transport, LocalTransport):
512
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
513
        transport = a_bzrdir.get_workingtree_transport(self)
514
        control_files = self._open_control_files(a_bzrdir)
515
        control_files.create_lock()
516
        control_files.lock_write()
517
        control_files.put_utf8('format', self.get_format_string())
518
        branch = a_bzrdir.open_branch()
519
        if revision_id is None:
520
            revision_id = branch.last_revision()
521
        local_path = transport.local_abspath('dirstate')
522
        dirstate.DirState.initialize(local_path)
523
        wt = WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
524
                         branch,
525
                         _format=self,
526
                         _bzrdir=a_bzrdir,
527
                         _control_files=control_files)
528
        wt._new_tree()
529
        wt.lock_write()
530
        try:
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
531
            #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.
532
            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.
533
            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.
534
            transform.build_tree(wt.basis_tree(), wt)
535
        finally:
536
            control_files.unlock()
537
            wt.unlock()
538
        return wt
539
540
541
    def _open(self, a_bzrdir, control_files):
542
        """Open the tree itself.
543
        
544
        :param a_bzrdir: the dir for the tree.
545
        :param control_files: the control files for the tree.
546
        """
547
        return WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
548
                           branch=a_bzrdir.open_branch(),
549
                           _format=self,
550
                           _bzrdir=a_bzrdir,
551
                           _control_files=control_files)
552
553
554
class DirStateRevisionTree(Tree):
555
    """A revision tree pulling the inventory from a dirstate."""
556
557
    def __init__(self, dirstate, revision_id, repository):
558
        self._dirstate = dirstate
559
        self._revision_id = revision_id
560
        self._repository = repository
561
        self._inventory = None
562
        self._locked = False
563
564
    def _comparison_data(self, entry, path):
565
        """See Tree._comparison_data."""
566
        if entry is None:
567
            return None, False, None
568
        # trust the entry as RevisionTree does, but this may not be
569
        # sensible: the entry might not have come from us?
570
        return entry.kind, entry.executable, None
571
2255.2.10 by Robert Collins
Now all tests matching dirstate pass - added generation of inventories for parent trees.
572
    def _file_size(self, entry, stat_value):
573
        return entry.text_size
574
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.
575
    def _generate_inventory(self):
576
        """Create and set self.inventory from the dirstate object.
577
        
578
        This is relatively expensive: we have to walk the entire dirstate.
579
        Ideally we would not, and instead would """
580
        assert self._locked, 'cannot generate inventory of an unlocked '\
581
            'dirstate revision tree'
582
        assert self._revision_id in self._dirstate.get_parent_ids(), \
583
            'parent %s has disappeared from %s' % (
584
            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.
585
        parent_index = self._dirstate.get_parent_ids().index(self._revision_id)
586
        rows = self._dirstate._iter_rows()
587
        root_row = rows.next()
588
        inv = Inventory(root_id=root_row[0][3].decode('utf8'),
589
            revision_id=self._revision_id)
590
        for line in rows:
591
            revid, kind, dirname, name, size, executable, sha1 = line[1][parent_index]
592
            if not revid:
593
                # not in this revision tree.
594
                continue
595
            parent_id = inv[inv.path2id(dirname.decode('utf8'))].file_id
596
            file_id = line[0][3].decode('utf8')
597
            entry = make_entry(kind, name.decode('utf8'), parent_id, file_id)
598
            entry.revision = revid.decode('utf8')
599
            if kind == 'file':
600
                entry.executable = executable
601
                entry.text_size = size
602
                entry.text_sha1 = sha1
603
            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.
604
        self._inventory = inv
605
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
606
    def get_file_sha1(self, file_id, path=None, stat_value=None):
607
        # TODO: if path is present, fast-path on that, as inventory
608
        # might not be present
609
        ie = self.inventory[file_id]
610
        if ie.kind == "file":
611
            return ie.text_sha1
612
        return None
613
614
    def get_file(self, file_id):
615
        return StringIO(self.get_file_text(file_id))
616
617
    def get_file_lines(self, file_id):
618
        ie = self.inventory[file_id]
619
        return self._repository.weave_store.get_weave(file_id,
620
                self._repository.get_transaction()).get_lines(ie.revision)
621
622
    def get_file_size(self, file_id):
623
        return self.inventory[file_id].text_size
624
625
    def get_file_text(self, file_id):
626
        return ''.join(self.get_file_lines(file_id))
627
628
    def _get_inventory(self):
629
        if self._inventory is not None:
630
            return self._inventory
631
        self._generate_inventory()
632
        return self._inventory
633
634
    inventory = property(_get_inventory,
635
                         doc="Inventory of this Tree")
636
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.
637
    def get_parent_ids(self):
638
        """The parents of a tree in the dirstate are not cached."""
639
        return self._repository.get_revision(self._revision_id).parent_ids
640
2255.2.30 by Robert Collins
Some workingtree_implementations/test_workingtree.py test work - add DirStateRevisionTree.has_filename, locks around appropriate calls in tests.
641
    def has_filename(self, filename):
642
        return bool(self.inventory.path2id(filename))
643
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
644
    def kind(self, file_id):
645
        return self.inventory[file_id].kind
646
647
    def is_executable(self, file_id, path=None):
648
        ie = self.inventory[file_id]
649
        if ie.kind != "file":
650
            return None 
651
        return ie.executable
652
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.
653
    def lock_read(self):
654
        """Lock the tree for a set of operations."""
655
        self._locked = True
656
657
    def unlock(self):
658
        """Unlock, freeing any cache memory used during the lock."""
659
        # outside of a lock, the inventory is suspect: release it.
660
        self._inventory = None
661
        self._locked = False