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