~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.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
38
38
 
39
39
from cStringIO import StringIO
40
40
import os
 
41
import sys
41
42
 
42
43
from bzrlib.lazy_import import lazy_import
43
44
lazy_import(globals(), """
44
45
from bisect import bisect_left
45
46
import collections
46
 
from copy import deepcopy
47
47
import errno
48
48
import itertools
49
49
import operator
50
50
import stat
51
51
from time import time
52
52
import warnings
 
53
import re
53
54
 
54
55
import bzrlib
55
56
from bzrlib import (
 
57
    branch,
56
58
    bzrdir,
57
59
    conflicts as _mod_conflicts,
58
60
    dirstate,
62
64
    hashcache,
63
65
    ignores,
64
66
    merge,
65
 
    osutils,
 
67
    revision as _mod_revision,
66
68
    revisiontree,
 
69
    repository,
67
70
    textui,
 
71
    trace,
68
72
    transform,
 
73
    ui,
69
74
    urlutils,
70
75
    xml5,
71
76
    xml6,
 
77
    xml7,
72
78
    )
73
79
import bzrlib.branch
74
80
from bzrlib.transport import get_transport
75
81
import bzrlib.ui
76
 
from bzrlib.workingtree_4 import WorkingTreeFormat4
 
82
from bzrlib.workingtree_4 import WorkingTreeFormat4, WorkingTreeFormat5
77
83
""")
78
84
 
79
85
from bzrlib import symbol_versioning
80
86
from bzrlib.decorators import needs_read_lock, needs_write_lock
81
 
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID
82
 
from bzrlib.lockable_files import LockableFiles, TransportLock
 
87
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, TreeReference
 
88
from bzrlib.lockable_files import LockableFiles
83
89
from bzrlib.lockdir import LockDir
84
90
import bzrlib.mutabletree
85
91
from bzrlib.mutabletree import needs_tree_write_lock
 
92
from bzrlib import osutils
86
93
from bzrlib.osutils import (
87
94
    compact_date,
88
95
    file_kind,
104
111
        deprecated_method,
105
112
        deprecated_function,
106
113
        DEPRECATED_PARAMETER,
107
 
        zero_eight,
108
 
        zero_eleven,
109
 
        zero_thirteen,
110
114
        )
111
115
 
112
116
 
113
117
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
114
118
CONFLICT_HEADER_1 = "BZR conflict list format 1"
115
119
 
116
 
 
117
 
@deprecated_function(zero_thirteen)
118
 
def gen_file_id(name):
119
 
    """Return new file id for the basename 'name'.
120
 
 
121
 
    Use bzrlib.generate_ids.gen_file_id() instead
122
 
    """
123
 
    return generate_ids.gen_file_id(name)
124
 
 
125
 
 
126
 
@deprecated_function(zero_thirteen)
127
 
def gen_root_id():
128
 
    """Return a new tree-root file id.
129
 
 
130
 
    This has been deprecated in favor of bzrlib.generate_ids.gen_root_id()
131
 
    """
132
 
    return generate_ids.gen_root_id()
 
120
ERROR_PATH_NOT_FOUND = 3    # WindowsError errno code, equivalent to ENOENT
133
121
 
134
122
 
135
123
class TreeEntry(object):
204
192
                 _internal=False,
205
193
                 _format=None,
206
194
                 _bzrdir=None):
207
 
        """Construct a WorkingTree for basedir.
 
195
        """Construct a WorkingTree instance. This is not a public API.
208
196
 
209
 
        If the branch is not supplied, it is opened automatically.
210
 
        If the branch is supplied, it must be the branch for this basedir.
211
 
        (branch.base is not cross checked, because for remote branches that
212
 
        would be meaningless).
 
197
        :param branch: A branch to override probing for the branch.
213
198
        """
214
199
        self._format = _format
215
200
        self.bzrdir = _bzrdir
216
201
        if not _internal:
217
 
            # not created via open etc.
218
 
            warnings.warn("WorkingTree() is deprecated as of bzr version 0.8. "
219
 
                 "Please use bzrdir.open_workingtree or WorkingTree.open().",
220
 
                 DeprecationWarning,
221
 
                 stacklevel=2)
222
 
            wt = WorkingTree.open(basedir)
223
 
            self._branch = wt.branch
224
 
            self.basedir = wt.basedir
225
 
            self._control_files = wt._control_files
226
 
            self._hashcache = wt._hashcache
227
 
            self._set_inventory(wt._inventory, dirty=False)
228
 
            self._format = wt._format
229
 
            self.bzrdir = wt.bzrdir
230
 
        assert isinstance(basedir, basestring), \
231
 
            "base directory %r is not a string" % basedir
 
202
            raise errors.BzrError("Please use bzrdir.open_workingtree or "
 
203
                "WorkingTree.open() to obtain a WorkingTree.")
232
204
        basedir = safe_unicode(basedir)
233
205
        mutter("opening working tree %r", basedir)
234
206
        if deprecated_passed(branch):
235
 
            if not _internal:
236
 
                warnings.warn("WorkingTree(..., branch=XXX) is deprecated"
237
 
                     " as of bzr 0.8. Please use bzrdir.open_workingtree() or"
238
 
                     " WorkingTree.open().",
239
 
                     DeprecationWarning,
240
 
                     stacklevel=2
241
 
                     )
242
207
            self._branch = branch
243
208
        else:
244
209
            self._branch = self.bzrdir.open_branch()
249
214
            self._control_files = self.branch.control_files
250
215
        else:
251
216
            # assume all other formats have their own control files.
252
 
            assert isinstance(_control_files, LockableFiles), \
253
 
                    "_control_files must be a LockableFiles, not %r" \
254
 
                    % _control_files
255
217
            self._control_files = _control_files
 
218
        self._transport = self._control_files._transport
256
219
        # update the whole cache up front and write to disk if anything changed;
257
220
        # in the future we might want to do this more selectively
258
221
        # two possible ways offer themselves : in self._unlock, write the cache
262
225
        wt_trans = self.bzrdir.get_workingtree_transport(None)
263
226
        cache_filename = wt_trans.local_abspath('stat-cache')
264
227
        self._hashcache = hashcache.HashCache(basedir, cache_filename,
265
 
                                              self._control_files._file_mode)
 
228
            self.bzrdir._get_file_mode())
266
229
        hc = self._hashcache
267
230
        hc.read()
268
231
        # is this scan needed ? it makes things kinda slow.
273
236
            hc.write()
274
237
 
275
238
        if _inventory is None:
 
239
            # This will be acquired on lock_read() or lock_write()
276
240
            self._inventory_is_modified = False
277
 
            self.read_working_inventory()
 
241
            self._inventory = None
278
242
        else:
279
243
            # the caller of __init__ has provided an inventory,
280
244
            # we assume they know what they are doing - as its only
281
245
            # the Format factory and creation methods that are
282
246
            # permitted to do this.
283
247
            self._set_inventory(_inventory, dirty=False)
 
248
        self._detect_case_handling()
 
249
        self._rules_searcher = None
 
250
 
 
251
    def _detect_case_handling(self):
 
252
        wt_trans = self.bzrdir.get_workingtree_transport(None)
 
253
        try:
 
254
            wt_trans.stat("FoRMaT")
 
255
        except errors.NoSuchFile:
 
256
            self.case_sensitive = True
 
257
        else:
 
258
            self.case_sensitive = False
 
259
 
 
260
        self._setup_directory_is_tree_reference()
284
261
 
285
262
    branch = property(
286
263
        fget=lambda self: self._branch,
301
278
        self._control_files.break_lock()
302
279
        self.branch.break_lock()
303
280
 
 
281
    def requires_rich_root(self):
 
282
        return self._format.requires_rich_root
 
283
 
 
284
    def supports_tree_reference(self):
 
285
        return False
 
286
 
 
287
    def supports_content_filtering(self):
 
288
        return self._format.supports_content_filtering()
 
289
 
 
290
    def supports_views(self):
 
291
        return self._format.supports_views()
 
292
 
304
293
    def _set_inventory(self, inv, dirty):
305
294
        """Set the internal cached inventory.
306
295
 
311
300
            False then the inventory is the same as that on disk and any
312
301
            serialisation would be unneeded overhead.
313
302
        """
314
 
        assert inv.root is not None
315
303
        self._inventory = inv
316
304
        self._inventory_is_modified = dirty
317
305
 
321
309
 
322
310
        """
323
311
        if path is None:
324
 
            path = os.path.getcwdu()
 
312
            path = osutils.getcwd()
325
313
        control = bzrdir.BzrDir.open(path, _unsupported)
326
314
        return control.open_workingtree(_unsupported)
327
 
        
 
315
 
328
316
    @staticmethod
329
317
    def open_containing(path=None):
330
318
        """Open an existing working tree which has its root about path.
331
 
        
 
319
 
332
320
        This probes for a working tree at path and searches upwards from there.
333
321
 
334
322
        Basically we keep looking up until we find the control directory or
352
340
        """
353
341
        return WorkingTree.open(path, _unsupported=True)
354
342
 
 
343
    @staticmethod
 
344
    def find_trees(location):
 
345
        def list_current(transport):
 
346
            return [d for d in transport.list_dir('') if d != '.bzr']
 
347
        def evaluate(bzrdir):
 
348
            try:
 
349
                tree = bzrdir.open_workingtree()
 
350
            except errors.NoWorkingTree:
 
351
                return True, None
 
352
            else:
 
353
                return True, tree
 
354
        transport = get_transport(location)
 
355
        iterator = bzrdir.BzrDir.find_bzrdirs(transport, evaluate=evaluate,
 
356
                                              list_current=list_current)
 
357
        return [t for t in iterator if t is not None]
 
358
 
 
359
    # should be deprecated - this is slow and in any case treating them as a
 
360
    # container is (we now know) bad style -- mbp 20070302
 
361
    ## @deprecated_method(zero_fifteen)
355
362
    def __iter__(self):
356
363
        """Iterate through file_ids for this tree.
357
364
 
363
370
            if osutils.lexists(self.abspath(path)):
364
371
                yield ie.file_id
365
372
 
 
373
    def all_file_ids(self):
 
374
        """See Tree.iter_all_file_ids"""
 
375
        return set(self.inventory)
 
376
 
366
377
    def __repr__(self):
367
378
        return "<%s of %s>" % (self.__class__.__name__,
368
379
                               getattr(self, 'basedir', None))
369
380
 
370
381
    def abspath(self, filename):
371
382
        return pathjoin(self.basedir, filename)
372
 
    
 
383
 
373
384
    def basis_tree(self):
374
385
        """Return RevisionTree for the current last revision.
375
386
        
376
387
        If the left most parent is a ghost then the returned tree will be an
377
 
        empty tree - one obtained by calling repository.revision_tree(None).
 
388
        empty tree - one obtained by calling 
 
389
        repository.revision_tree(NULL_REVISION).
378
390
        """
379
391
        try:
380
392
            revision_id = self.get_parent_ids()[0]
382
394
            # no parents, return an empty revision tree.
383
395
            # in the future this should return the tree for
384
396
            # 'empty:' - the implicit root empty tree.
385
 
            return self.branch.repository.revision_tree(None)
 
397
            return self.branch.repository.revision_tree(
 
398
                       _mod_revision.NULL_REVISION)
386
399
        try:
387
400
            return self.revision_tree(revision_id)
388
401
        except errors.NoSuchRevision:
392
405
        # at this point ?
393
406
        try:
394
407
            return self.branch.repository.revision_tree(revision_id)
395
 
        except errors.RevisionNotPresent:
 
408
        except (errors.RevisionNotPresent, errors.NoSuchRevision):
396
409
            # the basis tree *may* be a ghost or a low level error may have
397
410
            # occured. If the revision is present, its a problem, if its not
398
411
            # its a ghost.
399
412
            if self.branch.repository.has_revision(revision_id):
400
413
                raise
401
414
            # the basis tree is a ghost so return an empty tree.
402
 
            return self.branch.repository.revision_tree(None)
403
 
 
404
 
    @staticmethod
405
 
    @deprecated_method(zero_eight)
406
 
    def create(branch, directory):
407
 
        """Create a workingtree for branch at directory.
408
 
 
409
 
        If existing_directory already exists it must have a .bzr directory.
410
 
        If it does not exist, it will be created.
411
 
 
412
 
        This returns a new WorkingTree object for the new checkout.
413
 
 
414
 
        TODO FIXME RBC 20060124 when we have checkout formats in place this
415
 
        should accept an optional revisionid to checkout [and reject this if
416
 
        checking out into the same dir as a pre-checkout-aware branch format.]
417
 
 
418
 
        XXX: When BzrDir is present, these should be created through that 
419
 
        interface instead.
420
 
        """
421
 
        warnings.warn('delete WorkingTree.create', stacklevel=3)
422
 
        transport = get_transport(directory)
423
 
        if branch.bzrdir.root_transport.base == transport.base:
424
 
            # same dir 
425
 
            return branch.bzrdir.create_workingtree()
426
 
        # different directory, 
427
 
        # create a branch reference
428
 
        # and now a working tree.
429
 
        raise NotImplementedError
430
 
 
431
 
    @staticmethod
432
 
    @deprecated_method(zero_eight)
433
 
    def create_standalone(directory):
434
 
        """Create a checkout and a branch and a repo at directory.
435
 
 
436
 
        Directory must exist and be empty.
437
 
 
438
 
        please use BzrDir.create_standalone_workingtree
439
 
        """
440
 
        return bzrdir.BzrDir.create_standalone_workingtree(directory)
 
415
            return self.branch.repository.revision_tree(
 
416
                       _mod_revision.NULL_REVISION)
 
417
 
 
418
    def _cleanup(self):
 
419
        self._flush_ignore_list_cache()
441
420
 
442
421
    def relpath(self, path):
443
422
        """Return the local path portion from a given path.
450
429
    def has_filename(self, filename):
451
430
        return osutils.lexists(self.abspath(filename))
452
431
 
453
 
    def get_file(self, file_id):
454
 
        file_id = osutils.safe_file_id(file_id)
455
 
        return self.get_file_byname(self.id2path(file_id))
 
432
    def get_file(self, file_id, path=None):
 
433
        return self.get_file_with_stat(file_id, path)[0]
456
434
 
457
 
    def get_file_text(self, file_id):
458
 
        file_id = osutils.safe_file_id(file_id)
459
 
        return self.get_file(file_id).read()
 
435
    def get_file_with_stat(self, file_id, path=None, _fstat=os.fstat):
 
436
        """See MutableTree.get_file_with_stat."""
 
437
        if path is None:
 
438
            path = self.id2path(file_id)
 
439
        file_obj = self.get_file_byname(path)
 
440
        return (file_obj, _fstat(file_obj.fileno()))
460
441
 
461
442
    def get_file_byname(self, filename):
462
443
        return file(self.abspath(filename), 'rb')
463
444
 
 
445
    def get_file_lines(self, file_id, path=None):
 
446
        """See Tree.get_file_lines()"""
 
447
        file = self.get_file(file_id, path)
 
448
        try:
 
449
            return file.readlines()
 
450
        finally:
 
451
            file.close()
 
452
 
464
453
    @needs_read_lock
465
 
    def annotate_iter(self, file_id):
 
454
    def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
466
455
        """See Tree.annotate_iter
467
456
 
468
457
        This implementation will use the basis tree implementation if possible.
472
461
        incorrectly attributed to CURRENT_REVISION (but after committing, the
473
462
        attribution will be correct).
474
463
        """
475
 
        file_id = osutils.safe_file_id(file_id)
476
464
        basis = self.basis_tree()
477
 
        changes = self._iter_changes(basis, True, [self.id2path(file_id)],
478
 
            require_versioned=True).next()
479
 
        changed_content, kind = changes[2], changes[6]
480
 
        if not changed_content:
481
 
            return basis.annotate_iter(file_id)
482
 
        if kind[1] is None:
483
 
            return None
484
 
        import annotate
485
 
        if kind[0] != 'file':
486
 
            old_lines = []
487
 
        else:
488
 
            old_lines = list(basis.annotate_iter(file_id))
489
 
        old = [old_lines]
490
 
        for tree in self.branch.repository.revision_trees(
491
 
            self.get_parent_ids()[1:]):
492
 
            if file_id not in tree:
493
 
                continue
494
 
            old.append(list(tree.annotate_iter(file_id)))
495
 
        return annotate.reannotate(old, self.get_file(file_id).readlines(),
496
 
                                   CURRENT_REVISION)
 
465
        basis.lock_read()
 
466
        try:
 
467
            changes = self.iter_changes(basis, True, [self.id2path(file_id)],
 
468
                require_versioned=True).next()
 
469
            changed_content, kind = changes[2], changes[6]
 
470
            if not changed_content:
 
471
                return basis.annotate_iter(file_id)
 
472
            if kind[1] is None:
 
473
                return None
 
474
            import annotate
 
475
            if kind[0] != 'file':
 
476
                old_lines = []
 
477
            else:
 
478
                old_lines = list(basis.annotate_iter(file_id))
 
479
            old = [old_lines]
 
480
            for tree in self.branch.repository.revision_trees(
 
481
                self.get_parent_ids()[1:]):
 
482
                if file_id not in tree:
 
483
                    continue
 
484
                old.append(list(tree.annotate_iter(file_id)))
 
485
            return annotate.reannotate(old, self.get_file(file_id).readlines(),
 
486
                                       default_revision)
 
487
        finally:
 
488
            basis.unlock()
 
489
 
 
490
    def _get_ancestors(self, default_revision):
 
491
        ancestors = set([default_revision])
 
492
        for parent_id in self.get_parent_ids():
 
493
            ancestors.update(self.branch.repository.get_ancestry(
 
494
                             parent_id, topo_sorted=False))
 
495
        return ancestors
497
496
 
498
497
    def get_parent_ids(self):
499
498
        """See Tree.get_parent_ids.
501
500
        This implementation reads the pending merges list and last_revision
502
501
        value and uses that to decide what the parents list should be.
503
502
        """
504
 
        last_rev = self._last_revision()
505
 
        if last_rev is None:
 
503
        last_rev = _mod_revision.ensure_null(self._last_revision())
 
504
        if _mod_revision.NULL_REVISION == last_rev:
506
505
            parents = []
507
506
        else:
508
507
            parents = [last_rev]
509
508
        try:
510
 
            merges_file = self._control_files.get('pending-merges')
 
509
            merges_file = self._transport.get('pending-merges')
511
510
        except errors.NoSuchFile:
512
511
            pass
513
512
        else:
514
513
            for l in merges_file.readlines():
515
 
                revision_id = osutils.safe_revision_id(l.rstrip('\n'))
 
514
                revision_id = l.rstrip('\n')
516
515
                parents.append(revision_id)
517
516
        return parents
518
517
 
523
522
        
524
523
    def _get_store_filename(self, file_id):
525
524
        ## XXX: badly named; this is not in the store at all
526
 
        file_id = osutils.safe_file_id(file_id)
527
525
        return self.abspath(self.id2path(file_id))
528
526
 
529
527
    @needs_read_lock
530
 
    def clone(self, to_bzrdir, revision_id=None, basis=None):
 
528
    def clone(self, to_bzrdir, revision_id=None):
531
529
        """Duplicate this working tree into to_bzr, including all state.
532
530
        
533
531
        Specifically modified files are kept as modified, but
539
537
            If not None, the cloned tree will have its last revision set to 
540
538
            revision, and and difference between the source trees last revision
541
539
            and this one merged in.
542
 
 
543
 
        basis
544
 
            If not None, a closer copy of a tree which may have some files in
545
 
            common, and which file content should be preferentially copied from.
546
540
        """
547
541
        # assumes the target bzr dir format is compatible.
548
 
        result = self._format.initialize(to_bzrdir)
 
542
        result = to_bzrdir.create_workingtree()
549
543
        self.copy_content_into(result, revision_id)
550
544
        return result
551
545
 
562
556
            tree.set_parent_ids([revision_id])
563
557
 
564
558
    def id2abspath(self, file_id):
565
 
        file_id = osutils.safe_file_id(file_id)
566
559
        return self.abspath(self.id2path(file_id))
567
560
 
568
561
    def has_id(self, file_id):
569
562
        # files that have been deleted are excluded
570
 
        file_id = osutils.safe_file_id(file_id)
571
563
        inv = self.inventory
572
564
        if not inv.has_id(file_id):
573
565
            return False
575
567
        return osutils.lexists(self.abspath(path))
576
568
 
577
569
    def has_or_had_id(self, file_id):
578
 
        file_id = osutils.safe_file_id(file_id)
579
570
        if file_id == self.inventory.root.file_id:
580
571
            return True
581
572
        return self.inventory.has_id(file_id)
583
574
    __contains__ = has_id
584
575
 
585
576
    def get_file_size(self, file_id):
586
 
        file_id = osutils.safe_file_id(file_id)
587
 
        return os.path.getsize(self.id2abspath(file_id))
 
577
        """See Tree.get_file_size"""
 
578
        try:
 
579
            return os.path.getsize(self.id2abspath(file_id))
 
580
        except OSError, e:
 
581
            if e.errno != errno.ENOENT:
 
582
                raise
 
583
            else:
 
584
                return None
588
585
 
589
586
    @needs_read_lock
590
587
    def get_file_sha1(self, file_id, path=None, stat_value=None):
591
 
        file_id = osutils.safe_file_id(file_id)
592
588
        if not path:
593
589
            path = self._inventory.id2path(file_id)
594
590
        return self._hashcache.get_sha1(path, stat_value)
595
591
 
596
592
    def get_file_mtime(self, file_id, path=None):
597
 
        file_id = osutils.safe_file_id(file_id)
598
593
        if not path:
599
594
            path = self.inventory.id2path(file_id)
600
595
        return os.lstat(self.abspath(path)).st_mtime
601
596
 
 
597
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
 
598
        file_id = self.path2id(path)
 
599
        return self._inventory[file_id].executable
 
600
 
 
601
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
 
602
        mode = stat_result.st_mode
 
603
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
 
604
 
602
605
    if not supports_executable():
603
606
        def is_executable(self, file_id, path=None):
604
 
            file_id = osutils.safe_file_id(file_id)
605
607
            return self._inventory[file_id].executable
 
608
 
 
609
        _is_executable_from_path_and_stat = \
 
610
            _is_executable_from_path_and_stat_from_basis
606
611
    else:
607
612
        def is_executable(self, file_id, path=None):
608
613
            if not path:
609
 
                file_id = osutils.safe_file_id(file_id)
610
614
                path = self.id2path(file_id)
611
615
            mode = os.lstat(self.abspath(path)).st_mode
612
616
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
613
617
 
614
 
    @needs_write_lock
 
618
        _is_executable_from_path_and_stat = \
 
619
            _is_executable_from_path_and_stat_from_stat
 
620
 
 
621
    @needs_tree_write_lock
615
622
    def _add(self, files, ids, kinds):
616
623
        """See MutableTree._add."""
617
624
        # TODO: Re-adding a file that is removed in the working copy
618
625
        # should probably put it back with the previous ID.
619
626
        # the read and write working inventory should not occur in this 
620
627
        # function - they should be part of lock_write and unlock.
621
 
        inv = self.read_working_inventory()
 
628
        inv = self.inventory
622
629
        for f, file_id, kind in zip(files, ids, kinds):
623
 
            assert kind is not None
624
630
            if file_id is None:
625
631
                inv.add_path(f, kind=kind)
626
632
            else:
627
 
                file_id = osutils.safe_file_id(file_id)
628
633
                inv.add_path(f, kind=kind, file_id=file_id)
629
 
        self._write_inventory(inv)
 
634
            self._inventory_is_modified = True
630
635
 
631
636
    @needs_tree_write_lock
632
637
    def _gather_kinds(self, files, kinds):
692
697
        if updated:
693
698
            self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
694
699
 
695
 
    @deprecated_method(zero_eleven)
696
 
    @needs_read_lock
697
 
    def pending_merges(self):
698
 
        """Return a list of pending merges.
699
 
 
700
 
        These are revisions that have been merged into the working
701
 
        directory but not yet committed.
702
 
 
703
 
        As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
704
 
        instead - which is available on all tree objects.
705
 
        """
706
 
        return self.get_parent_ids()[1:]
 
700
    def path_content_summary(self, path, _lstat=os.lstat,
 
701
        _mapper=osutils.file_kind_from_stat_mode):
 
702
        """See Tree.path_content_summary."""
 
703
        abspath = self.abspath(path)
 
704
        try:
 
705
            stat_result = _lstat(abspath)
 
706
        except OSError, e:
 
707
            if getattr(e, 'errno', None) == errno.ENOENT:
 
708
                # no file.
 
709
                return ('missing', None, None, None)
 
710
            # propagate other errors
 
711
            raise
 
712
        kind = _mapper(stat_result.st_mode)
 
713
        if kind == 'file':
 
714
            size = stat_result.st_size
 
715
            # try for a stat cache lookup
 
716
            executable = self._is_executable_from_path_and_stat(path, stat_result)
 
717
            return (kind, size, executable, self._sha_from_stat(
 
718
                path, stat_result))
 
719
        elif kind == 'directory':
 
720
            # perhaps it looks like a plain directory, but it's really a
 
721
            # reference.
 
722
            if self._directory_is_tree_reference(path):
 
723
                kind = 'tree-reference'
 
724
            return kind, None, None, None
 
725
        elif kind == 'symlink':
 
726
            return ('symlink', None, None, os.readlink(abspath))
 
727
        else:
 
728
            return (kind, None, None, None)
707
729
 
708
730
    def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
709
731
        """Common ghost checking functionality from set_parent_*.
719
741
 
720
742
    def _set_merges_from_parent_ids(self, parent_ids):
721
743
        merges = parent_ids[1:]
722
 
        self._control_files.put_bytes('pending-merges', '\n'.join(merges))
 
744
        self._transport.put_bytes('pending-merges', '\n'.join(merges),
 
745
            mode=self._control_files._file_mode)
 
746
 
 
747
    def _filter_parent_ids_by_ancestry(self, revision_ids):
 
748
        """Check that all merged revisions are proper 'heads'.
 
749
 
 
750
        This will always return the first revision_id, and any merged revisions
 
751
        which are 
 
752
        """
 
753
        if len(revision_ids) == 0:
 
754
            return revision_ids
 
755
        graph = self.branch.repository.get_graph()
 
756
        heads = graph.heads(revision_ids)
 
757
        new_revision_ids = revision_ids[:1]
 
758
        for revision_id in revision_ids[1:]:
 
759
            if revision_id in heads and revision_id not in new_revision_ids:
 
760
                new_revision_ids.append(revision_id)
 
761
        if new_revision_ids != revision_ids:
 
762
            trace.mutter('requested to set revision_ids = %s,'
 
763
                         ' but filtered to %s', revision_ids, new_revision_ids)
 
764
        return new_revision_ids
723
765
 
724
766
    @needs_tree_write_lock
725
767
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
734
776
        :param revision_ids: The revision_ids to set as the parent ids of this
735
777
            working tree. Any of these may be ghosts.
736
778
        """
737
 
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
738
779
        self._check_parents_for_ghosts(revision_ids,
739
780
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
781
        for revision_id in revision_ids:
 
782
            _mod_revision.check_not_reserved_id(revision_id)
 
783
 
 
784
        revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
740
785
 
741
786
        if len(revision_ids) > 0:
742
787
            self.set_last_revision(revision_ids[0])
743
788
        else:
744
 
            self.set_last_revision(None)
 
789
            self.set_last_revision(_mod_revision.NULL_REVISION)
745
790
 
746
791
        self._set_merges_from_parent_ids(revision_ids)
747
792
 
748
793
    @needs_tree_write_lock
749
794
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
750
795
        """See MutableTree.set_parent_trees."""
751
 
        parent_ids = [osutils.safe_revision_id(rev) for (rev, tree) in parents_list]
 
796
        parent_ids = [rev for (rev, tree) in parents_list]
 
797
        for revision_id in parent_ids:
 
798
            _mod_revision.check_not_reserved_id(revision_id)
752
799
 
753
800
        self._check_parents_for_ghosts(parent_ids,
754
801
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
755
802
 
 
803
        parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
 
804
 
756
805
        if len(parent_ids) == 0:
757
 
            leftmost_parent_id = None
 
806
            leftmost_parent_id = _mod_revision.NULL_REVISION
758
807
            leftmost_parent_tree = None
759
808
        else:
760
809
            leftmost_parent_id, leftmost_parent_tree = parents_list[0]
785
834
                yield Stanza(file_id=file_id.decode('utf8'), hash=hash)
786
835
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
787
836
 
788
 
    @needs_tree_write_lock
 
837
    def _sha_from_stat(self, path, stat_result):
 
838
        """Get a sha digest from the tree's stat cache.
 
839
 
 
840
        The default implementation assumes no stat cache is present.
 
841
 
 
842
        :param path: The path.
 
843
        :param stat_result: The stat result being looked up.
 
844
        """
 
845
        return None
 
846
 
789
847
    def _put_rio(self, filename, stanzas, header):
 
848
        self._must_be_locked()
790
849
        my_file = rio_file(stanzas, header)
791
 
        self._control_files.put(filename, my_file)
 
850
        self._transport.put_file(filename, my_file,
 
851
            mode=self._control_files._file_mode)
792
852
 
793
853
    @needs_write_lock # because merge pulls data into the branch.
794
 
    def merge_from_branch(self, branch, to_revision=None):
 
854
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
 
855
        merge_type=None):
795
856
        """Merge from a branch into this working tree.
796
857
 
797
858
        :param branch: The branch to merge from.
810
871
            # local alterations
811
872
            merger.check_basis(check_clean=True, require_commits=False)
812
873
            if to_revision is None:
813
 
                to_revision = branch.last_revision()
814
 
            else:
815
 
                to_revision = osutils.safe_revision_id(to_revision)
 
874
                to_revision = _mod_revision.ensure_null(branch.last_revision())
816
875
            merger.other_rev_id = to_revision
817
 
            if merger.other_rev_id is None:
818
 
                raise error.NoCommits(branch)
 
876
            if _mod_revision.is_null(merger.other_rev_id):
 
877
                raise errors.NoCommits(branch)
819
878
            self.branch.fetch(branch, last_revision=merger.other_rev_id)
820
879
            merger.other_basis = merger.other_rev_id
821
880
            merger.other_tree = self.branch.repository.revision_tree(
822
881
                merger.other_rev_id)
 
882
            merger.other_branch = branch
823
883
            merger.pp.next_phase()
824
 
            merger.find_base()
 
884
            if from_revision is None:
 
885
                merger.find_base()
 
886
            else:
 
887
                merger.set_base_revision(from_revision, branch)
825
888
            if merger.base_rev_id == merger.other_rev_id:
826
889
                raise errors.PointlessMerge
827
890
            merger.backup_files = False
828
 
            merger.merge_type = Merge3Merger
 
891
            if merge_type is None:
 
892
                merger.merge_type = Merge3Merger
 
893
            else:
 
894
                merger.merge_type = merge_type
829
895
            merger.set_interesting_files(None)
830
896
            merger.show_base = False
831
897
            merger.reprocess = False
837
903
 
838
904
    @needs_read_lock
839
905
    def merge_modified(self):
 
906
        """Return a dictionary of files modified by a merge.
 
907
 
 
908
        The list is initialized by WorkingTree.set_merge_modified, which is 
 
909
        typically called after we make some automatic updates to the tree
 
910
        because of a merge.
 
911
 
 
912
        This returns a map of file_id->sha1, containing only files which are
 
913
        still in the working inventory and have that text hash.
 
914
        """
840
915
        try:
841
 
            hashfile = self._control_files.get('merge-hashes')
 
916
            hashfile = self._transport.get('merge-hashes')
842
917
        except errors.NoSuchFile:
843
918
            return {}
844
 
        merge_hashes = {}
845
919
        try:
846
 
            if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
 
920
            merge_hashes = {}
 
921
            try:
 
922
                if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
 
923
                    raise errors.MergeModifiedFormatError()
 
924
            except StopIteration:
847
925
                raise errors.MergeModifiedFormatError()
848
 
        except StopIteration:
849
 
            raise errors.MergeModifiedFormatError()
850
 
        for s in RioReader(hashfile):
851
 
            file_id = s.get("file_id")
852
 
            if file_id not in self.inventory:
853
 
                continue
854
 
            hash = s.get("hash")
855
 
            if hash == self.get_file_sha1(file_id):
856
 
                merge_hashes[file_id] = hash
857
 
        return merge_hashes
 
926
            for s in RioReader(hashfile):
 
927
                # RioReader reads in Unicode, so convert file_ids back to utf8
 
928
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
 
929
                if file_id not in self.inventory:
 
930
                    continue
 
931
                text_hash = s.get("hash")
 
932
                if text_hash == self.get_file_sha1(file_id):
 
933
                    merge_hashes[file_id] = text_hash
 
934
            return merge_hashes
 
935
        finally:
 
936
            hashfile.close()
858
937
 
859
938
    @needs_write_lock
860
939
    def mkdir(self, path, file_id=None):
868
947
    def get_symlink_target(self, file_id):
869
948
        return os.readlink(self.id2abspath(file_id))
870
949
 
871
 
    def file_class(self, filename):
872
 
        if self.path2id(filename):
873
 
            return 'V'
874
 
        elif self.is_ignored(filename):
875
 
            return 'I'
876
 
        else:
877
 
            return '?'
 
950
    @needs_write_lock
 
951
    def subsume(self, other_tree):
 
952
        def add_children(inventory, entry):
 
953
            for child_entry in entry.children.values():
 
954
                inventory._byid[child_entry.file_id] = child_entry
 
955
                if child_entry.kind == 'directory':
 
956
                    add_children(inventory, child_entry)
 
957
        if other_tree.get_root_id() == self.get_root_id():
 
958
            raise errors.BadSubsumeSource(self, other_tree,
 
959
                                          'Trees have the same root')
 
960
        try:
 
961
            other_tree_path = self.relpath(other_tree.basedir)
 
962
        except errors.PathNotChild:
 
963
            raise errors.BadSubsumeSource(self, other_tree,
 
964
                'Tree is not contained by the other')
 
965
        new_root_parent = self.path2id(osutils.dirname(other_tree_path))
 
966
        if new_root_parent is None:
 
967
            raise errors.BadSubsumeSource(self, other_tree,
 
968
                'Parent directory is not versioned.')
 
969
        # We need to ensure that the result of a fetch will have a
 
970
        # versionedfile for the other_tree root, and only fetching into
 
971
        # RepositoryKnit2 guarantees that.
 
972
        if not self.branch.repository.supports_rich_root():
 
973
            raise errors.SubsumeTargetNeedsUpgrade(other_tree)
 
974
        other_tree.lock_tree_write()
 
975
        try:
 
976
            new_parents = other_tree.get_parent_ids()
 
977
            other_root = other_tree.inventory.root
 
978
            other_root.parent_id = new_root_parent
 
979
            other_root.name = osutils.basename(other_tree_path)
 
980
            self.inventory.add(other_root)
 
981
            add_children(self.inventory, other_root)
 
982
            self._write_inventory(self.inventory)
 
983
            # normally we don't want to fetch whole repositories, but i think
 
984
            # here we really do want to consolidate the whole thing.
 
985
            for parent_id in other_tree.get_parent_ids():
 
986
                self.branch.fetch(other_tree.branch, parent_id)
 
987
                self.add_parent_tree_id(parent_id)
 
988
        finally:
 
989
            other_tree.unlock()
 
990
        other_tree.bzrdir.retire_bzrdir()
 
991
 
 
992
    def _setup_directory_is_tree_reference(self):
 
993
        if self._branch.repository._format.supports_tree_reference:
 
994
            self._directory_is_tree_reference = \
 
995
                self._directory_may_be_tree_reference
 
996
        else:
 
997
            self._directory_is_tree_reference = \
 
998
                self._directory_is_never_tree_reference
 
999
 
 
1000
    def _directory_is_never_tree_reference(self, relpath):
 
1001
        return False
 
1002
 
 
1003
    def _directory_may_be_tree_reference(self, relpath):
 
1004
        # as a special case, if a directory contains control files then 
 
1005
        # it's a tree reference, except that the root of the tree is not
 
1006
        return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
 
1007
        # TODO: We could ask all the control formats whether they
 
1008
        # recognize this directory, but at the moment there's no cheap api
 
1009
        # to do that.  Since we probably can only nest bzr checkouts and
 
1010
        # they always use this name it's ok for now.  -- mbp 20060306
 
1011
        #
 
1012
        # FIXME: There is an unhandled case here of a subdirectory
 
1013
        # containing .bzr but not a branch; that will probably blow up
 
1014
        # when you try to commit it.  It might happen if there is a
 
1015
        # checkout in a subdirectory.  This can be avoided by not adding
 
1016
        # it.  mbp 20070306
 
1017
 
 
1018
    @needs_tree_write_lock
 
1019
    def extract(self, file_id, format=None):
 
1020
        """Extract a subtree from this tree.
 
1021
        
 
1022
        A new branch will be created, relative to the path for this tree.
 
1023
        """
 
1024
        self.flush()
 
1025
        def mkdirs(path):
 
1026
            segments = osutils.splitpath(path)
 
1027
            transport = self.branch.bzrdir.root_transport
 
1028
            for name in segments:
 
1029
                transport = transport.clone(name)
 
1030
                transport.ensure_base()
 
1031
            return transport
 
1032
            
 
1033
        sub_path = self.id2path(file_id)
 
1034
        branch_transport = mkdirs(sub_path)
 
1035
        if format is None:
 
1036
            format = self.bzrdir.cloning_metadir()
 
1037
        branch_transport.ensure_base()
 
1038
        branch_bzrdir = format.initialize_on_transport(branch_transport)
 
1039
        try:
 
1040
            repo = branch_bzrdir.find_repository()
 
1041
        except errors.NoRepositoryPresent:
 
1042
            repo = branch_bzrdir.create_repository()
 
1043
        if not repo.supports_rich_root():
 
1044
            raise errors.RootNotRich()
 
1045
        new_branch = branch_bzrdir.create_branch()
 
1046
        new_branch.pull(self.branch)
 
1047
        for parent_id in self.get_parent_ids():
 
1048
            new_branch.fetch(self.branch, parent_id)
 
1049
        tree_transport = self.bzrdir.root_transport.clone(sub_path)
 
1050
        if tree_transport.base != branch_transport.base:
 
1051
            tree_bzrdir = format.initialize_on_transport(tree_transport)
 
1052
            branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
 
1053
        else:
 
1054
            tree_bzrdir = branch_bzrdir
 
1055
        wt = tree_bzrdir.create_workingtree(NULL_REVISION)
 
1056
        wt.set_parent_ids(self.get_parent_ids())
 
1057
        my_inv = self.inventory
 
1058
        child_inv = Inventory(root_id=None)
 
1059
        new_root = my_inv[file_id]
 
1060
        my_inv.remove_recursive_id(file_id)
 
1061
        new_root.parent_id = None
 
1062
        child_inv.add(new_root)
 
1063
        self._write_inventory(my_inv)
 
1064
        wt._write_inventory(child_inv)
 
1065
        return wt
 
1066
 
 
1067
    def _serialize(self, inventory, out_file):
 
1068
        xml5.serializer_v5.write_inventory(self._inventory, out_file,
 
1069
            working=True)
 
1070
 
 
1071
    def _deserialize(selt, in_file):
 
1072
        return xml5.serializer_v5.read_inventory(in_file)
878
1073
 
879
1074
    def flush(self):
880
1075
        """Write the in memory inventory to disk."""
882
1077
        if self._control_files._lock_mode != 'w':
883
1078
            raise errors.NotWriteLocked(self)
884
1079
        sio = StringIO()
885
 
        xml5.serializer_v5.write_inventory(self._inventory, sio)
 
1080
        self._serialize(self._inventory, sio)
886
1081
        sio.seek(0)
887
 
        self._control_files.put('inventory', sio)
 
1082
        self._transport.put_file('inventory', sio,
 
1083
            mode=self._control_files._file_mode)
888
1084
        self._inventory_is_modified = False
889
1085
 
 
1086
    def _kind(self, relpath):
 
1087
        return osutils.file_kind(self.abspath(relpath))
 
1088
 
890
1089
    def list_files(self, include_root=False):
891
1090
        """Recursively list all files as (path, class, kind, id, entry).
892
1091
 
907
1106
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
908
1107
        # Convert these into local objects to save lookup times
909
1108
        pathjoin = osutils.pathjoin
910
 
        file_kind = osutils.file_kind
 
1109
        file_kind = self._kind
911
1110
 
912
1111
        # transport.base ends in a slash, we want the piece
913
1112
        # between the last two slashes
973
1172
 
974
1173
                fk = file_kind(fap)
975
1174
 
976
 
                if f_ie:
977
 
                    if f_ie.kind != fk:
978
 
                        raise errors.BzrCheckError(
979
 
                            "file %r entered as kind %r id %r, now of kind %r"
980
 
                            % (fap, f_ie.kind, f_ie.file_id, fk))
981
 
 
982
1175
                # make a last minute entry
983
1176
                if f_ie:
984
1177
                    yield fp[1:], c, fk, f_ie.file_id, f_ie
1054
1247
                                       DeprecationWarning)
1055
1248
 
1056
1249
        # check destination directory
1057
 
        assert not isinstance(from_paths, basestring)
 
1250
        if isinstance(from_paths, basestring):
 
1251
            raise ValueError()
1058
1252
        inv = self.inventory
1059
1253
        to_abs = self.abspath(to_dir)
1060
1254
        if not isdir(to_abs):
1144
1338
                only_change_inv = True
1145
1339
            elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1146
1340
                only_change_inv = False
 
1341
            elif (not self.case_sensitive
 
1342
                  and from_rel.lower() == to_rel.lower()
 
1343
                  and self.has_filename(from_rel)):
 
1344
                only_change_inv = False
1147
1345
            else:
1148
1346
                # something is wrong, so lets determine what exactly
1149
1347
                if not self.has_filename(from_rel) and \
1152
1350
                        errors.PathsDoNotExist(paths=(str(from_rel),
1153
1351
                        str(to_rel))))
1154
1352
                else:
1155
 
                    raise errors.RenameFailedFilesExist(from_rel, to_rel,
1156
 
                        extra="(Use --after to update the Bazaar id)")
 
1353
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
1157
1354
            rename_entry.only_change_inv = only_change_inv
1158
1355
        return rename_entries
1159
1356
 
1294
1491
        # prevent race conditions with the lock
1295
1492
        return iter(
1296
1493
            [subp for subp in self.extras() if not self.is_ignored(subp)])
1297
 
    
 
1494
 
1298
1495
    @needs_tree_write_lock
1299
1496
    def unversion(self, file_ids):
1300
1497
        """Remove the file ids in file_ids from the current versioned set.
1306
1503
        :raises: NoSuchId if any fileid is not currently versioned.
1307
1504
        """
1308
1505
        for file_id in file_ids:
1309
 
            file_id = osutils.safe_file_id(file_id)
1310
1506
            if self._inventory.has_id(file_id):
1311
1507
                self._inventory.remove_recursive_id(file_id)
1312
1508
            else:
1322
1518
            # - RBC 20060907
1323
1519
            self._write_inventory(self._inventory)
1324
1520
    
1325
 
    @deprecated_method(zero_eight)
1326
 
    def iter_conflicts(self):
1327
 
        """List all files in the tree that have text or content conflicts.
1328
 
        DEPRECATED.  Use conflicts instead."""
1329
 
        return self._iter_conflicts()
1330
 
 
1331
1521
    def _iter_conflicts(self):
1332
1522
        conflicted = set()
1333
1523
        for info in self.list_files():
1341
1531
 
1342
1532
    @needs_write_lock
1343
1533
    def pull(self, source, overwrite=False, stop_revision=None,
1344
 
             change_reporter=None):
 
1534
             change_reporter=None, possible_transports=None):
1345
1535
        top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1346
1536
        source.lock_read()
1347
1537
        try:
1349
1539
            pp.next_phase()
1350
1540
            old_revision_info = self.branch.last_revision_info()
1351
1541
            basis_tree = self.basis_tree()
1352
 
            count = self.branch.pull(source, overwrite, stop_revision)
 
1542
            count = self.branch.pull(source, overwrite, stop_revision,
 
1543
                                     possible_transports=possible_transports)
1353
1544
            new_revision_info = self.branch.last_revision_info()
1354
1545
            if new_revision_info != old_revision_info:
1355
1546
                pp.next_phase()
1367
1558
                                change_reporter=change_reporter)
1368
1559
                    if (basis_tree.inventory.root is None and
1369
1560
                        new_basis_tree.inventory.root is not None):
1370
 
                        self.set_root_id(new_basis_tree.inventory.root.file_id)
 
1561
                        self.set_root_id(new_basis_tree.get_root_id())
1371
1562
                finally:
1372
1563
                    pb.finished()
1373
1564
                    basis_tree.unlock()
1393
1584
    @needs_write_lock
1394
1585
    def put_file_bytes_non_atomic(self, file_id, bytes):
1395
1586
        """See MutableTree.put_file_bytes_non_atomic."""
1396
 
        file_id = osutils.safe_file_id(file_id)
1397
1587
        stream = file(self.id2abspath(file_id), 'wb')
1398
1588
        try:
1399
1589
            stream.write(bytes)
1402
1592
        # TODO: update the hashcache here ?
1403
1593
 
1404
1594
    def extras(self):
1405
 
        """Yield all unknown files in this WorkingTree.
 
1595
        """Yield all unversioned files in this WorkingTree.
1406
1596
 
1407
 
        If there are any unknown directories then only the directory is
1408
 
        returned, not all its children.  But if there are unknown files
 
1597
        If there are any unversioned directories then only the directory is
 
1598
        returned, not all its children.  But if there are unversioned files
1409
1599
        under a versioned subdirectory, they are returned.
1410
1600
 
1411
1601
        Currently returned depth-first, sorted by name within directories.
 
1602
        This is the same order used by 'osutils.walkdirs'.
1412
1603
        """
1413
1604
        ## TODO: Work from given directory downwards
1414
1605
        for path, dir_entry in self.inventory.directories():
1423
1614
                if subf == '.bzr':
1424
1615
                    continue
1425
1616
                if subf not in dir_entry.children:
1426
 
                    subf_norm, can_access = osutils.normalized_filename(subf)
 
1617
                    try:
 
1618
                        (subf_norm,
 
1619
                         can_access) = osutils.normalized_filename(subf)
 
1620
                    except UnicodeDecodeError:
 
1621
                        path_os_enc = path.encode(osutils._fs_enc)
 
1622
                        relpath = path_os_enc + '/' + subf
 
1623
                        raise errors.BadFilenameEncoding(relpath,
 
1624
                                                         osutils._fs_enc)
1427
1625
                    if subf_norm != subf and can_access:
1428
1626
                        if subf_norm not in dir_entry.children:
1429
1627
                            fl.append(subf_norm)
1451
1649
        if ignoreset is not None:
1452
1650
            return ignoreset
1453
1651
 
1454
 
        ignore_globs = set(bzrlib.DEFAULT_IGNORE)
 
1652
        ignore_globs = set()
1455
1653
        ignore_globs.update(ignores.get_runtime_ignores())
1456
1654
        ignore_globs.update(ignores.get_user_ignores())
1457
1655
        if self.has_filename(bzrlib.IGNORE_FILENAME):
1484
1682
    def kind(self, file_id):
1485
1683
        return file_kind(self.id2abspath(file_id))
1486
1684
 
 
1685
    def stored_kind(self, file_id):
 
1686
        """See Tree.stored_kind"""
 
1687
        return self.inventory[file_id].kind
 
1688
 
1487
1689
    def _comparison_data(self, entry, path):
1488
1690
        abspath = self.abspath(path)
1489
1691
        try:
1499
1701
            mode = stat_value.st_mode
1500
1702
            kind = osutils.file_kind_from_stat_mode(mode)
1501
1703
            if not supports_executable():
1502
 
                executable = entry.executable
 
1704
                executable = entry is not None and entry.executable
1503
1705
            else:
1504
1706
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1505
1707
        return kind, executable, stat_value
1520
1722
    @needs_read_lock
1521
1723
    def _last_revision(self):
1522
1724
        """helper for get_parent_ids."""
1523
 
        return self.branch.last_revision()
 
1725
        return _mod_revision.ensure_null(self.branch.last_revision())
1524
1726
 
1525
1727
    def is_locked(self):
1526
1728
        return self._control_files.is_locked()
1527
1729
 
 
1730
    def _must_be_locked(self):
 
1731
        if not self.is_locked():
 
1732
            raise errors.ObjectNotLocked(self)
 
1733
 
1528
1734
    def lock_read(self):
1529
1735
        """See Branch.lock_read, and WorkingTree.unlock."""
 
1736
        if not self.is_locked():
 
1737
            self._reset_data()
1530
1738
        self.branch.lock_read()
1531
1739
        try:
1532
1740
            return self._control_files.lock_read()
1536
1744
 
1537
1745
    def lock_tree_write(self):
1538
1746
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
 
1747
        if not self.is_locked():
 
1748
            self._reset_data()
1539
1749
        self.branch.lock_read()
1540
1750
        try:
1541
1751
            return self._control_files.lock_write()
1545
1755
 
1546
1756
    def lock_write(self):
1547
1757
        """See MutableTree.lock_write, and WorkingTree.unlock."""
 
1758
        if not self.is_locked():
 
1759
            self._reset_data()
1548
1760
        self.branch.lock_write()
1549
1761
        try:
1550
1762
            return self._control_files.lock_write()
1558
1770
    def _basis_inventory_name(self):
1559
1771
        return 'basis-inventory-cache'
1560
1772
 
 
1773
    def _reset_data(self):
 
1774
        """Reset transient data that cannot be revalidated."""
 
1775
        self._inventory_is_modified = False
 
1776
        result = self._deserialize(self._transport.get('inventory'))
 
1777
        self._set_inventory(result, dirty=False)
 
1778
 
1561
1779
    @needs_tree_write_lock
1562
1780
    def set_last_revision(self, new_revision):
1563
1781
        """Change the last revision in the working tree."""
1564
 
        new_revision = osutils.safe_revision_id(new_revision)
1565
1782
        if self._change_last_revision(new_revision):
1566
1783
            self._cache_basis_inventory(new_revision)
1567
1784
 
1571
1788
        This is used to allow WorkingTree3 instances to not affect branch
1572
1789
        when their last revision is set.
1573
1790
        """
1574
 
        if new_revision is None:
 
1791
        if _mod_revision.is_null(new_revision):
1575
1792
            self.branch.set_revision_history([])
1576
1793
            return False
1577
1794
        try:
1583
1800
 
1584
1801
    def _write_basis_inventory(self, xml):
1585
1802
        """Write the basis inventory XML to the basis-inventory file"""
1586
 
        assert isinstance(xml, str), 'serialised xml must be bytestring.'
1587
1803
        path = self._basis_inventory_name()
1588
1804
        sio = StringIO(xml)
1589
 
        self._control_files.put(path, sio)
 
1805
        self._transport.put_file(path, sio,
 
1806
            mode=self._control_files._file_mode)
1590
1807
 
1591
1808
    def _create_basis_xml_from_inventory(self, revision_id, inventory):
1592
1809
        """Create the text that will be saved in basis-inventory"""
1593
 
        # TODO: jam 20070209 This should be redundant, as the revision_id
1594
 
        #       as all callers should have already converted the revision_id to
1595
 
        #       utf8
1596
 
        inventory.revision_id = osutils.safe_revision_id(revision_id)
1597
 
        return xml6.serializer_v6.write_inventory_to_string(inventory)
 
1810
        inventory.revision_id = revision_id
 
1811
        return xml7.serializer_v7.write_inventory_to_string(inventory)
1598
1812
 
1599
1813
    def _cache_basis_inventory(self, new_revision):
1600
1814
        """Cache new_revision as the basis inventory."""
1615
1829
            xml = self.branch.repository.get_inventory_xml(new_revision)
1616
1830
            firstline = xml.split('\n', 1)[0]
1617
1831
            if (not 'revision_id="' in firstline or 
1618
 
                'format="6"' not in firstline):
 
1832
                'format="7"' not in firstline):
1619
1833
                inv = self.branch.repository.deserialise_inventory(
1620
1834
                    new_revision, xml)
1621
1835
                xml = self._create_basis_xml_from_inventory(new_revision, inv)
1626
1840
    def read_basis_inventory(self):
1627
1841
        """Read the cached basis inventory."""
1628
1842
        path = self._basis_inventory_name()
1629
 
        return self._control_files.get(path).read()
 
1843
        return self._transport.get_bytes(path)
1630
1844
        
1631
1845
    @needs_read_lock
1632
1846
    def read_working_inventory(self):
1641
1855
        # binary.
1642
1856
        if self._inventory_is_modified:
1643
1857
            raise errors.InventoryModified(self)
1644
 
        result = xml5.serializer_v5.read_inventory(
1645
 
            self._control_files.get('inventory'))
 
1858
        result = self._deserialize(self._transport.get('inventory'))
1646
1859
        self._set_inventory(result, dirty=False)
1647
1860
        return result
1648
1861
 
1649
1862
    @needs_tree_write_lock
1650
 
    def remove(self, files, verbose=False, to_file=None):
1651
 
        """Remove nominated files from the working inventory..
1652
 
 
1653
 
        This does not remove their text.  This does not run on XXX on what? RBC
1654
 
 
1655
 
        TODO: Refuse to remove modified files unless --force is given?
1656
 
 
1657
 
        TODO: Do something useful with directories.
1658
 
 
1659
 
        TODO: Should this remove the text or not?  Tough call; not
1660
 
        removing may be useful and the user can just use use rm, and
1661
 
        is the opposite of add.  Removing it is consistent with most
1662
 
        other tools.  Maybe an option.
 
1863
    def remove(self, files, verbose=False, to_file=None, keep_files=True,
 
1864
        force=False):
 
1865
        """Remove nominated files from the working inventory.
 
1866
 
 
1867
        :files: File paths relative to the basedir.
 
1868
        :keep_files: If true, the files will also be kept.
 
1869
        :force: Delete files and directories, even if they are changed and
 
1870
            even if the directories are not empty.
1663
1871
        """
1664
 
        ## TODO: Normalize names
1665
 
        ## TODO: Remove nested loops; better scalability
1666
1872
        if isinstance(files, basestring):
1667
1873
            files = [files]
1668
1874
 
1669
 
        inv = self.inventory
1670
 
 
1671
 
        # do this before any modifications
 
1875
        inv_delta = []
 
1876
 
 
1877
        new_files=set()
 
1878
        unknown_nested_files=set()
 
1879
 
 
1880
        def recurse_directory_to_add_files(directory):
 
1881
            # Recurse directory and add all files
 
1882
            # so we can check if they have changed.
 
1883
            for parent_info, file_infos in\
 
1884
                self.walkdirs(directory):
 
1885
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
 
1886
                    # Is it versioned or ignored?
 
1887
                    if self.path2id(relpath) or self.is_ignored(relpath):
 
1888
                        # Add nested content for deletion.
 
1889
                        new_files.add(relpath)
 
1890
                    else:
 
1891
                        # Files which are not versioned and not ignored
 
1892
                        # should be treated as unknown.
 
1893
                        unknown_nested_files.add((relpath, None, kind))
 
1894
 
 
1895
        for filename in files:
 
1896
            # Get file name into canonical form.
 
1897
            abspath = self.abspath(filename)
 
1898
            filename = self.relpath(abspath)
 
1899
            if len(filename) > 0:
 
1900
                new_files.add(filename)
 
1901
                recurse_directory_to_add_files(filename)
 
1902
 
 
1903
        files = list(new_files)
 
1904
 
 
1905
        if len(files) == 0:
 
1906
            return # nothing to do
 
1907
 
 
1908
        # Sort needed to first handle directory content before the directory
 
1909
        files.sort(reverse=True)
 
1910
 
 
1911
        # Bail out if we are going to delete files we shouldn't
 
1912
        if not keep_files and not force:
 
1913
            has_changed_files = len(unknown_nested_files) > 0
 
1914
            if not has_changed_files:
 
1915
                for (file_id, path, content_change, versioned, parent_id, name,
 
1916
                     kind, executable) in self.iter_changes(self.basis_tree(),
 
1917
                         include_unchanged=True, require_versioned=False,
 
1918
                         want_unversioned=True, specific_files=files):
 
1919
                    if versioned == (False, False):
 
1920
                        # The record is unknown ...
 
1921
                        if not self.is_ignored(path[1]):
 
1922
                            # ... but not ignored
 
1923
                            has_changed_files = True
 
1924
                            break
 
1925
                    elif content_change and (kind[1] is not None):
 
1926
                        # Versioned and changed, but not deleted
 
1927
                        has_changed_files = True
 
1928
                        break
 
1929
 
 
1930
            if has_changed_files:
 
1931
                # Make delta show ALL applicable changes in error message.
 
1932
                tree_delta = self.changes_from(self.basis_tree(),
 
1933
                    require_versioned=False, want_unversioned=True,
 
1934
                    specific_files=files)
 
1935
                for unknown_file in unknown_nested_files:
 
1936
                    if unknown_file not in tree_delta.unversioned:
 
1937
                        tree_delta.unversioned.extend((unknown_file,))
 
1938
                raise errors.BzrRemoveChangedFilesError(tree_delta)
 
1939
 
 
1940
        # Build inv_delta and delete files where applicaple,
 
1941
        # do this before any modifications to inventory.
1672
1942
        for f in files:
1673
 
            fid = inv.path2id(f)
 
1943
            fid = self.path2id(f)
 
1944
            message = None
1674
1945
            if not fid:
1675
 
                note("%s is not versioned."%f)
 
1946
                message = "%s is not versioned." % (f,)
1676
1947
            else:
1677
1948
                if verbose:
1678
 
                    # having remove it, it must be either ignored or unknown
 
1949
                    # having removed it, it must be either ignored or unknown
1679
1950
                    if self.is_ignored(f):
1680
1951
                        new_status = 'I'
1681
1952
                    else:
1682
1953
                        new_status = '?'
1683
 
                    textui.show_status(new_status, inv[fid].kind, f,
 
1954
                    textui.show_status(new_status, self.kind(fid), f,
1684
1955
                                       to_file=to_file)
1685
 
                del inv[fid]
1686
 
 
1687
 
        self._write_inventory(inv)
 
1956
                # Unversion file
 
1957
                inv_delta.append((f, None, fid, None))
 
1958
                message = "removed %s" % (f,)
 
1959
 
 
1960
            if not keep_files:
 
1961
                abs_path = self.abspath(f)
 
1962
                if osutils.lexists(abs_path):
 
1963
                    if (osutils.isdir(abs_path) and
 
1964
                        len(os.listdir(abs_path)) > 0):
 
1965
                        if force:
 
1966
                            osutils.rmtree(abs_path)
 
1967
                        else:
 
1968
                            message = "%s is not an empty directory "\
 
1969
                                "and won't be deleted." % (f,)
 
1970
                    else:
 
1971
                        osutils.delete_any(abs_path)
 
1972
                        message = "deleted %s" % (f,)
 
1973
                elif message is not None:
 
1974
                    # Only care if we haven't done anything yet.
 
1975
                    message = "%s does not exist." % (f,)
 
1976
 
 
1977
            # Print only one message (if any) per file.
 
1978
            if message is not None:
 
1979
                note(message)
 
1980
        self.apply_inventory_delta(inv_delta)
1688
1981
 
1689
1982
    @needs_tree_write_lock
1690
 
    def revert(self, filenames, old_tree=None, backups=True, 
 
1983
    def revert(self, filenames=None, old_tree=None, backups=True,
1691
1984
               pb=DummyProgress(), report_changes=False):
1692
1985
        from bzrlib.conflicts import resolve
 
1986
        if filenames == []:
 
1987
            filenames = None
 
1988
            symbol_versioning.warn('Using [] to revert all files is deprecated'
 
1989
                ' as of bzr 0.91.  Please use None (the default) instead.',
 
1990
                DeprecationWarning, stacklevel=2)
1693
1991
        if old_tree is None:
1694
 
            old_tree = self.basis_tree()
1695
 
        conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1696
 
                                     report_changes)
1697
 
        if not len(filenames):
1698
 
            self.set_parent_ids(self.get_parent_ids()[:1])
1699
 
            resolve(self)
 
1992
            basis_tree = self.basis_tree()
 
1993
            basis_tree.lock_read()
 
1994
            old_tree = basis_tree
1700
1995
        else:
1701
 
            resolve(self, filenames, ignore_misses=True)
 
1996
            basis_tree = None
 
1997
        try:
 
1998
            conflicts = transform.revert(self, old_tree, filenames, backups, pb,
 
1999
                                         report_changes)
 
2000
            if filenames is None and len(self.get_parent_ids()) > 1:
 
2001
                parent_trees = []
 
2002
                last_revision = self.last_revision()
 
2003
                if last_revision != NULL_REVISION:
 
2004
                    if basis_tree is None:
 
2005
                        basis_tree = self.basis_tree()
 
2006
                        basis_tree.lock_read()
 
2007
                    parent_trees.append((last_revision, basis_tree))
 
2008
                self.set_parent_trees(parent_trees)
 
2009
                resolve(self)
 
2010
            else:
 
2011
                resolve(self, filenames, ignore_misses=True, recursive=True)
 
2012
        finally:
 
2013
            if basis_tree is not None:
 
2014
                basis_tree.unlock()
1702
2015
        return conflicts
1703
2016
 
1704
2017
    def revision_tree(self, revision_id):
1714
2027
                pass
1715
2028
            else:
1716
2029
                try:
1717
 
                    inv = xml6.serializer_v6.read_inventory_from_string(xml)
 
2030
                    inv = xml7.serializer_v7.read_inventory_from_string(xml)
1718
2031
                    # dont use the repository revision_tree api because we want
1719
2032
                    # to supply the inventory.
1720
2033
                    if inv.revision_id == revision_id:
1755
2068
        """Set the root id for this tree."""
1756
2069
        # for compatability 
1757
2070
        if file_id is None:
1758
 
            symbol_versioning.warn(symbol_versioning.zero_twelve
1759
 
                % 'WorkingTree.set_root_id with fileid=None',
1760
 
                DeprecationWarning,
1761
 
                stacklevel=3)
1762
 
            file_id = ROOT_ID
1763
 
        else:
1764
 
            file_id = osutils.safe_file_id(file_id)
 
2071
            raise ValueError(
 
2072
                'WorkingTree.set_root_id with fileid=None')
 
2073
        file_id = osutils.safe_file_id(file_id)
1765
2074
        self._set_root_id(file_id)
1766
2075
 
1767
2076
    def _set_root_id(self, file_id):
1801
2110
        """
1802
2111
        raise NotImplementedError(self.unlock)
1803
2112
 
1804
 
    def update(self):
 
2113
    def update(self, change_reporter=None, possible_transports=None):
1805
2114
        """Update a working tree along its branch.
1806
2115
 
1807
2116
        This will update the branch if its bound too, which means we have
1826
2135
          basis.
1827
2136
        - Do a 'normal' merge of the old branch basis if it is relevant.
1828
2137
        """
1829
 
        if self.branch.get_master_branch() is not None:
 
2138
        if self.branch.get_bound_location() is not None:
1830
2139
            self.lock_write()
1831
2140
            update_branch = True
1832
2141
        else:
1834
2143
            update_branch = False
1835
2144
        try:
1836
2145
            if update_branch:
1837
 
                old_tip = self.branch.update()
 
2146
                old_tip = self.branch.update(possible_transports)
1838
2147
            else:
1839
2148
                old_tip = None
1840
 
            return self._update_tree(old_tip)
 
2149
            return self._update_tree(old_tip, change_reporter)
1841
2150
        finally:
1842
2151
            self.unlock()
1843
2152
 
1844
2153
    @needs_tree_write_lock
1845
 
    def _update_tree(self, old_tip=None):
 
2154
    def _update_tree(self, old_tip=None, change_reporter=None):
1846
2155
        """Update a tree to the master branch.
1847
2156
 
1848
2157
        :param old_tip: if supplied, the previous tip revision the branch,
1862
2171
        try:
1863
2172
            last_rev = self.get_parent_ids()[0]
1864
2173
        except IndexError:
1865
 
            last_rev = None
1866
 
        if last_rev != self.branch.last_revision():
 
2174
            last_rev = _mod_revision.NULL_REVISION
 
2175
        if last_rev != _mod_revision.ensure_null(self.branch.last_revision()):
1867
2176
            # merge tree state up to new branch tip.
1868
2177
            basis = self.basis_tree()
1869
2178
            basis.lock_read()
1870
2179
            try:
1871
2180
                to_tree = self.branch.basis_tree()
1872
 
                if basis.inventory.root is None and self.inventory.root is None:
1873
 
                    self.set_root_id(to_tree.inventory.root.file_id)
 
2181
                if basis.inventory.root is None:
 
2182
                    self.set_root_id(to_tree.get_root_id())
 
2183
                    self.flush()
1874
2184
                result += merge.merge_inner(
1875
2185
                                      self.branch,
1876
2186
                                      to_tree,
1877
2187
                                      basis,
1878
 
                                      this_tree=self)
 
2188
                                      this_tree=self,
 
2189
                                      change_reporter=change_reporter)
1879
2190
            finally:
1880
2191
                basis.unlock()
1881
2192
            # TODO - dedup parents list with things merged by pull ?
1890
2201
            for parent in merges:
1891
2202
                parent_trees.append(
1892
2203
                    (parent, self.branch.repository.revision_tree(parent)))
1893
 
            if old_tip is not None:
 
2204
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
1894
2205
                parent_trees.append(
1895
2206
                    (old_tip, self.branch.repository.revision_tree(old_tip)))
1896
2207
            self.set_parent_trees(parent_trees)
1899
2210
            # the working tree had the same last-revision as the master
1900
2211
            # branch did. We may still have pivot local work from the local
1901
2212
            # branch into old_tip:
1902
 
            if old_tip is not None:
 
2213
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
1903
2214
                self.add_parent_tree_id(old_tip)
1904
 
        if old_tip and old_tip != last_rev:
 
2215
        if (old_tip is not None and not _mod_revision.is_null(old_tip)
 
2216
            and old_tip != last_rev):
1905
2217
            # our last revision was not the prior branch last revision
1906
2218
            # and we have converted that last revision to a pending merge.
1907
2219
            # base is somewhere between the branch tip now
1914
2226
            #       inventory and calls tree._write_inventory(). Ultimately we
1915
2227
            #       should be able to remove this extra flush.
1916
2228
            self.flush()
1917
 
            from bzrlib.revision import common_ancestor
1918
 
            try:
1919
 
                base_rev_id = common_ancestor(self.branch.last_revision(),
1920
 
                                              old_tip,
1921
 
                                              self.branch.repository)
1922
 
            except errors.NoCommonAncestor:
1923
 
                base_rev_id = None
 
2229
            graph = self.branch.repository.get_graph()
 
2230
            base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
 
2231
                                                old_tip)
1924
2232
            base_tree = self.branch.repository.revision_tree(base_rev_id)
1925
2233
            other_tree = self.branch.repository.revision_tree(old_tip)
1926
2234
            result += merge.merge_inner(
1927
2235
                                  self.branch,
1928
2236
                                  other_tree,
1929
2237
                                  base_tree,
1930
 
                                  this_tree=self)
 
2238
                                  this_tree=self,
 
2239
                                  change_reporter=change_reporter)
1931
2240
        return result
1932
2241
 
1933
2242
    def _write_hashcache_if_dirty(self):
1985
2294
    def walkdirs(self, prefix=""):
1986
2295
        """Walk the directories of this tree.
1987
2296
 
 
2297
        returns a generator which yields items in the form:
 
2298
                ((curren_directory_path, fileid),
 
2299
                 [(file1_path, file1_name, file1_kind, (lstat), file1_id,
 
2300
                   file1_kind), ... ])
 
2301
 
1988
2302
        This API returns a generator, which is only valid during the current
1989
2303
        tree transaction - within a single lock_read or lock_write duration.
1990
2304
 
1991
 
        If the tree is not locked, it may cause an error to be raised, depending
1992
 
        on the tree implementation.
 
2305
        If the tree is not locked, it may cause an error to be raised,
 
2306
        depending on the tree implementation.
1993
2307
        """
1994
2308
        disk_top = self.abspath(prefix)
1995
2309
        if disk_top.endswith('/'):
2001
2315
            current_disk = disk_iterator.next()
2002
2316
            disk_finished = False
2003
2317
        except OSError, e:
2004
 
            if e.errno != errno.ENOENT:
 
2318
            if not (e.errno == errno.ENOENT or
 
2319
                (sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
2005
2320
                raise
2006
2321
            current_disk = None
2007
2322
            disk_finished = True
2012
2327
            current_inv = None
2013
2328
            inv_finished = True
2014
2329
        while not inv_finished or not disk_finished:
 
2330
            if current_disk:
 
2331
                ((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
 
2332
                    cur_disk_dir_content) = current_disk
 
2333
            else:
 
2334
                ((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
 
2335
                    cur_disk_dir_content) = ((None, None), None)
2015
2336
            if not disk_finished:
2016
2337
                # strip out .bzr dirs
2017
 
                if current_disk[0][1][top_strip_len:] == '':
2018
 
                    # osutils.walkdirs can be made nicer - 
 
2338
                if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
 
2339
                    len(cur_disk_dir_content) > 0):
 
2340
                    # osutils.walkdirs can be made nicer -
2019
2341
                    # yield the path-from-prefix rather than the pathjoined
2020
2342
                    # value.
2021
 
                    bzrdir_loc = bisect_left(current_disk[1], ('.bzr', '.bzr'))
2022
 
                    if current_disk[1][bzrdir_loc][0] == '.bzr':
 
2343
                    bzrdir_loc = bisect_left(cur_disk_dir_content,
 
2344
                        ('.bzr', '.bzr'))
 
2345
                    if (bzrdir_loc < len(cur_disk_dir_content)
 
2346
                        and cur_disk_dir_content[bzrdir_loc][0] == '.bzr'):
2023
2347
                        # we dont yield the contents of, or, .bzr itself.
2024
 
                        del current_disk[1][bzrdir_loc]
 
2348
                        del cur_disk_dir_content[bzrdir_loc]
2025
2349
            if inv_finished:
2026
2350
                # everything is unknown
2027
2351
                direction = 1
2029
2353
                # everything is missing
2030
2354
                direction = -1
2031
2355
            else:
2032
 
                direction = cmp(current_inv[0][0], current_disk[0][0])
 
2356
                direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
2033
2357
            if direction > 0:
2034
2358
                # disk is before inventory - unknown
2035
2359
                dirblock = [(relpath, basename, kind, stat, None, None) for
2036
 
                    relpath, basename, kind, stat, top_path in current_disk[1]]
2037
 
                yield (current_disk[0][0], None), dirblock
 
2360
                    relpath, basename, kind, stat, top_path in
 
2361
                    cur_disk_dir_content]
 
2362
                yield (cur_disk_dir_relpath, None), dirblock
2038
2363
                try:
2039
2364
                    current_disk = disk_iterator.next()
2040
2365
                except StopIteration:
2042
2367
            elif direction < 0:
2043
2368
                # inventory is before disk - missing.
2044
2369
                dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
2045
 
                    for relpath, basename, dkind, stat, fileid, kind in 
 
2370
                    for relpath, basename, dkind, stat, fileid, kind in
2046
2371
                    current_inv[1]]
2047
2372
                yield (current_inv[0][0], current_inv[0][1]), dirblock
2048
2373
                try:
2054
2379
                # merge the inventory and disk data together
2055
2380
                dirblock = []
2056
2381
                for relpath, subiterator in itertools.groupby(sorted(
2057
 
                    current_inv[1] + current_disk[1], key=operator.itemgetter(0)), operator.itemgetter(1)):
 
2382
                    current_inv[1] + cur_disk_dir_content,
 
2383
                    key=operator.itemgetter(0)), operator.itemgetter(1)):
2058
2384
                    path_elements = list(subiterator)
2059
2385
                    if len(path_elements) == 2:
2060
2386
                        inv_row, disk_row = path_elements
2086
2412
                    disk_finished = True
2087
2413
 
2088
2414
    def _walkdirs(self, prefix=""):
 
2415
        """Walk the directories of this tree.
 
2416
 
 
2417
           :prefix: is used as the directrory to start with.
 
2418
           returns a generator which yields items in the form:
 
2419
                ((curren_directory_path, fileid),
 
2420
                 [(file1_path, file1_name, file1_kind, None, file1_id,
 
2421
                   file1_kind), ... ])
 
2422
        """
2089
2423
        _directory = 'directory'
2090
2424
        # get the root in the inventory
2091
2425
        inv = self.inventory
2105
2439
                relroot = ""
2106
2440
            # FIXME: stash the node in pending
2107
2441
            entry = inv[top_id]
2108
 
            for name, child in entry.sorted_children():
2109
 
                dirblock.append((relroot + name, name, child.kind, None,
2110
 
                    child.file_id, child.kind
2111
 
                    ))
 
2442
            if entry.kind == 'directory':
 
2443
                for name, child in entry.sorted_children():
 
2444
                    dirblock.append((relroot + name, name, child.kind, None,
 
2445
                        child.file_id, child.kind
 
2446
                        ))
2112
2447
            yield (currentdir[0], entry.file_id), dirblock
2113
2448
            # push the user specified dirs from dirblock
2114
2449
            for dir in reversed(dirblock):
2115
2450
                if dir[2] == _directory:
2116
2451
                    pending.append(dir)
2117
2452
 
 
2453
    @needs_tree_write_lock
 
2454
    def auto_resolve(self):
 
2455
        """Automatically resolve text conflicts according to contents.
 
2456
 
 
2457
        Only text conflicts are auto_resolvable. Files with no conflict markers
 
2458
        are considered 'resolved', because bzr always puts conflict markers
 
2459
        into files that have text conflicts.  The corresponding .THIS .BASE and
 
2460
        .OTHER files are deleted, as per 'resolve'.
 
2461
        :return: a tuple of ConflictLists: (un_resolved, resolved).
 
2462
        """
 
2463
        un_resolved = _mod_conflicts.ConflictList()
 
2464
        resolved = _mod_conflicts.ConflictList()
 
2465
        conflict_re = re.compile('^(<{7}|={7}|>{7})')
 
2466
        for conflict in self.conflicts():
 
2467
            if (conflict.typestring != 'text conflict' or
 
2468
                self.kind(conflict.file_id) != 'file'):
 
2469
                un_resolved.append(conflict)
 
2470
                continue
 
2471
            my_file = open(self.id2abspath(conflict.file_id), 'rb')
 
2472
            try:
 
2473
                for line in my_file:
 
2474
                    if conflict_re.search(line):
 
2475
                        un_resolved.append(conflict)
 
2476
                        break
 
2477
                else:
 
2478
                    resolved.append(conflict)
 
2479
            finally:
 
2480
                my_file.close()
 
2481
        resolved.remove_files(self)
 
2482
        self.set_conflicts(un_resolved)
 
2483
        return un_resolved, resolved
 
2484
 
 
2485
    @needs_read_lock
 
2486
    def _check(self):
 
2487
        tree_basis = self.basis_tree()
 
2488
        tree_basis.lock_read()
 
2489
        try:
 
2490
            repo_basis = self.branch.repository.revision_tree(
 
2491
                self.last_revision())
 
2492
            if len(list(repo_basis.iter_changes(tree_basis))) > 0:
 
2493
                raise errors.BzrCheckError(
 
2494
                    "Mismatched basis inventory content.")
 
2495
            self._validate()
 
2496
        finally:
 
2497
            tree_basis.unlock()
 
2498
 
 
2499
    def _validate(self):
 
2500
        """Validate internal structures.
 
2501
 
 
2502
        This is meant mostly for the test suite. To give it a chance to detect
 
2503
        corruption after actions have occurred. The default implementation is a
 
2504
        just a no-op.
 
2505
 
 
2506
        :return: None. An exception should be raised if there is an error.
 
2507
        """
 
2508
        return
 
2509
 
 
2510
    @needs_read_lock
 
2511
    def _get_rules_searcher(self, default_searcher):
 
2512
        """See Tree._get_rules_searcher."""
 
2513
        if self._rules_searcher is None:
 
2514
            self._rules_searcher = super(WorkingTree,
 
2515
                self)._get_rules_searcher(default_searcher)
 
2516
        return self._rules_searcher
 
2517
 
 
2518
    def get_shelf_manager(self):
 
2519
        """Return the ShelfManager for this WorkingTree."""
 
2520
        from bzrlib.shelf import ShelfManager
 
2521
        return ShelfManager(self, self._transport)
 
2522
 
2118
2523
 
2119
2524
class WorkingTree2(WorkingTree):
2120
2525
    """This is the Format 2 working tree.
2124
2529
     - uses the branch last-revision.
2125
2530
    """
2126
2531
 
 
2532
    def __init__(self, *args, **kwargs):
 
2533
        super(WorkingTree2, self).__init__(*args, **kwargs)
 
2534
        # WorkingTree2 has more of a constraint that self._inventory must
 
2535
        # exist. Because this is an older format, we don't mind the overhead
 
2536
        # caused by the extra computation here.
 
2537
 
 
2538
        # Newer WorkingTree's should only have self._inventory set when they
 
2539
        # have a read lock.
 
2540
        if self._inventory is None:
 
2541
            self.read_working_inventory()
 
2542
 
2127
2543
    def lock_tree_write(self):
2128
2544
        """See WorkingTree.lock_tree_write().
2129
2545
 
2138
2554
            raise
2139
2555
 
2140
2556
    def unlock(self):
 
2557
        # do non-implementation specific cleanup
 
2558
        self._cleanup()
 
2559
 
2141
2560
        # we share control files:
2142
2561
        if self._control_files._lock_count == 3:
2143
2562
            # _inventory_is_modified is always False during a read lock.
2166
2585
    def _last_revision(self):
2167
2586
        """See Mutable.last_revision."""
2168
2587
        try:
2169
 
            return osutils.safe_revision_id(
2170
 
                        self._control_files.get('last-revision').read())
 
2588
            return self._transport.get_bytes('last-revision')
2171
2589
        except errors.NoSuchFile:
2172
 
            return None
 
2590
            return _mod_revision.NULL_REVISION
2173
2591
 
2174
2592
    def _change_last_revision(self, revision_id):
2175
2593
        """See WorkingTree._change_last_revision."""
2176
2594
        if revision_id is None or revision_id == NULL_REVISION:
2177
2595
            try:
2178
 
                self._control_files._transport.delete('last-revision')
 
2596
                self._transport.delete('last-revision')
2179
2597
            except errors.NoSuchFile:
2180
2598
                pass
2181
2599
            return False
2182
2600
        else:
2183
 
            self._control_files.put_bytes('last-revision', revision_id)
 
2601
            self._transport.put_bytes('last-revision', revision_id,
 
2602
                mode=self._control_files._file_mode)
2184
2603
            return True
2185
2604
 
2186
2605
    @needs_tree_write_lock
2198
2617
    @needs_read_lock
2199
2618
    def conflicts(self):
2200
2619
        try:
2201
 
            confile = self._control_files.get('conflicts')
 
2620
            confile = self._transport.get('conflicts')
2202
2621
        except errors.NoSuchFile:
2203
2622
            return _mod_conflicts.ConflictList()
2204
2623
        try:
2205
 
            if confile.next() != CONFLICT_HEADER_1 + '\n':
 
2624
            try:
 
2625
                if confile.next() != CONFLICT_HEADER_1 + '\n':
 
2626
                    raise errors.ConflictFormatError()
 
2627
            except StopIteration:
2206
2628
                raise errors.ConflictFormatError()
2207
 
        except StopIteration:
2208
 
            raise errors.ConflictFormatError()
2209
 
        return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
 
2629
            return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
 
2630
        finally:
 
2631
            confile.close()
2210
2632
 
2211
2633
    def unlock(self):
 
2634
        # do non-implementation specific cleanup
 
2635
        self._cleanup()
2212
2636
        if self._control_files._lock_count == 1:
2213
2637
            # _inventory_is_modified is always False during a read lock.
2214
2638
            if self._inventory_is_modified:
2227
2651
            return path[:-len(suffix)]
2228
2652
 
2229
2653
 
2230
 
@deprecated_function(zero_eight)
2231
 
def is_control_file(filename):
2232
 
    """See WorkingTree.is_control_filename(filename)."""
2233
 
    ## FIXME: better check
2234
 
    filename = normpath(filename)
2235
 
    while filename != '':
2236
 
        head, tail = os.path.split(filename)
2237
 
        ## mutter('check %r for control file' % ((head, tail),))
2238
 
        if tail == '.bzr':
2239
 
            return True
2240
 
        if filename == head:
2241
 
            break
2242
 
        filename = head
2243
 
    return False
2244
 
 
2245
 
 
2246
2654
class WorkingTreeFormat(object):
2247
2655
    """An encapsulation of the initialization and open routines for a format.
2248
2656
 
2267
2675
    _formats = {}
2268
2676
    """The known formats."""
2269
2677
 
 
2678
    requires_rich_root = False
 
2679
 
 
2680
    upgrade_recommended = False
 
2681
 
2270
2682
    @classmethod
2271
2683
    def find_format(klass, a_bzrdir):
2272
2684
        """Return the format for the working tree object in a_bzrdir."""
2277
2689
        except errors.NoSuchFile:
2278
2690
            raise errors.NoWorkingTree(base=transport.base)
2279
2691
        except KeyError:
2280
 
            raise errors.UnknownFormatError(format=format_string)
 
2692
            raise errors.UnknownFormatError(format=format_string,
 
2693
                                            kind="working tree")
 
2694
 
 
2695
    def __eq__(self, other):
 
2696
        return self.__class__ is other.__class__
 
2697
 
 
2698
    def __ne__(self, other):
 
2699
        return not (self == other)
2281
2700
 
2282
2701
    @classmethod
2283
2702
    def get_default_format(klass):
2301
2720
        """
2302
2721
        return True
2303
2722
 
 
2723
    def supports_content_filtering(self):
 
2724
        """True if this format supports content filtering."""
 
2725
        return False
 
2726
 
 
2727
    def supports_views(self):
 
2728
        """True if this format supports stored views."""
 
2729
        return False
 
2730
 
2304
2731
    @classmethod
2305
2732
    def register_format(klass, format):
2306
2733
        klass._formats[format.get_format_string()] = format
2311
2738
 
2312
2739
    @classmethod
2313
2740
    def unregister_format(klass, format):
2314
 
        assert klass._formats[format.get_format_string()] is format
2315
2741
        del klass._formats[format.get_format_string()]
2316
2742
 
2317
2743
 
2318
 
 
2319
2744
class WorkingTreeFormat2(WorkingTreeFormat):
2320
2745
    """The second working tree format. 
2321
2746
 
2322
2747
    This format modified the hash cache from the format 1 hash cache.
2323
2748
    """
2324
2749
 
 
2750
    upgrade_recommended = True
 
2751
 
2325
2752
    def get_format_description(self):
2326
2753
        """See WorkingTreeFormat.get_format_description()."""
2327
2754
        return "Working tree format 2"
2328
2755
 
2329
 
    def stub_initialize_remote(self, control_files):
2330
 
        """As a special workaround create critical control files for a remote working tree
2331
 
        
 
2756
    def _stub_initialize_on_transport(self, transport, file_mode):
 
2757
        """Workaround: create control files for a remote working tree.
 
2758
 
2332
2759
        This ensures that it can later be updated and dealt with locally,
2333
 
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with 
 
2760
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2334
2761
        no working tree.  (See bug #43064).
2335
2762
        """
2336
2763
        sio = StringIO()
2337
2764
        inv = Inventory()
2338
 
        xml5.serializer_v5.write_inventory(inv, sio)
 
2765
        xml5.serializer_v5.write_inventory(inv, sio, working=True)
2339
2766
        sio.seek(0)
2340
 
        control_files.put('inventory', sio)
2341
 
 
2342
 
        control_files.put_bytes('pending-merges', '')
2343
 
        
2344
 
 
2345
 
    def initialize(self, a_bzrdir, revision_id=None):
 
2767
        transport.put_file('inventory', sio, file_mode)
 
2768
        transport.put_bytes('pending-merges', '', file_mode)
 
2769
 
 
2770
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
2771
                   accelerator_tree=None, hardlink=False):
2346
2772
        """See WorkingTreeFormat.initialize()."""
2347
2773
        if not isinstance(a_bzrdir.transport, LocalTransport):
2348
2774
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2349
 
        branch = a_bzrdir.open_branch()
2350
 
        if revision_id is not None:
2351
 
            revision_id = osutils.safe_revision_id(revision_id)
2352
 
            branch.lock_write()
2353
 
            try:
2354
 
                revision_history = branch.revision_history()
2355
 
                try:
2356
 
                    position = revision_history.index(revision_id)
2357
 
                except ValueError:
2358
 
                    raise errors.NoSuchRevision(branch, revision_id)
2359
 
                branch.set_revision_history(revision_history[:position + 1])
2360
 
            finally:
2361
 
                branch.unlock()
2362
 
        revision = branch.last_revision()
 
2775
        if from_branch is not None:
 
2776
            branch = from_branch
 
2777
        else:
 
2778
            branch = a_bzrdir.open_branch()
 
2779
        if revision_id is None:
 
2780
            revision_id = _mod_revision.ensure_null(branch.last_revision())
 
2781
        branch.lock_write()
 
2782
        try:
 
2783
            branch.generate_revision_history(revision_id)
 
2784
        finally:
 
2785
            branch.unlock()
2363
2786
        inv = Inventory()
2364
2787
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2365
2788
                         branch,
2367
2790
                         _internal=True,
2368
2791
                         _format=self,
2369
2792
                         _bzrdir=a_bzrdir)
2370
 
        basis_tree = branch.repository.revision_tree(revision)
 
2793
        basis_tree = branch.repository.revision_tree(revision_id)
2371
2794
        if basis_tree.inventory.root is not None:
2372
 
            wt.set_root_id(basis_tree.inventory.root.file_id)
 
2795
            wt.set_root_id(basis_tree.get_root_id())
2373
2796
        # set the parent list and cache the basis tree.
2374
 
        wt.set_parent_trees([(revision, basis_tree)])
 
2797
        if _mod_revision.is_null(revision_id):
 
2798
            parent_trees = []
 
2799
        else:
 
2800
            parent_trees = [(revision_id, basis_tree)]
 
2801
        wt.set_parent_trees(parent_trees)
2375
2802
        transform.build_tree(basis_tree, wt)
2376
2803
        return wt
2377
2804
 
2390
2817
            raise NotImplementedError
2391
2818
        if not isinstance(a_bzrdir.transport, LocalTransport):
2392
2819
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2393
 
        return WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
 
2820
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2394
2821
                           _internal=True,
2395
2822
                           _format=self,
2396
2823
                           _bzrdir=a_bzrdir)
2397
 
 
 
2824
        return wt
2398
2825
 
2399
2826
class WorkingTreeFormat3(WorkingTreeFormat):
2400
2827
    """The second working tree format updated to record a format marker.
2407
2834
        - is new in bzr 0.8
2408
2835
        - uses a LockDir to guard access for writes.
2409
2836
    """
 
2837
    
 
2838
    upgrade_recommended = True
2410
2839
 
2411
2840
    def get_format_string(self):
2412
2841
        """See WorkingTreeFormat.get_format_string()."""
2419
2848
    _lock_file_name = 'lock'
2420
2849
    _lock_class = LockDir
2421
2850
 
 
2851
    _tree_class = WorkingTree3
 
2852
 
 
2853
    def __get_matchingbzrdir(self):
 
2854
        return bzrdir.BzrDirMetaFormat1()
 
2855
 
 
2856
    _matchingbzrdir = property(__get_matchingbzrdir)
 
2857
 
2422
2858
    def _open_control_files(self, a_bzrdir):
2423
2859
        transport = a_bzrdir.get_workingtree_transport(None)
2424
2860
        return LockableFiles(transport, self._lock_file_name, 
2425
2861
                             self._lock_class)
2426
2862
 
2427
 
    def initialize(self, a_bzrdir, revision_id=None):
 
2863
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
2864
                   accelerator_tree=None, hardlink=False):
2428
2865
        """See WorkingTreeFormat.initialize().
2429
2866
        
2430
 
        revision_id allows creating a working tree at a different
2431
 
        revision than the branch is at.
 
2867
        :param revision_id: if supplied, create a working tree at a different
 
2868
            revision than the branch is at.
 
2869
        :param accelerator_tree: A tree which can be used for retrieving file
 
2870
            contents more quickly than the revision tree, i.e. a workingtree.
 
2871
            The revision tree will be used for cases where accelerator_tree's
 
2872
            content is different.
 
2873
        :param hardlink: If true, hard-link files from accelerator_tree,
 
2874
            where possible.
2432
2875
        """
2433
2876
        if not isinstance(a_bzrdir.transport, LocalTransport):
2434
2877
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2436
2879
        control_files = self._open_control_files(a_bzrdir)
2437
2880
        control_files.create_lock()
2438
2881
        control_files.lock_write()
2439
 
        control_files.put_utf8('format', self.get_format_string())
2440
 
        branch = a_bzrdir.open_branch()
 
2882
        transport.put_bytes('format', self.get_format_string(),
 
2883
            mode=control_files._file_mode)
 
2884
        if from_branch is not None:
 
2885
            branch = from_branch
 
2886
        else:
 
2887
            branch = a_bzrdir.open_branch()
2441
2888
        if revision_id is None:
2442
 
            revision_id = branch.last_revision()
2443
 
        else:
2444
 
            revision_id = osutils.safe_revision_id(revision_id)
 
2889
            revision_id = _mod_revision.ensure_null(branch.last_revision())
2445
2890
        # WorkingTree3 can handle an inventory which has a unique root id.
2446
2891
        # as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
2447
2892
        # those trees. And because there isn't a format bump inbetween, we
2448
2893
        # are maintaining compatibility with older clients.
2449
2894
        # inv = Inventory(root_id=gen_root_id())
2450
 
        inv = Inventory()
2451
 
        wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
 
2895
        inv = self._initial_inventory()
 
2896
        wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
2452
2897
                         branch,
2453
2898
                         inv,
2454
2899
                         _internal=True,
2460
2905
            basis_tree = branch.repository.revision_tree(revision_id)
2461
2906
            # only set an explicit root id if there is one to set.
2462
2907
            if basis_tree.inventory.root is not None:
2463
 
                wt.set_root_id(basis_tree.inventory.root.file_id)
 
2908
                wt.set_root_id(basis_tree.get_root_id())
2464
2909
            if revision_id == NULL_REVISION:
2465
2910
                wt.set_parent_trees([])
2466
2911
            else:
2473
2918
            wt.unlock()
2474
2919
        return wt
2475
2920
 
 
2921
    def _initial_inventory(self):
 
2922
        return Inventory()
 
2923
 
2476
2924
    def __init__(self):
2477
2925
        super(WorkingTreeFormat3, self).__init__()
2478
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2479
2926
 
2480
2927
    def open(self, a_bzrdir, _found=False):
2481
2928
        """Return the WorkingTree object for a_bzrdir
2488
2935
            raise NotImplementedError
2489
2936
        if not isinstance(a_bzrdir.transport, LocalTransport):
2490
2937
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2491
 
        return self._open(a_bzrdir, self._open_control_files(a_bzrdir))
 
2938
        wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
 
2939
        return wt
2492
2940
 
2493
2941
    def _open(self, a_bzrdir, control_files):
2494
2942
        """Open the tree itself.
2496
2944
        :param a_bzrdir: the dir for the tree.
2497
2945
        :param control_files: the control files for the tree.
2498
2946
        """
2499
 
        return WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2500
 
                           _internal=True,
2501
 
                           _format=self,
2502
 
                           _bzrdir=a_bzrdir,
2503
 
                           _control_files=control_files)
 
2947
        return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
 
2948
                                _internal=True,
 
2949
                                _format=self,
 
2950
                                _bzrdir=a_bzrdir,
 
2951
                                _control_files=control_files)
2504
2952
 
2505
2953
    def __str__(self):
2506
2954
        return self.get_format_string()
2507
2955
 
2508
2956
 
2509
 
__default_format = WorkingTreeFormat3()
 
2957
__default_format = WorkingTreeFormat4()
2510
2958
WorkingTreeFormat.register_format(__default_format)
2511
 
WorkingTreeFormat.register_format(WorkingTreeFormat4())
 
2959
WorkingTreeFormat.register_format(WorkingTreeFormat5())
 
2960
WorkingTreeFormat.register_format(WorkingTreeFormat3())
2512
2961
WorkingTreeFormat.set_default_format(__default_format)
2513
2962
# formats which have no format string are not discoverable
2514
2963
# and not independently creatable, so are not registered.
2515
2964
_legacy_formats = [WorkingTreeFormat2(),
2516
2965
                   ]
2517
 
 
2518
 
 
2519
 
class WorkingTreeTestProviderAdapter(object):
2520
 
    """A tool to generate a suite testing multiple workingtree formats at once.
2521
 
 
2522
 
    This is done by copying the test once for each transport and injecting
2523
 
    the transport_server, transport_readonly_server, and workingtree_format
2524
 
    classes into each copy. Each copy is also given a new id() to make it
2525
 
    easy to identify.
2526
 
    """
2527
 
 
2528
 
    def __init__(self, transport_server, transport_readonly_server, formats):
2529
 
        self._transport_server = transport_server
2530
 
        self._transport_readonly_server = transport_readonly_server
2531
 
        self._formats = formats
2532
 
    
2533
 
    def _clone_test(self, test, bzrdir_format, workingtree_format, variation):
2534
 
        """Clone test for adaption."""
2535
 
        new_test = deepcopy(test)
2536
 
        new_test.transport_server = self._transport_server
2537
 
        new_test.transport_readonly_server = self._transport_readonly_server
2538
 
        new_test.bzrdir_format = bzrdir_format
2539
 
        new_test.workingtree_format = workingtree_format
2540
 
        def make_new_test_id():
2541
 
            new_id = "%s(%s)" % (test.id(), variation)
2542
 
            return lambda: new_id
2543
 
        new_test.id = make_new_test_id()
2544
 
        return new_test
2545
 
    
2546
 
    def adapt(self, test):
2547
 
        from bzrlib.tests import TestSuite
2548
 
        result = TestSuite()
2549
 
        for workingtree_format, bzrdir_format in self._formats:
2550
 
            new_test = self._clone_test(
2551
 
                test,
2552
 
                bzrdir_format,
2553
 
                workingtree_format, workingtree_format.__class__.__name__)
2554
 
            result.addTest(new_test)
2555
 
        return result