/brz/remove-bazaar

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