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