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