/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.81 by Robert Collins
WorkingTree4: Implement filter_unversioned_files to use dirstate bisection.
196
    def filter_unversioned_files(self, paths):
197
        """Filter out paths that are not versioned.
198
199
        :return: set of paths.
200
        """
201
        # TODO: make a generic multi-bisect routine roughly that should list
202
        # the paths, then process one half at a time recursively, and feed the
203
        # results of each bisect in further still
204
        paths = sorted(paths)
205
        result = set()
206
        state = self.current_dirstate()
207
        # TODO we want a paths_to_dirblocks helper I think
208
        for path in paths:
209
            dirname, basename = os.path.split(path.encode('utf8'))
210
            _, _, _, path_is_versioned = state._get_block_row_index(
211
                dirname, basename)
212
            if path_is_versioned:
213
                result.add(path)
214
        return result
215
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
216
    def flush(self):
217
        """Write all cached data to disk."""
2255.2.39 by Robert Collins
WorkingTree4: flush can only be used during write locks.
218
        if self._control_files._lock_mode != 'w':
219
            raise errors.NotWriteLocked(self)
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
220
        self.current_dirstate().save()
221
        self._inventory = None
222
        self._dirty = False
223
2255.2.34 by Robert Collins
Fix WorkingTree4 parent_ids logic to use the dirstate to answer parent ids list queries.
224
    def _generate_inventory(self):
225
        """Create and set self.inventory from the dirstate object.
226
        
227
        This is relatively expensive: we have to walk the entire dirstate.
228
        Ideally we would not, and can deprecate this function.
229
        """
2255.2.82 by Robert Collins
various notes about find_ids_across_trees
230
        #: uncomment to trap on inventory requests.
231
        # import pdb;pdb.set_trace()
2255.2.75 by Robert Collins
Correct generation of revisiontree inventories to handle out of order parents.
232
        state = self.current_dirstate()
233
        state._read_dirblocks_if_needed()
234
        rows = state._iter_rows()
235
        current_row = state._root_row
236
        current_id = current_row[0][3].decode('utf8')
237
        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.
238
        # we could do this straight out of the dirstate; it might be fast
239
        # and should be profiled - RBC 20070216
240
        parent_ids = {'' : inv.root.file_id}
2255.2.77 by Robert Collins
Tune working inventory generation more: walk the blocks, skipping deleted rows.
241
        for block in state._dirblocks:
242
            # block of unversioned files, skip.
243
            if block[0] == '/':
2255.2.34 by Robert Collins
Fix WorkingTree4 parent_ids logic to use the dirstate to answer parent ids list queries.
244
                continue
2255.2.77 by Robert Collins
Tune working inventory generation more: walk the blocks, skipping deleted rows.
245
            dirname = block[0]
246
            parent_id = parent_ids[block[0]]
247
            for line in block[1]:
248
                _, name, kind, fileid_utf8, size, stat, link_or_sha1 = line[0]
249
                file_id = fileid_utf8.decode('utf8')
250
                entry = entry_factory[kind](file_id, name.decode('utf8'), parent_id)
251
                if kind == 'file':
252
                    #entry.executable = executable
253
                    #entry.text_size = size
254
                    #entry.text_sha1 = sha1
255
                    pass
256
                elif kind == 'directory':
257
                    parent_ids[(dirname + '/' + name).strip('/')] = file_id
258
                inv.add(entry)
2255.2.34 by Robert Collins
Fix WorkingTree4 parent_ids logic to use the dirstate to answer parent ids list queries.
259
        self._inventory = inv
260
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
261
    def get_file_sha1(self, file_id, path=None, stat_value=None):
262
        #if not path:
263
        #    path = self.inventory.id2path(file_id)
264
        #    # 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.
265
        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.
266
        assert row is not None, 'what error should this raise'
267
        # TODO:
268
        # 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.
269
        if path is None:
270
            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.
271
        return self._hashcache.get_sha1(path, stat_value)
272
2255.2.17 by Robert Collins
tweaks - finishes off all the test_test_trees tests for dirstate.
273
    def _get_inventory(self):
274
        """Get the inventory for the tree. This is only valid within a lock."""
275
        if self._inventory is not None:
276
            return self._inventory
277
        self._generate_inventory()
278
        return self._inventory
279
280
    inventory = property(_get_inventory,
281
                         doc="Inventory of this Tree")
282
283
    @needs_read_lock
2255.2.34 by Robert Collins
Fix WorkingTree4 parent_ids logic to use the dirstate to answer parent ids list queries.
284
    def get_parent_ids(self):
285
        """See Tree.get_parent_ids.
286
        
287
        This implementation requests the ids list from the dirstate file.
288
        """
289
        return self.current_dirstate().get_parent_ids()
290
291
    @needs_read_lock
2255.2.17 by Robert Collins
tweaks - finishes off all the test_test_trees tests for dirstate.
292
    def get_root_id(self):
293
        """Return the id of this trees root"""
294
        return self.current_dirstate()._iter_rows().next()[0][3].decode('utf8')
295
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
296
    def _get_row(self, file_id=None, path=None):
297
        """Get the dirstate row for file_id or path.
298
299
        If either file_id or path is supplied, it is used as the key to lookup.
300
        If both are supplied, the fastest lookup is used, and an error is
301
        raised if they do not both point at the same row.
302
        
303
        :param file_id: An optional unicode file_id to be looked up.
304
        :param path: An optional unicode path to be looked up.
305
        :return: The dirstate row tuple for path/file_id, or (None, None)
306
        """
307
        if file_id is None and path is None:
308
            raise errors.BzrError('must supply file_id or path')
309
        state = self.current_dirstate()
310
        state._read_dirblocks_if_needed()
311
        if file_id is not None:
312
            fileid_utf8 = file_id.encode('utf8')
313
        if path is not None:
314
            # path lookups are faster
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
315
            row = state._get_row(path.encode('utf8'))
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
316
            if file_id:
317
                if row[0][3] != fileid_utf8:
318
                    raise BzrError('integrity error ? : mismatching file_id and path')
319
            return row
320
        else:
321
            for row in state._iter_rows():
322
                if row[0][3] == fileid_utf8:
323
                    return row
324
            return None, None
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
325
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
326
    def has_id(self, file_id):
327
        state = self.current_dirstate()
328
        fileid_utf8 = file_id.encode('utf8')
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
329
        row, parents = self._get_row(file_id)
330
        if row is None:
331
            return False
332
        return osutils.lexists(pathjoin(
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
333
                    self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
334
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
335
    @needs_read_lock
336
    def id2path(self, fileid):
337
        state = self.current_dirstate()
338
        fileid_utf8 = fileid.encode('utf8')
339
        for row, parents in state._iter_rows():
340
            if row[3] == fileid_utf8:
341
                return (row[0] + '/' + row[1]).decode('utf8').strip('/')
342
343
    @needs_read_lock
344
    def __iter__(self):
345
        """Iterate through file_ids for this tree.
346
347
        file_ids are in a WorkingTree if they are in the working inventory
348
        and the working file exists.
349
        """
350
        result = []
351
        for row, parents in self.current_dirstate()._iter_rows():
352
            if row[0] == '/':
353
                continue
354
            path = pathjoin(self.basedir, row[0].decode('utf8'), row[1].decode('utf8'))
355
            if osutils.lexists(path):
356
                result.append(row[3].decode('utf8'))
357
        return iter(result)
358
2255.2.21 by Robert Collins
Add WorkingTree4._last_revision, making workingtree_implementations.test_changes_from pass.
359
    @needs_read_lock
360
    def _last_revision(self):
361
        """See Mutable.last_revision."""
362
        parent_ids = self.current_dirstate().get_parent_ids()
363
        if parent_ids:
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
364
            return parent_ids[0]
2255.2.21 by Robert Collins
Add WorkingTree4._last_revision, making workingtree_implementations.test_changes_from pass.
365
        else:
366
            return None
367
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
368
    @needs_tree_write_lock
369
    def move(self, from_paths, to_dir=None, after=False, **kwargs):
370
        """See WorkingTree.move()."""
371
        if not from_paths:
372
            return ()
373
374
        state = self.current_dirstate()
375
376
        # check for deprecated use of signature
377
        if to_dir is None:
378
            to_dir = kwargs.get('to_name', None)
379
            if to_dir is None:
380
                raise TypeError('You must supply a target directory')
381
            else:
382
                symbol_versioning.warn('The parameter to_name was deprecated'
383
                                       ' in version 0.13. Use to_dir instead',
384
                                       DeprecationWarning)
385
386
        assert not isinstance(from_paths, basestring)
387
        to_dir_utf8 = to_dir.encode('utf8')
388
        to_row_dirname, to_basename = os.path.split(to_dir_utf8)
389
        # check destination directory
390
        # get the details for it
391
        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
392
            state._get_block_row_index(to_row_dirname, to_basename)
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
393
        if not row_present:
394
            raise errors.BzrMoveFailedError('', to_dir,
395
                errors.NotInWorkingDirectory(to_dir))
396
        to_row = state._dirblocks[to_row_block_index][1][to_row_row_index][0]
397
        # get a handle on the block itself.
398
        to_block_index = state._ensure_block(
399
            to_row_block_index, to_row_row_index, to_dir_utf8)
400
        to_block = state._dirblocks[to_block_index]
401
        to_abs = self.abspath(to_dir)
402
        if not isdir(to_abs):
403
            raise errors.BzrMoveFailedError('',to_dir,
404
                errors.NotADirectory(to_abs))
405
406
        if to_row[2] != 'directory':
407
            raise errors.BzrMoveFailedError('',to_dir,
408
                errors.NotADirectory(to_abs))
409
410
        if self._inventory is not None:
411
            update_inventory = True
412
            inv = self.inventory
413
            to_dir_ie = inv[to_dir_id]
414
            to_dir_id = to_row[3].decode('utf8')
415
        else:
416
            update_inventory = False
417
418
        # create rename entries and tuples
419
        for from_rel in from_paths:
420
            # from_rel is 'pathinroot/foo/bar'
421
            from_dirname, from_tail = os.path.split(from_rel)
422
            from_dirname = from_dirname.encode('utf8')
423
            from_row = self._get_row(path=from_rel)
424
            if from_row == (None, None):
425
                raise errors.BzrMoveFailedError(from_rel,to_dir,
426
                    errors.NotVersionedError(path=str(from_rel)))
427
428
            from_id = from_row[0][3].decode('utf8')
429
            to_rel = pathjoin(to_dir, from_tail)
430
            item_to_row = self._get_row(path=to_rel)
431
            if item_to_row != (None, None):
432
                raise errors.BzrMoveFailedError(from_rel, to_rel,
433
                    "Target is already versioned.")
434
435
            if from_rel == to_rel:
436
                raise errors.BzrMoveFailedError(from_rel, to_rel,
437
                    "Source and target are identical.")
438
439
            from_missing = not self.has_filename(from_rel)
440
            to_missing = not self.has_filename(to_rel)
441
            if after:
442
                move_file = False
443
            else:
444
                move_file = True
445
            if to_missing:
446
                if not move_file:
447
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
448
                        errors.NoSuchFile(path=to_rel,
449
                        extra="New file has not been created yet"))
450
                elif from_missing:
451
                    # neither path exists
452
                    raise errors.BzrRenameFailedError(from_rel, to_rel,
453
                        errors.PathsDoNotExist(paths=(from_rel, to_rel)))
454
            else:
455
                if from_missing: # implicitly just update our path mapping
456
                    move_file = False
457
                else:
458
                    raise errors.RenameFailedFilesExist(from_rel, to_rel,
459
                        extra="(Use --after to update the Bazaar id)")
460
461
            rollbacks = []
462
            def rollback_rename():
463
                """A single rename has failed, roll it back."""
464
                error = None
465
                for rollback in reversed(rollbacks):
466
                    try:
467
                        rollback()
468
                    except e:
469
                        error = e
470
                if error:
471
                    raise error
472
473
            # perform the disk move first - its the most likely failure point.
474
            from_rel_abs = self.abspath(from_rel)
475
            to_rel_abs = self.abspath(to_rel)
476
            try:
477
                osutils.rename(from_rel_abs, to_rel_abs)
478
            except OSError, e:
479
                raise errors.BzrMoveFailedError(from_rel, to_rel, e[1])
480
            rollbacks.append(lambda: osutils.rename(to_rel_abs, from_rel_abs))
481
            try:
482
                # perform the rename in the inventory next if needed: its easy
483
                # to rollback
484
                if update_inventory:
485
                    # rename the entry
486
                    from_entry = inv[from_id]
487
                    current_parent = from_entry.parent_id
488
                    inv.rename(from_id, to_dir_id, from_tail)
489
                    rollbacks.append(
490
                        lambda: inv.rename(from_id, current_parent, from_tail))
491
                # finally do the rename in the dirstate, which is a little
492
                # tricky to rollback, but least likely to need it.
493
                basename = from_tail.encode('utf8')
494
                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
495
                    state._get_block_row_index(from_dirname, basename)
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
496
                old_block = state._dirblocks[old_block_index][1]
497
                # remove the old row
498
                old_row = old_block.pop(old_row_index)
499
                rollbacks.append(lambda:old_block.insert(old_row_index, old_row))
500
                # create new row in current block
501
                new_row = ((to_block[0],) + old_row[0][1:], old_row[1])
502
                insert_position = bisect_left(to_block[1], new_row)
503
                to_block[1].insert(insert_position, new_row)
504
                rollbacks.append(lambda:to_block[1].pop(insert_position))
505
                if new_row[0][2] == 'directory':
506
                    import pdb;pdb.set_trace()
507
                    # if a directory, rename all the contents of child blocks
508
                    # adding rollbacks as each is inserted to remove them and
509
                    # restore the original
510
                    # TODO: large scale slice assignment.
511
                    # setup new list
512
                    # save old list region
513
                    # move up or down the old region
514
                    # add rollback to move the region back
515
                    # assign new list to new region
516
                    # done
517
            except:
518
                rollback_rename()
519
                raise
520
            state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
521
            self._dirty = True
522
523
        return #rename_tuples
524
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.
525
    def _new_tree(self):
526
        """Initialize the state in this tree to be a new tree."""
527
        self._dirty = True
528
529
    @needs_read_lock
2255.2.17 by Robert Collins
tweaks - finishes off all the test_test_trees tests for dirstate.
530
    def path2id(self, path):
531
        """Return the id for path in this tree."""
532
        state = self.current_dirstate()
2255.2.59 by Robert Collins
All WorkingTree4 and dirstate tests passing.
533
        row = self._get_row(path=path)
534
        if row == (None, None):
535
            return None
536
        return row[0][3].decode('utf8')
2255.2.17 by Robert Collins
tweaks - finishes off all the test_test_trees tests for dirstate.
537
2255.2.45 by Robert Collins
Dirstate - fix revision_tree() behaviour to match the interface contract.
538
    def read_working_inventory(self):
539
        """Read the working inventory.
540
        
541
        This is a meaningless operation for dirstate, but we obey it anyhow.
542
        """
543
        return self.inventory
544
2255.2.17 by Robert Collins
tweaks - finishes off all the test_test_trees tests for dirstate.
545
    @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.
546
    def revision_tree(self, revision_id):
547
        """See Tree.revision_tree.
548
549
        WorkingTree4 supplies revision_trees for any basis tree.
550
        """
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
551
        revision_id = osutils.safe_revision_id(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.
552
        dirstate = self.current_dirstate()
553
        parent_ids = dirstate.get_parent_ids()
554
        if revision_id not in parent_ids:
555
            raise errors.NoSuchRevisionInTree(self, revision_id)
2255.2.45 by Robert Collins
Dirstate - fix revision_tree() behaviour to match the interface contract.
556
        if revision_id in dirstate.get_ghosts():
557
            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.
558
        return DirStateRevisionTree(dirstate, revision_id,
559
            self.branch.repository)
560
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
561
    @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.
562
    def set_last_revision(self, new_revision):
563
        """Change the last revision in the working tree."""
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
564
        new_revision = osutils.safe_revision_id(new_revision)
2255.2.37 by Robert Collins
Get TestExecutable.test_06_pull working on DirState: fix cloning and the set_last_revision api on WorkingTree4.
565
        parents = self.get_parent_ids()
566
        if new_revision in (NULL_REVISION, None):
2255.2.56 by Robert Collins
Dirstate: bring set_last_revision into line with the tested API.
567
            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.
568
                "setting the last parent to none with a pending merge is "
569
                "unsupported.")
570
            self.set_parent_ids([])
571
        else:
2255.2.56 by Robert Collins
Dirstate: bring set_last_revision into line with the tested API.
572
            self.set_parent_ids([new_revision] + parents[1:],
573
                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.
574
575
    @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.
576
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
577
        """Set the parent ids to revision_ids.
578
        
579
        See also set_parent_trees. This api will try to retrieve the tree data
580
        for each element of revision_ids from the trees repository. If you have
581
        tree data already available, it is more efficient to use
582
        set_parent_trees rather than set_parent_ids. set_parent_ids is however
583
        an easier API to use.
584
585
        :param revision_ids: The revision_ids to set as the parent ids of this
586
            working tree. Any of these may be ghosts.
587
        """
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
588
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
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.
589
        trees = []
590
        for revision_id in revision_ids:
591
            try:
592
                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
593
                # TODO: jam 20070213 KnitVersionedFile raises
594
                #       RevisionNotPresent rather than NoSuchRevision if a
595
                #       given revision_id is not present. Should Repository be
596
                #       catching it and re-raising NoSuchRevision?
597
            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.
598
                revtree = None
599
            trees.append((revision_id, revtree))
600
        self.set_parent_trees(trees,
601
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
602
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
603
    @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.
604
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
605
        """Set the parents of the working tree.
606
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
607
        :param parents_list: A list of (revision_id, tree) tuples.
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.
608
            If tree is None, then that element is treated as an unreachable
609
            parent tree - i.e. a ghost.
610
        """
611
        dirstate = self.current_dirstate()
612
        if len(parents_list) > 0:
613
            if not allow_leftmost_as_ghost and parents_list[0][1] is None:
2255.2.42 by Robert Collins
Fix WorkingTree4.set_parent_trees.
614
                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.
615
        real_trees = []
616
        ghosts = []
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
617
        # convert absent trees to the null tree, which we convert back to
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.
618
        # missing on access.
619
        for rev_id, tree in parents_list:
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
620
            rev_id = osutils.safe_revision_id(rev_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.
621
            if tree is not None:
622
                real_trees.append((rev_id, tree))
623
            else:
624
                real_trees.append((rev_id,
625
                    self.branch.repository.revision_tree(None)))
626
                ghosts.append(rev_id)
627
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
628
        self._dirty = True
629
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
630
    def _set_root_id(self, file_id):
631
        """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.
632
        state = self.current_dirstate()
633
        state.set_path_id('', file_id)
634
        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.
635
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.
636
    def unlock(self):
637
        """Unlock in format 4 trees needs to write the entire dirstate."""
638
        if self._control_files._lock_count == 1:
2255.2.44 by Robert Collins
Fix tree unlock on readonly Format4 trees with dirty hashcache.
639
            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.
640
            # eventually we should do signature checking during read locks for
641
            # dirstate updates.
642
            if self._control_files._lock_mode == 'w':
643
                if self._dirty:
644
                    self.flush()
645
            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.
646
            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.
647
        # reverse order of locking.
648
        try:
649
            return self._control_files.unlock()
650
        finally:
651
            self.branch.unlock()
652
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
653
    @needs_tree_write_lock
654
    def unversion(self, file_ids):
655
        """Remove the file ids in file_ids from the current versioned set.
656
657
        When a file_id is unversioned, all of its children are automatically
658
        unversioned.
659
660
        :param file_ids: The file ids to stop versioning.
661
        :raises: NoSuchId if any fileid is not currently versioned.
662
        """
663
        if not file_ids:
664
            return
665
        state = self.current_dirstate()
666
        state._read_dirblocks_if_needed()
667
        ids_to_unversion = set()
668
        for fileid in file_ids:
669
            ids_to_unversion.add(fileid.encode('utf8'))
670
        paths_to_unversion = set()
671
        # sketch:
672
        # check if the root is to be unversioned, if so, assert for now.
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
673
        # make a copy of the _dirblocks data
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
674
        # during the copy,
675
        #  skip paths in paths_to_unversion
676
        #  skip ids in ids_to_unversion, and add their paths to
677
        #  paths_to_unversion if they are a directory
678
        # if there are any un-unversioned ids at the end, raise
679
        if state._root_row[0][3] in ids_to_unversion:
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
680
            # I haven't written the code to unversion / yet - it should be
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
681
            # supported.
682
            raise errors.BzrError('Unversioning the / is not currently supported')
683
        new_blocks = []
2255.2.36 by Robert Collins
Fix Dirstate unversioning of entries which are in a parent.
684
        deleted_rows = []
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
685
        for block in state._dirblocks:
686
            # first check: is the path one to remove - it or its children
687
            delete_block = False
688
            for path in paths_to_unversion:
689
                if (block[0].startswith(path) and
690
                    (len(block[0]) == len(path) or
691
                     block[0][len(path)] == '/')):
692
                    # this path should be deleted
693
                    delete_block = True
694
                    break
695
            # TODO: trim paths_to_unversion as we pass by paths
696
            if delete_block:
697
                # this block is to be deleted. skip it.
698
                continue
699
            # copy undeleted rows from within the the block
700
            new_blocks.append((block[0], []))
701
            new_row = new_blocks[-1][1]
702
            for row, row_parents in block[1]:
703
                if row[3] not in ids_to_unversion:
704
                    new_row.append((row, row_parents))
705
                else:
706
                    # skip the row, and if its a dir mark its path to be removed
707
                    if row[2] == 'directory':
708
                        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.
709
                    if row_parents:
710
                        deleted_rows.append((row[3], row_parents))
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
711
                    ids_to_unversion.remove(row[3])
712
        if ids_to_unversion:
713
            raise errors.NoSuchId(self, iter(ids_to_unversion).next())
714
        state._dirblocks = new_blocks
2255.2.36 by Robert Collins
Fix Dirstate unversioning of entries which are in a parent.
715
        for fileid_utf8, parents in deleted_rows:
716
            state.add_deleted(fileid_utf8, parents)
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
717
        state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
2255.2.46 by Robert Collins
Dirstate - unversion should set the tree state as dirty.
718
        self._dirty = True
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
719
        # have to change the legacy inventory too.
720
        if self._inventory is not None:
721
            for file_id in file_ids:
2255.2.33 by Robert Collins
Correct thunko in refactoring a few commits back.
722
                self._inventory.remove_recursive_id(file_id)
2255.2.22 by Robert Collins
Dirstate: implement WorkingTree4.unversion, letting some test_commit tests pass.
723
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.
724
    @needs_tree_write_lock
725
    def _write_inventory(self, inv):
726
        """Write inventory as the current inventory."""
727
        assert not self._dirty, "attempting to write an inventory when the dirstate is dirty will cause data loss"
728
        self.current_dirstate().set_state_from_inventory(inv)
729
        self._dirty = True
730
        self.flush()
731
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.
732
733
class WorkingTreeFormat4(WorkingTreeFormat3):
734
    """The first consolidated dirstate working tree format.
735
736
    This format:
737
        - exists within a metadir controlling .bzr
738
        - includes an explicit version marker for the workingtree control
739
          files, separate from the BzrDir format
740
        - modifies the hash cache format
741
        - is new in bzr TODO FIXME SETBEFOREMERGE
742
        - uses a LockDir to guard access to it.
743
    """
744
745
    def get_format_string(self):
746
        """See WorkingTreeFormat.get_format_string()."""
747
        return "Bazaar Working Tree format 4\n"
748
749
    def get_format_description(self):
750
        """See WorkingTreeFormat.get_format_description()."""
751
        return "Working tree format 4"
752
753
    def initialize(self, a_bzrdir, revision_id=None):
754
        """See WorkingTreeFormat.initialize().
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
755
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.
756
        revision_id allows creating a working tree at a different
757
        revision than the branch is at.
758
        """
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
759
        revision_id = osutils.safe_revision_id(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.
760
        if not isinstance(a_bzrdir.transport, LocalTransport):
761
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
762
        transport = a_bzrdir.get_workingtree_transport(self)
763
        control_files = self._open_control_files(a_bzrdir)
764
        control_files.create_lock()
765
        control_files.lock_write()
766
        control_files.put_utf8('format', self.get_format_string())
767
        branch = a_bzrdir.open_branch()
768
        if revision_id is None:
769
            revision_id = branch.last_revision()
770
        local_path = transport.local_abspath('dirstate')
771
        dirstate.DirState.initialize(local_path)
772
        wt = WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
773
                         branch,
774
                         _format=self,
775
                         _bzrdir=a_bzrdir,
776
                         _control_files=control_files)
777
        wt._new_tree()
778
        wt.lock_write()
779
        try:
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
780
            #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.
781
            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.
782
            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.
783
            basis = wt.basis_tree()
784
            basis.lock_read()
785
            transform.build_tree(basis, wt)
786
            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.
787
        finally:
788
            control_files.unlock()
789
            wt.unlock()
790
        return wt
791
792
793
    def _open(self, a_bzrdir, control_files):
794
        """Open the tree itself.
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
795
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.
796
        :param a_bzrdir: the dir for the tree.
797
        :param control_files: the control files for the tree.
798
        """
799
        return WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
800
                           branch=a_bzrdir.open_branch(),
801
                           _format=self,
802
                           _bzrdir=a_bzrdir,
803
                           _control_files=control_files)
804
805
806
class DirStateRevisionTree(Tree):
807
    """A revision tree pulling the inventory from a dirstate."""
808
809
    def __init__(self, dirstate, revision_id, repository):
810
        self._dirstate = dirstate
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
811
        self._revision_id = osutils.safe_revision_id(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.
812
        self._repository = repository
813
        self._inventory = None
2255.2.38 by Robert Collins
Fix WorkingTree4.pull to work.
814
        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.
815
2255.2.69 by John Arbash Meinel
Implement annotate_iter, get_revision_id, and walkdirs so that all tree_implementations now pass
816
    def annotate_iter(self, file_id):
817
        """See Tree.annotate_iter"""
818
        w = self._repository.weave_store.get_weave(file_id,
819
                           self._repository.get_transaction())
820
        return w.annotate_iter(self.inventory[file_id].revision)
821
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.
822
    def _comparison_data(self, entry, path):
823
        """See Tree._comparison_data."""
824
        if entry is None:
825
            return None, False, None
826
        # trust the entry as RevisionTree does, but this may not be
827
        # sensible: the entry might not have come from us?
828
        return entry.kind, entry.executable, None
829
2255.2.10 by Robert Collins
Now all tests matching dirstate pass - added generation of inventories for parent trees.
830
    def _file_size(self, entry, stat_value):
831
        return entry.text_size
832
2255.2.78 by Robert Collins
Really finish the prior commit.
833
    def filter_unversioned_files(self, paths):
834
        """Filter out paths that are not versioned.
835
836
        :return: set of paths.
837
        """
838
        pred = self.has_filename
839
        return set((p for p in paths if not pred(p)))
840
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.
841
    def _generate_inventory(self):
842
        """Create and set self.inventory from the dirstate object.
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
843
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.
844
        This is relatively expensive: we have to walk the entire dirstate.
845
        Ideally we would not, and instead would """
846
        assert self._locked, 'cannot generate inventory of an unlocked '\
847
            'dirstate revision tree'
848
        assert self._revision_id in self._dirstate.get_parent_ids(), \
849
            'parent %s has disappeared from %s' % (
850
            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.
851
        # separate call for profiling - makes it clear where the costs are.
852
        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.
853
        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.
854
        # because the parent tree may look dramatically different to the current
855
        # tree, we grab and sort the tree content  all at once, then
856
        # deserialise into an inventory.
2255.2.10 by Robert Collins
Now all tests matching dirstate pass - added generation of inventories for parent trees.
857
        rows = self._dirstate._iter_rows()
2255.2.75 by Robert Collins
Correct generation of revisiontree inventories to handle out of order parents.
858
        parent_rows = []
859
        for row in rows:
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
860
            parent_rows.append((row[1][parent_index], row[0][3]))
2255.2.75 by Robert Collins
Correct generation of revisiontree inventories to handle out of order parents.
861
        parent_rows = iter(sorted(parent_rows, key=lambda x:x[0][2:3]))
862
        root_row = parent_rows.next()
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
863
        inv = Inventory(root_id=root_row[1].decode('utf8'),
864
                        revision_id=self._revision_id)
865
        inv.root.revision = root_row[1][parent_index][0]
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.
866
        # we could do this straight out of the dirstate; it might be fast
867
        # and should be profiled - RBC 20070216
868
        parent_ids = {'' : inv.root.file_id}
2255.2.75 by Robert Collins
Correct generation of revisiontree inventories to handle out of order parents.
869
        for line in parent_rows:
870
            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.
871
            if not revid:
872
                # not in this revision tree.
873
                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.
874
            parent_id = parent_ids[dirname]
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
875
            file_id = line[1].decode('utf8')
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.
876
            entry = entry_factory[kind](file_id, name.decode('utf8'), parent_id)
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
877
            entry.revision = revid
2255.2.10 by Robert Collins
Now all tests matching dirstate pass - added generation of inventories for parent trees.
878
            if kind == 'file':
879
                entry.executable = executable
880
                entry.text_size = size
881
                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.
882
            elif kind == 'directory':
883
                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.
884
            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.
885
        self._inventory = inv
886
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
887
    def get_file_sha1(self, file_id, path=None, stat_value=None):
888
        # TODO: if path is present, fast-path on that, as inventory
889
        # might not be present
890
        ie = self.inventory[file_id]
891
        if ie.kind == "file":
892
            return ie.text_sha1
893
        return None
894
895
    def get_file(self, file_id):
896
        return StringIO(self.get_file_text(file_id))
897
898
    def get_file_lines(self, file_id):
899
        ie = self.inventory[file_id]
900
        return self._repository.weave_store.get_weave(file_id,
901
                self._repository.get_transaction()).get_lines(ie.revision)
902
903
    def get_file_size(self, file_id):
904
        return self.inventory[file_id].text_size
905
906
    def get_file_text(self, file_id):
907
        return ''.join(self.get_file_lines(file_id))
908
2255.2.69 by John Arbash Meinel
Implement annotate_iter, get_revision_id, and walkdirs so that all tree_implementations now pass
909
    def get_revision_id(self):
910
        """Return the revision id for this tree."""
911
        return self._revision_id
912
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
913
    def _get_inventory(self):
914
        if self._inventory is not None:
915
            return self._inventory
916
        self._generate_inventory()
917
        return self._inventory
918
919
    inventory = property(_get_inventory,
920
                         doc="Inventory of this Tree")
921
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.
922
    def get_parent_ids(self):
923
        """The parents of a tree in the dirstate are not cached."""
924
        return self._repository.get_revision(self._revision_id).parent_ids
925
2255.2.30 by Robert Collins
Some workingtree_implementations/test_workingtree.py test work - add DirStateRevisionTree.has_filename, locks around appropriate calls in tests.
926
    def has_filename(self, filename):
927
        return bool(self.inventory.path2id(filename))
928
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
929
    def kind(self, file_id):
930
        return self.inventory[file_id].kind
931
932
    def is_executable(self, file_id, path=None):
933
        ie = self.inventory[file_id]
934
        if ie.kind != "file":
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
935
            return None
2255.2.31 by Robert Collins
Work in progress to make merge_inner work with dirstate trees.
936
        return ie.executable
937
2255.2.71 by John Arbash Meinel
Add a test for list_files, and implement it for DirStateRevisionTree
938
    def list_files(self, include_root=False):
939
        # We use a standard implementation, because DirStateRevisionTree is
940
        # dealing with one of the parents of the current state
941
        inv = self._get_inventory()
942
        entries = inv.iter_entries()
943
        if self.inventory.root is not None and not include_root:
944
            entries.next()
945
        for path, entry in entries:
946
            yield path, 'V', entry.kind, entry.file_id, entry
2255.2.84 by John Arbash Meinel
Remove now-unecessary encode/decode calls for revision ids.
947
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.
948
    def lock_read(self):
949
        """Lock the tree for a set of operations."""
2255.2.79 by Robert Collins
Take out repository locks from Dirstate revision trees, to improve file text access performance.
950
        if not self._locked:
951
            self._repository.lock_read()
2255.2.38 by Robert Collins
Fix WorkingTree4.pull to work.
952
        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.
953
2255.2.65 by John Arbash Meinel
override path2id because it should be optimized anyway
954
    def path2id(self, path):
955
        """Return the id for path in this tree."""
2255.2.78 by Robert Collins
Really finish the prior commit.
956
        # TODO: if there is no inventory, do an optimistic lookup in the
957
        # dirstate by the path; commonly this will work.
958
        return self.inventory.path2id(path)
2255.2.65 by John Arbash Meinel
override path2id because it should be optimized anyway
959
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.
960
    def unlock(self):
961
        """Unlock, freeing any cache memory used during the lock."""
962
        # outside of a lock, the inventory is suspect: release it.
2255.2.38 by Robert Collins
Fix WorkingTree4.pull to work.
963
        self._locked -=1
964
        if not self._locked:
965
            self._inventory = None
966
            self._locked = False
2255.2.79 by Robert Collins
Take out repository locks from Dirstate revision trees, to improve file text access performance.
967
            self._repository.unlock()
2255.2.69 by John Arbash Meinel
Implement annotate_iter, get_revision_id, and walkdirs so that all tree_implementations now pass
968
969
    def walkdirs(self, prefix=""):
970
        # TODO: jam 20070215 This is the cheap way by cheating and using the
971
        #       RevisionTree implementation.
972
        #       This should be cleaned up to use the much faster Dirstate code
2255.2.70 by John Arbash Meinel
Minor improvements to DirStateRevisionTree.walkdirs()
973
        #       This is a little tricky, though, because the dirstate is
974
        #       indexed by current path, not by parent path.
975
        #       So for now, we just build up the parent inventory, and extract
976
        #       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
977
        _directory = 'directory'
2255.2.70 by John Arbash Meinel
Minor improvements to DirStateRevisionTree.walkdirs()
978
        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
979
        top_id = inv.path2id(prefix)
980
        if top_id is None:
981
            pending = []
982
        else:
2255.2.70 by John Arbash Meinel
Minor improvements to DirStateRevisionTree.walkdirs()
983
            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
984
        while pending:
985
            dirblock = []
2255.2.70 by John Arbash Meinel
Minor improvements to DirStateRevisionTree.walkdirs()
986
            relpath, file_id = pending.pop()
987
            # 0 - relpath, 1- file-id
988
            if relpath:
989
                relroot = relpath + '/'
2255.2.69 by John Arbash Meinel
Implement annotate_iter, get_revision_id, and walkdirs so that all tree_implementations now pass
990
            else:
991
                relroot = ""
992
            # FIXME: stash the node in pending
2255.2.70 by John Arbash Meinel
Minor improvements to DirStateRevisionTree.walkdirs()
993
            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
994
            for name, child in entry.sorted_children():
995
                toppath = relroot + name
996
                dirblock.append((toppath, name, child.kind, None,
997
                    child.file_id, child.kind
998
                    ))
2255.2.70 by John Arbash Meinel
Minor improvements to DirStateRevisionTree.walkdirs()
999
            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
1000
            # push the user specified dirs from dirblock
1001
            for dir in reversed(dirblock):
1002
                if dir[2] == _directory:
2255.2.70 by John Arbash Meinel
Minor improvements to DirStateRevisionTree.walkdirs()
1003
                    pending.append((dir[0], dir[4]))