~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

merge only needs a lock_tree_write() on the working tree, not a full lock_write()

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 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
42
41
 
43
42
from bzrlib.lazy_import import lazy_import
44
43
lazy_import(globals(), """
45
44
from bisect import bisect_left
46
45
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
54
53
 
55
54
import bzrlib
56
55
from bzrlib import (
57
 
    branch,
58
56
    bzrdir,
59
57
    conflicts as _mod_conflicts,
60
58
    dirstate,
64
62
    hashcache,
65
63
    ignores,
66
64
    merge,
67
 
    revision as _mod_revision,
 
65
    osutils,
68
66
    revisiontree,
69
 
    repository,
70
67
    textui,
71
 
    trace,
72
68
    transform,
73
 
    ui,
74
69
    urlutils,
75
70
    xml5,
76
71
    xml6,
77
 
    xml7,
78
72
    )
79
73
import bzrlib.branch
80
74
from bzrlib.transport import get_transport
81
75
import bzrlib.ui
82
 
from bzrlib.workingtree_4 import WorkingTreeFormat4, WorkingTreeFormat5
 
76
from bzrlib.workingtree_4 import WorkingTreeFormat4
83
77
""")
84
78
 
85
79
from bzrlib import symbol_versioning
86
80
from bzrlib.decorators import needs_read_lock, needs_write_lock
87
 
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, TreeReference
88
 
from bzrlib.lockable_files import LockableFiles
 
81
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID
 
82
from bzrlib.lockable_files import LockableFiles, TransportLock
89
83
from bzrlib.lockdir import LockDir
90
84
import bzrlib.mutabletree
91
85
from bzrlib.mutabletree import needs_tree_write_lock
92
 
from bzrlib import osutils
93
86
from bzrlib.osutils import (
94
87
    compact_date,
95
88
    file_kind,
111
104
        deprecated_method,
112
105
        deprecated_function,
113
106
        DEPRECATED_PARAMETER,
 
107
        zero_eight,
 
108
        zero_eleven,
 
109
        zero_thirteen,
114
110
        )
115
111
 
116
112
 
117
113
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
118
114
CONFLICT_HEADER_1 = "BZR conflict list format 1"
119
115
 
120
 
ERROR_PATH_NOT_FOUND = 3    # WindowsError errno code, equivalent to ENOENT
 
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()
121
133
 
122
134
 
123
135
class TreeEntry(object):
192
204
                 _internal=False,
193
205
                 _format=None,
194
206
                 _bzrdir=None):
195
 
        """Construct a WorkingTree instance. This is not a public API.
 
207
        """Construct a WorkingTree for basedir.
196
208
 
197
 
        :param branch: A branch to override probing for the branch.
 
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).
198
213
        """
199
214
        self._format = _format
200
215
        self.bzrdir = _bzrdir
201
216
        if not _internal:
202
 
            raise errors.BzrError("Please use bzrdir.open_workingtree or "
203
 
                "WorkingTree.open() to obtain a WorkingTree.")
 
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
204
232
        basedir = safe_unicode(basedir)
205
233
        mutter("opening working tree %r", basedir)
206
234
        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
                     )
207
242
            self._branch = branch
208
243
        else:
209
244
            self._branch = self.bzrdir.open_branch()
214
249
            self._control_files = self.branch.control_files
215
250
        else:
216
251
            # 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
217
255
            self._control_files = _control_files
218
 
        self._transport = self._control_files._transport
219
256
        # update the whole cache up front and write to disk if anything changed;
220
257
        # in the future we might want to do this more selectively
221
258
        # two possible ways offer themselves : in self._unlock, write the cache
225
262
        wt_trans = self.bzrdir.get_workingtree_transport(None)
226
263
        cache_filename = wt_trans.local_abspath('stat-cache')
227
264
        self._hashcache = hashcache.HashCache(basedir, cache_filename,
228
 
            self.bzrdir._get_file_mode())
 
265
                                              self._control_files._file_mode)
229
266
        hc = self._hashcache
230
267
        hc.read()
231
268
        # is this scan needed ? it makes things kinda slow.
236
273
            hc.write()
237
274
 
238
275
        if _inventory is None:
239
 
            # This will be acquired on lock_read() or lock_write()
240
276
            self._inventory_is_modified = False
241
 
            self._inventory = None
 
277
            self.read_working_inventory()
242
278
        else:
243
279
            # the caller of __init__ has provided an inventory,
244
280
            # we assume they know what they are doing - as its only
245
281
            # the Format factory and creation methods that are
246
282
            # permitted to do this.
247
283
            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()
261
284
 
262
285
    branch = property(
263
286
        fget=lambda self: self._branch,
278
301
        self._control_files.break_lock()
279
302
        self.branch.break_lock()
280
303
 
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
 
 
293
304
    def _set_inventory(self, inv, dirty):
294
305
        """Set the internal cached inventory.
295
306
 
300
311
            False then the inventory is the same as that on disk and any
301
312
            serialisation would be unneeded overhead.
302
313
        """
 
314
        assert inv.root is not None
303
315
        self._inventory = inv
304
316
        self._inventory_is_modified = dirty
305
317
 
309
321
 
310
322
        """
311
323
        if path is None:
312
 
            path = osutils.getcwd()
 
324
            path = os.path.getcwdu()
313
325
        control = bzrdir.BzrDir.open(path, _unsupported)
314
326
        return control.open_workingtree(_unsupported)
315
 
 
 
327
        
316
328
    @staticmethod
317
329
    def open_containing(path=None):
318
330
        """Open an existing working tree which has its root about path.
319
 
 
 
331
        
320
332
        This probes for a working tree at path and searches upwards from there.
321
333
 
322
334
        Basically we keep looking up until we find the control directory or
340
352
        """
341
353
        return WorkingTree.open(path, _unsupported=True)
342
354
 
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)
362
355
    def __iter__(self):
363
356
        """Iterate through file_ids for this tree.
364
357
 
370
363
            if osutils.lexists(self.abspath(path)):
371
364
                yield ie.file_id
372
365
 
373
 
    def all_file_ids(self):
374
 
        """See Tree.iter_all_file_ids"""
375
 
        return set(self.inventory)
376
 
 
377
366
    def __repr__(self):
378
367
        return "<%s of %s>" % (self.__class__.__name__,
379
368
                               getattr(self, 'basedir', None))
380
369
 
381
370
    def abspath(self, filename):
382
371
        return pathjoin(self.basedir, filename)
383
 
 
 
372
    
384
373
    def basis_tree(self):
385
374
        """Return RevisionTree for the current last revision.
386
375
        
387
376
        If the left most parent is a ghost then the returned tree will be an
388
 
        empty tree - one obtained by calling 
389
 
        repository.revision_tree(NULL_REVISION).
 
377
        empty tree - one obtained by calling repository.revision_tree(None).
390
378
        """
391
379
        try:
392
380
            revision_id = self.get_parent_ids()[0]
394
382
            # no parents, return an empty revision tree.
395
383
            # in the future this should return the tree for
396
384
            # 'empty:' - the implicit root empty tree.
397
 
            return self.branch.repository.revision_tree(
398
 
                       _mod_revision.NULL_REVISION)
 
385
            return self.branch.repository.revision_tree(None)
399
386
        try:
400
387
            return self.revision_tree(revision_id)
401
388
        except errors.NoSuchRevision:
405
392
        # at this point ?
406
393
        try:
407
394
            return self.branch.repository.revision_tree(revision_id)
408
 
        except (errors.RevisionNotPresent, errors.NoSuchRevision):
 
395
        except errors.RevisionNotPresent:
409
396
            # the basis tree *may* be a ghost or a low level error may have
410
397
            # occured. If the revision is present, its a problem, if its not
411
398
            # its a ghost.
412
399
            if self.branch.repository.has_revision(revision_id):
413
400
                raise
414
401
            # the basis tree is a ghost so return an empty tree.
415
 
            return self.branch.repository.revision_tree(
416
 
                       _mod_revision.NULL_REVISION)
417
 
 
418
 
    def _cleanup(self):
419
 
        self._flush_ignore_list_cache()
 
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)
420
441
 
421
442
    def relpath(self, path):
422
443
        """Return the local path portion from a given path.
429
450
    def has_filename(self, filename):
430
451
        return osutils.lexists(self.abspath(filename))
431
452
 
432
 
    def get_file(self, file_id, path=None):
433
 
        return self.get_file_with_stat(file_id, path)[0]
 
453
    def get_file(self, file_id):
 
454
        return self.get_file_byname(self.id2path(file_id))
434
455
 
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()))
 
456
    def get_file_text(self, file_id):
 
457
        return self.get_file(file_id).read()
441
458
 
442
459
    def get_file_byname(self, filename):
443
460
        return file(self.abspath(filename), 'rb')
444
461
 
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
 
 
453
462
    @needs_read_lock
454
 
    def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
 
463
    def annotate_iter(self, file_id):
455
464
        """See Tree.annotate_iter
456
465
 
457
466
        This implementation will use the basis tree implementation if possible.
462
471
        attribution will be correct).
463
472
        """
464
473
        basis = self.basis_tree()
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
 
474
        changes = self._iter_changes(basis, True, [file_id]).next()
 
475
        changed_content, kind = changes[2], changes[6]
 
476
        if not changed_content:
 
477
            return basis.annotate_iter(file_id)
 
478
        if kind[1] is None:
 
479
            return None
 
480
        import annotate
 
481
        if kind[0] != 'file':
 
482
            old_lines = []
 
483
        else:
 
484
            old_lines = list(basis.annotate_iter(file_id))
 
485
        old = [old_lines]
 
486
        for tree in self.branch.repository.revision_trees(
 
487
            self.get_parent_ids()[1:]):
 
488
            if file_id not in tree:
 
489
                continue
 
490
            old.append(list(tree.annotate_iter(file_id)))
 
491
        return annotate.reannotate(old, self.get_file(file_id).readlines(),
 
492
                                   CURRENT_REVISION)
496
493
 
497
494
    def get_parent_ids(self):
498
495
        """See Tree.get_parent_ids.
500
497
        This implementation reads the pending merges list and last_revision
501
498
        value and uses that to decide what the parents list should be.
502
499
        """
503
 
        last_rev = _mod_revision.ensure_null(self._last_revision())
504
 
        if _mod_revision.NULL_REVISION == last_rev:
 
500
        last_rev = self._last_revision()
 
501
        if last_rev is None:
505
502
            parents = []
506
503
        else:
507
504
            parents = [last_rev]
508
505
        try:
509
 
            merges_file = self._transport.get('pending-merges')
 
506
            merges_file = self._control_files.get_utf8('pending-merges')
510
507
        except errors.NoSuchFile:
511
508
            pass
512
509
        else:
513
510
            for l in merges_file.readlines():
514
 
                revision_id = l.rstrip('\n')
515
 
                parents.append(revision_id)
 
511
                parents.append(l.rstrip('\n'))
516
512
        return parents
517
513
 
518
514
    @needs_read_lock
525
521
        return self.abspath(self.id2path(file_id))
526
522
 
527
523
    @needs_read_lock
528
 
    def clone(self, to_bzrdir, revision_id=None):
 
524
    def clone(self, to_bzrdir, revision_id=None, basis=None):
529
525
        """Duplicate this working tree into to_bzr, including all state.
530
526
        
531
527
        Specifically modified files are kept as modified, but
537
533
            If not None, the cloned tree will have its last revision set to 
538
534
            revision, and and difference between the source trees last revision
539
535
            and this one merged in.
 
536
 
 
537
        basis
 
538
            If not None, a closer copy of a tree which may have some files in
 
539
            common, and which file content should be preferentially copied from.
540
540
        """
541
541
        # assumes the target bzr dir format is compatible.
542
 
        result = to_bzrdir.create_workingtree()
 
542
        result = self._format.initialize(to_bzrdir)
543
543
        self.copy_content_into(result, revision_id)
544
544
        return result
545
545
 
560
560
 
561
561
    def has_id(self, file_id):
562
562
        # files that have been deleted are excluded
563
 
        inv = self.inventory
 
563
        inv = self._inventory
564
564
        if not inv.has_id(file_id):
565
565
            return False
566
566
        path = inv.id2path(file_id)
574
574
    __contains__ = has_id
575
575
 
576
576
    def get_file_size(self, 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
 
577
        return os.path.getsize(self.id2abspath(file_id))
585
578
 
586
579
    @needs_read_lock
587
580
    def get_file_sha1(self, file_id, path=None, stat_value=None):
591
584
 
592
585
    def get_file_mtime(self, file_id, path=None):
593
586
        if not path:
594
 
            path = self.inventory.id2path(file_id)
 
587
            path = self._inventory.id2path(file_id)
595
588
        return os.lstat(self.abspath(path)).st_mtime
596
589
 
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
 
 
605
590
    if not supports_executable():
606
591
        def is_executable(self, file_id, path=None):
607
592
            return self._inventory[file_id].executable
608
 
 
609
 
        _is_executable_from_path_and_stat = \
610
 
            _is_executable_from_path_and_stat_from_basis
611
593
    else:
612
594
        def is_executable(self, file_id, path=None):
613
595
            if not path:
615
597
            mode = os.lstat(self.abspath(path)).st_mode
616
598
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
617
599
 
618
 
        _is_executable_from_path_and_stat = \
619
 
            _is_executable_from_path_and_stat_from_stat
620
 
 
621
 
    @needs_tree_write_lock
 
600
    @needs_write_lock
622
601
    def _add(self, files, ids, kinds):
623
602
        """See MutableTree._add."""
624
603
        # TODO: Re-adding a file that is removed in the working copy
625
604
        # should probably put it back with the previous ID.
626
605
        # the read and write working inventory should not occur in this 
627
606
        # function - they should be part of lock_write and unlock.
628
 
        inv = self.inventory
 
607
        inv = self.read_working_inventory()
629
608
        for f, file_id, kind in zip(files, ids, kinds):
 
609
            assert kind is not None
630
610
            if file_id is None:
631
611
                inv.add_path(f, kind=kind)
632
612
            else:
633
613
                inv.add_path(f, kind=kind, file_id=file_id)
634
 
            self._inventory_is_modified = True
 
614
        self._write_inventory(inv)
635
615
 
636
616
    @needs_tree_write_lock
637
617
    def _gather_kinds(self, files, kinds):
697
677
        if updated:
698
678
            self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
699
679
 
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.encode(osutils._fs_enc)))
727
 
        else:
728
 
            return (kind, None, None, None)
 
680
    @deprecated_method(zero_eleven)
 
681
    @needs_read_lock
 
682
    def pending_merges(self):
 
683
        """Return a list of pending merges.
 
684
 
 
685
        These are revisions that have been merged into the working
 
686
        directory but not yet committed.
 
687
 
 
688
        As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
 
689
        instead - which is available on all tree objects.
 
690
        """
 
691
        return self.get_parent_ids()[1:]
729
692
 
730
693
    def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
731
694
        """Common ghost checking functionality from set_parent_*.
741
704
 
742
705
    def _set_merges_from_parent_ids(self, parent_ids):
743
706
        merges = parent_ids[1:]
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
 
707
        self._control_files.put_utf8('pending-merges', '\n'.join(merges))
765
708
 
766
709
    @needs_tree_write_lock
767
710
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
778
721
        """
779
722
        self._check_parents_for_ghosts(revision_ids,
780
723
            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)
785
724
 
786
725
        if len(revision_ids) > 0:
787
726
            self.set_last_revision(revision_ids[0])
788
727
        else:
789
 
            self.set_last_revision(_mod_revision.NULL_REVISION)
 
728
            self.set_last_revision(None)
790
729
 
791
730
        self._set_merges_from_parent_ids(revision_ids)
792
731
 
794
733
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
795
734
        """See MutableTree.set_parent_trees."""
796
735
        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)
799
736
 
800
737
        self._check_parents_for_ghosts(parent_ids,
801
738
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
802
739
 
803
 
        parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
804
 
 
805
740
        if len(parent_ids) == 0:
806
 
            leftmost_parent_id = _mod_revision.NULL_REVISION
 
741
            leftmost_parent_id = None
807
742
            leftmost_parent_tree = None
808
743
        else:
809
744
            leftmost_parent_id, leftmost_parent_tree = parents_list[0]
831
766
    def set_merge_modified(self, modified_hashes):
832
767
        def iter_stanzas():
833
768
            for file_id, hash in modified_hashes.iteritems():
834
 
                yield Stanza(file_id=file_id.decode('utf8'), hash=hash)
 
769
                yield Stanza(file_id=file_id, hash=hash)
835
770
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
836
771
 
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
 
 
 
772
    @needs_tree_write_lock
847
773
    def _put_rio(self, filename, stanzas, header):
848
 
        self._must_be_locked()
849
774
        my_file = rio_file(stanzas, header)
850
 
        self._transport.put_file(filename, my_file,
851
 
            mode=self._control_files._file_mode)
 
775
        self._control_files.put(filename, my_file)
852
776
 
853
777
    @needs_write_lock # because merge pulls data into the branch.
854
 
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
855
 
        merge_type=None):
 
778
    def merge_from_branch(self, branch, to_revision=None):
856
779
        """Merge from a branch into this working tree.
857
780
 
858
781
        :param branch: The branch to merge from.
871
794
            # local alterations
872
795
            merger.check_basis(check_clean=True, require_commits=False)
873
796
            if to_revision is None:
874
 
                to_revision = _mod_revision.ensure_null(branch.last_revision())
 
797
                to_revision = branch.last_revision()
875
798
            merger.other_rev_id = to_revision
876
 
            if _mod_revision.is_null(merger.other_rev_id):
877
 
                raise errors.NoCommits(branch)
 
799
            if merger.other_rev_id is None:
 
800
                raise error.NoCommits(branch)
878
801
            self.branch.fetch(branch, last_revision=merger.other_rev_id)
879
802
            merger.other_basis = merger.other_rev_id
880
803
            merger.other_tree = self.branch.repository.revision_tree(
881
804
                merger.other_rev_id)
882
 
            merger.other_branch = branch
883
805
            merger.pp.next_phase()
884
 
            if from_revision is None:
885
 
                merger.find_base()
886
 
            else:
887
 
                merger.set_base_revision(from_revision, branch)
 
806
            merger.find_base()
888
807
            if merger.base_rev_id == merger.other_rev_id:
889
808
                raise errors.PointlessMerge
890
809
            merger.backup_files = False
891
 
            if merge_type is None:
892
 
                merger.merge_type = Merge3Merger
893
 
            else:
894
 
                merger.merge_type = merge_type
 
810
            merger.merge_type = Merge3Merger
895
811
            merger.set_interesting_files(None)
896
812
            merger.show_base = False
897
813
            merger.reprocess = False
903
819
 
904
820
    @needs_read_lock
905
821
    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
 
        """
915
822
        try:
916
 
            hashfile = self._transport.get('merge-hashes')
 
823
            hashfile = self._control_files.get('merge-hashes')
917
824
        except errors.NoSuchFile:
918
825
            return {}
 
826
        merge_hashes = {}
919
827
        try:
920
 
            merge_hashes = {}
921
 
            try:
922
 
                if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
923
 
                    raise errors.MergeModifiedFormatError()
924
 
            except StopIteration:
 
828
            if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
925
829
                raise errors.MergeModifiedFormatError()
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()
 
830
        except StopIteration:
 
831
            raise errors.MergeModifiedFormatError()
 
832
        for s in RioReader(hashfile):
 
833
            file_id = s.get("file_id")
 
834
            if file_id not in self.inventory:
 
835
                continue
 
836
            hash = s.get("hash")
 
837
            if hash == self.get_file_sha1(file_id):
 
838
                merge_hashes[file_id] = hash
 
839
        return merge_hashes
937
840
 
938
841
    @needs_write_lock
939
842
    def mkdir(self, path, file_id=None):
945
848
        return file_id
946
849
 
947
850
    def get_symlink_target(self, file_id):
948
 
        return os.readlink(self.id2abspath(file_id).encode(osutils._fs_enc))
949
 
 
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)
 
851
        return os.readlink(self.id2abspath(file_id))
 
852
 
 
853
    def file_class(self, filename):
 
854
        if self.path2id(filename):
 
855
            return 'V'
 
856
        elif self.is_ignored(filename):
 
857
            return 'I'
 
858
        else:
 
859
            return '?'
1073
860
 
1074
861
    def flush(self):
1075
862
        """Write the in memory inventory to disk."""
1077
864
        if self._control_files._lock_mode != 'w':
1078
865
            raise errors.NotWriteLocked(self)
1079
866
        sio = StringIO()
1080
 
        self._serialize(self._inventory, sio)
 
867
        xml5.serializer_v5.write_inventory(self._inventory, sio)
1081
868
        sio.seek(0)
1082
 
        self._transport.put_file('inventory', sio,
1083
 
            mode=self._control_files._file_mode)
 
869
        self._control_files.put('inventory', sio)
1084
870
        self._inventory_is_modified = False
1085
871
 
1086
 
    def _kind(self, relpath):
1087
 
        return osutils.file_kind(self.abspath(relpath))
1088
 
 
1089
872
    def list_files(self, include_root=False):
1090
873
        """Recursively list all files as (path, class, kind, id, entry).
1091
874
 
1096
879
 
1097
880
        Skips the control directory.
1098
881
        """
1099
 
        # list_files is an iterator, so @needs_read_lock doesn't work properly
1100
 
        # with it. So callers should be careful to always read_lock the tree.
1101
 
        if not self.is_locked():
1102
 
            raise errors.ObjectNotLocked(self)
1103
 
 
1104
 
        inv = self.inventory
 
882
        inv = self._inventory
1105
883
        if include_root is True:
1106
884
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
1107
885
        # Convert these into local objects to save lookup times
1108
886
        pathjoin = osutils.pathjoin
1109
 
        file_kind = self._kind
 
887
        file_kind = osutils.file_kind
1110
888
 
1111
889
        # transport.base ends in a slash, we want the piece
1112
890
        # between the last two slashes
1172
950
 
1173
951
                fk = file_kind(fap)
1174
952
 
 
953
                if f_ie:
 
954
                    if f_ie.kind != fk:
 
955
                        raise errors.BzrCheckError(
 
956
                            "file %r entered as kind %r id %r, now of kind %r"
 
957
                            % (fap, f_ie.kind, f_ie.file_id, fk))
 
958
 
1175
959
                # make a last minute entry
1176
960
                if f_ie:
1177
961
                    yield fp[1:], c, fk, f_ie.file_id, f_ie
1247
1031
                                       DeprecationWarning)
1248
1032
 
1249
1033
        # check destination directory
1250
 
        if isinstance(from_paths, basestring):
1251
 
            raise ValueError()
 
1034
        assert not isinstance(from_paths, basestring)
1252
1035
        inv = self.inventory
1253
1036
        to_abs = self.abspath(to_dir)
1254
1037
        if not isdir(to_abs):
1338
1121
                only_change_inv = True
1339
1122
            elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1340
1123
                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
1345
1124
            else:
1346
1125
                # something is wrong, so lets determine what exactly
1347
1126
                if not self.has_filename(from_rel) and \
1350
1129
                        errors.PathsDoNotExist(paths=(str(from_rel),
1351
1130
                        str(to_rel))))
1352
1131
                else:
1353
 
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
 
1132
                    raise errors.RenameFailedFilesExist(from_rel, to_rel,
 
1133
                        extra="(Use --after to update the Bazaar id)")
1354
1134
            rename_entry.only_change_inv = only_change_inv
1355
1135
        return rename_entries
1356
1136
 
1491
1271
        # prevent race conditions with the lock
1492
1272
        return iter(
1493
1273
            [subp for subp in self.extras() if not self.is_ignored(subp)])
1494
 
 
 
1274
    
1495
1275
    @needs_tree_write_lock
1496
1276
    def unversion(self, file_ids):
1497
1277
        """Remove the file ids in file_ids from the current versioned set.
1518
1298
            # - RBC 20060907
1519
1299
            self._write_inventory(self._inventory)
1520
1300
    
 
1301
    @deprecated_method(zero_eight)
 
1302
    def iter_conflicts(self):
 
1303
        """List all files in the tree that have text or content conflicts.
 
1304
        DEPRECATED.  Use conflicts instead."""
 
1305
        return self._iter_conflicts()
 
1306
 
1521
1307
    def _iter_conflicts(self):
1522
1308
        conflicted = set()
1523
1309
        for info in self.list_files():
1530
1316
                yield stem
1531
1317
 
1532
1318
    @needs_write_lock
1533
 
    def pull(self, source, overwrite=False, stop_revision=None,
1534
 
             change_reporter=None, possible_transports=None):
 
1319
    def pull(self, source, overwrite=False, stop_revision=None):
1535
1320
        top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1536
1321
        source.lock_read()
1537
1322
        try:
1538
1323
            pp = ProgressPhase("Pull phase", 2, top_pb)
1539
1324
            pp.next_phase()
1540
 
            old_revision_info = self.branch.last_revision_info()
 
1325
            old_revision_history = self.branch.revision_history()
1541
1326
            basis_tree = self.basis_tree()
1542
 
            count = self.branch.pull(source, overwrite, stop_revision,
1543
 
                                     possible_transports=possible_transports)
1544
 
            new_revision_info = self.branch.last_revision_info()
1545
 
            if new_revision_info != old_revision_info:
 
1327
            count = self.branch.pull(source, overwrite, stop_revision)
 
1328
            new_revision_history = self.branch.revision_history()
 
1329
            if new_revision_history != old_revision_history:
1546
1330
                pp.next_phase()
 
1331
                if len(old_revision_history):
 
1332
                    other_revision = old_revision_history[-1]
 
1333
                else:
 
1334
                    other_revision = None
1547
1335
                repository = self.branch.repository
1548
1336
                pb = bzrlib.ui.ui_factory.nested_progress_bar()
1549
1337
                basis_tree.lock_read()
1554
1342
                                new_basis_tree,
1555
1343
                                basis_tree,
1556
1344
                                this_tree=self,
1557
 
                                pb=pb,
1558
 
                                change_reporter=change_reporter)
 
1345
                                pb=pb)
1559
1346
                    if (basis_tree.inventory.root is None and
1560
1347
                        new_basis_tree.inventory.root is not None):
1561
 
                        self.set_root_id(new_basis_tree.get_root_id())
 
1348
                        self.set_root_id(new_basis_tree.inventory.root.file_id)
1562
1349
                finally:
1563
1350
                    pb.finished()
1564
1351
                    basis_tree.unlock()
1592
1379
        # TODO: update the hashcache here ?
1593
1380
 
1594
1381
    def extras(self):
1595
 
        """Yield all unversioned files in this WorkingTree.
 
1382
        """Yield all unknown files in this WorkingTree.
1596
1383
 
1597
 
        If there are any unversioned directories then only the directory is
1598
 
        returned, not all its children.  But if there are unversioned files
 
1384
        If there are any unknown directories then only the directory is
 
1385
        returned, not all its children.  But if there are unknown files
1599
1386
        under a versioned subdirectory, they are returned.
1600
1387
 
1601
1388
        Currently returned depth-first, sorted by name within directories.
1602
 
        This is the same order used by 'osutils.walkdirs'.
1603
1389
        """
1604
1390
        ## TODO: Work from given directory downwards
1605
1391
        for path, dir_entry in self.inventory.directories():
1614
1400
                if subf == '.bzr':
1615
1401
                    continue
1616
1402
                if subf not in dir_entry.children:
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)
 
1403
                    subf_norm, can_access = osutils.normalized_filename(subf)
1625
1404
                    if subf_norm != subf and can_access:
1626
1405
                        if subf_norm not in dir_entry.children:
1627
1406
                            fl.append(subf_norm)
1649
1428
        if ignoreset is not None:
1650
1429
            return ignoreset
1651
1430
 
1652
 
        ignore_globs = set()
 
1431
        ignore_globs = set(bzrlib.DEFAULT_IGNORE)
1653
1432
        ignore_globs.update(ignores.get_runtime_ignores())
1654
1433
        ignore_globs.update(ignores.get_user_ignores())
1655
1434
        if self.has_filename(bzrlib.IGNORE_FILENAME):
1682
1461
    def kind(self, file_id):
1683
1462
        return file_kind(self.id2abspath(file_id))
1684
1463
 
1685
 
    def stored_kind(self, file_id):
1686
 
        """See Tree.stored_kind"""
1687
 
        return self.inventory[file_id].kind
1688
 
 
1689
1464
    def _comparison_data(self, entry, path):
1690
1465
        abspath = self.abspath(path)
1691
1466
        try:
1701
1476
            mode = stat_value.st_mode
1702
1477
            kind = osutils.file_kind_from_stat_mode(mode)
1703
1478
            if not supports_executable():
1704
 
                executable = entry is not None and entry.executable
 
1479
                executable = entry.executable
1705
1480
            else:
1706
1481
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1707
1482
        return kind, executable, stat_value
1722
1497
    @needs_read_lock
1723
1498
    def _last_revision(self):
1724
1499
        """helper for get_parent_ids."""
1725
 
        return _mod_revision.ensure_null(self.branch.last_revision())
 
1500
        return self.branch.last_revision()
1726
1501
 
1727
1502
    def is_locked(self):
1728
1503
        return self._control_files.is_locked()
1729
1504
 
1730
 
    def _must_be_locked(self):
1731
 
        if not self.is_locked():
1732
 
            raise errors.ObjectNotLocked(self)
1733
 
 
1734
1505
    def lock_read(self):
1735
1506
        """See Branch.lock_read, and WorkingTree.unlock."""
1736
 
        if not self.is_locked():
1737
 
            self._reset_data()
1738
1507
        self.branch.lock_read()
1739
1508
        try:
1740
1509
            return self._control_files.lock_read()
1744
1513
 
1745
1514
    def lock_tree_write(self):
1746
1515
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
1747
 
        if not self.is_locked():
1748
 
            self._reset_data()
1749
1516
        self.branch.lock_read()
1750
1517
        try:
1751
1518
            return self._control_files.lock_write()
1755
1522
 
1756
1523
    def lock_write(self):
1757
1524
        """See MutableTree.lock_write, and WorkingTree.unlock."""
1758
 
        if not self.is_locked():
1759
 
            self._reset_data()
1760
1525
        self.branch.lock_write()
1761
1526
        try:
1762
1527
            return self._control_files.lock_write()
1770
1535
    def _basis_inventory_name(self):
1771
1536
        return 'basis-inventory-cache'
1772
1537
 
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
 
 
1779
1538
    @needs_tree_write_lock
1780
1539
    def set_last_revision(self, new_revision):
1781
1540
        """Change the last revision in the working tree."""
1788
1547
        This is used to allow WorkingTree3 instances to not affect branch
1789
1548
        when their last revision is set.
1790
1549
        """
1791
 
        if _mod_revision.is_null(new_revision):
 
1550
        if new_revision is None:
1792
1551
            self.branch.set_revision_history([])
1793
1552
            return False
1794
1553
        try:
1800
1559
 
1801
1560
    def _write_basis_inventory(self, xml):
1802
1561
        """Write the basis inventory XML to the basis-inventory file"""
 
1562
        assert isinstance(xml, str), 'serialised xml must be bytestring.'
1803
1563
        path = self._basis_inventory_name()
1804
1564
        sio = StringIO(xml)
1805
 
        self._transport.put_file(path, sio,
1806
 
            mode=self._control_files._file_mode)
 
1565
        self._control_files.put(path, sio)
1807
1566
 
1808
1567
    def _create_basis_xml_from_inventory(self, revision_id, inventory):
1809
1568
        """Create the text that will be saved in basis-inventory"""
1810
1569
        inventory.revision_id = revision_id
1811
 
        return xml7.serializer_v7.write_inventory_to_string(inventory)
 
1570
        return xml6.serializer_v6.write_inventory_to_string(inventory)
1812
1571
 
1813
1572
    def _cache_basis_inventory(self, new_revision):
1814
1573
        """Cache new_revision as the basis inventory."""
1829
1588
            xml = self.branch.repository.get_inventory_xml(new_revision)
1830
1589
            firstline = xml.split('\n', 1)[0]
1831
1590
            if (not 'revision_id="' in firstline or 
1832
 
                'format="7"' not in firstline):
 
1591
                'format="6"' not in firstline):
1833
1592
                inv = self.branch.repository.deserialise_inventory(
1834
1593
                    new_revision, xml)
1835
1594
                xml = self._create_basis_xml_from_inventory(new_revision, inv)
1840
1599
    def read_basis_inventory(self):
1841
1600
        """Read the cached basis inventory."""
1842
1601
        path = self._basis_inventory_name()
1843
 
        return self._transport.get_bytes(path)
 
1602
        return self._control_files.get(path).read()
1844
1603
        
1845
1604
    @needs_read_lock
1846
1605
    def read_working_inventory(self):
1855
1614
        # binary.
1856
1615
        if self._inventory_is_modified:
1857
1616
            raise errors.InventoryModified(self)
1858
 
        result = self._deserialize(self._transport.get('inventory'))
 
1617
        result = xml5.serializer_v5.read_inventory(
 
1618
            self._control_files.get('inventory'))
1859
1619
        self._set_inventory(result, dirty=False)
1860
1620
        return result
1861
1621
 
1862
1622
    @needs_tree_write_lock
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.
 
1623
    def remove(self, files, verbose=False, to_file=None):
 
1624
        """Remove nominated files from the working inventory..
 
1625
 
 
1626
        This does not remove their text.  This does not run on XXX on what? RBC
 
1627
 
 
1628
        TODO: Refuse to remove modified files unless --force is given?
 
1629
 
 
1630
        TODO: Do something useful with directories.
 
1631
 
 
1632
        TODO: Should this remove the text or not?  Tough call; not
 
1633
        removing may be useful and the user can just use use rm, and
 
1634
        is the opposite of add.  Removing it is consistent with most
 
1635
        other tools.  Maybe an option.
1871
1636
        """
 
1637
        ## TODO: Normalize names
 
1638
        ## TODO: Remove nested loops; better scalability
1872
1639
        if isinstance(files, basestring):
1873
1640
            files = [files]
1874
1641
 
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.
 
1642
        inv = self.inventory
 
1643
 
 
1644
        # do this before any modifications
1942
1645
        for f in files:
1943
 
            fid = self.path2id(f)
1944
 
            message = None
 
1646
            fid = inv.path2id(f)
1945
1647
            if not fid:
1946
 
                message = "%s is not versioned." % (f,)
1947
 
            else:
1948
 
                if verbose:
1949
 
                    # having removed it, it must be either ignored or unknown
1950
 
                    if self.is_ignored(f):
1951
 
                        new_status = 'I'
1952
 
                    else:
1953
 
                        new_status = '?'
1954
 
                    textui.show_status(new_status, self.kind(fid), f,
1955
 
                                       to_file=to_file)
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)
 
1648
                # TODO: Perhaps make this just a warning, and continue?
 
1649
                # This tends to happen when 
 
1650
                raise errors.NotVersionedError(path=f)
 
1651
            if verbose:
 
1652
                # having remove it, it must be either ignored or unknown
 
1653
                if self.is_ignored(f):
 
1654
                    new_status = 'I'
 
1655
                else:
 
1656
                    new_status = '?'
 
1657
                textui.show_status(new_status, inv[fid].kind, f,
 
1658
                                   to_file=to_file)
 
1659
            del inv[fid]
 
1660
 
 
1661
        self._write_inventory(inv)
1981
1662
 
1982
1663
    @needs_tree_write_lock
1983
 
    def revert(self, filenames=None, old_tree=None, backups=True,
 
1664
    def revert(self, filenames, old_tree=None, backups=True, 
1984
1665
               pb=DummyProgress(), report_changes=False):
1985
1666
        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)
1991
1667
        if old_tree is None:
1992
 
            basis_tree = self.basis_tree()
1993
 
            basis_tree.lock_read()
1994
 
            old_tree = basis_tree
 
1668
            old_tree = self.basis_tree()
 
1669
        conflicts = transform.revert(self, old_tree, filenames, backups, pb,
 
1670
                                     report_changes)
 
1671
        if not len(filenames):
 
1672
            self.set_parent_ids(self.get_parent_ids()[:1])
 
1673
            resolve(self)
1995
1674
        else:
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()
 
1675
            resolve(self, filenames, ignore_misses=True)
2015
1676
        return conflicts
2016
1677
 
2017
1678
    def revision_tree(self, revision_id):
2027
1688
                pass
2028
1689
            else:
2029
1690
                try:
2030
 
                    inv = xml7.serializer_v7.read_inventory_from_string(xml)
 
1691
                    inv = xml6.serializer_v6.read_inventory_from_string(xml)
2031
1692
                    # dont use the repository revision_tree api because we want
2032
1693
                    # to supply the inventory.
2033
1694
                    if inv.revision_id == revision_id:
2068
1729
        """Set the root id for this tree."""
2069
1730
        # for compatability 
2070
1731
        if file_id is None:
2071
 
            raise ValueError(
2072
 
                'WorkingTree.set_root_id with fileid=None')
2073
 
        file_id = osutils.safe_file_id(file_id)
 
1732
            symbol_versioning.warn(symbol_versioning.zero_twelve
 
1733
                % 'WorkingTree.set_root_id with fileid=None',
 
1734
                DeprecationWarning,
 
1735
                stacklevel=3)
 
1736
            file_id = ROOT_ID
2074
1737
        self._set_root_id(file_id)
2075
1738
 
2076
1739
    def _set_root_id(self, file_id):
2110
1773
        """
2111
1774
        raise NotImplementedError(self.unlock)
2112
1775
 
2113
 
    def update(self, change_reporter=None, possible_transports=None):
 
1776
    def update(self):
2114
1777
        """Update a working tree along its branch.
2115
1778
 
2116
1779
        This will update the branch if its bound too, which means we have
2135
1798
          basis.
2136
1799
        - Do a 'normal' merge of the old branch basis if it is relevant.
2137
1800
        """
2138
 
        if self.branch.get_bound_location() is not None:
 
1801
        if self.branch.get_master_branch() is not None:
2139
1802
            self.lock_write()
2140
1803
            update_branch = True
2141
1804
        else:
2143
1806
            update_branch = False
2144
1807
        try:
2145
1808
            if update_branch:
2146
 
                old_tip = self.branch.update(possible_transports)
 
1809
                old_tip = self.branch.update()
2147
1810
            else:
2148
1811
                old_tip = None
2149
 
            return self._update_tree(old_tip, change_reporter)
 
1812
            return self._update_tree(old_tip)
2150
1813
        finally:
2151
1814
            self.unlock()
2152
1815
 
2153
1816
    @needs_tree_write_lock
2154
 
    def _update_tree(self, old_tip=None, change_reporter=None):
 
1817
    def _update_tree(self, old_tip=None):
2155
1818
        """Update a tree to the master branch.
2156
1819
 
2157
1820
        :param old_tip: if supplied, the previous tip revision the branch,
2171
1834
        try:
2172
1835
            last_rev = self.get_parent_ids()[0]
2173
1836
        except IndexError:
2174
 
            last_rev = _mod_revision.NULL_REVISION
2175
 
        if last_rev != _mod_revision.ensure_null(self.branch.last_revision()):
 
1837
            last_rev = None
 
1838
        if last_rev != self.branch.last_revision():
2176
1839
            # merge tree state up to new branch tip.
2177
1840
            basis = self.basis_tree()
2178
1841
            basis.lock_read()
2179
1842
            try:
2180
1843
                to_tree = self.branch.basis_tree()
2181
1844
                if basis.inventory.root is None:
2182
 
                    self.set_root_id(to_tree.get_root_id())
2183
 
                    self.flush()
 
1845
                    self.set_root_id(to_tree.inventory.root.file_id)
2184
1846
                result += merge.merge_inner(
2185
1847
                                      self.branch,
2186
1848
                                      to_tree,
2187
1849
                                      basis,
2188
 
                                      this_tree=self,
2189
 
                                      change_reporter=change_reporter)
 
1850
                                      this_tree=self)
2190
1851
            finally:
2191
1852
                basis.unlock()
2192
1853
            # TODO - dedup parents list with things merged by pull ?
2201
1862
            for parent in merges:
2202
1863
                parent_trees.append(
2203
1864
                    (parent, self.branch.repository.revision_tree(parent)))
2204
 
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
 
1865
            if old_tip is not None:
2205
1866
                parent_trees.append(
2206
1867
                    (old_tip, self.branch.repository.revision_tree(old_tip)))
2207
1868
            self.set_parent_trees(parent_trees)
2210
1871
            # the working tree had the same last-revision as the master
2211
1872
            # branch did. We may still have pivot local work from the local
2212
1873
            # branch into old_tip:
2213
 
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
 
1874
            if old_tip is not None:
2214
1875
                self.add_parent_tree_id(old_tip)
2215
 
        if (old_tip is not None and not _mod_revision.is_null(old_tip)
2216
 
            and old_tip != last_rev):
 
1876
        if old_tip and old_tip != last_rev:
2217
1877
            # our last revision was not the prior branch last revision
2218
1878
            # and we have converted that last revision to a pending merge.
2219
1879
            # base is somewhere between the branch tip now
2220
1880
            # and the now pending merge
2221
 
 
2222
 
            # Since we just modified the working tree and inventory, flush out
2223
 
            # the current state, before we modify it again.
2224
 
            # TODO: jam 20070214 WorkingTree3 doesn't require this, dirstate
2225
 
            #       requires it only because TreeTransform directly munges the
2226
 
            #       inventory and calls tree._write_inventory(). Ultimately we
2227
 
            #       should be able to remove this extra flush.
2228
 
            self.flush()
2229
 
            graph = self.branch.repository.get_graph()
2230
 
            base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
2231
 
                                                old_tip)
 
1881
            from bzrlib.revision import common_ancestor
 
1882
            try:
 
1883
                base_rev_id = common_ancestor(self.branch.last_revision(),
 
1884
                                              old_tip,
 
1885
                                              self.branch.repository)
 
1886
            except errors.NoCommonAncestor:
 
1887
                base_rev_id = None
2232
1888
            base_tree = self.branch.repository.revision_tree(base_rev_id)
2233
1889
            other_tree = self.branch.repository.revision_tree(old_tip)
2234
1890
            result += merge.merge_inner(
2235
1891
                                  self.branch,
2236
1892
                                  other_tree,
2237
1893
                                  base_tree,
2238
 
                                  this_tree=self,
2239
 
                                  change_reporter=change_reporter)
 
1894
                                  this_tree=self)
2240
1895
        return result
2241
1896
 
2242
1897
    def _write_hashcache_if_dirty(self):
2294
1949
    def walkdirs(self, prefix=""):
2295
1950
        """Walk the directories of this tree.
2296
1951
 
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
 
 
2302
1952
        This API returns a generator, which is only valid during the current
2303
1953
        tree transaction - within a single lock_read or lock_write duration.
2304
1954
 
2305
 
        If the tree is not locked, it may cause an error to be raised,
2306
 
        depending on the tree implementation.
 
1955
        If the tree is not locked, it may cause an error to be raised, depending
 
1956
        on the tree implementation.
2307
1957
        """
2308
1958
        disk_top = self.abspath(prefix)
2309
1959
        if disk_top.endswith('/'):
2315
1965
            current_disk = disk_iterator.next()
2316
1966
            disk_finished = False
2317
1967
        except OSError, e:
2318
 
            if not (e.errno == errno.ENOENT or
2319
 
                (sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
 
1968
            if e.errno != errno.ENOENT:
2320
1969
                raise
2321
1970
            current_disk = None
2322
1971
            disk_finished = True
2327
1976
            current_inv = None
2328
1977
            inv_finished = True
2329
1978
        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)
2336
1979
            if not disk_finished:
2337
1980
                # strip out .bzr dirs
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 -
 
1981
                if current_disk[0][1][top_strip_len:] == '':
 
1982
                    # osutils.walkdirs can be made nicer - 
2341
1983
                    # yield the path-from-prefix rather than the pathjoined
2342
1984
                    # value.
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'):
 
1985
                    bzrdir_loc = bisect_left(current_disk[1], ('.bzr', '.bzr'))
 
1986
                    if current_disk[1][bzrdir_loc][0] == '.bzr':
2347
1987
                        # we dont yield the contents of, or, .bzr itself.
2348
 
                        del cur_disk_dir_content[bzrdir_loc]
 
1988
                        del current_disk[1][bzrdir_loc]
2349
1989
            if inv_finished:
2350
1990
                # everything is unknown
2351
1991
                direction = 1
2353
1993
                # everything is missing
2354
1994
                direction = -1
2355
1995
            else:
2356
 
                direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
 
1996
                direction = cmp(current_inv[0][0], current_disk[0][0])
2357
1997
            if direction > 0:
2358
1998
                # disk is before inventory - unknown
2359
1999
                dirblock = [(relpath, basename, kind, stat, None, None) for
2360
 
                    relpath, basename, kind, stat, top_path in
2361
 
                    cur_disk_dir_content]
2362
 
                yield (cur_disk_dir_relpath, None), dirblock
 
2000
                    relpath, basename, kind, stat, top_path in current_disk[1]]
 
2001
                yield (current_disk[0][0], None), dirblock
2363
2002
                try:
2364
2003
                    current_disk = disk_iterator.next()
2365
2004
                except StopIteration:
2367
2006
            elif direction < 0:
2368
2007
                # inventory is before disk - missing.
2369
2008
                dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
2370
 
                    for relpath, basename, dkind, stat, fileid, kind in
 
2009
                    for relpath, basename, dkind, stat, fileid, kind in 
2371
2010
                    current_inv[1]]
2372
2011
                yield (current_inv[0][0], current_inv[0][1]), dirblock
2373
2012
                try:
2379
2018
                # merge the inventory and disk data together
2380
2019
                dirblock = []
2381
2020
                for relpath, subiterator in itertools.groupby(sorted(
2382
 
                    current_inv[1] + cur_disk_dir_content,
2383
 
                    key=operator.itemgetter(0)), operator.itemgetter(1)):
 
2021
                    current_inv[1] + current_disk[1], key=operator.itemgetter(0)), operator.itemgetter(1)):
2384
2022
                    path_elements = list(subiterator)
2385
2023
                    if len(path_elements) == 2:
2386
2024
                        inv_row, disk_row = path_elements
2412
2050
                    disk_finished = True
2413
2051
 
2414
2052
    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
 
        """
2423
2053
        _directory = 'directory'
2424
2054
        # get the root in the inventory
2425
2055
        inv = self.inventory
2439
2069
                relroot = ""
2440
2070
            # FIXME: stash the node in pending
2441
2071
            entry = inv[top_id]
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
 
                        ))
 
2072
            for name, child in entry.sorted_children():
 
2073
                dirblock.append((relroot + name, name, child.kind, None,
 
2074
                    child.file_id, child.kind
 
2075
                    ))
2447
2076
            yield (currentdir[0], entry.file_id), dirblock
2448
2077
            # push the user specified dirs from dirblock
2449
2078
            for dir in reversed(dirblock):
2450
2079
                if dir[2] == _directory:
2451
2080
                    pending.append(dir)
2452
2081
 
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
 
 
2523
2082
 
2524
2083
class WorkingTree2(WorkingTree):
2525
2084
    """This is the Format 2 working tree.
2529
2088
     - uses the branch last-revision.
2530
2089
    """
2531
2090
 
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
 
 
2543
2091
    def lock_tree_write(self):
2544
2092
        """See WorkingTree.lock_tree_write().
2545
2093
 
2554
2102
            raise
2555
2103
 
2556
2104
    def unlock(self):
2557
 
        # do non-implementation specific cleanup
2558
 
        self._cleanup()
2559
 
 
2560
2105
        # we share control files:
2561
2106
        if self._control_files._lock_count == 3:
2562
2107
            # _inventory_is_modified is always False during a read lock.
2585
2130
    def _last_revision(self):
2586
2131
        """See Mutable.last_revision."""
2587
2132
        try:
2588
 
            return self._transport.get_bytes('last-revision')
 
2133
            return self._control_files.get_utf8('last-revision').read()
2589
2134
        except errors.NoSuchFile:
2590
 
            return _mod_revision.NULL_REVISION
 
2135
            return None
2591
2136
 
2592
2137
    def _change_last_revision(self, revision_id):
2593
2138
        """See WorkingTree._change_last_revision."""
2594
2139
        if revision_id is None or revision_id == NULL_REVISION:
2595
2140
            try:
2596
 
                self._transport.delete('last-revision')
 
2141
                self._control_files._transport.delete('last-revision')
2597
2142
            except errors.NoSuchFile:
2598
2143
                pass
2599
2144
            return False
2600
2145
        else:
2601
 
            self._transport.put_bytes('last-revision', revision_id,
2602
 
                mode=self._control_files._file_mode)
 
2146
            self._control_files.put_utf8('last-revision', revision_id)
2603
2147
            return True
2604
2148
 
2605
2149
    @needs_tree_write_lock
2617
2161
    @needs_read_lock
2618
2162
    def conflicts(self):
2619
2163
        try:
2620
 
            confile = self._transport.get('conflicts')
 
2164
            confile = self._control_files.get('conflicts')
2621
2165
        except errors.NoSuchFile:
2622
2166
            return _mod_conflicts.ConflictList()
2623
2167
        try:
2624
 
            try:
2625
 
                if confile.next() != CONFLICT_HEADER_1 + '\n':
2626
 
                    raise errors.ConflictFormatError()
2627
 
            except StopIteration:
 
2168
            if confile.next() != CONFLICT_HEADER_1 + '\n':
2628
2169
                raise errors.ConflictFormatError()
2629
 
            return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
2630
 
        finally:
2631
 
            confile.close()
 
2170
        except StopIteration:
 
2171
            raise errors.ConflictFormatError()
 
2172
        return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
2632
2173
 
2633
2174
    def unlock(self):
2634
 
        # do non-implementation specific cleanup
2635
 
        self._cleanup()
2636
2175
        if self._control_files._lock_count == 1:
2637
2176
            # _inventory_is_modified is always False during a read lock.
2638
2177
            if self._inventory_is_modified:
2651
2190
            return path[:-len(suffix)]
2652
2191
 
2653
2192
 
 
2193
@deprecated_function(zero_eight)
 
2194
def is_control_file(filename):
 
2195
    """See WorkingTree.is_control_filename(filename)."""
 
2196
    ## FIXME: better check
 
2197
    filename = normpath(filename)
 
2198
    while filename != '':
 
2199
        head, tail = os.path.split(filename)
 
2200
        ## mutter('check %r for control file' % ((head, tail),))
 
2201
        if tail == '.bzr':
 
2202
            return True
 
2203
        if filename == head:
 
2204
            break
 
2205
        filename = head
 
2206
    return False
 
2207
 
 
2208
 
2654
2209
class WorkingTreeFormat(object):
2655
2210
    """An encapsulation of the initialization and open routines for a format.
2656
2211
 
2675
2230
    _formats = {}
2676
2231
    """The known formats."""
2677
2232
 
2678
 
    requires_rich_root = False
2679
 
 
2680
 
    upgrade_recommended = False
2681
 
 
2682
2233
    @classmethod
2683
2234
    def find_format(klass, a_bzrdir):
2684
2235
        """Return the format for the working tree object in a_bzrdir."""
2689
2240
        except errors.NoSuchFile:
2690
2241
            raise errors.NoWorkingTree(base=transport.base)
2691
2242
        except KeyError:
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)
 
2243
            raise errors.UnknownFormatError(format=format_string)
2700
2244
 
2701
2245
    @classmethod
2702
2246
    def get_default_format(klass):
2720
2264
        """
2721
2265
        return True
2722
2266
 
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
 
 
2731
2267
    @classmethod
2732
2268
    def register_format(klass, format):
2733
2269
        klass._formats[format.get_format_string()] = format
2738
2274
 
2739
2275
    @classmethod
2740
2276
    def unregister_format(klass, format):
 
2277
        assert klass._formats[format.get_format_string()] is format
2741
2278
        del klass._formats[format.get_format_string()]
2742
2279
 
2743
2280
 
 
2281
 
2744
2282
class WorkingTreeFormat2(WorkingTreeFormat):
2745
2283
    """The second working tree format. 
2746
2284
 
2747
2285
    This format modified the hash cache from the format 1 hash cache.
2748
2286
    """
2749
2287
 
2750
 
    upgrade_recommended = True
2751
 
 
2752
2288
    def get_format_description(self):
2753
2289
        """See WorkingTreeFormat.get_format_description()."""
2754
2290
        return "Working tree format 2"
2755
2291
 
2756
 
    def _stub_initialize_on_transport(self, transport, file_mode):
2757
 
        """Workaround: create control files for a remote working tree.
2758
 
 
 
2292
    def stub_initialize_remote(self, control_files):
 
2293
        """As a special workaround create critical control files for a remote working tree
 
2294
        
2759
2295
        This ensures that it can later be updated and dealt with locally,
2760
 
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
 
2296
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with 
2761
2297
        no working tree.  (See bug #43064).
2762
2298
        """
2763
2299
        sio = StringIO()
2764
2300
        inv = Inventory()
2765
 
        xml5.serializer_v5.write_inventory(inv, sio, working=True)
 
2301
        xml5.serializer_v5.write_inventory(inv, sio)
2766
2302
        sio.seek(0)
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):
 
2303
        control_files.put('inventory', sio)
 
2304
 
 
2305
        control_files.put_utf8('pending-merges', '')
 
2306
        
 
2307
 
 
2308
    def initialize(self, a_bzrdir, revision_id=None):
2772
2309
        """See WorkingTreeFormat.initialize()."""
2773
2310
        if not isinstance(a_bzrdir.transport, LocalTransport):
2774
2311
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
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()
 
2312
        branch = a_bzrdir.open_branch()
 
2313
        if revision_id is not None:
 
2314
            branch.lock_write()
 
2315
            try:
 
2316
                revision_history = branch.revision_history()
 
2317
                try:
 
2318
                    position = revision_history.index(revision_id)
 
2319
                except ValueError:
 
2320
                    raise errors.NoSuchRevision(branch, revision_id)
 
2321
                branch.set_revision_history(revision_history[:position + 1])
 
2322
            finally:
 
2323
                branch.unlock()
 
2324
        revision = branch.last_revision()
2786
2325
        inv = Inventory()
2787
2326
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2788
2327
                         branch,
2790
2329
                         _internal=True,
2791
2330
                         _format=self,
2792
2331
                         _bzrdir=a_bzrdir)
2793
 
        basis_tree = branch.repository.revision_tree(revision_id)
 
2332
        basis_tree = branch.repository.revision_tree(revision)
2794
2333
        if basis_tree.inventory.root is not None:
2795
 
            wt.set_root_id(basis_tree.get_root_id())
 
2334
            wt.set_root_id(basis_tree.inventory.root.file_id)
2796
2335
        # set the parent list and cache the 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)
 
2336
        wt.set_parent_trees([(revision, basis_tree)])
2802
2337
        transform.build_tree(basis_tree, wt)
2803
2338
        return wt
2804
2339
 
2817
2352
            raise NotImplementedError
2818
2353
        if not isinstance(a_bzrdir.transport, LocalTransport):
2819
2354
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2820
 
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
 
2355
        return WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2821
2356
                           _internal=True,
2822
2357
                           _format=self,
2823
2358
                           _bzrdir=a_bzrdir)
2824
 
        return wt
 
2359
 
2825
2360
 
2826
2361
class WorkingTreeFormat3(WorkingTreeFormat):
2827
2362
    """The second working tree format updated to record a format marker.
2834
2369
        - is new in bzr 0.8
2835
2370
        - uses a LockDir to guard access for writes.
2836
2371
    """
2837
 
    
2838
 
    upgrade_recommended = True
2839
2372
 
2840
2373
    def get_format_string(self):
2841
2374
        """See WorkingTreeFormat.get_format_string()."""
2848
2381
    _lock_file_name = 'lock'
2849
2382
    _lock_class = LockDir
2850
2383
 
2851
 
    _tree_class = WorkingTree3
2852
 
 
2853
 
    def __get_matchingbzrdir(self):
2854
 
        return bzrdir.BzrDirMetaFormat1()
2855
 
 
2856
 
    _matchingbzrdir = property(__get_matchingbzrdir)
2857
 
 
2858
2384
    def _open_control_files(self, a_bzrdir):
2859
2385
        transport = a_bzrdir.get_workingtree_transport(None)
2860
2386
        return LockableFiles(transport, self._lock_file_name, 
2861
2387
                             self._lock_class)
2862
2388
 
2863
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2864
 
                   accelerator_tree=None, hardlink=False):
 
2389
    def initialize(self, a_bzrdir, revision_id=None):
2865
2390
        """See WorkingTreeFormat.initialize().
2866
2391
        
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.
 
2392
        revision_id allows creating a working tree at a different
 
2393
        revision than the branch is at.
2875
2394
        """
2876
2395
        if not isinstance(a_bzrdir.transport, LocalTransport):
2877
2396
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2879
2398
        control_files = self._open_control_files(a_bzrdir)
2880
2399
        control_files.create_lock()
2881
2400
        control_files.lock_write()
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()
 
2401
        control_files.put_utf8('format', self.get_format_string())
 
2402
        branch = a_bzrdir.open_branch()
2888
2403
        if revision_id is None:
2889
 
            revision_id = _mod_revision.ensure_null(branch.last_revision())
 
2404
            revision_id = branch.last_revision()
2890
2405
        # WorkingTree3 can handle an inventory which has a unique root id.
2891
2406
        # as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
2892
2407
        # those trees. And because there isn't a format bump inbetween, we
2893
2408
        # are maintaining compatibility with older clients.
2894
2409
        # inv = Inventory(root_id=gen_root_id())
2895
 
        inv = self._initial_inventory()
2896
 
        wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
 
2410
        inv = Inventory()
 
2411
        wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2897
2412
                         branch,
2898
2413
                         inv,
2899
2414
                         _internal=True,
2905
2420
            basis_tree = branch.repository.revision_tree(revision_id)
2906
2421
            # only set an explicit root id if there is one to set.
2907
2422
            if basis_tree.inventory.root is not None:
2908
 
                wt.set_root_id(basis_tree.get_root_id())
 
2423
                wt.set_root_id(basis_tree.inventory.root.file_id)
2909
2424
            if revision_id == NULL_REVISION:
2910
2425
                wt.set_parent_trees([])
2911
2426
            else:
2918
2433
            wt.unlock()
2919
2434
        return wt
2920
2435
 
2921
 
    def _initial_inventory(self):
2922
 
        return Inventory()
2923
 
 
2924
2436
    def __init__(self):
2925
2437
        super(WorkingTreeFormat3, self).__init__()
 
2438
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2926
2439
 
2927
2440
    def open(self, a_bzrdir, _found=False):
2928
2441
        """Return the WorkingTree object for a_bzrdir
2935
2448
            raise NotImplementedError
2936
2449
        if not isinstance(a_bzrdir.transport, LocalTransport):
2937
2450
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2938
 
        wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
2939
 
        return wt
 
2451
        return self._open(a_bzrdir, self._open_control_files(a_bzrdir))
2940
2452
 
2941
2453
    def _open(self, a_bzrdir, control_files):
2942
2454
        """Open the tree itself.
2944
2456
        :param a_bzrdir: the dir for the tree.
2945
2457
        :param control_files: the control files for the tree.
2946
2458
        """
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)
 
2459
        return WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
 
2460
                           _internal=True,
 
2461
                           _format=self,
 
2462
                           _bzrdir=a_bzrdir,
 
2463
                           _control_files=control_files)
2952
2464
 
2953
2465
    def __str__(self):
2954
2466
        return self.get_format_string()
2955
2467
 
2956
2468
 
2957
 
__default_format = WorkingTreeFormat4()
 
2469
__default_format = WorkingTreeFormat3()
2958
2470
WorkingTreeFormat.register_format(__default_format)
2959
 
WorkingTreeFormat.register_format(WorkingTreeFormat5())
2960
 
WorkingTreeFormat.register_format(WorkingTreeFormat3())
 
2471
WorkingTreeFormat.register_format(WorkingTreeFormat4())
2961
2472
WorkingTreeFormat.set_default_format(__default_format)
2962
2473
# formats which have no format string are not discoverable
2963
2474
# and not independently creatable, so are not registered.
2964
2475
_legacy_formats = [WorkingTreeFormat2(),
2965
2476
                   ]
 
2477
 
 
2478
 
 
2479
class WorkingTreeTestProviderAdapter(object):
 
2480
    """A tool to generate a suite testing multiple workingtree formats at once.
 
2481
 
 
2482
    This is done by copying the test once for each transport and injecting
 
2483
    the transport_server, transport_readonly_server, and workingtree_format
 
2484
    classes into each copy. Each copy is also given a new id() to make it
 
2485
    easy to identify.
 
2486
    """
 
2487
 
 
2488
    def __init__(self, transport_server, transport_readonly_server, formats):
 
2489
        self._transport_server = transport_server
 
2490
        self._transport_readonly_server = transport_readonly_server
 
2491
        self._formats = formats
 
2492
    
 
2493
    def _clone_test(self, test, bzrdir_format, workingtree_format, variation):
 
2494
        """Clone test for adaption."""
 
2495
        new_test = deepcopy(test)
 
2496
        new_test.transport_server = self._transport_server
 
2497
        new_test.transport_readonly_server = self._transport_readonly_server
 
2498
        new_test.bzrdir_format = bzrdir_format
 
2499
        new_test.workingtree_format = workingtree_format
 
2500
        def make_new_test_id():
 
2501
            new_id = "%s(%s)" % (test.id(), variation)
 
2502
            return lambda: new_id
 
2503
        new_test.id = make_new_test_id()
 
2504
        return new_test
 
2505
    
 
2506
    def adapt(self, test):
 
2507
        from bzrlib.tests import TestSuite
 
2508
        result = TestSuite()
 
2509
        for workingtree_format, bzrdir_format in self._formats:
 
2510
            new_test = self._clone_test(
 
2511
                test,
 
2512
                bzrdir_format,
 
2513
                workingtree_format, workingtree_format.__class__.__name__)
 
2514
            result.addTest(new_test)
 
2515
        return result