~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

  • Committer: Jelmer Vernooij
  • Date: 2009-01-28 18:42:55 UTC
  • mto: This revision was merged to the branch mainline in revision 3968.
  • Revision ID: jelmer@samba.org-20090128184255-bdmklkvm83ltk191
Update NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
43
43
    bzrdir,
44
44
    cache_utf8,
45
45
    conflicts as _mod_conflicts,
 
46
    debug,
46
47
    delta,
47
48
    dirstate,
48
49
    errors,
49
50
    generate_ids,
50
51
    globbing,
51
 
    hashcache,
52
52
    ignores,
53
53
    merge,
54
54
    osutils,
 
55
    revision as _mod_revision,
55
56
    revisiontree,
56
57
    textui,
 
58
    trace,
57
59
    transform,
58
60
    urlutils,
59
61
    xml5,
67
69
from bzrlib import symbol_versioning
68
70
from bzrlib.decorators import needs_read_lock, needs_write_lock
69
71
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, entry_factory
70
 
from bzrlib.lockable_files import LockableFiles, TransportLock
71
 
from bzrlib.lockdir import LockDir
72
72
import bzrlib.mutabletree
73
73
from bzrlib.mutabletree import needs_tree_write_lock
74
74
from bzrlib.osutils import (
75
 
    compact_date,
76
75
    file_kind,
77
76
    isdir,
78
77
    normpath,
81
80
    realpath,
82
81
    safe_unicode,
83
82
    splitpath,
84
 
    supports_executable,
85
83
    )
86
84
from bzrlib.trace import mutter, note
87
85
from bzrlib.transport.local import LocalTransport
93
91
        deprecated_method,
94
92
        deprecated_function,
95
93
        DEPRECATED_PARAMETER,
96
 
        zero_eight,
97
 
        zero_eleven,
98
 
        zero_thirteen,
99
94
        )
100
95
from bzrlib.tree import Tree
101
96
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
102
97
 
103
98
 
104
 
class WorkingTree4(WorkingTree3):
105
 
    """This is the Format 4 working tree.
106
 
 
107
 
    This differs from WorkingTree3 by:
108
 
     - having a consolidated internal dirstate.
109
 
     - not having a regular inventory attribute.
110
 
 
111
 
    This is new in bzr TODO FIXME SETMEBEFORE MERGE.
112
 
    """
113
 
 
 
99
class DirStateWorkingTree(WorkingTree3):
114
100
    def __init__(self, basedir,
115
101
                 branch,
116
102
                 _control_files=None,
125
111
        """
126
112
        self._format = _format
127
113
        self.bzrdir = _bzrdir
128
 
        from bzrlib.hashcache import HashCache
129
 
        from bzrlib.trace import note, mutter
130
 
        assert isinstance(basedir, basestring), \
131
 
            "base directory %r is not a string" % basedir
132
114
        basedir = safe_unicode(basedir)
133
115
        mutter("opening working tree %r", basedir)
134
116
        self._branch = branch
135
 
        assert isinstance(self.branch, bzrlib.branch.Branch), \
136
 
            "branch %r is not a Branch" % self.branch
137
117
        self.basedir = realpath(basedir)
138
118
        # if branch is at our basedir and is a format 6 or less
139
119
        # assume all other formats have their own control files.
140
 
        assert isinstance(_control_files, LockableFiles), \
141
 
            "_control_files must be a LockableFiles, not %r" % _control_files
142
120
        self._control_files = _control_files
143
 
        # update the whole cache up front and write to disk if anything changed;
144
 
        # in the future we might want to do this more selectively
145
 
        # two possible ways offer themselves : in self._unlock, write the cache
146
 
        # if needed, or, when the cache sees a change, append it to the hash
147
 
        # cache file, and have the parser take the most recent entry for a
148
 
        # given path only.
149
 
        cache_filename = self.bzrdir.get_workingtree_transport(None).local_abspath('stat-cache')
150
 
        hc = self._hashcache = HashCache(basedir, cache_filename, self._control_files._file_mode)
151
 
        hc.read()
152
 
        # is this scan needed ? it makes things kinda slow.
153
 
        #hc.scan()
154
 
 
155
 
        if hc.needs_write:
156
 
            mutter("write hc")
157
 
            hc.write()
158
 
 
 
121
        self._transport = self._control_files._transport
159
122
        self._dirty = None
160
123
        #-------------
161
124
        # during a read or write lock these objects are set, and are
163
126
        self._dirstate = None
164
127
        self._inventory = None
165
128
        #-------------
 
129
        self._setup_directory_is_tree_reference()
 
130
        self._detect_case_handling()
 
131
        self._rules_searcher = None
 
132
        #--- allow tests to select the dirstate iter_changes implementation
 
133
        self._iter_changes = dirstate._process_entry
166
134
 
167
135
    @needs_tree_write_lock
168
136
    def _add(self, files, ids, kinds):
170
138
        state = self.current_dirstate()
171
139
        for f, file_id, kind in zip(files, ids, kinds):
172
140
            f = f.strip('/')
173
 
            assert '//' not in f
174
 
            assert '..' not in f
 
141
            if self.path2id(f):
 
142
                # special case tree root handling.
 
143
                if f == '' and self.path2id(f) == ROOT_ID:
 
144
                    state.set_path_id('', generate_ids.gen_file_id(f))
 
145
                continue
175
146
            if file_id is None:
176
147
                file_id = generate_ids.gen_file_id(f)
177
148
            # deliberately add the file with no cached stat or sha1
178
149
            # - on the first access it will be gathered, and we can
179
150
            # always change this once tests are all passing.
180
151
            state.add(f, file_id, kind, None, '')
 
152
        self._make_dirty(reset_inventory=True)
 
153
 
 
154
    def _make_dirty(self, reset_inventory):
 
155
        """Make the tree state dirty.
 
156
 
 
157
        :param reset_inventory: True if the cached inventory should be removed
 
158
            (presuming there is one).
 
159
        """
181
160
        self._dirty = True
 
161
        if reset_inventory and self._inventory is not None:
 
162
            self._inventory = None
 
163
 
 
164
    @needs_tree_write_lock
 
165
    def add_reference(self, sub_tree):
 
166
        # use standard implementation, which calls back to self._add
 
167
        # 
 
168
        # So we don't store the reference_revision in the working dirstate,
 
169
        # it's just recorded at the moment of commit. 
 
170
        self._add_reference(sub_tree)
182
171
 
183
172
    def break_lock(self):
184
173
        """Break a lock if one is present from another instance.
217
206
        self._control_files.break_lock()
218
207
        self.branch.break_lock()
219
208
 
 
209
    def _comparison_data(self, entry, path):
 
210
        kind, executable, stat_value = \
 
211
            WorkingTree3._comparison_data(self, entry, path)
 
212
        # it looks like a plain directory, but it's really a reference -- see
 
213
        # also kind()
 
214
        if (self._repo_supports_tree_reference and
 
215
            kind == 'directory' and
 
216
            self._directory_is_tree_reference(path)):
 
217
            kind = 'tree-reference'
 
218
        return kind, executable, stat_value
 
219
 
 
220
    @needs_write_lock
 
221
    def commit(self, message=None, revprops=None, *args, **kwargs):
 
222
        # mark the tree as dirty post commit - commit
 
223
        # can change the current versioned list by doing deletes.
 
224
        result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
 
225
        self._make_dirty(reset_inventory=True)
 
226
        return result
 
227
 
220
228
    def current_dirstate(self):
221
 
        """Return the current dirstate object. 
 
229
        """Return the current dirstate object.
222
230
 
223
231
        This is not part of the tree interface and only exposed for ease of
224
232
        testing.
225
233
 
226
 
        :raises errors.NotWriteLocked: when not in a lock. 
 
234
        :raises errors.NotWriteLocked: when not in a lock.
227
235
        """
228
 
        if not self._control_files._lock_count:
229
 
            raise errors.ObjectNotLocked(self)
 
236
        self._must_be_locked()
230
237
        return self._current_dirstate()
231
238
 
232
239
    def _current_dirstate(self):
233
240
        """Internal function that does not check lock status.
234
 
        
 
241
 
235
242
        This is needed for break_lock which also needs the dirstate.
236
243
        """
237
244
        if self._dirstate is not None:
242
249
        return self._dirstate
243
250
 
244
251
    def filter_unversioned_files(self, paths):
245
 
        """Filter out paths that are not versioned.
 
252
        """Filter out paths that are versioned.
246
253
 
247
254
        :return: set of paths.
248
255
        """
257
264
            dirname, basename = os.path.split(path.encode('utf8'))
258
265
            _, _, _, path_is_versioned = state._get_block_entry_index(
259
266
                dirname, basename, 0)
260
 
            if path_is_versioned:
 
267
            if not path_is_versioned:
261
268
                result.add(path)
262
269
        return result
263
270
 
269
276
        self._inventory = None
270
277
        self._dirty = False
271
278
 
 
279
    @needs_tree_write_lock
 
280
    def _gather_kinds(self, files, kinds):
 
281
        """See MutableTree._gather_kinds."""
 
282
        for pos, f in enumerate(files):
 
283
            if kinds[pos] is None:
 
284
                kinds[pos] = self._kind(f)
 
285
 
272
286
    def _generate_inventory(self):
273
287
        """Create and set self.inventory from the dirstate object.
274
288
        
281
295
        state._read_dirblocks_if_needed()
282
296
        root_key, current_entry = self._get_entry(path='')
283
297
        current_id = root_key[2]
284
 
        assert current_entry[0][0] == 'd' # directory
 
298
        if not (current_entry[0][0] == 'd'): # directory
 
299
            raise AssertionError(current_entry)
285
300
        inv = Inventory(root_id=current_id)
286
301
        # Turn some things into local variables
287
302
        minikind_to_kind = dirstate.DirState._minikind_to_kind
310
325
                inv_entry = factory[kind](file_id, name_unicode,
311
326
                                          parent_ie.file_id)
312
327
                if kind == 'file':
 
328
                    # This is only needed on win32, where this is the only way
 
329
                    # we know the executable bit.
 
330
                    inv_entry.executable = executable
313
331
                    # not strictly needed: working tree
314
 
                    #entry.executable = executable
315
 
                    #entry.text_size = size
316
 
                    #entry.text_sha1 = sha1
317
 
                    pass
 
332
                    #inv_entry.text_size = size
 
333
                    #inv_entry.text_sha1 = sha1
318
334
                elif kind == 'directory':
319
335
                    # add this entry to the parent map.
320
336
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
 
337
                elif kind == 'tree-reference':
 
338
                    if not self._repo_supports_tree_reference:
 
339
                        raise AssertionError(
 
340
                            "repository of %r "
 
341
                            "doesn't support tree references "
 
342
                            "required by entry %r"
 
343
                            % (self, name))
 
344
                    inv_entry.reference_revision = link_or_sha1 or None
 
345
                elif kind != 'symlink':
 
346
                    raise AssertionError("unknown kind %r" % kind)
321
347
                # These checks cost us around 40ms on a 55k entry tree
322
 
                assert file_id not in inv_byid, ('file_id %s already in'
323
 
                    ' inventory as %s' % (file_id, inv_byid[file_id]))
324
 
                assert name_unicode not in parent_ie.children
 
348
                if file_id in inv_byid:
 
349
                    raise AssertionError('file_id %s already in'
 
350
                        ' inventory as %s' % (file_id, inv_byid[file_id]))
 
351
                if name_unicode in parent_ie.children:
 
352
                    raise AssertionError('name %r already in parent'
 
353
                        % (name_unicode,))
325
354
                inv_byid[file_id] = inv_entry
326
355
                parent_ie.children[name_unicode] = inv_entry
327
356
        self._inventory = inv
346
375
 
347
376
    def get_file_sha1(self, file_id, path=None, stat_value=None):
348
377
        # check file id is valid unconditionally.
349
 
        key, details = self._get_entry(file_id=file_id, path=path)
350
 
        assert key is not None, 'what error should this raise'
351
 
        # TODO:
352
 
        # if row stat is valid, use cached sha1, else, get a new sha1.
 
378
        entry = self._get_entry(file_id=file_id, path=path)
 
379
        if entry[0] is None:
 
380
            raise errors.NoSuchId(self, file_id)
353
381
        if path is None:
354
 
            path = pathjoin(key[0], key[1]).decode('utf8')
355
 
        return self._hashcache.get_sha1(path, stat_value)
 
382
            path = pathjoin(entry[0][0], entry[0][1]).decode('utf8')
 
383
 
 
384
        file_abspath = self.abspath(path)
 
385
        state = self.current_dirstate()
 
386
        if stat_value is None:
 
387
            try:
 
388
                stat_value = os.lstat(file_abspath)
 
389
            except OSError, e:
 
390
                if e.errno == errno.ENOENT:
 
391
                    return None
 
392
                else:
 
393
                    raise
 
394
        link_or_sha1 = dirstate.update_entry(state, entry, file_abspath,
 
395
            stat_value=stat_value)
 
396
        if entry[1][0][0] == 'f':
 
397
            if link_or_sha1 is None:
 
398
                file_obj, statvalue = self.get_file_with_stat(file_id, path)
 
399
                try:
 
400
                    sha1 = osutils.sha_file(file_obj)
 
401
                finally:
 
402
                    file_obj.close()
 
403
                self._observed_sha1(file_id, path, (sha1, statvalue))
 
404
                return sha1
 
405
            else:
 
406
                return link_or_sha1
 
407
        return None
356
408
 
357
409
    def _get_inventory(self):
358
410
        """Get the inventory for the tree. This is only valid within a lock."""
 
411
        if 'evil' in debug.debug_flags:
 
412
            trace.mutter_callsite(2,
 
413
                "accessing .inventory forces a size of tree translation.")
359
414
        if self._inventory is not None:
360
415
            return self._inventory
 
416
        self._must_be_locked()
361
417
        self._generate_inventory()
362
418
        return self._inventory
363
419
 
372
428
        """
373
429
        return self.current_dirstate().get_parent_ids()
374
430
 
 
431
    def get_reference_revision(self, file_id, path=None):
 
432
        # referenced tree's revision is whatever's currently there
 
433
        return self.get_nested_tree(file_id, path).last_revision()
 
434
 
 
435
    def get_nested_tree(self, file_id, path=None):
 
436
        if path is None:
 
437
            path = self.id2path(file_id)
 
438
        # else: check file_id is at path?
 
439
        return WorkingTree.open(self.abspath(path))
 
440
 
375
441
    @needs_read_lock
376
442
    def get_root_id(self):
377
443
        """Return the id of this trees root"""
379
445
 
380
446
    def has_id(self, file_id):
381
447
        state = self.current_dirstate()
382
 
        file_id = osutils.safe_file_id(file_id)
383
448
        row, parents = self._get_entry(file_id=file_id)
384
449
        if row is None:
385
450
            return False
388
453
 
389
454
    @needs_read_lock
390
455
    def id2path(self, file_id):
391
 
        file_id = osutils.safe_file_id(file_id)
 
456
        "Convert a file-id to a path."
392
457
        state = self.current_dirstate()
393
458
        entry = self._get_entry(file_id=file_id)
394
459
        if entry == (None, None):
395
 
            return None
 
460
            raise errors.NoSuchId(tree=self, file_id=file_id)
396
461
        path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
397
462
        return path_utf8.decode('utf8')
398
463
 
 
464
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
 
465
        entry = self._get_entry(path=path)
 
466
        if entry == (None, None):
 
467
            return False # Missing entries are not executable
 
468
        return entry[1][0][3] # Executable?
 
469
 
 
470
    if not osutils.supports_executable():
 
471
        def is_executable(self, file_id, path=None):
 
472
            """Test if a file is executable or not.
 
473
 
 
474
            Note: The caller is expected to take a read-lock before calling this.
 
475
            """
 
476
            entry = self._get_entry(file_id=file_id, path=path)
 
477
            if entry == (None, None):
 
478
                return False
 
479
            return entry[1][0][3]
 
480
 
 
481
        _is_executable_from_path_and_stat = \
 
482
            _is_executable_from_path_and_stat_from_basis
 
483
    else:
 
484
        def is_executable(self, file_id, path=None):
 
485
            """Test if a file is executable or not.
 
486
 
 
487
            Note: The caller is expected to take a read-lock before calling this.
 
488
            """
 
489
            self._must_be_locked()
 
490
            if not path:
 
491
                path = self.id2path(file_id)
 
492
            mode = os.lstat(self.abspath(path)).st_mode
 
493
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
 
494
 
 
495
    def all_file_ids(self):
 
496
        """See Tree.iter_all_file_ids"""
 
497
        self._must_be_locked()
 
498
        result = set()
 
499
        for key, tree_details in self.current_dirstate()._iter_entries():
 
500
            if tree_details[0][0] in ('a', 'r'): # relocated
 
501
                continue
 
502
            result.add(key[2])
 
503
        return result
 
504
 
399
505
    @needs_read_lock
400
506
    def __iter__(self):
401
507
        """Iterate through file_ids for this tree.
413
519
                result.append(key[2])
414
520
        return iter(result)
415
521
 
 
522
    def iter_references(self):
 
523
        if not self._repo_supports_tree_reference:
 
524
            # When the repo doesn't support references, we will have nothing to
 
525
            # return
 
526
            return
 
527
        for key, tree_details in self.current_dirstate()._iter_entries():
 
528
            if tree_details[0][0] in ('a', 'r'): # absent, relocated
 
529
                # not relevant to the working tree
 
530
                continue
 
531
            if not key[1]:
 
532
                # the root is not a reference.
 
533
                continue
 
534
            relpath = pathjoin(key[0].decode('utf8'), key[1].decode('utf8'))
 
535
            try:
 
536
                if self._kind(relpath) == 'tree-reference':
 
537
                    yield relpath, key[2]
 
538
            except errors.NoSuchFile:
 
539
                # path is missing on disk.
 
540
                continue
 
541
 
 
542
    def _observed_sha1(self, file_id, path, (sha1, statvalue)):
 
543
        """See MutableTree._observed_sha1."""
 
544
        state = self.current_dirstate()
 
545
        entry = self._get_entry(file_id=file_id, path=path)
 
546
        state._observed_sha1(entry, sha1, statvalue)
 
547
 
 
548
    def kind(self, file_id):
 
549
        """Return the kind of a file.
 
550
 
 
551
        This is always the actual kind that's on disk, regardless of what it
 
552
        was added as.
 
553
 
 
554
        Note: The caller is expected to take a read-lock before calling this.
 
555
        """
 
556
        relpath = self.id2path(file_id)
 
557
        if relpath is None:
 
558
            raise AssertionError(
 
559
                "path for id {%s} is None!" % file_id)
 
560
        return self._kind(relpath)
 
561
 
 
562
    def _kind(self, relpath):
 
563
        abspath = self.abspath(relpath)
 
564
        kind = file_kind(abspath)
 
565
        if (self._repo_supports_tree_reference and
 
566
            kind == 'directory' and
 
567
            self._directory_is_tree_reference(relpath)):
 
568
            kind = 'tree-reference'
 
569
        return kind
 
570
 
416
571
    @needs_read_lock
417
572
    def _last_revision(self):
418
573
        """See Mutable.last_revision."""
420
575
        if parent_ids:
421
576
            return parent_ids[0]
422
577
        else:
423
 
            return None
 
578
            return _mod_revision.NULL_REVISION
424
579
 
425
580
    def lock_read(self):
426
 
        super(WorkingTree4, self).lock_read()
427
 
        if self._dirstate is None:
428
 
            self.current_dirstate()
429
 
            self._dirstate.lock_read()
 
581
        """See Branch.lock_read, and WorkingTree.unlock."""
 
582
        self.branch.lock_read()
 
583
        try:
 
584
            self._control_files.lock_read()
 
585
            try:
 
586
                state = self.current_dirstate()
 
587
                if not state._lock_token:
 
588
                    state.lock_read()
 
589
                # set our support for tree references from the repository in
 
590
                # use.
 
591
                self._repo_supports_tree_reference = getattr(
 
592
                    self.branch.repository._format, "supports_tree_reference",
 
593
                    False)
 
594
            except:
 
595
                self._control_files.unlock()
 
596
                raise
 
597
        except:
 
598
            self.branch.unlock()
 
599
            raise
 
600
 
 
601
    def _lock_self_write(self):
 
602
        """This should be called after the branch is locked."""
 
603
        try:
 
604
            self._control_files.lock_write()
 
605
            try:
 
606
                state = self.current_dirstate()
 
607
                if not state._lock_token:
 
608
                    state.lock_write()
 
609
                # set our support for tree references from the repository in
 
610
                # use.
 
611
                self._repo_supports_tree_reference = getattr(
 
612
                    self.branch.repository._format, "supports_tree_reference",
 
613
                    False)
 
614
            except:
 
615
                self._control_files.unlock()
 
616
                raise
 
617
        except:
 
618
            self.branch.unlock()
 
619
            raise
430
620
 
431
621
    def lock_tree_write(self):
432
 
        super(WorkingTree4, self).lock_tree_write()
433
 
        if self._dirstate is None:
434
 
            self.current_dirstate()
435
 
            self._dirstate.lock_write()
 
622
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
 
623
        self.branch.lock_read()
 
624
        self._lock_self_write()
436
625
 
437
626
    def lock_write(self):
438
 
        super(WorkingTree4, self).lock_write()
439
 
        if self._dirstate is None:
440
 
            self.current_dirstate()
441
 
            self._dirstate.lock_write()
 
627
        """See MutableTree.lock_write, and WorkingTree.unlock."""
 
628
        self.branch.lock_write()
 
629
        self._lock_self_write()
442
630
 
443
631
    @needs_tree_write_lock
444
632
    def move(self, from_paths, to_dir, after=False):
446
634
        result = []
447
635
        if not from_paths:
448
636
            return result
449
 
 
450
637
        state = self.current_dirstate()
451
 
 
452
 
        assert not isinstance(from_paths, basestring)
 
638
        if isinstance(from_paths, basestring):
 
639
            raise ValueError()
453
640
        to_dir_utf8 = to_dir.encode('utf8')
454
641
        to_entry_dirname, to_basename = os.path.split(to_dir_utf8)
455
642
        id_index = state._get_id_index()
459
646
            state._get_block_entry_index(to_entry_dirname, to_basename, 0)
460
647
        if not entry_present:
461
648
            raise errors.BzrMoveFailedError('', to_dir,
462
 
                errors.NotInWorkingDirectory(to_dir))
 
649
                errors.NotVersionedError(to_dir))
463
650
        to_entry = state._dirblocks[to_entry_block_index][1][to_entry_entry_index]
464
651
        # get a handle on the block itself.
465
652
        to_block_index = state._ensure_block(
477
664
        if self._inventory is not None:
478
665
            update_inventory = True
479
666
            inv = self.inventory
 
667
            to_dir_id = to_entry[0][2]
480
668
            to_dir_ie = inv[to_dir_id]
481
 
            to_dir_id = to_entry[0][2]
482
669
        else:
483
670
            update_inventory = False
484
671
 
507
694
            new_entry = to_block[1][added_entry_index]
508
695
            rollbacks.append(lambda:state._make_absent(new_entry))
509
696
 
510
 
        # create rename entries and tuples
511
697
        for from_rel in from_paths:
512
698
            # from_rel is 'pathinroot/foo/bar'
513
699
            from_rel_utf8 = from_rel.encode('utf8')
549
735
                if from_missing: # implicitly just update our path mapping
550
736
                    move_file = False
551
737
                elif not after:
552
 
                    raise errors.RenameFailedFilesExist(from_rel, to_rel,
553
 
                        extra="(Use --after to update the Bazaar id)")
 
738
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
554
739
 
555
740
            rollbacks = []
556
741
            def rollback_rename():
557
742
                """A single rename has failed, roll it back."""
 
743
                # roll back everything, even if we encounter trouble doing one
 
744
                # of them.
 
745
                #
 
746
                # TODO: at least log the other exceptions rather than just
 
747
                # losing them mbp 20070307
558
748
                exc_info = None
559
749
                for rollback in reversed(rollbacks):
560
750
                    try:
561
751
                        rollback()
562
752
                    except Exception, e:
563
 
                        import pdb;pdb.set_trace()
564
753
                        exc_info = sys.exc_info()
565
754
                if exc_info:
566
755
                    raise exc_info[0], exc_info[1], exc_info[2]
607
796
 
608
797
                if minikind == 'd':
609
798
                    def update_dirblock(from_dir, to_key, to_dir_utf8):
610
 
                        """all entries in this block need updating.
611
 
 
612
 
                        TODO: This is pretty ugly, and doesn't support
613
 
                        reverting, but it works.
614
 
                        """
615
 
                        assert from_dir != '', "renaming root not supported"
 
799
                        """Recursively update all entries in this dirblock."""
 
800
                        if from_dir == '':
 
801
                            raise AssertionError("renaming root not supported")
616
802
                        from_key = (from_dir, '')
617
803
                        from_block_idx, present = \
618
804
                            state._find_block_index_from_key(from_key)
628
814
                        to_block_index = state._ensure_block(
629
815
                            to_block_index, to_entry_index, to_dir_utf8)
630
816
                        to_block = state._dirblocks[to_block_index]
631
 
                        for entry in from_block[1]:
632
 
                            assert entry[0][0] == from_dir
 
817
 
 
818
                        # Grab a copy since move_one may update the list.
 
819
                        for entry in from_block[1][:]:
 
820
                            if not (entry[0][0] == from_dir):
 
821
                                raise AssertionError()
633
822
                            cur_details = entry[1][0]
634
823
                            to_key = (to_dir_utf8, entry[0][1], entry[0][2])
635
824
                            from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
636
825
                            to_path_utf8 = osutils.pathjoin(to_dir_utf8, entry[0][1])
637
826
                            minikind = cur_details[0]
 
827
                            if minikind in 'ar':
 
828
                                # Deleted children of a renamed directory
 
829
                                # Do not need to be updated.
 
830
                                # Children that have been renamed out of this
 
831
                                # directory should also not be updated
 
832
                                continue
638
833
                            move_one(entry, from_path_utf8=from_path_utf8,
639
834
                                     minikind=minikind,
640
835
                                     executable=cur_details[3],
643
838
                                     size=cur_details[2],
644
839
                                     to_block=to_block,
645
840
                                     to_key=to_key,
646
 
                                     to_path_utf8=to_rel_utf8)
 
841
                                     to_path_utf8=to_path_utf8)
647
842
                            if minikind == 'd':
648
843
                                # We need to move all the children of this
649
844
                                # entry
655
850
                raise
656
851
            result.append((from_rel, to_rel))
657
852
            state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
658
 
            self._dirty = True
 
853
            self._make_dirty(reset_inventory=False)
659
854
 
660
855
        return result
661
856
 
 
857
    def _must_be_locked(self):
 
858
        if not self._control_files._lock_count:
 
859
            raise errors.ObjectNotLocked(self)
 
860
 
662
861
    def _new_tree(self):
663
862
        """Initialize the state in this tree to be a new tree."""
664
863
        self._dirty = True
684
883
        for tree in trees:
685
884
            if not (isinstance(tree, DirStateRevisionTree) and tree._revision_id in
686
885
                parents):
687
 
                return super(WorkingTree4, self).paths2ids(paths, trees, require_versioned)
 
886
                return super(DirStateWorkingTree, self).paths2ids(paths,
 
887
                    trees, require_versioned)
688
888
        search_indexes = [0] + [1 + parents.index(tree._revision_id) for tree in trees]
689
889
        # -- make all paths utf8 --
690
890
        paths_utf8 = set()
750
950
            if not all_versioned:
751
951
                raise errors.PathsNotVersionedError(paths)
752
952
        # -- remove redundancy in supplied paths to prevent over-scanning --
753
 
        search_paths = set()
754
 
        for path in paths:
755
 
            other_paths = paths.difference(set([path]))
756
 
            if not osutils.is_inside_any(other_paths, path):
757
 
                # this is a top level path, we must check it.
758
 
                search_paths.add(path)
 
953
        search_paths = osutils.minimum_path_selection(paths)
759
954
        # sketch: 
760
955
        # for all search_indexs in each path at or under each element of
761
956
        # search_paths, if the detail is relocated: add the id, and add the
829
1024
 
830
1025
        WorkingTree4 supplies revision_trees for any basis tree.
831
1026
        """
832
 
        revision_id = osutils.safe_revision_id(revision_id)
833
1027
        dirstate = self.current_dirstate()
834
1028
        parent_ids = dirstate.get_parent_ids()
835
1029
        if revision_id not in parent_ids:
842
1036
    @needs_tree_write_lock
843
1037
    def set_last_revision(self, new_revision):
844
1038
        """Change the last revision in the working tree."""
845
 
        new_revision = osutils.safe_revision_id(new_revision)
846
1039
        parents = self.get_parent_ids()
847
1040
        if new_revision in (NULL_REVISION, None):
848
 
            assert len(parents) < 2, (
849
 
                "setting the last parent to none with a pending merge is "
850
 
                "unsupported.")
 
1041
            if len(parents) >= 2:
 
1042
                raise AssertionError(
 
1043
                    "setting the last parent to none with a pending merge is "
 
1044
                    "unsupported.")
851
1045
            self.set_parent_ids([])
852
1046
        else:
853
1047
            self.set_parent_ids([new_revision] + parents[1:],
866
1060
        :param revision_ids: The revision_ids to set as the parent ids of this
867
1061
            working tree. Any of these may be ghosts.
868
1062
        """
869
 
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
870
1063
        trees = []
871
1064
        for revision_id in revision_ids:
872
1065
            try:
895
1088
                raise errors.GhostRevisionUnusableHere(parents_list[0][0])
896
1089
        real_trees = []
897
1090
        ghosts = []
 
1091
 
 
1092
        parent_ids = [rev_id for rev_id, tree in parents_list]
 
1093
        graph = self.branch.repository.get_graph()
 
1094
        heads = graph.heads(parent_ids)
 
1095
        accepted_revisions = set()
 
1096
 
898
1097
        # convert absent trees to the null tree, which we convert back to
899
1098
        # missing on access.
900
1099
        for rev_id, tree in parents_list:
901
 
            rev_id = osutils.safe_revision_id(rev_id)
 
1100
            if len(accepted_revisions) > 0:
 
1101
                # we always accept the first tree
 
1102
                if rev_id in accepted_revisions or rev_id not in heads:
 
1103
                    # We have already included either this tree, or its
 
1104
                    # descendent, so we skip it.
 
1105
                    continue
 
1106
            _mod_revision.check_not_reserved_id(rev_id)
902
1107
            if tree is not None:
903
1108
                real_trees.append((rev_id, tree))
904
1109
            else:
905
1110
                real_trees.append((rev_id,
906
 
                    self.branch.repository.revision_tree(None)))
 
1111
                    self.branch.repository.revision_tree(
 
1112
                        _mod_revision.NULL_REVISION)))
907
1113
                ghosts.append(rev_id)
 
1114
            accepted_revisions.add(rev_id)
908
1115
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
909
 
        self._dirty = True
 
1116
        self._make_dirty(reset_inventory=False)
910
1117
 
911
1118
    def _set_root_id(self, file_id):
912
1119
        """See WorkingTree.set_root_id."""
913
1120
        state = self.current_dirstate()
914
1121
        state.set_path_id('', file_id)
915
 
        self._dirty = state._dirblock_state == dirstate.DirState.IN_MEMORY_MODIFIED
 
1122
        if state._dirblock_state == dirstate.DirState.IN_MEMORY_MODIFIED:
 
1123
            self._make_dirty(reset_inventory=True)
 
1124
 
 
1125
    def _sha_from_stat(self, path, stat_result):
 
1126
        """Get a sha digest from the tree's stat cache.
 
1127
 
 
1128
        The default implementation assumes no stat cache is present.
 
1129
 
 
1130
        :param path: The path.
 
1131
        :param stat_result: The stat result being looked up.
 
1132
        """
 
1133
        return self.current_dirstate().sha1_from_stat(path, stat_result)
 
1134
 
 
1135
    @needs_read_lock
 
1136
    def supports_tree_reference(self):
 
1137
        return self._repo_supports_tree_reference
916
1138
 
917
1139
    def unlock(self):
918
1140
        """Unlock in format 4 trees needs to write the entire dirstate."""
 
1141
        # do non-implementation specific cleanup
 
1142
        self._cleanup()
 
1143
 
919
1144
        if self._control_files._lock_count == 1:
920
 
            self._write_hashcache_if_dirty()
921
1145
            # eventually we should do signature checking during read locks for
922
1146
            # dirstate updates.
923
1147
            if self._control_files._lock_mode == 'w':
924
1148
                if self._dirty:
925
1149
                    self.flush()
926
1150
            if self._dirstate is not None:
 
1151
                # This is a no-op if there are no modifications.
 
1152
                self._dirstate.save()
927
1153
                self._dirstate.unlock()
 
1154
            # TODO: jam 20070301 We shouldn't have to wipe the dirstate at this
 
1155
            #       point. Instead, it could check if the header has been
 
1156
            #       modified when it is locked, and if not, it can hang on to
 
1157
            #       the data it has in memory.
928
1158
            self._dirstate = None
929
1159
            self._inventory = None
930
1160
        # reverse order of locking.
947
1177
            return
948
1178
        state = self.current_dirstate()
949
1179
        state._read_dirblocks_if_needed()
950
 
        ids_to_unversion = set()
951
 
        for file_id in file_ids:
952
 
            ids_to_unversion.add(osutils.safe_file_id(file_id))
 
1180
        ids_to_unversion = set(file_ids)
953
1181
        paths_to_unversion = set()
954
1182
        # sketch:
955
1183
        # check if the root is to be unversioned, if so, assert for now.
983
1211
                entry_index = 0
984
1212
                while entry_index < len(block[1]):
985
1213
                    # Mark this file id as having been removed
986
 
                    ids_to_unversion.discard(block[1][entry_index][0][2])
987
 
                    if not state._make_absent(block[1][entry_index]):
 
1214
                    entry = block[1][entry_index]
 
1215
                    ids_to_unversion.discard(entry[0][2])
 
1216
                    if (entry[1][0][0] in 'ar' # don't remove absent or renamed
 
1217
                                               # entries
 
1218
                        or not state._make_absent(entry)):
988
1219
                        entry_index += 1
989
1220
                # go to the next block. (At the moment we dont delete empty
990
1221
                # dirblocks)
1008
1239
            block_index += 1
1009
1240
        if ids_to_unversion:
1010
1241
            raise errors.NoSuchId(self, iter(ids_to_unversion).next())
1011
 
        self._dirty = True
 
1242
        self._make_dirty(reset_inventory=False)
1012
1243
        # have to change the legacy inventory too.
1013
1244
        if self._inventory is not None:
1014
1245
            for file_id in file_ids:
1015
1246
                self._inventory.remove_recursive_id(file_id)
1016
1247
 
1017
1248
    @needs_tree_write_lock
 
1249
    def rename_one(self, from_rel, to_rel, after=False):
 
1250
        """See WorkingTree.rename_one"""
 
1251
        self.flush()
 
1252
        WorkingTree.rename_one(self, from_rel, to_rel, after)
 
1253
 
 
1254
    @needs_tree_write_lock
 
1255
    def apply_inventory_delta(self, changes):
 
1256
        """See MutableTree.apply_inventory_delta"""
 
1257
        state = self.current_dirstate()
 
1258
        state.update_by_delta(changes)
 
1259
        self._make_dirty(reset_inventory=True)
 
1260
 
 
1261
    def update_basis_by_delta(self, new_revid, delta):
 
1262
        """See MutableTree.update_basis_by_delta."""
 
1263
        if self.last_revision() == new_revid:
 
1264
            raise AssertionError()
 
1265
        self.current_dirstate().update_basis_by_delta(delta, new_revid)
 
1266
 
 
1267
    @needs_read_lock
 
1268
    def _validate(self):
 
1269
        self._dirstate._validate()
 
1270
 
 
1271
    @needs_tree_write_lock
1018
1272
    def _write_inventory(self, inv):
1019
1273
        """Write inventory as the current inventory."""
1020
 
        assert not self._dirty, "attempting to write an inventory when the dirstate is dirty will cause data loss"
 
1274
        if self._dirty:
 
1275
            raise AssertionError("attempting to write an inventory when the "
 
1276
                "dirstate is dirty will lose pending changes")
1021
1277
        self.current_dirstate().set_state_from_inventory(inv)
1022
 
        self._dirty = True
 
1278
        self._make_dirty(reset_inventory=False)
 
1279
        if self._inventory is not None:
 
1280
            self._inventory = inv
1023
1281
        self.flush()
1024
1282
 
1025
1283
 
1026
 
class WorkingTreeFormat4(WorkingTreeFormat3):
1027
 
    """The first consolidated dirstate working tree format.
1028
 
 
1029
 
    This format:
1030
 
        - exists within a metadir controlling .bzr
1031
 
        - includes an explicit version marker for the workingtree control
1032
 
          files, separate from the BzrDir format
1033
 
        - modifies the hash cache format
1034
 
        - is new in bzr TODO FIXME SETBEFOREMERGE
1035
 
        - uses a LockDir to guard access to it.
1036
 
    """
1037
 
 
1038
 
    def get_format_string(self):
1039
 
        """See WorkingTreeFormat.get_format_string()."""
1040
 
        return "Bazaar Working Tree format 4\n"
1041
 
 
1042
 
    def get_format_description(self):
1043
 
        """See WorkingTreeFormat.get_format_description()."""
1044
 
        return "Working tree format 4"
1045
 
 
1046
 
    def initialize(self, a_bzrdir, revision_id=None):
 
1284
class WorkingTree4(DirStateWorkingTree):
 
1285
    """This is the Format 4 working tree.
 
1286
 
 
1287
    This differs from WorkingTree3 by:
 
1288
     - Having a consolidated internal dirstate, stored in a
 
1289
       randomly-accessible sorted file on disk.
 
1290
     - Not having a regular inventory attribute.  One can be synthesized 
 
1291
       on demand but this is expensive and should be avoided.
 
1292
 
 
1293
    This is new in bzr 0.15.
 
1294
    """
 
1295
 
 
1296
 
 
1297
class WorkingTree5(DirStateWorkingTree):
 
1298
    """This is the Format 5 working tree.
 
1299
 
 
1300
    This differs from WorkingTree4 by:
 
1301
     - Supporting content filtering.
 
1302
     - Supporting a current view that may mask the set of files in a tree
 
1303
       impacted by most user operations.
 
1304
 
 
1305
    This is new in bzr 1.11.
 
1306
    """
 
1307
 
 
1308
 
 
1309
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
 
1310
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
1311
                   accelerator_tree=None, hardlink=False):
1047
1312
        """See WorkingTreeFormat.initialize().
1048
1313
 
1049
 
        revision_id allows creating a working tree at a different
 
1314
        :param revision_id: allows creating a working tree at a different
1050
1315
        revision than the branch is at.
 
1316
        :param accelerator_tree: A tree which can be used for retrieving file
 
1317
            contents more quickly than the revision tree, i.e. a workingtree.
 
1318
            The revision tree will be used for cases where accelerator_tree's
 
1319
            content is different.
 
1320
        :param hardlink: If true, hard-link files from accelerator_tree,
 
1321
            where possible.
 
1322
 
 
1323
        These trees get an initial random root id, if their repository supports
 
1324
        rich root data, TREE_ROOT otherwise.
1051
1325
        """
1052
 
        revision_id = osutils.safe_revision_id(revision_id)
1053
1326
        if not isinstance(a_bzrdir.transport, LocalTransport):
1054
1327
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1055
1328
        transport = a_bzrdir.get_workingtree_transport(self)
1056
1329
        control_files = self._open_control_files(a_bzrdir)
1057
1330
        control_files.create_lock()
1058
1331
        control_files.lock_write()
1059
 
        control_files.put_utf8('format', self.get_format_string())
1060
 
        branch = a_bzrdir.open_branch()
 
1332
        transport.put_bytes('format', self.get_format_string(),
 
1333
            mode=a_bzrdir._get_file_mode())
 
1334
        if from_branch is not None:
 
1335
            branch = from_branch
 
1336
        else:
 
1337
            branch = a_bzrdir.open_branch()
1061
1338
        if revision_id is None:
1062
1339
            revision_id = branch.last_revision()
1063
1340
        local_path = transport.local_abspath('dirstate')
 
1341
        # write out new dirstate (must exist when we create the tree)
1064
1342
        state = dirstate.DirState.initialize(local_path)
1065
1343
        state.unlock()
1066
 
        wt = WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
 
1344
        del state
 
1345
        wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1067
1346
                         branch,
1068
1347
                         _format=self,
1069
1348
                         _bzrdir=a_bzrdir,
1071
1350
        wt._new_tree()
1072
1351
        wt.lock_tree_write()
1073
1352
        try:
1074
 
            #wt.current_dirstate().set_path_id('', NEWROOT)
1075
 
            wt.set_last_revision(revision_id)
1076
 
            wt.flush()
1077
 
            basis = wt.basis_tree()
 
1353
            self._init_custom_control_files(wt)
 
1354
            if revision_id in (None, NULL_REVISION):
 
1355
                if branch.repository.supports_rich_root():
 
1356
                    wt._set_root_id(generate_ids.gen_root_id())
 
1357
                else:
 
1358
                    wt._set_root_id(ROOT_ID)
 
1359
                wt.flush()
 
1360
            basis = None
 
1361
            # frequently, we will get here due to branching.  The accelerator
 
1362
            # tree will be the tree from the branch, so the desired basis
 
1363
            # tree will often be a parent of the accelerator tree.
 
1364
            if accelerator_tree is not None:
 
1365
                try:
 
1366
                    basis = accelerator_tree.revision_tree(revision_id)
 
1367
                except errors.NoSuchRevision:
 
1368
                    pass
 
1369
            if basis is None:
 
1370
                basis = branch.repository.revision_tree(revision_id)
 
1371
            if revision_id == NULL_REVISION:
 
1372
                parents_list = []
 
1373
            else:
 
1374
                parents_list = [(revision_id, basis)]
1078
1375
            basis.lock_read()
1079
 
            transform.build_tree(basis, wt)
1080
 
            basis.unlock()
 
1376
            try:
 
1377
                wt.set_parent_trees(parents_list, allow_leftmost_as_ghost=True)
 
1378
                wt.flush()
 
1379
                # if the basis has a root id we have to use that; otherwise we
 
1380
                # use a new random one
 
1381
                basis_root_id = basis.get_root_id()
 
1382
                if basis_root_id is not None:
 
1383
                    wt._set_root_id(basis_root_id)
 
1384
                    wt.flush()
 
1385
                # delta_from_tree is safe even for DirStateRevisionTrees,
 
1386
                # because wt4.apply_inventory_delta does not mutate the input
 
1387
                # inventory entries.
 
1388
                transform.build_tree(basis, wt, accelerator_tree,
 
1389
                                     hardlink=hardlink, delta_from_tree=True)
 
1390
            finally:
 
1391
                basis.unlock()
1081
1392
        finally:
1082
1393
            control_files.unlock()
1083
1394
            wt.unlock()
1084
1395
        return wt
1085
1396
 
 
1397
    def _init_custom_control_files(self, wt):
 
1398
        """Subclasses with custom control files should override this method.
 
1399
        
 
1400
        The working tree and control files are locked for writing when this
 
1401
        method is called.
 
1402
        
 
1403
        :param wt: the WorkingTree object
 
1404
        """
1086
1405
 
1087
1406
    def _open(self, a_bzrdir, control_files):
1088
1407
        """Open the tree itself.
1090
1409
        :param a_bzrdir: the dir for the tree.
1091
1410
        :param control_files: the control files for the tree.
1092
1411
        """
1093
 
        return WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
 
1412
        return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1094
1413
                           branch=a_bzrdir.open_branch(),
1095
1414
                           _format=self,
1096
1415
                           _bzrdir=a_bzrdir,
1097
1416
                           _control_files=control_files)
1098
1417
 
 
1418
    def __get_matchingbzrdir(self):
 
1419
        # please test against something that will let us do tree references
 
1420
        return bzrdir.format_registry.make_bzrdir(
 
1421
            'dirstate-with-subtree')
 
1422
 
 
1423
    _matchingbzrdir = property(__get_matchingbzrdir)
 
1424
 
 
1425
 
 
1426
class WorkingTreeFormat4(DirStateWorkingTreeFormat):
 
1427
    """The first consolidated dirstate working tree format.
 
1428
 
 
1429
    This format:
 
1430
        - exists within a metadir controlling .bzr
 
1431
        - includes an explicit version marker for the workingtree control
 
1432
          files, separate from the BzrDir format
 
1433
        - modifies the hash cache format
 
1434
        - is new in bzr 0.15
 
1435
        - uses a LockDir to guard access to it.
 
1436
    """
 
1437
 
 
1438
    upgrade_recommended = False
 
1439
 
 
1440
    _tree_class = WorkingTree4
 
1441
 
 
1442
    def get_format_string(self):
 
1443
        """See WorkingTreeFormat.get_format_string()."""
 
1444
        return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
 
1445
 
 
1446
    def get_format_description(self):
 
1447
        """See WorkingTreeFormat.get_format_description()."""
 
1448
        return "Working tree format 4"
 
1449
 
 
1450
 
 
1451
class WorkingTreeFormat5(DirStateWorkingTreeFormat):
 
1452
    """WorkingTree format supporting views.
 
1453
    """
 
1454
 
 
1455
    upgrade_recommended = False
 
1456
 
 
1457
    _tree_class = WorkingTree5
 
1458
 
 
1459
    def get_format_string(self):
 
1460
        """See WorkingTreeFormat.get_format_string()."""
 
1461
        return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
 
1462
 
 
1463
    def get_format_description(self):
 
1464
        """See WorkingTreeFormat.get_format_description()."""
 
1465
        return "Working tree format 5"
 
1466
 
 
1467
    def _init_custom_control_files(self, wt):
 
1468
        """Subclasses with custom control files should override this method."""
 
1469
        wt._transport.put_bytes('views', '', mode=wt.bzrdir._get_file_mode())
 
1470
 
 
1471
    def supports_content_filtering(self):
 
1472
        return True
 
1473
 
 
1474
    def supports_views(self):
 
1475
        return True
 
1476
 
1099
1477
 
1100
1478
class DirStateRevisionTree(Tree):
1101
1479
    """A revision tree pulling the inventory from a dirstate."""
1102
1480
 
1103
1481
    def __init__(self, dirstate, revision_id, repository):
1104
1482
        self._dirstate = dirstate
1105
 
        self._revision_id = osutils.safe_revision_id(revision_id)
 
1483
        self._revision_id = revision_id
1106
1484
        self._repository = repository
1107
1485
        self._inventory = None
1108
1486
        self._locked = 0
1109
1487
        self._dirstate_locked = False
1110
 
 
1111
 
    def annotate_iter(self, file_id):
 
1488
        self._repo_supports_tree_reference = getattr(
 
1489
            repository._format, "supports_tree_reference",
 
1490
            False)
 
1491
 
 
1492
    def __repr__(self):
 
1493
        return "<%s of %s in %s>" % \
 
1494
            (self.__class__.__name__, self._revision_id, self._dirstate)
 
1495
 
 
1496
    def annotate_iter(self, file_id,
 
1497
                      default_revision=_mod_revision.CURRENT_REVISION):
1112
1498
        """See Tree.annotate_iter"""
1113
 
        w = self._repository.weave_store.get_weave(file_id,
1114
 
                           self._repository.get_transaction())
1115
 
        return w.annotate_iter(self.inventory[file_id].revision)
 
1499
        text_key = (file_id, self.inventory[file_id].revision)
 
1500
        annotations = self._repository.texts.annotate(text_key)
 
1501
        return [(key[-1], line) for (key, line) in annotations]
1116
1502
 
 
1503
    def _get_ancestors(self, default_revision):
 
1504
        return set(self._repository.get_ancestry(self._revision_id,
 
1505
                                                 topo_sorted=False))
1117
1506
    def _comparison_data(self, entry, path):
1118
1507
        """See Tree._comparison_data."""
1119
1508
        if entry is None:
1133
1522
        pred = self.has_filename
1134
1523
        return set((p for p in paths if not pred(p)))
1135
1524
 
 
1525
    def get_root_id(self):
 
1526
        return self.path2id('')
 
1527
 
 
1528
    def id2path(self, file_id):
 
1529
        "Convert a file-id to a path."
 
1530
        entry = self._get_entry(file_id=file_id)
 
1531
        if entry == (None, None):
 
1532
            raise errors.NoSuchId(tree=self, file_id=file_id)
 
1533
        path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
 
1534
        return path_utf8.decode('utf8')
 
1535
 
 
1536
    def iter_references(self):
 
1537
        if not self._repo_supports_tree_reference:
 
1538
            # When the repo doesn't support references, we will have nothing to
 
1539
            # return
 
1540
            return iter([])
 
1541
        # Otherwise, fall back to the default implementation
 
1542
        return super(DirStateRevisionTree, self).iter_references()
 
1543
 
1136
1544
    def _get_parent_index(self):
1137
1545
        """Return the index in the dirstate referenced by this tree."""
1138
1546
        return self._dirstate.get_parent_ids().index(self._revision_id) + 1
1150
1558
        """
1151
1559
        if file_id is None and path is None:
1152
1560
            raise errors.BzrError('must supply file_id or path')
1153
 
        file_id = osutils.safe_file_id(file_id)
1154
1561
        if path is not None:
1155
1562
            path = path.encode('utf8')
1156
1563
        parent_index = self._get_parent_index()
1159
1566
    def _generate_inventory(self):
1160
1567
        """Create and set self.inventory from the dirstate object.
1161
1568
 
 
1569
        (So this is only called the first time the inventory is requested for
 
1570
        this tree; it then remains in memory until it's out of date.)
 
1571
 
1162
1572
        This is relatively expensive: we have to walk the entire dirstate.
1163
 
        Ideally we would not, and instead would """
1164
 
        assert self._locked, 'cannot generate inventory of an unlocked '\
1165
 
            'dirstate revision tree'
 
1573
        """
 
1574
        if not self._locked:
 
1575
            raise AssertionError(
 
1576
                'cannot generate inventory of an unlocked '
 
1577
                'dirstate revision tree')
1166
1578
        # separate call for profiling - makes it clear where the costs are.
1167
1579
        self._dirstate._read_dirblocks_if_needed()
1168
 
        assert self._revision_id in self._dirstate.get_parent_ids(), \
1169
 
            'parent %s has disappeared from %s' % (
1170
 
            self._revision_id, self._dirstate.get_parent_ids())
 
1580
        if self._revision_id not in self._dirstate.get_parent_ids():
 
1581
            raise AssertionError(
 
1582
                'parent %s has disappeared from %s' % (
 
1583
                self._revision_id, self._dirstate.get_parent_ids()))
1171
1584
        parent_index = self._dirstate.get_parent_ids().index(self._revision_id) + 1
1172
1585
        # This is identical now to the WorkingTree _generate_inventory except
1173
1586
        # for the tree index use.
1174
1587
        root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
1175
1588
        current_id = root_key[2]
1176
 
        assert current_entry[parent_index][0] == 'd'
 
1589
        if current_entry[parent_index][0] != 'd':
 
1590
            raise AssertionError()
1177
1591
        inv = Inventory(root_id=current_id, revision_id=self._revision_id)
1178
1592
        inv.root.revision = current_entry[parent_index][4]
1179
1593
        # Turn some things into local variables
1192
1606
                # all the paths in this block are not versioned in this tree
1193
1607
                continue
1194
1608
            for key, entry in block[1]:
1195
 
                minikind, link_or_sha1, size, executable, revid = entry[parent_index]
 
1609
                minikind, fingerprint, size, executable, revid = entry[parent_index]
1196
1610
                if minikind in ('a', 'r'): # absent, relocated
1197
1611
                    # not this tree
1198
1612
                    continue
1206
1620
                if kind == 'file':
1207
1621
                    inv_entry.executable = executable
1208
1622
                    inv_entry.text_size = size
1209
 
                    inv_entry.text_sha1 = link_or_sha1
 
1623
                    inv_entry.text_sha1 = fingerprint
1210
1624
                elif kind == 'directory':
1211
1625
                    parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1212
1626
                elif kind == 'symlink':
1213
1627
                    inv_entry.executable = False
1214
 
                    inv_entry.text_size = size
1215
 
                    inv_entry.symlink_target = utf8_decode(link_or_sha1)[0]
 
1628
                    inv_entry.text_size = None
 
1629
                    inv_entry.symlink_target = utf8_decode(fingerprint)[0]
 
1630
                elif kind == 'tree-reference':
 
1631
                    inv_entry.reference_revision = fingerprint or None
1216
1632
                else:
1217
 
                    raise Exception, kind
 
1633
                    raise AssertionError("cannot convert entry %r into an InventoryEntry"
 
1634
                            % entry)
1218
1635
                # These checks cost us around 40ms on a 55k entry tree
1219
 
                assert file_id not in inv_byid
1220
 
                assert name_unicode not in parent_ie.children
 
1636
                if file_id in inv_byid:
 
1637
                    raise AssertionError('file_id %s already in'
 
1638
                        ' inventory as %s' % (file_id, inv_byid[file_id]))
 
1639
                if name_unicode in parent_ie.children:
 
1640
                    raise AssertionError('name %r already in parent'
 
1641
                        % (name_unicode,))
1221
1642
                inv_byid[file_id] = inv_entry
1222
1643
                parent_ie.children[name_unicode] = inv_entry
1223
1644
        self._inventory = inv
1236
1657
        return self._repository.get_revision(last_changed_revision).timestamp
1237
1658
 
1238
1659
    def get_file_sha1(self, file_id, path=None, stat_value=None):
1239
 
        # TODO: if path is present, fast-path on that, as inventory
1240
 
        # might not be present
1241
 
        ie = self.inventory[file_id]
1242
 
        if ie.kind == "file":
1243
 
            return ie.text_sha1
 
1660
        entry = self._get_entry(file_id=file_id, path=path)
 
1661
        parent_index = self._get_parent_index()
 
1662
        parent_details = entry[1][parent_index]
 
1663
        if parent_details[0] == 'f':
 
1664
            return parent_details[1]
1244
1665
        return None
1245
1666
 
1246
 
    def get_file(self, file_id):
 
1667
    def get_file(self, file_id, path=None):
1247
1668
        return StringIO(self.get_file_text(file_id))
1248
1669
 
1249
 
    def get_file_lines(self, file_id):
1250
 
        ie = self.inventory[file_id]
1251
 
        return self._repository.weave_store.get_weave(file_id,
1252
 
                self._repository.get_transaction()).get_lines(ie.revision)
1253
 
 
1254
1670
    def get_file_size(self, file_id):
 
1671
        """See Tree.get_file_size"""
1255
1672
        return self.inventory[file_id].text_size
1256
1673
 
1257
 
    def get_file_text(self, file_id):
1258
 
        return ''.join(self.get_file_lines(file_id))
 
1674
    def get_file_text(self, file_id, path=None):
 
1675
        return list(self.iter_files_bytes([(file_id, None)]))[0][1]
 
1676
 
 
1677
    def get_reference_revision(self, file_id, path=None):
 
1678
        return self.inventory[file_id].reference_revision
 
1679
 
 
1680
    def iter_files_bytes(self, desired_files):
 
1681
        """See Tree.iter_files_bytes.
 
1682
 
 
1683
        This version is implemented on top of Repository.iter_files_bytes"""
 
1684
        parent_index = self._get_parent_index()
 
1685
        repo_desired_files = []
 
1686
        for file_id, identifier in desired_files:
 
1687
            entry = self._get_entry(file_id)
 
1688
            if entry == (None, None):
 
1689
                raise errors.NoSuchId(self, file_id)
 
1690
            repo_desired_files.append((file_id, entry[1][parent_index][4],
 
1691
                                       identifier))
 
1692
        return self._repository.iter_files_bytes(repo_desired_files)
1259
1693
 
1260
1694
    def get_symlink_target(self, file_id):
1261
1695
        entry = self._get_entry(file_id=file_id)
1275
1709
    def _get_inventory(self):
1276
1710
        if self._inventory is not None:
1277
1711
            return self._inventory
 
1712
        self._must_be_locked()
1278
1713
        self._generate_inventory()
1279
1714
        return self._inventory
1280
1715
 
1289
1724
        return bool(self.path2id(filename))
1290
1725
 
1291
1726
    def kind(self, file_id):
1292
 
        return self.inventory[file_id].kind
 
1727
        entry = self._get_entry(file_id=file_id)[1]
 
1728
        if entry is None:
 
1729
            raise errors.NoSuchId(tree=self, file_id=file_id)
 
1730
        return dirstate.DirState._minikind_to_kind[entry[1][0]]
 
1731
 
 
1732
    def stored_kind(self, file_id):
 
1733
        """See Tree.stored_kind"""
 
1734
        return self.kind(file_id)
 
1735
 
 
1736
    def path_content_summary(self, path):
 
1737
        """See Tree.path_content_summary."""
 
1738
        id = self.inventory.path2id(path)
 
1739
        if id is None:
 
1740
            return ('missing', None, None, None)
 
1741
        entry = self._inventory[id]
 
1742
        kind = entry.kind
 
1743
        if kind == 'file':
 
1744
            return (kind, entry.text_size, entry.executable, entry.text_sha1)
 
1745
        elif kind == 'symlink':
 
1746
            return (kind, None, None, entry.symlink_target)
 
1747
        else:
 
1748
            return (kind, None, None, None)
1293
1749
 
1294
1750
    def is_executable(self, file_id, path=None):
1295
1751
        ie = self.inventory[file_id]
1316
1772
                self._dirstate_locked = True
1317
1773
        self._locked += 1
1318
1774
 
 
1775
    def _must_be_locked(self):
 
1776
        if not self._locked:
 
1777
            raise errors.ObjectNotLocked(self)
 
1778
 
1319
1779
    @needs_read_lock
1320
1780
    def path2id(self, path):
1321
1781
        """Return the id for path in this tree."""
1337
1797
                self._dirstate_locked = False
1338
1798
            self._repository.unlock()
1339
1799
 
 
1800
    @needs_read_lock
 
1801
    def supports_tree_reference(self):
 
1802
        return self._repo_supports_tree_reference
 
1803
 
1340
1804
    def walkdirs(self, prefix=""):
1341
 
        # TODO: jam 20070215 This is the cheap way by cheating and using the
1342
 
        #       RevisionTree implementation.
1343
 
        #       This should be cleaned up to use the much faster Dirstate code
1344
 
        #       This is a little tricky, though, because the dirstate is
1345
 
        #       indexed by current path, not by parent path.
1346
 
        #       So for now, we just build up the parent inventory, and extract
1347
 
        #       it the same way RevisionTree does.
 
1805
        # TODO: jam 20070215 This is the lazy way by using the RevisionTree
 
1806
        # implementation based on an inventory.  
 
1807
        # This should be cleaned up to use the much faster Dirstate code
 
1808
        # So for now, we just build up the parent inventory, and extract
 
1809
        # it the same way RevisionTree does.
1348
1810
        _directory = 'directory'
1349
1811
        inv = self._get_inventory()
1350
1812
        top_id = inv.path2id(prefix)
1375
1837
 
1376
1838
 
1377
1839
class InterDirStateTree(InterTree):
1378
 
    """Fast path optimiser for changes_from with dirstate trees."""
 
1840
    """Fast path optimiser for changes_from with dirstate trees.
 
1841
    
 
1842
    This is used only when both trees are in the dirstate working file, and 
 
1843
    the source is any parent within the dirstate, and the destination is 
 
1844
    the current working tree of the same dirstate.
 
1845
    """
 
1846
    # this could be generalized to allow comparisons between any trees in the
 
1847
    # dirstate, and possibly between trees stored in different dirstates.
1379
1848
 
1380
1849
    def __init__(self, source, target):
1381
1850
        super(InterDirStateTree, self).__init__(source, target)
1390
1859
        target.set_parent_ids([revid])
1391
1860
        return target.basis_tree(), target
1392
1861
 
 
1862
    @classmethod
 
1863
    def make_source_parent_tree_python_dirstate(klass, test_case, source, target):
 
1864
        result = klass.make_source_parent_tree(source, target)
 
1865
        result[1]._iter_changes = dirstate.ProcessEntryPython
 
1866
        return result
 
1867
 
 
1868
    @classmethod
 
1869
    def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
 
1870
        from bzrlib.tests.test__dirstate_helpers import \
 
1871
            CompiledDirstateHelpersFeature
 
1872
        if not CompiledDirstateHelpersFeature.available():
 
1873
            from bzrlib.tests import UnavailableFeature
 
1874
            raise UnavailableFeature(CompiledDirstateHelpersFeature)
 
1875
        from bzrlib._dirstate_helpers_c import ProcessEntryC
 
1876
        result = klass.make_source_parent_tree(source, target)
 
1877
        result[1]._iter_changes = ProcessEntryC
 
1878
        return result
 
1879
 
1393
1880
    _matching_from_tree_format = WorkingTreeFormat4()
1394
1881
    _matching_to_tree_format = WorkingTreeFormat4()
1395
 
    _test_mutable_trees_to_test_trees = make_source_parent_tree
1396
 
 
1397
 
    def _iter_changes(self, include_unchanged=False,
 
1882
 
 
1883
    @classmethod
 
1884
    def _test_mutable_trees_to_test_trees(klass, test_case, source, target):
 
1885
        # This method shouldn't be called, because we have python and C
 
1886
        # specific flavours.
 
1887
        raise NotImplementedError
 
1888
 
 
1889
    def iter_changes(self, include_unchanged=False,
1398
1890
                      specific_files=None, pb=None, extra_trees=[],
1399
 
                      require_versioned=True):
 
1891
                      require_versioned=True, want_unversioned=False):
1400
1892
        """Return the changes from source to target.
1401
1893
 
1402
 
        :return: An iterator that yields tuples. See InterTree._iter_changes
 
1894
        :return: An iterator that yields tuples. See InterTree.iter_changes
1403
1895
            for details.
1404
1896
        :param specific_files: An optional list of file paths to restrict the
1405
1897
            comparison to. When mapping filenames to ids, all matches in all
1412
1904
        :param require_versioned: If True, all files in specific_files must be
1413
1905
            versioned in one of source, target, extra_trees or
1414
1906
            PathsNotVersionedError is raised.
 
1907
        :param want_unversioned: Should unversioned files be returned in the
 
1908
            output. An unversioned file is defined as one with (False, False)
 
1909
            for the versioned pair.
1415
1910
        """
1416
 
        utf8_decode = cache_utf8._utf8_decode
1417
 
        _minikind_to_kind = dirstate.DirState._minikind_to_kind
1418
1911
        # NB: show_status depends on being able to pass in non-versioned files
1419
1912
        # and report them as unknown
1420
 
            # TODO: handle extra trees in the dirstate.
1421
 
        if extra_trees:
1422
 
            for f in super(InterDirStateTree, self)._iter_changes(
 
1913
        # TODO: handle extra trees in the dirstate.
 
1914
        if (extra_trees or specific_files == []):
 
1915
            # we can't fast-path these cases (yet)
 
1916
            return super(InterDirStateTree, self).iter_changes(
1423
1917
                include_unchanged, specific_files, pb, extra_trees,
1424
 
                require_versioned):
1425
 
                yield f
1426
 
            return
 
1918
                require_versioned, want_unversioned=want_unversioned)
1427
1919
        parent_ids = self.target.get_parent_ids()
 
1920
        if not (self.source._revision_id in parent_ids
 
1921
                or self.source._revision_id == NULL_REVISION):
 
1922
            raise AssertionError(
 
1923
                "revision {%s} is not stored in {%s}, but %s "
 
1924
                "can only be used for trees stored in the dirstate"
 
1925
                % (self.source._revision_id, self.target, self.iter_changes))
1428
1926
        target_index = 0
1429
1927
        if self.source._revision_id == NULL_REVISION:
1430
1928
            source_index = None
1431
1929
            indices = (target_index,)
1432
1930
        else:
1433
 
            assert (self.source._revision_id in parent_ids), \
1434
 
                "Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
1435
 
                self.source._revision_id, parent_ids)
 
1931
            if not (self.source._revision_id in parent_ids):
 
1932
                raise AssertionError(
 
1933
                    "Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
 
1934
                    self.source._revision_id, parent_ids))
1436
1935
            source_index = 1 + parent_ids.index(self.source._revision_id)
1437
 
            indices = (source_index,target_index)
 
1936
            indices = (source_index, target_index)
1438
1937
        # -- make all specific_files utf8 --
1439
1938
        if specific_files:
1440
1939
            specific_files_utf8 = set()
1441
1940
            for path in specific_files:
 
1941
                # Note, if there are many specific files, using cache_utf8
 
1942
                # would be good here.
1442
1943
                specific_files_utf8.add(path.encode('utf8'))
1443
1944
            specific_files = specific_files_utf8
1444
1945
        else:
1445
1946
            specific_files = set([''])
1446
1947
        # -- specific_files is now a utf8 path set --
 
1948
        search_specific_files = set()
1447
1949
        # -- get the state object and prepare it.
1448
1950
        state = self.target.current_dirstate()
1449
1951
        state._read_dirblocks_if_needed()
1450
 
        def _entries_for_path(path):
1451
 
            """Return a list with all the entries that match path for all ids.
1452
 
            """
1453
 
            dirname, basename = os.path.split(path)
1454
 
            key = (dirname, basename, '')
1455
 
            block_index, present = state._find_block_index_from_key(key)
1456
 
            if not present:
1457
 
                # the block which should contain path is absent.
1458
 
                return []
1459
 
            result = []
1460
 
            block = state._dirblocks[block_index][1]
1461
 
            entry_index, _ = state._find_entry_index(key, block)
1462
 
            # we may need to look at multiple entries at this path: walk while the specific_files match.
1463
 
            while (entry_index < len(block) and
1464
 
                block[entry_index][0][0:2] == key[0:2]):
1465
 
                result.append(block[entry_index])
1466
 
                entry_index += 1
1467
 
            return result
1468
1952
        if require_versioned:
1469
1953
            # -- check all supplied paths are versioned in a search tree. --
1470
1954
            all_versioned = True
1471
1955
            for path in specific_files:
1472
 
                path_entries = _entries_for_path(path)
 
1956
                path_entries = state._entries_for_path(path)
1473
1957
                if not path_entries:
1474
1958
                    # this specified path is not present at all: error
1475
1959
                    all_versioned = False
1489
1973
                    all_versioned = False
1490
1974
                    break
1491
1975
            if not all_versioned:
1492
 
                raise errors.PathsNotVersionedError(paths)
 
1976
                raise errors.PathsNotVersionedError(specific_files)
1493
1977
        # -- remove redundancy in supplied specific_files to prevent over-scanning --
1494
 
        search_specific_files = set()
1495
1978
        for path in specific_files:
1496
1979
            other_specific_files = specific_files.difference(set([path]))
1497
1980
            if not osutils.is_inside_any(other_specific_files, path):
1498
1981
                # this is a top level path, we must check it.
1499
1982
                search_specific_files.add(path)
1500
 
        # sketch: 
1501
 
        # compare source_index and target_index at or under each element of search_specific_files.
1502
 
        # follow the following comparison table. Note that we only want to do diff operations when
1503
 
        # the target is fdl because thats when the walkdirs logic will have exposed the pathinfo 
1504
 
        # for the target.
1505
 
        # cases:
1506
 
        # 
1507
 
        # Source | Target | disk | action
1508
 
        #   r    | fdl    |      | add source to search, add id path move and perform
1509
 
        #        |        |      | diff check on source-target
1510
 
        #   r    | fdl    |  a   | dangling file that was present in the basis. 
1511
 
        #        |        |      | ???
1512
 
        #   r    |  a     |      | add source to search
1513
 
        #   r    |  a     |  a   | 
1514
 
        #   r    |  r     |      | this path is present in a non-examined tree, skip.
1515
 
        #   r    |  r     |  a   | this path is present in a non-examined tree, skip.
1516
 
        #   a    | fdl    |      | add new id
1517
 
        #   a    | fdl    |  a   | dangling locally added file, skip
1518
 
        #   a    |  a     |      | not present in either tree, skip
1519
 
        #   a    |  a     |  a   | not present in any tree, skip
1520
 
        #   a    |  r     |      | not present in either tree at this path, skip as it
1521
 
        #        |        |      | may not be selected by the users list of paths.
1522
 
        #   a    |  r     |  a   | not present in either tree at this path, skip as it
1523
 
        #        |        |      | may not be selected by the users list of paths.
1524
 
        #  fdl   | fdl    |      | content in both: diff them
1525
 
        #  fdl   | fdl    |  a   | deleted locally, but not unversioned - show as deleted ?
1526
 
        #  fdl   |  a     |      | unversioned: output deleted id for now
1527
 
        #  fdl   |  a     |  a   | unversioned and deleted: output deleted id
1528
 
        #  fdl   |  r     |      | relocated in this tree, so add target to search.
1529
 
        #        |        |      | Dont diff, we will see an r,fd; pair when we reach
1530
 
        #        |        |      | this id at the other path.
1531
 
        #  fdl   |  r     |  a   | relocated in this tree, so add target to search.
1532
 
        #        |        |      | Dont diff, we will see an r,fd; pair when we reach
1533
 
        #        |        |      | this id at the other path.
1534
 
 
1535
 
        # for all search_indexs in each path at or under each element of
1536
 
        # search_specific_files, if the detail is relocated: add the id, and add the
1537
 
        # relocated path as one to search if its not searched already. If the
1538
 
        # detail is not relocated, add the id.
1539
 
        searched_specific_files = set()
1540
 
        NULL_PARENT_DETAILS = dirstate.DirState.NULL_PARENT_DETAILS
1541
 
        # Using a list so that we can access the values and change them in
1542
 
        # nested scope. Each one is [path, file_id, entry]
1543
 
        last_source_parent = [None, None, None]
1544
 
        last_target_parent = [None, None, None]
1545
 
 
1546
 
        def _process_entry(entry, path_info):
1547
 
            """Compare an entry and real disk to generate delta information.
1548
 
 
1549
 
            :param path_info: top_relpath, basename, kind, lstat, abspath for
1550
 
                the path of entry. If None, then the path is considered absent.
1551
 
                (Perhaps we should pass in a concrete entry for this ?)
1552
 
            """
1553
 
            # TODO: when a parent has been renamed, dont emit path renames for children,
1554
 
            if source_index is None:
1555
 
                source_details = NULL_PARENT_DETAILS
1556
 
            else:
1557
 
                source_details = entry[1][source_index]
1558
 
            target_details = entry[1][target_index]
1559
 
            source_minikind = source_details[0]
1560
 
            target_minikind = target_details[0]
1561
 
            if source_minikind in 'fdlr' and target_minikind in 'fdl':
1562
 
                # claimed content in both: diff
1563
 
                #   r    | fdl    |      | add source to search, add id path move and perform
1564
 
                #        |        |      | diff check on source-target
1565
 
                #   r    | fdl    |  a   | dangling file that was present in the basis.
1566
 
                #        |        |      | ???
1567
 
                if source_minikind in 'r':
1568
 
                    # add the source to the search path to find any children it
1569
 
                    # has.  TODO ? : only add if it is a container ?
1570
 
                    if not osutils.is_inside_any(searched_specific_files,
1571
 
                                                 source_details[1]):
1572
 
                        search_specific_files.add(source_details[1])
1573
 
                    # generate the old path; this is needed for stating later
1574
 
                    # as well.
1575
 
                    old_path = source_details[1]
1576
 
                    old_dirname, old_basename = os.path.split(old_path)
1577
 
                    path = pathjoin(entry[0][0], entry[0][1])
1578
 
                    old_entry = state._get_entry(source_index,
1579
 
                                                 path_utf8=old_path)
1580
 
                    # update the source details variable to be the real
1581
 
                    # location.
1582
 
                    source_details = old_entry[1][source_index]
1583
 
                    source_minikind = source_details[0]
1584
 
                else:
1585
 
                    old_dirname = entry[0][0]
1586
 
                    old_basename = entry[0][1]
1587
 
                    old_path = path = pathjoin(old_dirname, old_basename)
1588
 
                if path_info is None:
1589
 
                    # the file is missing on disk, show as removed.
1590
 
                    old_path = pathjoin(entry[0][0], entry[0][1])
1591
 
                    content_change = True
1592
 
                    target_kind = None
1593
 
                    target_exec = False
1594
 
                else:
1595
 
                    # source and target are both versioned and disk file is present.
1596
 
                    target_kind = path_info[2]
1597
 
                    if target_kind == 'directory':
1598
 
                        if source_minikind != 'd':
1599
 
                            content_change = True
1600
 
                        else:
1601
 
                            # directories have no fingerprint
1602
 
                            content_change = False
1603
 
                        target_exec = False
1604
 
                    elif target_kind == 'file':
1605
 
                        if source_minikind != 'f':
1606
 
                            content_change = True
1607
 
                        else:
1608
 
                            # has it changed? fast path: size, slow path: sha1.
1609
 
                            if source_details[2] != path_info[3].st_size:
1610
 
                                content_change = True
1611
 
                            else:
1612
 
                                # maybe the same. Get the hash
1613
 
                                new_hash = self.target._hashcache.get_sha1(
1614
 
                                                            path, path_info[3])
1615
 
                                content_change = (new_hash != source_details[1])
1616
 
                        target_exec = bool(
1617
 
                            stat.S_ISREG(path_info[3].st_mode)
1618
 
                            and stat.S_IEXEC & path_info[3].st_mode)
1619
 
                    elif target_kind == 'symlink':
1620
 
                        if source_minikind != 'l':
1621
 
                            content_change = True
1622
 
                        else:
1623
 
                            # TODO: check symlink supported for windows users
1624
 
                            # and grab from target state here.
1625
 
                            link_target = os.readlink(path_info[4])
1626
 
                            content_change = (link_target != source_details[1])
1627
 
                        target_exec = False
1628
 
                    else:
1629
 
                        raise Exception, "unknown kind %s" % path_info[2]
1630
 
                # parent id is the entry for the path in the target tree
1631
 
                if old_dirname == last_source_parent[0]:
1632
 
                    source_parent_id = last_source_parent[1]
1633
 
                else:
1634
 
                    source_parent_entry = state._get_entry(source_index,
1635
 
                                                           path_utf8=old_dirname)
1636
 
                    source_parent_id = source_parent_entry[0][2]
1637
 
                    if source_parent_id == entry[0][2]:
1638
 
                        # This is the root, so the parent is None
1639
 
                        source_parent_id = None
1640
 
                    else:
1641
 
                        last_source_parent[0] = old_dirname
1642
 
                        last_source_parent[1] = source_parent_id
1643
 
                        last_source_parent[2] = source_parent_entry
1644
 
 
1645
 
                new_dirname = entry[0][0]
1646
 
                if new_dirname == last_target_parent[0]:
1647
 
                    target_parent_id = last_target_parent[1]
1648
 
                else:
1649
 
                    # TODO: We don't always need to do the lookup, because the
1650
 
                    #       parent entry will be the same as the source entry.
1651
 
                    target_parent_entry = state._get_entry(target_index,
1652
 
                                                           path_utf8=new_dirname)
1653
 
                    target_parent_id = target_parent_entry[0][2]
1654
 
                    if target_parent_id == entry[0][2]:
1655
 
                        # This is the root, so the parent is None
1656
 
                        target_parent_id = None
1657
 
                    else:
1658
 
                        last_target_parent[0] = new_dirname
1659
 
                        last_target_parent[1] = target_parent_id
1660
 
                        last_target_parent[2] = target_parent_entry
1661
 
 
1662
 
                source_exec = source_details[3]
1663
 
                path_unicode = utf8_decode(path)[0]
1664
 
                return ((entry[0][2], path_unicode, content_change,
1665
 
                        (True, True),
1666
 
                        (source_parent_id, target_parent_id),
1667
 
                        (old_basename, entry[0][1]),
1668
 
                        (_minikind_to_kind[source_minikind], target_kind),
1669
 
                        (source_exec, target_exec)),)
1670
 
            elif source_minikind in 'a' and target_minikind in 'fdl':
1671
 
                # looks like a new file
1672
 
                if path_info is not None:
1673
 
                    path = pathjoin(entry[0][0], entry[0][1])
1674
 
                    # parent id is the entry for the path in the target tree
1675
 
                    # TODO: these are the same for an entire directory: cache em.
1676
 
                    parent_id = state._get_entry(target_index, path_utf8=entry[0][0])[0][2]
1677
 
                    if parent_id == entry[0][2]:
1678
 
                        parent_id = None
1679
 
                    # basename
1680
 
                    new_executable = bool(
1681
 
                        stat.S_ISREG(path_info[3].st_mode)
1682
 
                        and stat.S_IEXEC & path_info[3].st_mode)
1683
 
                    path_unicode = utf8_decode(path)[0]
1684
 
                    return ((entry[0][2], path_unicode, True,
1685
 
                            (False, True),
1686
 
                            (None, parent_id),
1687
 
                            (None, entry[0][1]),
1688
 
                            (None, path_info[2]),
1689
 
                            (None, new_executable)),)
1690
 
                else:
1691
 
                    # but its not on disk: we deliberately treat this as just
1692
 
                    # never-present. (Why ?! - RBC 20070224)
1693
 
                    pass
1694
 
            elif source_minikind in 'fdl' and target_minikind in 'a':
1695
 
                # unversioned, possibly, or possibly not deleted: we dont care.
1696
 
                # if its still on disk, *and* theres no other entry at this
1697
 
                # path [we dont know this in this routine at the moment -
1698
 
                # perhaps we should change this - then it would be an unknown.
1699
 
                old_path = pathjoin(entry[0][0], entry[0][1])
1700
 
                # parent id is the entry for the path in the target tree
1701
 
                parent_id = state._get_entry(source_index, path_utf8=entry[0][0])[0][2]
1702
 
                if parent_id == entry[0][2]:
1703
 
                    parent_id = None
1704
 
                old_path_unicode = utf8_decode(old_path)[0]
1705
 
                return ((entry[0][2], old_path_unicode, True,
1706
 
                        (True, False),
1707
 
                        (parent_id, None),
1708
 
                        (entry[0][1], None),
1709
 
                        (_minikind_to_kind[source_minikind], None),
1710
 
                        (source_details[3], None)),)
1711
 
            elif source_minikind in 'fdl' and target_minikind in 'r':
1712
 
                # a rename; could be a true rename, or a rename inherited from
1713
 
                # a renamed parent. TODO: handle this efficiently. Its not
1714
 
                # common case to rename dirs though, so a correct but slow
1715
 
                # implementation will do.
1716
 
                if not osutils.is_inside_any(searched_specific_files, target_details[1]):
1717
 
                    search_specific_files.add(target_details[1])
1718
 
            elif source_minikind in 'r' and target_minikind in 'r':
1719
 
                # neither of the selected trees contain this file,
1720
 
                # so skip over it. This is not currently directly tested, but
1721
 
                # is indirectly via test_too_much.TestCommands.test_conflicts.
1722
 
                pass
1723
 
            else:
1724
 
                print "*******", source_minikind, target_minikind
1725
 
                import pdb;pdb.set_trace()
1726
 
            return ()
1727
 
        while search_specific_files:
1728
 
            # TODO: the pending list should be lexically sorted?
1729
 
            current_root = search_specific_files.pop()
1730
 
            searched_specific_files.add(current_root)
1731
 
            # process the entries for this containing directory: the rest will be
1732
 
            # found by their parents recursively.
1733
 
            root_entries = _entries_for_path(current_root)
1734
 
            root_abspath = self.target.abspath(current_root)
1735
 
            try:
1736
 
                root_stat = os.lstat(root_abspath)
1737
 
            except OSError, e:
1738
 
                if e.errno == errno.ENOENT:
1739
 
                    # the path does not exist: let _process_entry know that.
1740
 
                    root_dir_info = None
1741
 
                else:
1742
 
                    # some other random error: hand it up.
1743
 
                    raise
1744
 
            else:
1745
 
                root_dir_info = ('', current_root,
1746
 
                    osutils.file_kind_from_stat_mode(root_stat.st_mode), root_stat,
1747
 
                    root_abspath)
1748
 
            if not root_entries and not root_dir_info:
1749
 
                # this specified path is not present at all, skip it.
1750
 
                continue
1751
 
            for entry in root_entries:
1752
 
                for result in _process_entry(entry, root_dir_info):
1753
 
                    # this check should probably be outside the loop: one
1754
 
                    # 'iterate two trees' api, and then _iter_changes filters
1755
 
                    # unchanged pairs. - RBC 20070226
1756
 
                    if (include_unchanged
1757
 
                        or result[2]                    # content change
1758
 
                        or result[3][0] != result[3][1] # versioned status
1759
 
                        or result[4][0] != result[4][1] # parent id
1760
 
                        or result[5][0] != result[5][1] # name
1761
 
                        or result[6][0] != result[6][1] # kind
1762
 
                        or result[7][0] != result[7][1] # executable
1763
 
                        ):
1764
 
                        yield result
1765
 
            dir_iterator = osutils._walkdirs_utf8(root_abspath, prefix=current_root)
1766
 
            initial_key = (current_root, '', '')
1767
 
            block_index, _ = state._find_block_index_from_key(initial_key)
1768
 
            if block_index == 0:
1769
 
                # we have processed the total root already, but because the
1770
 
                # initial key matched it we should skip it here.
1771
 
                block_index +=1
1772
 
            try:
1773
 
                current_dir_info = dir_iterator.next()
1774
 
            except OSError, e:
1775
 
                if e.errno in (errno.ENOENT, errno.ENOTDIR):
1776
 
                    # there may be directories in the inventory even though
1777
 
                    # this path is not a file on disk: so mark it as end of
1778
 
                    # iterator
1779
 
                    current_dir_info = None
1780
 
                else:
1781
 
                    raise
1782
 
            else:
1783
 
                if current_dir_info[0][0] == '':
1784
 
                    # remove .bzr from iteration
1785
 
                    bzr_index = bisect_left(current_dir_info[1], ('.bzr',))
1786
 
                    assert current_dir_info[1][bzr_index][0] == '.bzr'
1787
 
                    del current_dir_info[1][bzr_index]
1788
 
            # walk until both the directory listing and the versioned metadata
1789
 
            # are exhausted. TODO: reevaluate this, perhaps we should stop when
1790
 
            # the versioned data runs out.
1791
 
            if (block_index < len(state._dirblocks) and
1792
 
                osutils.is_inside(current_root, state._dirblocks[block_index][0])):
1793
 
                current_block = state._dirblocks[block_index]
1794
 
            else:
1795
 
                current_block = None
1796
 
            while (current_dir_info is not None or
1797
 
                   current_block is not None):
1798
 
                if (current_dir_info and current_block
1799
 
                    and current_dir_info[0][0] != current_block[0]):
1800
 
                    if current_dir_info[0][0] < current_block[0] :
1801
 
                        # import pdb; pdb.set_trace()
1802
 
                        # print 'unversioned dir'
1803
 
                        # filesystem data refers to paths not covered by the dirblock.
1804
 
                        # this has two possibilities:
1805
 
                        # A) it is versioned but empty, so there is no block for it
1806
 
                        # B) it is not versioned.
1807
 
                        # in either case it was processed by the containing directories walk:
1808
 
                        # if it is root/foo, when we walked root we emitted it,
1809
 
                        # or if we ere given root/foo to walk specifically, we
1810
 
                        # emitted it when checking the walk-root entries
1811
 
                        # advance the iterator and loop - we dont need to emit it.
1812
 
                        try:
1813
 
                            current_dir_info = dir_iterator.next()
1814
 
                        except StopIteration:
1815
 
                            current_dir_info = None
1816
 
                    else:
1817
 
                        # We have a dirblock entry for this location, but there
1818
 
                        # is no filesystem path for this. This is most likely
1819
 
                        # because a directory was removed from the disk.
1820
 
                        # We don't have to report the missing directory,
1821
 
                        # because that should have already been handled, but we
1822
 
                        # need to handle all of the files that are contained
1823
 
                        # within.
1824
 
                        for current_entry in current_block[1]:
1825
 
                            # entry referring to file not present on disk.
1826
 
                            # advance the entry only, after processing.
1827
 
                            for result in _process_entry(current_entry, None):
1828
 
                                # this check should probably be outside the loop: one
1829
 
                                # 'iterate two trees' api, and then _iter_changes filters
1830
 
                                # unchanged pairs. - RBC 20070226
1831
 
                                if (include_unchanged
1832
 
                                    or result[2]                    # content change
1833
 
                                    or result[3][0] != result[3][1] # versioned status
1834
 
                                    or result[4][0] != result[4][1] # parent id
1835
 
                                    or result[5][0] != result[5][1] # name
1836
 
                                    or result[6][0] != result[6][1] # kind
1837
 
                                    or result[7][0] != result[7][1] # executable
1838
 
                                    ):
1839
 
                                    yield result
1840
 
                        block_index +=1
1841
 
                        if (block_index < len(state._dirblocks) and
1842
 
                            osutils.is_inside(current_root,
1843
 
                                              state._dirblocks[block_index][0])):
1844
 
                            current_block = state._dirblocks[block_index]
1845
 
                        else:
1846
 
                            current_block = None
1847
 
                    continue
1848
 
                entry_index = 0
1849
 
                if current_block and entry_index < len(current_block[1]):
1850
 
                    current_entry = current_block[1][entry_index]
1851
 
                else:
1852
 
                    current_entry = None
1853
 
                advance_entry = True
1854
 
                path_index = 0
1855
 
                if current_dir_info and path_index < len(current_dir_info[1]):
1856
 
                    current_path_info = current_dir_info[1][path_index]
1857
 
                else:
1858
 
                    current_path_info = None
1859
 
                advance_path = True
1860
 
                while (current_entry is not None or
1861
 
                    current_path_info is not None):
1862
 
                    if current_entry is None:
1863
 
                        # no more entries: yield current_pathinfo as an
1864
 
                        # unversioned file: its not the same as a path in any
1865
 
                        # tree in the dirstate.
1866
 
                        new_executable = bool(
1867
 
                            stat.S_ISREG(current_path_info[3].st_mode)
1868
 
                            and stat.S_IEXEC & current_path_info[3].st_mode)
1869
 
                        yield (None, current_path_info[0], True,
1870
 
                               (False, False),
1871
 
                               (None, None),
1872
 
                               (None, current_path_info[1]),
1873
 
                               (None, current_path_info[2]),
1874
 
                               (None, new_executable))
1875
 
                    elif current_path_info is None:
1876
 
                        # no path is fine: the per entry code will handle it.
1877
 
                        for result in _process_entry(current_entry, current_path_info):
1878
 
                            # this check should probably be outside the loop: one
1879
 
                            # 'iterate two trees' api, and then _iter_changes filters
1880
 
                            # unchanged pairs. - RBC 20070226
1881
 
                            if (include_unchanged
1882
 
                                or result[2]                    # content change
1883
 
                                or result[3][0] != result[3][1] # versioned status
1884
 
                                or result[4][0] != result[4][1] # parent id
1885
 
                                or result[5][0] != result[5][1] # name
1886
 
                                or result[6][0] != result[6][1] # kind
1887
 
                                or result[7][0] != result[7][1] # executable
1888
 
                                ):
1889
 
                                yield result
1890
 
                    elif current_entry[0][1] != current_path_info[1]:
1891
 
                        if current_path_info[1] < current_entry[0][1]:
1892
 
                            # extra file on disk: pass for now, but only
1893
 
                            # increment the path, not the entry
1894
 
                            # import pdb; pdb.set_trace()
1895
 
                            # print 'unversioned file'
1896
 
                            advance_entry = False
1897
 
                        else:
1898
 
                            # entry referring to file not present on disk.
1899
 
                            # advance the entry only, after processing.
1900
 
                            for result in _process_entry(current_entry, None):
1901
 
                                # this check should probably be outside the loop: one
1902
 
                                # 'iterate two trees' api, and then _iter_changes filters
1903
 
                                # unchanged pairs. - RBC 20070226
1904
 
                                if (include_unchanged
1905
 
                                    or result[2]                    # content change
1906
 
                                    or result[3][0] != result[3][1] # versioned status
1907
 
                                    or result[4][0] != result[4][1] # parent id
1908
 
                                    or result[5][0] != result[5][1] # name
1909
 
                                    or result[6][0] != result[6][1] # kind
1910
 
                                    or result[7][0] != result[7][1] # executable
1911
 
                                    ):
1912
 
                                    yield result
1913
 
                            advance_path = False
1914
 
                    else:
1915
 
                        for result in _process_entry(current_entry, current_path_info):
1916
 
                            # this check should probably be outside the loop: one
1917
 
                            # 'iterate two trees' api, and then _iter_changes filters
1918
 
                            # unchanged pairs. - RBC 20070226
1919
 
                            if (include_unchanged
1920
 
                                or result[2]                    # content change
1921
 
                                or result[3][0] != result[3][1] # versioned status
1922
 
                                or result[4][0] != result[4][1] # parent id
1923
 
                                or result[5][0] != result[5][1] # name
1924
 
                                or result[6][0] != result[6][1] # kind
1925
 
                                or result[7][0] != result[7][1] # executable
1926
 
                                ):
1927
 
                                yield result
1928
 
                    if advance_entry and current_entry is not None:
1929
 
                        entry_index += 1
1930
 
                        if entry_index < len(current_block[1]):
1931
 
                            current_entry = current_block[1][entry_index]
1932
 
                        else:
1933
 
                            current_entry = None
1934
 
                    else:
1935
 
                        advance_entry = True # reset the advance flaga
1936
 
                    if advance_path and current_path_info is not None:
1937
 
                        path_index += 1
1938
 
                        if path_index < len(current_dir_info[1]):
1939
 
                            current_path_info = current_dir_info[1][path_index]
1940
 
                        else:
1941
 
                            current_path_info = None
1942
 
                    else:
1943
 
                        advance_path = True # reset the advance flagg.
1944
 
                if current_block is not None:
1945
 
                    block_index += 1
1946
 
                    if (block_index < len(state._dirblocks) and
1947
 
                        osutils.is_inside(current_root, state._dirblocks[block_index][0])):
1948
 
                        current_block = state._dirblocks[block_index]
1949
 
                    else:
1950
 
                        current_block = None
1951
 
                if current_dir_info is not None:
1952
 
                    try:
1953
 
                        current_dir_info = dir_iterator.next()
1954
 
                    except StopIteration:
1955
 
                        current_dir_info = None
1956
 
 
 
1983
 
 
1984
        use_filesystem_for_exec = (sys.platform != 'win32')
 
1985
        iter_changes = self.target._iter_changes(include_unchanged,
 
1986
            use_filesystem_for_exec, search_specific_files, state,
 
1987
            source_index, target_index, want_unversioned, self.target)
 
1988
        return iter_changes.iter_changes()
1957
1989
 
1958
1990
    @staticmethod
1959
1991
    def is_compatible(source, target):
1960
1992
        # the target must be a dirstate working tree
1961
 
        if not isinstance(target, WorkingTree4):
 
1993
        if not isinstance(target, DirStateWorkingTree):
1962
1994
            return False
1963
 
        # the source must be a revtreee or dirstate rev tree.
 
1995
        # the source must be a revtree or dirstate rev tree.
1964
1996
        if not isinstance(source,
1965
1997
            (revisiontree.RevisionTree, DirStateRevisionTree)):
1966
1998
            return False
1973
2005
        return True
1974
2006
 
1975
2007
InterTree.register_optimiser(InterDirStateTree)
 
2008
 
 
2009
 
 
2010
class Converter3to4(object):
 
2011
    """Perform an in-place upgrade of format 3 to format 4 trees."""
 
2012
 
 
2013
    def __init__(self):
 
2014
        self.target_format = WorkingTreeFormat4()
 
2015
 
 
2016
    def convert(self, tree):
 
2017
        # lock the control files not the tree, so that we dont get tree
 
2018
        # on-unlock behaviours, and so that noone else diddles with the 
 
2019
        # tree during upgrade.
 
2020
        tree._control_files.lock_write()
 
2021
        try:
 
2022
            tree.read_working_inventory()
 
2023
            self.create_dirstate_data(tree)
 
2024
            self.update_format(tree)
 
2025
            self.remove_xml_files(tree)
 
2026
        finally:
 
2027
            tree._control_files.unlock()
 
2028
 
 
2029
    def create_dirstate_data(self, tree):
 
2030
        """Create the dirstate based data for tree."""
 
2031
        local_path = tree.bzrdir.get_workingtree_transport(None
 
2032
            ).local_abspath('dirstate')
 
2033
        state = dirstate.DirState.from_tree(tree, local_path)
 
2034
        state.save()
 
2035
        state.unlock()
 
2036
 
 
2037
    def remove_xml_files(self, tree):
 
2038
        """Remove the oldformat 3 data."""
 
2039
        transport = tree.bzrdir.get_workingtree_transport(None)
 
2040
        for path in ['basis-inventory-cache', 'inventory', 'last-revision',
 
2041
            'pending-merges', 'stat-cache']:
 
2042
            try:
 
2043
                transport.delete(path)
 
2044
            except errors.NoSuchFile:
 
2045
                # some files are optional - just deal.
 
2046
                pass
 
2047
 
 
2048
    def update_format(self, tree):
 
2049
        """Change the format marker."""
 
2050
        tree._transport.put_bytes('format',
 
2051
            self.target_format.get_format_string(),
 
2052
            mode=tree.bzrdir._get_file_mode())
 
2053
 
 
2054
 
 
2055
class Converter4to5(object):
 
2056
    """Perform an in-place upgrade of format 4 to format 5 trees."""
 
2057
 
 
2058
    def __init__(self):
 
2059
        self.target_format = WorkingTreeFormat5()
 
2060
 
 
2061
    def convert(self, tree):
 
2062
        # lock the control files not the tree, so that we don't get tree
 
2063
        # on-unlock behaviours, and so that no-one else diddles with the 
 
2064
        # tree during upgrade.
 
2065
        tree._control_files.lock_write()
 
2066
        try:
 
2067
            self.init_custom_control_files(tree)
 
2068
            self.update_format(tree)
 
2069
        finally:
 
2070
            tree._control_files.unlock()
 
2071
 
 
2072
    def init_custom_control_files(self, tree):
 
2073
        """Initialize custom control files."""
 
2074
        tree._transport.put_bytes('views', '',
 
2075
            mode=tree.bzrdir._get_file_mode())
 
2076
 
 
2077
    def update_format(self, tree):
 
2078
        """Change the format marker."""
 
2079
        tree._transport.put_bytes('format',
 
2080
            self.target_format.get_format_string(),
 
2081
            mode=tree.bzrdir._get_file_mode())