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