/brz/remove-bazaar

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