~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Martin Pool
  • Date: 2005-07-07 07:57:12 UTC
  • mto: This revision was merged to the branch mainline in revision 852.
  • Revision ID: mbp@sourcefrog.net-20050707075712-4784aa908809b905
- preliminary merge conflict detection

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
"""WorkingTree object and friends.
18
 
 
19
 
A WorkingTree represents the editable working copy of a branch.
20
 
Operations which represent the WorkingTree are also done here, 
21
 
such as renaming or adding files.  The WorkingTree has an inventory 
22
 
which is updated by these operations.  A commit produces a 
23
 
new revision based on the workingtree and its inventory.
24
 
 
25
 
At the moment every WorkingTree has its own branch.  Remote
26
 
WorkingTrees aren't supported.
27
 
 
28
 
To get a WorkingTree, call bzrdir.open_workingtree() or
29
 
WorkingTree.open(dir).
30
 
"""
31
 
 
32
 
# TODO: Give the workingtree sole responsibility for the working inventory;
33
 
# remove the variable and references to it from the branch.  This may require
34
 
# updating the commit code so as to update the inventory within the working
35
 
# copy, and making sure there's only one WorkingTree for any directory on disk.
36
 
# At the moment they may alias the inventory and have old copies of it in
37
 
# memory.  (Now done? -- mbp 20060309)
38
 
 
39
 
from cStringIO import StringIO
40
 
import os
41
 
 
42
 
from bzrlib.lazy_import import lazy_import
43
 
lazy_import(globals(), """
44
 
import collections
45
 
from copy import deepcopy
46
 
import errno
47
 
import stat
48
 
from time import time
49
 
import warnings
50
 
 
51
 
import bzrlib
52
 
from bzrlib import (
53
 
    bzrdir,
54
 
    conflicts as _mod_conflicts,
55
 
    errors,
56
 
    generate_ids,
57
 
    globbing,
58
 
    hashcache,
59
 
    ignores,
60
 
    merge,
61
 
    osutils,
62
 
    textui,
63
 
    transform,
64
 
    urlutils,
65
 
    xml5,
66
 
    xml6,
67
 
    )
68
 
import bzrlib.branch
69
 
from bzrlib.transport import get_transport
70
 
import bzrlib.ui
71
 
""")
72
 
 
73
 
from bzrlib import symbol_versioning
74
 
from bzrlib.decorators import needs_read_lock, needs_write_lock
75
 
from bzrlib.errors import (BzrCheckError,
76
 
                           BzrError,
77
 
                           ConflictFormatError,
78
 
                           WeaveRevisionNotPresent,
79
 
                           NotBranchError,
80
 
                           NoSuchFile,
81
 
                           NotVersionedError,
82
 
                           MergeModifiedFormatError,
83
 
                           UnsupportedOperation,
84
 
                           )
85
 
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID
86
 
from bzrlib.lockable_files import LockableFiles, TransportLock
87
 
from bzrlib.lockdir import LockDir
88
 
import bzrlib.mutabletree
89
 
from bzrlib.mutabletree import needs_tree_write_lock
90
 
from bzrlib.osutils import (
91
 
    compact_date,
92
 
    file_kind,
93
 
    isdir,
94
 
    pathjoin,
95
 
    safe_unicode,
96
 
    splitpath,
97
 
    rand_chars,
98
 
    normpath,
99
 
    realpath,
100
 
    supports_executable,
101
 
    )
102
 
from bzrlib.trace import mutter, note
103
 
from bzrlib.transport.local import LocalTransport
104
 
import bzrlib.tree
105
 
from bzrlib.progress import DummyProgress, ProgressPhase
106
 
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
107
 
import bzrlib.revisiontree
108
 
from bzrlib.rio import RioReader, rio_file, Stanza
109
 
from bzrlib.symbol_versioning import (deprecated_passed,
110
 
        deprecated_method,
111
 
        deprecated_function,
112
 
        DEPRECATED_PARAMETER,
113
 
        zero_eight,
114
 
        zero_eleven,
115
 
        zero_thirteen,
116
 
        )
117
 
 
118
 
 
119
 
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
120
 
CONFLICT_HEADER_1 = "BZR conflict list format 1"
121
 
 
122
 
 
123
 
@deprecated_function(zero_thirteen)
124
 
def gen_file_id(name):
125
 
    """Return new file id for the basename 'name'.
126
 
 
127
 
    Use bzrlib.generate_ids.gen_file_id() instead
128
 
    """
129
 
    return generate_ids.gen_file_id(name)
130
 
 
131
 
 
132
 
@deprecated_function(zero_thirteen)
133
 
def gen_root_id():
134
 
    """Return a new tree-root file id.
135
 
 
136
 
    This has been deprecated in favor of bzrlib.generate_ids.gen_root_id()
137
 
    """
138
 
    return generate_ids.gen_root_id()
139
 
 
140
 
 
141
 
class TreeEntry(object):
142
 
    """An entry that implements the minimum interface used by commands.
143
 
 
144
 
    This needs further inspection, it may be better to have 
145
 
    InventoryEntries without ids - though that seems wrong. For now,
146
 
    this is a parallel hierarchy to InventoryEntry, and needs to become
147
 
    one of several things: decorates to that hierarchy, children of, or
148
 
    parents of it.
149
 
    Another note is that these objects are currently only used when there is
150
 
    no InventoryEntry available - i.e. for unversioned objects.
151
 
    Perhaps they should be UnversionedEntry et al. ? - RBC 20051003
152
 
    """
153
 
 
154
 
    def __eq__(self, other):
155
 
        # yes, this us ugly, TODO: best practice __eq__ style.
156
 
        return (isinstance(other, TreeEntry)
157
 
                and other.__class__ == self.__class__)
158
 
 
159
 
    def kind_character(self):
160
 
        return "???"
161
 
 
162
 
 
163
 
class TreeDirectory(TreeEntry):
164
 
    """See TreeEntry. This is a directory in a working tree."""
165
 
 
166
 
    def __eq__(self, other):
167
 
        return (isinstance(other, TreeDirectory)
168
 
                and other.__class__ == self.__class__)
169
 
 
170
 
    def kind_character(self):
171
 
        return "/"
172
 
 
173
 
 
174
 
class TreeFile(TreeEntry):
175
 
    """See TreeEntry. This is a regular file in a working tree."""
176
 
 
177
 
    def __eq__(self, other):
178
 
        return (isinstance(other, TreeFile)
179
 
                and other.__class__ == self.__class__)
180
 
 
181
 
    def kind_character(self):
182
 
        return ''
183
 
 
184
 
 
185
 
class TreeLink(TreeEntry):
186
 
    """See TreeEntry. This is a symlink in a working tree."""
187
 
 
188
 
    def __eq__(self, other):
189
 
        return (isinstance(other, TreeLink)
190
 
                and other.__class__ == self.__class__)
191
 
 
192
 
    def kind_character(self):
193
 
        return ''
194
 
 
195
 
 
196
 
class WorkingTree(bzrlib.mutabletree.MutableTree):
197
 
    """Working copy tree.
198
 
 
199
 
    The inventory is held in the `Branch` working-inventory, and the
200
 
    files are in a directory on disk.
201
 
 
202
 
    It is possible for a `WorkingTree` to have a filename which is
203
 
    not listed in the Inventory and vice versa.
204
 
    """
205
 
 
206
 
    def __init__(self, basedir='.',
207
 
                 branch=DEPRECATED_PARAMETER,
208
 
                 _inventory=None,
209
 
                 _control_files=None,
210
 
                 _internal=False,
211
 
                 _format=None,
212
 
                 _bzrdir=None):
213
 
        """Construct a WorkingTree for basedir.
214
 
 
215
 
        If the branch is not supplied, it is opened automatically.
216
 
        If the branch is supplied, it must be the branch for this basedir.
217
 
        (branch.base is not cross checked, because for remote branches that
218
 
        would be meaningless).
219
 
        """
220
 
        self._format = _format
221
 
        self.bzrdir = _bzrdir
222
 
        if not _internal:
223
 
            # not created via open etc.
224
 
            warnings.warn("WorkingTree() is deprecated as of bzr version 0.8. "
225
 
                 "Please use bzrdir.open_workingtree or WorkingTree.open().",
226
 
                 DeprecationWarning,
227
 
                 stacklevel=2)
228
 
            wt = WorkingTree.open(basedir)
229
 
            self._branch = wt.branch
230
 
            self.basedir = wt.basedir
231
 
            self._control_files = wt._control_files
232
 
            self._hashcache = wt._hashcache
233
 
            self._set_inventory(wt._inventory, dirty=False)
234
 
            self._format = wt._format
235
 
            self.bzrdir = wt.bzrdir
236
 
        assert isinstance(basedir, basestring), \
237
 
            "base directory %r is not a string" % basedir
238
 
        basedir = safe_unicode(basedir)
239
 
        mutter("opening working tree %r", basedir)
240
 
        if deprecated_passed(branch):
241
 
            if not _internal:
242
 
                warnings.warn("WorkingTree(..., branch=XXX) is deprecated as of bzr 0.8."
243
 
                     " Please use bzrdir.open_workingtree() or"
244
 
                     " WorkingTree.open().",
245
 
                     DeprecationWarning,
246
 
                     stacklevel=2
247
 
                     )
248
 
            self._branch = branch
249
 
        else:
250
 
            self._branch = self.bzrdir.open_branch()
251
 
        self.basedir = realpath(basedir)
252
 
        # if branch is at our basedir and is a format 6 or less
253
 
        if isinstance(self._format, WorkingTreeFormat2):
254
 
            # share control object
255
 
            self._control_files = self.branch.control_files
256
 
        else:
257
 
            # assume all other formats have their own control files.
258
 
            assert isinstance(_control_files, LockableFiles), \
259
 
                    "_control_files must be a LockableFiles, not %r" \
260
 
                    % _control_files
261
 
            self._control_files = _control_files
262
 
        # update the whole cache up front and write to disk if anything changed;
263
 
        # in the future we might want to do this more selectively
264
 
        # two possible ways offer themselves : in self._unlock, write the cache
265
 
        # if needed, or, when the cache sees a change, append it to the hash
266
 
        # cache file, and have the parser take the most recent entry for a
267
 
        # given path only.
268
 
        cache_filename = self.bzrdir.get_workingtree_transport(None).local_abspath('stat-cache')
269
 
        self._hashcache = hashcache.HashCache(basedir, cache_filename,
270
 
                                              self._control_files._file_mode)
271
 
        hc = self._hashcache
272
 
        hc.read()
273
 
        # is this scan needed ? it makes things kinda slow.
274
 
        #hc.scan()
275
 
 
276
 
        if hc.needs_write:
277
 
            mutter("write hc")
278
 
            hc.write()
279
 
 
280
 
        if _inventory is None:
281
 
            self._inventory_is_modified = False
282
 
            self.read_working_inventory()
283
 
        else:
284
 
            # the caller of __init__ has provided an inventory,
285
 
            # we assume they know what they are doing - as its only
286
 
            # the Format factory and creation methods that are
287
 
            # permitted to do this.
288
 
            self._set_inventory(_inventory, dirty=False)
289
 
 
290
 
    branch = property(
291
 
        fget=lambda self: self._branch,
292
 
        doc="""The branch this WorkingTree is connected to.
293
 
 
294
 
            This cannot be set - it is reflective of the actual disk structure
295
 
            the working tree has been constructed from.
296
 
            """)
297
 
 
298
 
    def break_lock(self):
299
 
        """Break a lock if one is present from another instance.
300
 
 
301
 
        Uses the ui factory to ask for confirmation if the lock may be from
302
 
        an active process.
303
 
 
304
 
        This will probe the repository for its lock as well.
305
 
        """
306
 
        self._control_files.break_lock()
307
 
        self.branch.break_lock()
308
 
 
309
 
    def _set_inventory(self, inv, dirty):
310
 
        """Set the internal cached inventory.
311
 
 
312
 
        :param inv: The inventory to set.
313
 
        :param dirty: A boolean indicating whether the inventory is the same
314
 
            logical inventory as whats on disk. If True the inventory is not
315
 
            the same and should be written to disk or data will be lost, if
316
 
            False then the inventory is the same as that on disk and any
317
 
            serialisation would be unneeded overhead.
318
 
        """
319
 
        assert inv.root is not None
320
 
        self._inventory = inv
321
 
        self._inventory_is_modified = dirty
322
 
 
323
 
    @staticmethod
324
 
    def open(path=None, _unsupported=False):
325
 
        """Open an existing working tree at path.
326
 
 
327
 
        """
328
 
        if path is None:
329
 
            path = os.path.getcwdu()
330
 
        control = bzrdir.BzrDir.open(path, _unsupported)
331
 
        return control.open_workingtree(_unsupported)
332
 
        
333
 
    @staticmethod
334
 
    def open_containing(path=None):
335
 
        """Open an existing working tree which has its root about path.
336
 
        
337
 
        This probes for a working tree at path and searches upwards from there.
338
 
 
339
 
        Basically we keep looking up until we find the control directory or
340
 
        run into /.  If there isn't one, raises NotBranchError.
341
 
        TODO: give this a new exception.
342
 
        If there is one, it is returned, along with the unused portion of path.
343
 
 
344
 
        :return: The WorkingTree that contains 'path', and the rest of path
345
 
        """
346
 
        if path is None:
347
 
            path = osutils.getcwd()
348
 
        control, relpath = bzrdir.BzrDir.open_containing(path)
349
 
 
350
 
        return control.open_workingtree(), relpath
351
 
 
352
 
    @staticmethod
353
 
    def open_downlevel(path=None):
354
 
        """Open an unsupported working tree.
355
 
 
356
 
        Only intended for advanced situations like upgrading part of a bzrdir.
357
 
        """
358
 
        return WorkingTree.open(path, _unsupported=True)
359
 
 
360
 
    def __iter__(self):
361
 
        """Iterate through file_ids for this tree.
362
 
 
363
 
        file_ids are in a WorkingTree if they are in the working inventory
364
 
        and the working file exists.
365
 
        """
366
 
        inv = self._inventory
367
 
        for path, ie in inv.iter_entries():
368
 
            if osutils.lexists(self.abspath(path)):
369
 
                yield ie.file_id
370
 
 
371
 
    def __repr__(self):
372
 
        return "<%s of %s>" % (self.__class__.__name__,
373
 
                               getattr(self, 'basedir', None))
374
 
 
375
 
    def abspath(self, filename):
376
 
        return pathjoin(self.basedir, filename)
377
 
    
378
 
    def basis_tree(self):
379
 
        """Return RevisionTree for the current last revision.
380
 
        
381
 
        If the left most parent is a ghost then the returned tree will be an
382
 
        empty tree - one obtained by calling repository.revision_tree(None).
383
 
        """
384
 
        try:
385
 
            revision_id = self.get_parent_ids()[0]
386
 
        except IndexError:
387
 
            # no parents, return an empty revision tree.
388
 
            # in the future this should return the tree for
389
 
            # 'empty:' - the implicit root empty tree.
390
 
            return self.branch.repository.revision_tree(None)
391
 
        else:
392
 
            try:
393
 
                xml = self.read_basis_inventory()
394
 
                inv = xml6.serializer_v6.read_inventory_from_string(xml)
395
 
                if inv is not None and inv.revision_id == revision_id:
396
 
                    return bzrlib.revisiontree.RevisionTree(
397
 
                        self.branch.repository, inv, revision_id)
398
 
            except (NoSuchFile, errors.BadInventoryFormat):
399
 
                pass
400
 
        # No cached copy available, retrieve from the repository.
401
 
        # FIXME? RBC 20060403 should we cache the inventory locally
402
 
        # at this point ?
403
 
        try:
404
 
            return self.branch.repository.revision_tree(revision_id)
405
 
        except errors.RevisionNotPresent:
406
 
            # the basis tree *may* be a ghost or a low level error may have
407
 
            # occured. If the revision is present, its a problem, if its not
408
 
            # its a ghost.
409
 
            if self.branch.repository.has_revision(revision_id):
410
 
                raise
411
 
            # the basis tree is a ghost so return an empty tree.
412
 
            return self.branch.repository.revision_tree(None)
413
 
 
414
 
    @staticmethod
415
 
    @deprecated_method(zero_eight)
416
 
    def create(branch, directory):
417
 
        """Create a workingtree for branch at directory.
418
 
 
419
 
        If existing_directory already exists it must have a .bzr directory.
420
 
        If it does not exist, it will be created.
421
 
 
422
 
        This returns a new WorkingTree object for the new checkout.
423
 
 
424
 
        TODO FIXME RBC 20060124 when we have checkout formats in place this
425
 
        should accept an optional revisionid to checkout [and reject this if
426
 
        checking out into the same dir as a pre-checkout-aware branch format.]
427
 
 
428
 
        XXX: When BzrDir is present, these should be created through that 
429
 
        interface instead.
430
 
        """
431
 
        warnings.warn('delete WorkingTree.create', stacklevel=3)
432
 
        transport = get_transport(directory)
433
 
        if branch.bzrdir.root_transport.base == transport.base:
434
 
            # same dir 
435
 
            return branch.bzrdir.create_workingtree()
436
 
        # different directory, 
437
 
        # create a branch reference
438
 
        # and now a working tree.
439
 
        raise NotImplementedError
440
 
 
441
 
    @staticmethod
442
 
    @deprecated_method(zero_eight)
443
 
    def create_standalone(directory):
444
 
        """Create a checkout and a branch and a repo at directory.
445
 
 
446
 
        Directory must exist and be empty.
447
 
 
448
 
        please use BzrDir.create_standalone_workingtree
449
 
        """
450
 
        return bzrdir.BzrDir.create_standalone_workingtree(directory)
451
 
 
452
 
    def relpath(self, path):
453
 
        """Return the local path portion from a given path.
454
 
        
455
 
        The path may be absolute or relative. If its a relative path it is 
456
 
        interpreted relative to the python current working directory.
457
 
        """
458
 
        return osutils.relpath(self.basedir, path)
459
 
 
460
 
    def has_filename(self, filename):
461
 
        return osutils.lexists(self.abspath(filename))
462
 
 
463
 
    def get_file(self, file_id):
464
 
        return self.get_file_byname(self.id2path(file_id))
465
 
 
466
 
    def get_file_text(self, file_id):
467
 
        return self.get_file(file_id).read()
468
 
 
469
 
    def get_file_byname(self, filename):
470
 
        return file(self.abspath(filename), 'rb')
471
 
 
472
 
    def annotate_iter(self, file_id):
473
 
        """See Tree.annotate_iter
474
 
 
475
 
        This implementation will use the basis tree implementation if possible.
476
 
        Lines not in the basis are attributed to CURRENT_REVISION
477
 
 
478
 
        If there are pending merges, lines added by those merges will be
479
 
        incorrectly attributed to CURRENT_REVISION (but after committing, the
480
 
        attribution will be correct).
481
 
        """
482
 
        basis = self.basis_tree()
483
 
        changes = self._iter_changes(basis, True, [file_id]).next()
484
 
        changed_content, kind = changes[2], changes[6]
485
 
        if not changed_content:
486
 
            return basis.annotate_iter(file_id)
487
 
        if kind[1] is None:
488
 
            return None
489
 
        import annotate
490
 
        if kind[0] != 'file':
491
 
            old_lines = []
492
 
        else:
493
 
            old_lines = list(basis.annotate_iter(file_id))
494
 
        old = [old_lines]
495
 
        for tree in self.branch.repository.revision_trees(
496
 
            self.get_parent_ids()[1:]):
497
 
            if file_id not in tree:
498
 
                continue
499
 
            old.append(list(tree.annotate_iter(file_id)))
500
 
        return annotate.reannotate(old, self.get_file(file_id).readlines(),
501
 
                                   CURRENT_REVISION)
502
 
 
503
 
    def get_parent_ids(self):
504
 
        """See Tree.get_parent_ids.
505
 
        
506
 
        This implementation reads the pending merges list and last_revision
507
 
        value and uses that to decide what the parents list should be.
508
 
        """
509
 
        last_rev = self._last_revision()
510
 
        if last_rev is None:
511
 
            parents = []
512
 
        else:
513
 
            parents = [last_rev]
514
 
        try:
515
 
            merges_file = self._control_files.get_utf8('pending-merges')
516
 
        except NoSuchFile:
517
 
            pass
518
 
        else:
519
 
            for l in merges_file.readlines():
520
 
                parents.append(l.rstrip('\n'))
521
 
        return parents
522
 
 
523
 
    @needs_read_lock
524
 
    def get_root_id(self):
525
 
        """Return the id of this trees root"""
526
 
        return self._inventory.root.file_id
527
 
        
528
 
    def _get_store_filename(self, file_id):
529
 
        ## XXX: badly named; this is not in the store at all
530
 
        return self.abspath(self.id2path(file_id))
531
 
 
532
 
    @needs_read_lock
533
 
    def clone(self, to_bzrdir, revision_id=None, basis=None):
534
 
        """Duplicate this working tree into to_bzr, including all state.
535
 
        
536
 
        Specifically modified files are kept as modified, but
537
 
        ignored and unknown files are discarded.
538
 
 
539
 
        If you want to make a new line of development, see bzrdir.sprout()
540
 
 
541
 
        revision
542
 
            If not None, the cloned tree will have its last revision set to 
543
 
            revision, and and difference between the source trees last revision
544
 
            and this one merged in.
545
 
 
546
 
        basis
547
 
            If not None, a closer copy of a tree which may have some files in
548
 
            common, and which file content should be preferentially copied from.
549
 
        """
550
 
        # assumes the target bzr dir format is compatible.
551
 
        result = self._format.initialize(to_bzrdir)
552
 
        self.copy_content_into(result, revision_id)
553
 
        return result
554
 
 
555
 
    @needs_read_lock
556
 
    def copy_content_into(self, tree, revision_id=None):
557
 
        """Copy the current content and user files of this tree into tree."""
558
 
        tree.set_root_id(self.get_root_id())
559
 
        if revision_id is None:
560
 
            merge.transform_tree(tree, self)
561
 
        else:
562
 
            # TODO now merge from tree.last_revision to revision (to preserve
563
 
            # user local changes)
564
 
            merge.transform_tree(tree, self)
565
 
            tree.set_parent_ids([revision_id])
566
 
 
567
 
    def id2abspath(self, file_id):
568
 
        return self.abspath(self.id2path(file_id))
569
 
 
570
 
    def has_id(self, file_id):
571
 
        # files that have been deleted are excluded
572
 
        inv = self._inventory
573
 
        if not inv.has_id(file_id):
574
 
            return False
575
 
        path = inv.id2path(file_id)
576
 
        return osutils.lexists(self.abspath(path))
577
 
 
578
 
    def has_or_had_id(self, file_id):
579
 
        if file_id == self.inventory.root.file_id:
580
 
            return True
581
 
        return self.inventory.has_id(file_id)
582
 
 
583
 
    __contains__ = has_id
584
 
 
585
 
    def get_file_size(self, file_id):
586
 
        return os.path.getsize(self.id2abspath(file_id))
587
 
 
588
 
    @needs_read_lock
589
 
    def get_file_sha1(self, file_id, path=None, stat_value=None):
590
 
        if not path:
591
 
            path = self._inventory.id2path(file_id)
592
 
        return self._hashcache.get_sha1(path, stat_value)
593
 
 
594
 
    def get_file_mtime(self, file_id, path=None):
595
 
        if not path:
596
 
            path = self._inventory.id2path(file_id)
597
 
        return os.lstat(self.abspath(path)).st_mtime
598
 
 
599
 
    if not supports_executable():
600
 
        def is_executable(self, file_id, path=None):
601
 
            return self._inventory[file_id].executable
602
 
    else:
603
 
        def is_executable(self, file_id, path=None):
604
 
            if not path:
605
 
                path = self._inventory.id2path(file_id)
606
 
            mode = os.lstat(self.abspath(path)).st_mode
607
 
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
608
 
 
609
 
    @needs_write_lock
610
 
    def _add(self, files, ids, kinds):
611
 
        """See MutableTree._add."""
612
 
        # TODO: Re-adding a file that is removed in the working copy
613
 
        # should probably put it back with the previous ID.
614
 
        # the read and write working inventory should not occur in this 
615
 
        # function - they should be part of lock_write and unlock.
616
 
        inv = self.read_working_inventory()
617
 
        for f, file_id, kind in zip(files, ids, kinds):
618
 
            assert kind is not None
619
 
            if file_id is None:
620
 
                inv.add_path(f, kind=kind)
621
 
            else:
622
 
                inv.add_path(f, kind=kind, file_id=file_id)
623
 
        self._write_inventory(inv)
624
 
 
625
 
    @needs_tree_write_lock
626
 
    def _gather_kinds(self, files, kinds):
627
 
        """See MutableTree._gather_kinds."""
628
 
        for pos, f in enumerate(files):
629
 
            if kinds[pos] is None:
630
 
                fullpath = normpath(self.abspath(f))
631
 
                try:
632
 
                    kinds[pos] = file_kind(fullpath)
633
 
                except OSError, e:
634
 
                    if e.errno == errno.ENOENT:
635
 
                        raise NoSuchFile(fullpath)
636
 
 
637
 
    @needs_write_lock
638
 
    def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
639
 
        """Add revision_id as a parent.
640
 
 
641
 
        This is equivalent to retrieving the current list of parent ids
642
 
        and setting the list to its value plus revision_id.
643
 
 
644
 
        :param revision_id: The revision id to add to the parent list. It may
645
 
        be a ghost revision as long as its not the first parent to be added,
646
 
        or the allow_leftmost_as_ghost parameter is set True.
647
 
        :param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
648
 
        """
649
 
        parents = self.get_parent_ids() + [revision_id]
650
 
        self.set_parent_ids(parents,
651
 
            allow_leftmost_as_ghost=len(parents) > 1 or allow_leftmost_as_ghost)
652
 
 
653
 
    @needs_tree_write_lock
654
 
    def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
655
 
        """Add revision_id, tree tuple as a parent.
656
 
 
657
 
        This is equivalent to retrieving the current list of parent trees
658
 
        and setting the list to its value plus parent_tuple. See also
659
 
        add_parent_tree_id - if you only have a parent id available it will be
660
 
        simpler to use that api. If you have the parent already available, using
661
 
        this api is preferred.
662
 
 
663
 
        :param parent_tuple: The (revision id, tree) to add to the parent list.
664
 
            If the revision_id is a ghost, pass None for the tree.
665
 
        :param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
666
 
        """
667
 
        parent_ids = self.get_parent_ids() + [parent_tuple[0]]
668
 
        if len(parent_ids) > 1:
669
 
            # the leftmost may have already been a ghost, preserve that if it
670
 
            # was.
671
 
            allow_leftmost_as_ghost = True
672
 
        self.set_parent_ids(parent_ids,
673
 
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
674
 
 
675
 
    @needs_tree_write_lock
676
 
    def add_pending_merge(self, *revision_ids):
677
 
        # TODO: Perhaps should check at this point that the
678
 
        # history of the revision is actually present?
679
 
        parents = self.get_parent_ids()
680
 
        updated = False
681
 
        for rev_id in revision_ids:
682
 
            if rev_id in parents:
683
 
                continue
684
 
            parents.append(rev_id)
685
 
            updated = True
686
 
        if updated:
687
 
            self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
688
 
 
689
 
    @deprecated_method(zero_eleven)
690
 
    @needs_read_lock
691
 
    def pending_merges(self):
692
 
        """Return a list of pending merges.
693
 
 
694
 
        These are revisions that have been merged into the working
695
 
        directory but not yet committed.
696
 
 
697
 
        As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
698
 
        instead - which is available on all tree objects.
699
 
        """
700
 
        return self.get_parent_ids()[1:]
701
 
 
702
 
    def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
703
 
        """Common ghost checking functionality from set_parent_*.
704
 
 
705
 
        This checks that the left hand-parent exists if there are any
706
 
        revisions present.
707
 
        """
708
 
        if len(revision_ids) > 0:
709
 
            leftmost_id = revision_ids[0]
710
 
            if (not allow_leftmost_as_ghost and not
711
 
                self.branch.repository.has_revision(leftmost_id)):
712
 
                raise errors.GhostRevisionUnusableHere(leftmost_id)
713
 
 
714
 
    def _set_merges_from_parent_ids(self, parent_ids):
715
 
        merges = parent_ids[1:]
716
 
        self._control_files.put_utf8('pending-merges', '\n'.join(merges))
717
 
 
718
 
    @needs_tree_write_lock
719
 
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
720
 
        """Set the parent ids to revision_ids.
721
 
        
722
 
        See also set_parent_trees. This api will try to retrieve the tree data
723
 
        for each element of revision_ids from the trees repository. If you have
724
 
        tree data already available, it is more efficient to use
725
 
        set_parent_trees rather than set_parent_ids. set_parent_ids is however
726
 
        an easier API to use.
727
 
 
728
 
        :param revision_ids: The revision_ids to set as the parent ids of this
729
 
            working tree. Any of these may be ghosts.
730
 
        """
731
 
        self._check_parents_for_ghosts(revision_ids,
732
 
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
733
 
 
734
 
        if len(revision_ids) > 0:
735
 
            self.set_last_revision(revision_ids[0])
736
 
        else:
737
 
            self.set_last_revision(None)
738
 
 
739
 
        self._set_merges_from_parent_ids(revision_ids)
740
 
 
741
 
    @needs_tree_write_lock
742
 
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
743
 
        """See MutableTree.set_parent_trees."""
744
 
        parent_ids = [rev for (rev, tree) in parents_list]
745
 
 
746
 
        self._check_parents_for_ghosts(parent_ids,
747
 
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
748
 
 
749
 
        if len(parent_ids) == 0:
750
 
            leftmost_parent_id = None
751
 
            leftmost_parent_tree = None
752
 
        else:
753
 
            leftmost_parent_id, leftmost_parent_tree = parents_list[0]
754
 
 
755
 
        if self._change_last_revision(leftmost_parent_id):
756
 
            if leftmost_parent_tree is None:
757
 
                # If we don't have a tree, fall back to reading the
758
 
                # parent tree from the repository.
759
 
                self._cache_basis_inventory(leftmost_parent_id)
760
 
            else:
761
 
                inv = leftmost_parent_tree.inventory
762
 
                xml = self._create_basis_xml_from_inventory(
763
 
                                        leftmost_parent_id, inv)
764
 
                self._write_basis_inventory(xml)
765
 
        self._set_merges_from_parent_ids(parent_ids)
766
 
 
767
 
    @needs_tree_write_lock
768
 
    def set_pending_merges(self, rev_list):
769
 
        parents = self.get_parent_ids()
770
 
        leftmost = parents[:1]
771
 
        new_parents = leftmost + rev_list
772
 
        self.set_parent_ids(new_parents)
773
 
 
774
 
    @needs_tree_write_lock
775
 
    def set_merge_modified(self, modified_hashes):
776
 
        def iter_stanzas():
777
 
            for file_id, hash in modified_hashes.iteritems():
778
 
                yield Stanza(file_id=file_id, hash=hash)
779
 
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
780
 
 
781
 
    @needs_tree_write_lock
782
 
    def _put_rio(self, filename, stanzas, header):
783
 
        my_file = rio_file(stanzas, header)
784
 
        self._control_files.put(filename, my_file)
785
 
 
786
 
    @needs_write_lock # because merge pulls data into the branch.
787
 
    def merge_from_branch(self, branch, to_revision=None):
788
 
        """Merge from a branch into this working tree.
789
 
 
790
 
        :param branch: The branch to merge from.
791
 
        :param to_revision: If non-None, the merge will merge to to_revision, but 
792
 
            not beyond it. to_revision does not need to be in the history of
793
 
            the branch when it is supplied. If None, to_revision defaults to
794
 
            branch.last_revision().
795
 
        """
796
 
        from bzrlib.merge import Merger, Merge3Merger
797
 
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
798
 
        try:
799
 
            merger = Merger(self.branch, this_tree=self, pb=pb)
800
 
            merger.pp = ProgressPhase("Merge phase", 5, pb)
801
 
            merger.pp.next_phase()
802
 
            # check that there are no
803
 
            # local alterations
804
 
            merger.check_basis(check_clean=True, require_commits=False)
805
 
            if to_revision is None:
806
 
                to_revision = branch.last_revision()
807
 
            merger.other_rev_id = to_revision
808
 
            if merger.other_rev_id is None:
809
 
                raise error.NoCommits(branch)
810
 
            self.branch.fetch(branch, last_revision=merger.other_rev_id)
811
 
            merger.other_basis = merger.other_rev_id
812
 
            merger.other_tree = self.branch.repository.revision_tree(
813
 
                merger.other_rev_id)
814
 
            merger.pp.next_phase()
815
 
            merger.find_base()
816
 
            if merger.base_rev_id == merger.other_rev_id:
817
 
                raise errors.PointlessMerge
818
 
            merger.backup_files = False
819
 
            merger.merge_type = Merge3Merger
820
 
            merger.set_interesting_files(None)
821
 
            merger.show_base = False
822
 
            merger.reprocess = False
823
 
            conflicts = merger.do_merge()
824
 
            merger.set_pending()
825
 
        finally:
826
 
            pb.finished()
827
 
        return conflicts
828
 
 
829
 
    @needs_read_lock
830
 
    def merge_modified(self):
831
 
        try:
832
 
            hashfile = self._control_files.get('merge-hashes')
833
 
        except NoSuchFile:
834
 
            return {}
835
 
        merge_hashes = {}
836
 
        try:
837
 
            if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
838
 
                raise MergeModifiedFormatError()
839
 
        except StopIteration:
840
 
            raise MergeModifiedFormatError()
841
 
        for s in RioReader(hashfile):
842
 
            file_id = s.get("file_id")
843
 
            if file_id not in self.inventory:
844
 
                continue
845
 
            hash = s.get("hash")
846
 
            if hash == self.get_file_sha1(file_id):
847
 
                merge_hashes[file_id] = hash
848
 
        return merge_hashes
849
 
 
850
 
    @needs_write_lock
851
 
    def mkdir(self, path, file_id=None):
852
 
        """See MutableTree.mkdir()."""
853
 
        if file_id is None:
854
 
            file_id = generate_ids.gen_file_id(os.path.basename(path))
855
 
        os.mkdir(self.abspath(path))
856
 
        self.add(path, file_id, 'directory')
857
 
        return file_id
858
 
 
859
 
    def get_symlink_target(self, file_id):
860
 
        return os.readlink(self.id2abspath(file_id))
861
 
 
862
 
    def file_class(self, filename):
863
 
        if self.path2id(filename):
864
 
            return 'V'
865
 
        elif self.is_ignored(filename):
866
 
            return 'I'
867
 
        else:
868
 
            return '?'
869
 
 
870
 
    def flush(self):
871
 
        """Write the in memory inventory to disk."""
872
 
        # TODO: Maybe this should only write on dirty ?
873
 
        if self._control_files._lock_mode != 'w':
874
 
            raise errors.NotWriteLocked(self)
875
 
        sio = StringIO()
876
 
        xml5.serializer_v5.write_inventory(self._inventory, sio)
877
 
        sio.seek(0)
878
 
        self._control_files.put('inventory', sio)
879
 
        self._inventory_is_modified = False
880
 
 
881
 
    def list_files(self, include_root=False):
882
 
        """Recursively list all files as (path, class, kind, id, entry).
883
 
 
884
 
        Lists, but does not descend into unversioned directories.
885
 
 
886
 
        This does not include files that have been deleted in this
887
 
        tree.
888
 
 
889
 
        Skips the control directory.
890
 
        """
891
 
        inv = self._inventory
892
 
        if include_root is True:
893
 
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
894
 
        # Convert these into local objects to save lookup times
895
 
        pathjoin = osutils.pathjoin
896
 
        file_kind = osutils.file_kind
897
 
 
898
 
        # transport.base ends in a slash, we want the piece
899
 
        # between the last two slashes
900
 
        transport_base_dir = self.bzrdir.transport.base.rsplit('/', 2)[1]
901
 
 
902
 
        fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
903
 
 
904
 
        # directory file_id, relative path, absolute path, reverse sorted children
905
 
        children = os.listdir(self.basedir)
906
 
        children.sort()
907
 
        # jam 20060527 The kernel sized tree seems equivalent whether we 
908
 
        # use a deque and popleft to keep them sorted, or if we use a plain
909
 
        # list and just reverse() them.
910
 
        children = collections.deque(children)
911
 
        stack = [(inv.root.file_id, u'', self.basedir, children)]
912
 
        while stack:
913
 
            from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
914
 
 
915
 
            while children:
916
 
                f = children.popleft()
917
 
                ## TODO: If we find a subdirectory with its own .bzr
918
 
                ## directory, then that is a separate tree and we
919
 
                ## should exclude it.
920
 
 
921
 
                # the bzrdir for this tree
922
 
                if transport_base_dir == f:
923
 
                    continue
924
 
 
925
 
                # we know that from_dir_relpath and from_dir_abspath never end in a slash
926
 
                # and 'f' doesn't begin with one, we can do a string op, rather
927
 
                # than the checks of pathjoin(), all relative paths will have an extra slash
928
 
                # at the beginning
929
 
                fp = from_dir_relpath + '/' + f
930
 
 
931
 
                # absolute path
932
 
                fap = from_dir_abspath + '/' + f
933
 
                
934
 
                f_ie = inv.get_child(from_dir_id, f)
935
 
                if f_ie:
936
 
                    c = 'V'
937
 
                elif self.is_ignored(fp[1:]):
938
 
                    c = 'I'
939
 
                else:
940
 
                    # we may not have found this file, because of a unicode issue
941
 
                    f_norm, can_access = osutils.normalized_filename(f)
942
 
                    if f == f_norm or not can_access:
943
 
                        # No change, so treat this file normally
944
 
                        c = '?'
945
 
                    else:
946
 
                        # this file can be accessed by a normalized path
947
 
                        # check again if it is versioned
948
 
                        # these lines are repeated here for performance
949
 
                        f = f_norm
950
 
                        fp = from_dir_relpath + '/' + f
951
 
                        fap = from_dir_abspath + '/' + f
952
 
                        f_ie = inv.get_child(from_dir_id, f)
953
 
                        if f_ie:
954
 
                            c = 'V'
955
 
                        elif self.is_ignored(fp[1:]):
956
 
                            c = 'I'
957
 
                        else:
958
 
                            c = '?'
959
 
 
960
 
                fk = file_kind(fap)
961
 
 
962
 
                if f_ie:
963
 
                    if f_ie.kind != fk:
964
 
                        raise BzrCheckError("file %r entered as kind %r id %r, "
965
 
                                            "now of kind %r"
966
 
                                            % (fap, f_ie.kind, f_ie.file_id, fk))
967
 
 
968
 
                # make a last minute entry
969
 
                if f_ie:
970
 
                    yield fp[1:], c, fk, f_ie.file_id, f_ie
971
 
                else:
972
 
                    try:
973
 
                        yield fp[1:], c, fk, None, fk_entries[fk]()
974
 
                    except KeyError:
975
 
                        yield fp[1:], c, fk, None, TreeEntry()
976
 
                    continue
977
 
                
978
 
                if fk != 'directory':
979
 
                    continue
980
 
 
981
 
                # But do this child first
982
 
                new_children = os.listdir(fap)
983
 
                new_children.sort()
984
 
                new_children = collections.deque(new_children)
985
 
                stack.append((f_ie.file_id, fp, fap, new_children))
986
 
                # Break out of inner loop, so that we start outer loop with child
987
 
                break
988
 
            else:
989
 
                # if we finished all children, pop it off the stack
990
 
                stack.pop()
991
 
 
992
 
    @needs_tree_write_lock
993
 
    def move(self, from_paths, to_name):
994
 
        """Rename files.
995
 
 
996
 
        to_name must exist in the inventory.
997
 
 
998
 
        If to_name exists and is a directory, the files are moved into
999
 
        it, keeping their old names.  
1000
 
 
1001
 
        Note that to_name is only the last component of the new name;
1002
 
        this doesn't change the directory.
1003
 
 
1004
 
        This returns a list of (from_path, to_path) pairs for each
1005
 
        entry that is moved.
1006
 
        """
1007
 
        result = []
1008
 
        ## TODO: Option to move IDs only
1009
 
        assert not isinstance(from_paths, basestring)
1010
 
        inv = self.inventory
1011
 
        to_abs = self.abspath(to_name)
1012
 
        if not isdir(to_abs):
1013
 
            raise BzrError("destination %r is not a directory" % to_abs)
1014
 
        if not self.has_filename(to_name):
1015
 
            raise BzrError("destination %r not in working directory" % to_abs)
1016
 
        to_dir_id = inv.path2id(to_name)
1017
 
        if to_dir_id is None and to_name != '':
1018
 
            raise BzrError("destination %r is not a versioned directory" % to_name)
1019
 
        to_dir_ie = inv[to_dir_id]
1020
 
        if to_dir_ie.kind != 'directory':
1021
 
            raise BzrError("destination %r is not a directory" % to_abs)
1022
 
 
1023
 
        to_idpath = inv.get_idpath(to_dir_id)
1024
 
 
1025
 
        for f in from_paths:
1026
 
            if not self.has_filename(f):
1027
 
                raise BzrError("%r does not exist in working tree" % f)
1028
 
            f_id = inv.path2id(f)
1029
 
            if f_id is None:
1030
 
                raise BzrError("%r is not versioned" % f)
1031
 
            name_tail = splitpath(f)[-1]
1032
 
            dest_path = pathjoin(to_name, name_tail)
1033
 
            if self.has_filename(dest_path):
1034
 
                raise BzrError("destination %r already exists" % dest_path)
1035
 
            if f_id in to_idpath:
1036
 
                raise BzrError("can't move %r to a subdirectory of itself" % f)
1037
 
 
1038
 
        # OK, so there's a race here, it's possible that someone will
1039
 
        # create a file in this interval and then the rename might be
1040
 
        # left half-done.  But we should have caught most problems.
1041
 
        orig_inv = deepcopy(self.inventory)
1042
 
        original_modified = self._inventory_is_modified
1043
 
        try:
1044
 
            if len(from_paths):
1045
 
                self._inventory_is_modified = True
1046
 
            for f in from_paths:
1047
 
                name_tail = splitpath(f)[-1]
1048
 
                dest_path = pathjoin(to_name, name_tail)
1049
 
                result.append((f, dest_path))
1050
 
                inv.rename(inv.path2id(f), to_dir_id, name_tail)
1051
 
                try:
1052
 
                    osutils.rename(self.abspath(f), self.abspath(dest_path))
1053
 
                except OSError, e:
1054
 
                    raise BzrError("failed to rename %r to %r: %s" %
1055
 
                                   (f, dest_path, e[1]))
1056
 
        except:
1057
 
            # restore the inventory on error
1058
 
            self._set_inventory(orig_inv, dirty=original_modified)
1059
 
            raise
1060
 
        self._write_inventory(inv)
1061
 
        return result
1062
 
 
1063
 
    @needs_tree_write_lock
1064
 
    def rename_one(self, from_rel, to_rel):
1065
 
        """Rename one file.
1066
 
 
1067
 
        This can change the directory or the filename or both.
1068
 
        """
1069
 
        inv = self.inventory
1070
 
        if not self.has_filename(from_rel):
1071
 
            raise BzrError("can't rename: old working file %r does not exist" % from_rel)
1072
 
        if self.has_filename(to_rel):
1073
 
            raise BzrError("can't rename: new working file %r already exists" % to_rel)
1074
 
 
1075
 
        file_id = inv.path2id(from_rel)
1076
 
        if file_id is None:
1077
 
            raise BzrError("can't rename: old name %r is not versioned" % from_rel)
1078
 
 
1079
 
        entry = inv[file_id]
1080
 
        from_parent = entry.parent_id
1081
 
        from_name = entry.name
1082
 
        
1083
 
        if inv.path2id(to_rel):
1084
 
            raise BzrError("can't rename: new name %r is already versioned" % to_rel)
1085
 
 
1086
 
        to_dir, to_tail = os.path.split(to_rel)
1087
 
        to_dir_id = inv.path2id(to_dir)
1088
 
        if to_dir_id is None and to_dir != '':
1089
 
            raise BzrError("can't determine destination directory id for %r" % to_dir)
1090
 
 
1091
 
        mutter("rename_one:")
1092
 
        mutter("  file_id    {%s}" % file_id)
1093
 
        mutter("  from_rel   %r" % from_rel)
1094
 
        mutter("  to_rel     %r" % to_rel)
1095
 
        mutter("  to_dir     %r" % to_dir)
1096
 
        mutter("  to_dir_id  {%s}" % to_dir_id)
1097
 
 
1098
 
        inv.rename(file_id, to_dir_id, to_tail)
1099
 
 
1100
 
        from_abs = self.abspath(from_rel)
1101
 
        to_abs = self.abspath(to_rel)
1102
 
        try:
1103
 
            osutils.rename(from_abs, to_abs)
1104
 
        except OSError, e:
1105
 
            inv.rename(file_id, from_parent, from_name)
1106
 
            raise BzrError("failed to rename %r to %r: %s"
1107
 
                    % (from_abs, to_abs, e[1]))
1108
 
        self._write_inventory(inv)
1109
 
 
1110
 
    @needs_read_lock
1111
 
    def unknowns(self):
1112
 
        """Return all unknown files.
1113
 
 
1114
 
        These are files in the working directory that are not versioned or
1115
 
        control files or ignored.
1116
 
        """
1117
 
        for subp in self.extras():
1118
 
            if not self.is_ignored(subp):
1119
 
                yield subp
1120
 
    
1121
 
    @needs_tree_write_lock
1122
 
    def unversion(self, file_ids):
1123
 
        """Remove the file ids in file_ids from the current versioned set.
1124
 
 
1125
 
        When a file_id is unversioned, all of its children are automatically
1126
 
        unversioned.
1127
 
 
1128
 
        :param file_ids: The file ids to stop versioning.
1129
 
        :raises: NoSuchId if any fileid is not currently versioned.
1130
 
        """
1131
 
        for file_id in file_ids:
1132
 
            if self._inventory.has_id(file_id):
1133
 
                self._inventory.remove_recursive_id(file_id)
1134
 
            else:
1135
 
                raise errors.NoSuchId(self, file_id)
1136
 
        if len(file_ids):
1137
 
            # in the future this should just set a dirty bit to wait for the 
1138
 
            # final unlock. However, until all methods of workingtree start
1139
 
            # with the current in -memory inventory rather than triggering 
1140
 
            # a read, it is more complex - we need to teach read_inventory
1141
 
            # to know when to read, and when to not read first... and possibly
1142
 
            # to save first when the in memory one may be corrupted.
1143
 
            # so for now, we just only write it if it is indeed dirty.
1144
 
            # - RBC 20060907
1145
 
            self._write_inventory(self._inventory)
1146
 
    
1147
 
    @deprecated_method(zero_eight)
1148
 
    def iter_conflicts(self):
1149
 
        """List all files in the tree that have text or content conflicts.
1150
 
        DEPRECATED.  Use conflicts instead."""
1151
 
        return self._iter_conflicts()
1152
 
 
1153
 
    def _iter_conflicts(self):
1154
 
        conflicted = set()
1155
 
        for info in self.list_files():
1156
 
            path = info[0]
1157
 
            stem = get_conflicted_stem(path)
1158
 
            if stem is None:
1159
 
                continue
1160
 
            if stem not in conflicted:
1161
 
                conflicted.add(stem)
1162
 
                yield stem
1163
 
 
1164
 
    @needs_write_lock
1165
 
    def pull(self, source, overwrite=False, stop_revision=None):
1166
 
        top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1167
 
        source.lock_read()
1168
 
        try:
1169
 
            pp = ProgressPhase("Pull phase", 2, top_pb)
1170
 
            pp.next_phase()
1171
 
            old_revision_history = self.branch.revision_history()
1172
 
            basis_tree = self.basis_tree()
1173
 
            count = self.branch.pull(source, overwrite, stop_revision)
1174
 
            new_revision_history = self.branch.revision_history()
1175
 
            if new_revision_history != old_revision_history:
1176
 
                pp.next_phase()
1177
 
                if len(old_revision_history):
1178
 
                    other_revision = old_revision_history[-1]
1179
 
                else:
1180
 
                    other_revision = None
1181
 
                repository = self.branch.repository
1182
 
                pb = bzrlib.ui.ui_factory.nested_progress_bar()
1183
 
                try:
1184
 
                    new_basis_tree = self.branch.basis_tree()
1185
 
                    merge.merge_inner(
1186
 
                                self.branch,
1187
 
                                new_basis_tree,
1188
 
                                basis_tree,
1189
 
                                this_tree=self,
1190
 
                                pb=pb)
1191
 
                    if (basis_tree.inventory.root is None and
1192
 
                        new_basis_tree.inventory.root is not None):
1193
 
                        self.set_root_id(new_basis_tree.inventory.root.file_id)
1194
 
                finally:
1195
 
                    pb.finished()
1196
 
                # TODO - dedup parents list with things merged by pull ?
1197
 
                # reuse the revisiontree we merged against to set the new
1198
 
                # tree data.
1199
 
                parent_trees = [(self.branch.last_revision(), new_basis_tree)]
1200
 
                # we have to pull the merge trees out again, because 
1201
 
                # merge_inner has set the ids. - this corner is not yet 
1202
 
                # layered well enough to prevent double handling.
1203
 
                merges = self.get_parent_ids()[1:]
1204
 
                parent_trees.extend([
1205
 
                    (parent, repository.revision_tree(parent)) for
1206
 
                     parent in merges])
1207
 
                self.set_parent_trees(parent_trees)
1208
 
            return count
1209
 
        finally:
1210
 
            source.unlock()
1211
 
            top_pb.finished()
1212
 
 
1213
 
    @needs_write_lock
1214
 
    def put_file_bytes_non_atomic(self, file_id, bytes):
1215
 
        """See MutableTree.put_file_bytes_non_atomic."""
1216
 
        stream = file(self.id2abspath(file_id), 'wb')
1217
 
        try:
1218
 
            stream.write(bytes)
1219
 
        finally:
1220
 
            stream.close()
1221
 
        # TODO: update the hashcache here ?
1222
 
 
1223
 
    def extras(self):
1224
 
        """Yield all unknown files in this WorkingTree.
1225
 
 
1226
 
        If there are any unknown directories then only the directory is
1227
 
        returned, not all its children.  But if there are unknown files
1228
 
        under a versioned subdirectory, they are returned.
1229
 
 
1230
 
        Currently returned depth-first, sorted by name within directories.
1231
 
        """
1232
 
        ## TODO: Work from given directory downwards
1233
 
        for path, dir_entry in self.inventory.directories():
1234
 
            # mutter("search for unknowns in %r", path)
1235
 
            dirabs = self.abspath(path)
1236
 
            if not isdir(dirabs):
1237
 
                # e.g. directory deleted
1238
 
                continue
1239
 
 
1240
 
            fl = []
1241
 
            for subf in os.listdir(dirabs):
1242
 
                if subf == '.bzr':
1243
 
                    continue
1244
 
                if subf not in dir_entry.children:
1245
 
                    subf_norm, can_access = osutils.normalized_filename(subf)
1246
 
                    if subf_norm != subf and can_access:
1247
 
                        if subf_norm not in dir_entry.children:
1248
 
                            fl.append(subf_norm)
1249
 
                    else:
1250
 
                        fl.append(subf)
1251
 
            
1252
 
            fl.sort()
1253
 
            for subf in fl:
1254
 
                subp = pathjoin(path, subf)
1255
 
                yield subp
1256
 
 
1257
 
 
1258
 
    def ignored_files(self):
1259
 
        """Yield list of PATH, IGNORE_PATTERN"""
1260
 
        for subp in self.extras():
1261
 
            pat = self.is_ignored(subp)
1262
 
            if pat is not None:
1263
 
                yield subp, pat
1264
 
 
1265
 
    def get_ignore_list(self):
1266
 
        """Return list of ignore patterns.
1267
 
 
1268
 
        Cached in the Tree object after the first call.
1269
 
        """
1270
 
        ignoreset = getattr(self, '_ignoreset', None)
1271
 
        if ignoreset is not None:
1272
 
            return ignoreset
1273
 
 
1274
 
        ignore_globs = set(bzrlib.DEFAULT_IGNORE)
1275
 
        ignore_globs.update(ignores.get_runtime_ignores())
1276
 
        ignore_globs.update(ignores.get_user_ignores())
1277
 
        if self.has_filename(bzrlib.IGNORE_FILENAME):
1278
 
            f = self.get_file_byname(bzrlib.IGNORE_FILENAME)
1279
 
            try:
1280
 
                ignore_globs.update(ignores.parse_ignore_file(f))
1281
 
            finally:
1282
 
                f.close()
1283
 
        self._ignoreset = ignore_globs
1284
 
        return ignore_globs
1285
 
 
1286
 
    def _flush_ignore_list_cache(self):
1287
 
        """Resets the cached ignore list to force a cache rebuild."""
1288
 
        self._ignoreset = None
1289
 
        self._ignoreglobster = None
1290
 
 
1291
 
    def is_ignored(self, filename):
1292
 
        r"""Check whether the filename matches an ignore pattern.
1293
 
 
1294
 
        Patterns containing '/' or '\' need to match the whole path;
1295
 
        others match against only the last component.
1296
 
 
1297
 
        If the file is ignored, returns the pattern which caused it to
1298
 
        be ignored, otherwise None.  So this can simply be used as a
1299
 
        boolean if desired."""
1300
 
        if getattr(self, '_ignoreglobster', None) is None:
1301
 
            self._ignoreglobster = globbing.Globster(self.get_ignore_list())
1302
 
        return self._ignoreglobster.match(filename)
1303
 
 
1304
 
    def kind(self, file_id):
1305
 
        return file_kind(self.id2abspath(file_id))
1306
 
 
1307
 
    def _comparison_data(self, entry, path):
1308
 
        abspath = self.abspath(path)
1309
 
        try:
1310
 
            stat_value = os.lstat(abspath)
1311
 
        except OSError, e:
1312
 
            if getattr(e, 'errno', None) == errno.ENOENT:
1313
 
                stat_value = None
1314
 
                kind = None
1315
 
                executable = False
1316
 
            else:
1317
 
                raise
1318
 
        else:
1319
 
            mode = stat_value.st_mode
1320
 
            kind = osutils.file_kind_from_stat_mode(mode)
1321
 
            if not supports_executable():
1322
 
                executable = entry.executable
1323
 
            else:
1324
 
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1325
 
        return kind, executable, stat_value
1326
 
 
1327
 
    def _file_size(self, entry, stat_value):
1328
 
        return stat_value.st_size
1329
 
 
1330
 
    def last_revision(self):
1331
 
        """Return the last revision of the branch for this tree.
1332
 
 
1333
 
        This format tree does not support a separate marker for last-revision
1334
 
        compared to the branch.
1335
 
 
1336
 
        See MutableTree.last_revision
1337
 
        """
1338
 
        return self._last_revision()
1339
 
 
1340
 
    @needs_read_lock
1341
 
    def _last_revision(self):
1342
 
        """helper for get_parent_ids."""
1343
 
        return self.branch.last_revision()
1344
 
 
1345
 
    def is_locked(self):
1346
 
        return self._control_files.is_locked()
1347
 
 
1348
 
    def lock_read(self):
1349
 
        """See Branch.lock_read, and WorkingTree.unlock."""
1350
 
        self.branch.lock_read()
1351
 
        try:
1352
 
            return self._control_files.lock_read()
1353
 
        except:
1354
 
            self.branch.unlock()
1355
 
            raise
1356
 
 
1357
 
    def lock_tree_write(self):
1358
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
1359
 
        self.branch.lock_read()
1360
 
        try:
1361
 
            return self._control_files.lock_write()
1362
 
        except:
1363
 
            self.branch.unlock()
1364
 
            raise
1365
 
 
1366
 
    def lock_write(self):
1367
 
        """See MutableTree.lock_write, and WorkingTree.unlock."""
1368
 
        self.branch.lock_write()
1369
 
        try:
1370
 
            return self._control_files.lock_write()
1371
 
        except:
1372
 
            self.branch.unlock()
1373
 
            raise
1374
 
 
1375
 
    def get_physical_lock_status(self):
1376
 
        return self._control_files.get_physical_lock_status()
1377
 
 
1378
 
    def _basis_inventory_name(self):
1379
 
        return 'basis-inventory-cache'
1380
 
 
1381
 
    @needs_tree_write_lock
1382
 
    def set_last_revision(self, new_revision):
1383
 
        """Change the last revision in the working tree."""
1384
 
        if self._change_last_revision(new_revision):
1385
 
            self._cache_basis_inventory(new_revision)
1386
 
 
1387
 
    def _change_last_revision(self, new_revision):
1388
 
        """Template method part of set_last_revision to perform the change.
1389
 
        
1390
 
        This is used to allow WorkingTree3 instances to not affect branch
1391
 
        when their last revision is set.
1392
 
        """
1393
 
        if new_revision is None:
1394
 
            self.branch.set_revision_history([])
1395
 
            return False
1396
 
        try:
1397
 
            self.branch.generate_revision_history(new_revision)
1398
 
        except errors.NoSuchRevision:
1399
 
            # not present in the repo - dont try to set it deeper than the tip
1400
 
            self.branch.set_revision_history([new_revision])
1401
 
        return True
1402
 
 
1403
 
    def _write_basis_inventory(self, xml):
1404
 
        """Write the basis inventory XML to the basis-inventory file"""
1405
 
        assert isinstance(xml, str), 'serialised xml must be bytestring.'
1406
 
        path = self._basis_inventory_name()
1407
 
        sio = StringIO(xml)
1408
 
        self._control_files.put(path, sio)
1409
 
 
1410
 
    def _create_basis_xml_from_inventory(self, revision_id, inventory):
1411
 
        """Create the text that will be saved in basis-inventory"""
1412
 
        inventory.revision_id = revision_id
1413
 
        return xml6.serializer_v6.write_inventory_to_string(inventory)
1414
 
 
1415
 
    def _cache_basis_inventory(self, new_revision):
1416
 
        """Cache new_revision as the basis inventory."""
1417
 
        # TODO: this should allow the ready-to-use inventory to be passed in,
1418
 
        # as commit already has that ready-to-use [while the format is the
1419
 
        # same, that is].
1420
 
        try:
1421
 
            # this double handles the inventory - unpack and repack - 
1422
 
            # but is easier to understand. We can/should put a conditional
1423
 
            # in here based on whether the inventory is in the latest format
1424
 
            # - perhaps we should repack all inventories on a repository
1425
 
            # upgrade ?
1426
 
            # the fast path is to copy the raw xml from the repository. If the
1427
 
            # xml contains 'revision_id="', then we assume the right 
1428
 
            # revision_id is set. We must check for this full string, because a
1429
 
            # root node id can legitimately look like 'revision_id' but cannot
1430
 
            # contain a '"'.
1431
 
            xml = self.branch.repository.get_inventory_xml(new_revision)
1432
 
            firstline = xml.split('\n', 1)[0]
1433
 
            if (not 'revision_id="' in firstline or 
1434
 
                'format="6"' not in firstline):
1435
 
                inv = self.branch.repository.deserialise_inventory(
1436
 
                    new_revision, xml)
1437
 
                xml = self._create_basis_xml_from_inventory(new_revision, inv)
1438
 
            self._write_basis_inventory(xml)
1439
 
        except (errors.NoSuchRevision, errors.RevisionNotPresent):
1440
 
            pass
1441
 
 
1442
 
    def read_basis_inventory(self):
1443
 
        """Read the cached basis inventory."""
1444
 
        path = self._basis_inventory_name()
1445
 
        return self._control_files.get(path).read()
1446
 
        
1447
 
    @needs_read_lock
1448
 
    def read_working_inventory(self):
1449
 
        """Read the working inventory.
1450
 
        
1451
 
        :raises errors.InventoryModified: read_working_inventory will fail
1452
 
            when the current in memory inventory has been modified.
1453
 
        """
1454
 
        # conceptually this should be an implementation detail of the tree. 
1455
 
        # XXX: Deprecate this.
1456
 
        # ElementTree does its own conversion from UTF-8, so open in
1457
 
        # binary.
1458
 
        if self._inventory_is_modified:
1459
 
            raise errors.InventoryModified(self)
1460
 
        result = xml5.serializer_v5.read_inventory(
1461
 
            self._control_files.get('inventory'))
1462
 
        self._set_inventory(result, dirty=False)
1463
 
        return result
1464
 
 
1465
 
    @needs_tree_write_lock
1466
 
    def remove(self, files, verbose=False, to_file=None):
1467
 
        """Remove nominated files from the working inventory..
1468
 
 
1469
 
        This does not remove their text.  This does not run on XXX on what? RBC
1470
 
 
1471
 
        TODO: Refuse to remove modified files unless --force is given?
1472
 
 
1473
 
        TODO: Do something useful with directories.
1474
 
 
1475
 
        TODO: Should this remove the text or not?  Tough call; not
1476
 
        removing may be useful and the user can just use use rm, and
1477
 
        is the opposite of add.  Removing it is consistent with most
1478
 
        other tools.  Maybe an option.
1479
 
        """
1480
 
        ## TODO: Normalize names
1481
 
        ## TODO: Remove nested loops; better scalability
1482
 
        if isinstance(files, basestring):
1483
 
            files = [files]
1484
 
 
1485
 
        inv = self.inventory
1486
 
 
1487
 
        # do this before any modifications
1488
 
        for f in files:
1489
 
            fid = inv.path2id(f)
1490
 
            if not fid:
1491
 
                # TODO: Perhaps make this just a warning, and continue?
1492
 
                # This tends to happen when 
1493
 
                raise NotVersionedError(path=f)
1494
 
            if verbose:
1495
 
                # having remove it, it must be either ignored or unknown
1496
 
                if self.is_ignored(f):
1497
 
                    new_status = 'I'
1498
 
                else:
1499
 
                    new_status = '?'
1500
 
                textui.show_status(new_status, inv[fid].kind, f,
1501
 
                                   to_file=to_file)
1502
 
            del inv[fid]
1503
 
 
1504
 
        self._write_inventory(inv)
1505
 
 
1506
 
    @needs_tree_write_lock
1507
 
    def revert(self, filenames, old_tree=None, backups=True, 
1508
 
               pb=DummyProgress()):
1509
 
        from bzrlib.conflicts import resolve
1510
 
        if old_tree is None:
1511
 
            old_tree = self.basis_tree()
1512
 
        conflicts = transform.revert(self, old_tree, filenames, backups, pb)
1513
 
        if not len(filenames):
1514
 
            self.set_parent_ids(self.get_parent_ids()[:1])
1515
 
            resolve(self)
1516
 
        else:
1517
 
            resolve(self, filenames, ignore_misses=True)
1518
 
        return conflicts
1519
 
 
1520
 
    # XXX: This method should be deprecated in favour of taking in a proper
1521
 
    # new Inventory object.
1522
 
    @needs_tree_write_lock
1523
 
    def set_inventory(self, new_inventory_list):
1524
 
        from bzrlib.inventory import (Inventory,
1525
 
                                      InventoryDirectory,
1526
 
                                      InventoryEntry,
1527
 
                                      InventoryFile,
1528
 
                                      InventoryLink)
1529
 
        inv = Inventory(self.get_root_id())
1530
 
        for path, file_id, parent, kind in new_inventory_list:
1531
 
            name = os.path.basename(path)
1532
 
            if name == "":
1533
 
                continue
1534
 
            # fixme, there should be a factory function inv,add_?? 
1535
 
            if kind == 'directory':
1536
 
                inv.add(InventoryDirectory(file_id, name, parent))
1537
 
            elif kind == 'file':
1538
 
                inv.add(InventoryFile(file_id, name, parent))
1539
 
            elif kind == 'symlink':
1540
 
                inv.add(InventoryLink(file_id, name, parent))
1541
 
            else:
1542
 
                raise BzrError("unknown kind %r" % kind)
1543
 
        self._write_inventory(inv)
1544
 
 
1545
 
    @needs_tree_write_lock
1546
 
    def set_root_id(self, file_id):
1547
 
        """Set the root id for this tree."""
1548
 
        # for compatability 
1549
 
        if file_id is None:
1550
 
            symbol_versioning.warn(symbol_versioning.zero_twelve
1551
 
                % 'WorkingTree.set_root_id with fileid=None',
1552
 
                DeprecationWarning,
1553
 
                stacklevel=3)
1554
 
            file_id = ROOT_ID
1555
 
        inv = self._inventory
1556
 
        orig_root_id = inv.root.file_id
1557
 
        # TODO: it might be nice to exit early if there was nothing
1558
 
        # to do, saving us from trigger a sync on unlock.
1559
 
        self._inventory_is_modified = True
1560
 
        # we preserve the root inventory entry object, but
1561
 
        # unlinkit from the byid index
1562
 
        del inv._byid[inv.root.file_id]
1563
 
        inv.root.file_id = file_id
1564
 
        # and link it into the index with the new changed id.
1565
 
        inv._byid[inv.root.file_id] = inv.root
1566
 
        # and finally update all children to reference the new id.
1567
 
        # XXX: this should be safe to just look at the root.children
1568
 
        # list, not the WHOLE INVENTORY.
1569
 
        for fid in inv:
1570
 
            entry = inv[fid]
1571
 
            if entry.parent_id == orig_root_id:
1572
 
                entry.parent_id = inv.root.file_id
1573
 
 
1574
 
    def unlock(self):
1575
 
        """See Branch.unlock.
1576
 
        
1577
 
        WorkingTree locking just uses the Branch locking facilities.
1578
 
        This is current because all working trees have an embedded branch
1579
 
        within them. IF in the future, we were to make branch data shareable
1580
 
        between multiple working trees, i.e. via shared storage, then we 
1581
 
        would probably want to lock both the local tree, and the branch.
1582
 
        """
1583
 
        raise NotImplementedError(self.unlock)
1584
 
 
1585
 
    def update(self):
1586
 
        """Update a working tree along its branch.
1587
 
 
1588
 
        This will update the branch if its bound too, which means we have
1589
 
        multiple trees involved:
1590
 
 
1591
 
        - The new basis tree of the master.
1592
 
        - The old basis tree of the branch.
1593
 
        - The old basis tree of the working tree.
1594
 
        - The current working tree state.
1595
 
 
1596
 
        Pathologically, all three may be different, and non-ancestors of each
1597
 
        other.  Conceptually we want to:
1598
 
 
1599
 
        - Preserve the wt.basis->wt.state changes
1600
 
        - Transform the wt.basis to the new master basis.
1601
 
        - Apply a merge of the old branch basis to get any 'local' changes from
1602
 
          it into the tree.
1603
 
        - Restore the wt.basis->wt.state changes.
1604
 
 
1605
 
        There isn't a single operation at the moment to do that, so we:
1606
 
        - Merge current state -> basis tree of the master w.r.t. the old tree
1607
 
          basis.
1608
 
        - Do a 'normal' merge of the old branch basis if it is relevant.
1609
 
        """
1610
 
        if self.branch.get_master_branch() is not None:
1611
 
            self.lock_write()
1612
 
            update_branch = True
1613
 
        else:
1614
 
            self.lock_tree_write()
1615
 
            update_branch = False
1616
 
        try:
1617
 
            if update_branch:
1618
 
                old_tip = self.branch.update()
1619
 
            else:
1620
 
                old_tip = None
1621
 
            return self._update_tree(old_tip)
1622
 
        finally:
1623
 
            self.unlock()
1624
 
 
1625
 
    @needs_tree_write_lock
1626
 
    def _update_tree(self, old_tip=None):
1627
 
        """Update a tree to the master branch.
1628
 
 
1629
 
        :param old_tip: if supplied, the previous tip revision the branch,
1630
 
            before it was changed to the master branch's tip.
1631
 
        """
1632
 
        # here if old_tip is not None, it is the old tip of the branch before
1633
 
        # it was updated from the master branch. This should become a pending
1634
 
        # merge in the working tree to preserve the user existing work.  we
1635
 
        # cant set that until we update the working trees last revision to be
1636
 
        # one from the new branch, because it will just get absorbed by the
1637
 
        # parent de-duplication logic.
1638
 
        # 
1639
 
        # We MUST save it even if an error occurs, because otherwise the users
1640
 
        # local work is unreferenced and will appear to have been lost.
1641
 
        # 
1642
 
        result = 0
1643
 
        try:
1644
 
            last_rev = self.get_parent_ids()[0]
1645
 
        except IndexError:
1646
 
            last_rev = None
1647
 
        if last_rev != self.branch.last_revision():
1648
 
            # merge tree state up to new branch tip.
1649
 
            basis = self.basis_tree()
1650
 
            to_tree = self.branch.basis_tree()
1651
 
            if basis.inventory.root is None:
1652
 
                self.set_root_id(to_tree.inventory.root.file_id)
1653
 
            result += merge.merge_inner(
1654
 
                                  self.branch,
1655
 
                                  to_tree,
1656
 
                                  basis,
1657
 
                                  this_tree=self)
1658
 
            # TODO - dedup parents list with things merged by pull ?
1659
 
            # reuse the tree we've updated to to set the basis:
1660
 
            parent_trees = [(self.branch.last_revision(), to_tree)]
1661
 
            merges = self.get_parent_ids()[1:]
1662
 
            # Ideally we ask the tree for the trees here, that way the working
1663
 
            # tree can decide whether to give us teh entire tree or give us a
1664
 
            # lazy initialised tree. dirstate for instance will have the trees
1665
 
            # in ram already, whereas a last-revision + basis-inventory tree
1666
 
            # will not, but also does not need them when setting parents.
1667
 
            for parent in merges:
1668
 
                parent_trees.append(
1669
 
                    (parent, self.branch.repository.revision_tree(parent)))
1670
 
            if old_tip is not None:
1671
 
                parent_trees.append(
1672
 
                    (old_tip, self.branch.repository.revision_tree(old_tip)))
1673
 
            self.set_parent_trees(parent_trees)
1674
 
            last_rev = parent_trees[0][0]
1675
 
        else:
1676
 
            # the working tree had the same last-revision as the master
1677
 
            # branch did. We may still have pivot local work from the local
1678
 
            # branch into old_tip:
1679
 
            if old_tip is not None:
1680
 
                self.add_parent_tree_id(old_tip)
1681
 
        if old_tip and old_tip != last_rev:
1682
 
            # our last revision was not the prior branch last revision
1683
 
            # and we have converted that last revision to a pending merge.
1684
 
            # base is somewhere between the branch tip now
1685
 
            # and the now pending merge
1686
 
            from bzrlib.revision import common_ancestor
1687
 
            try:
1688
 
                base_rev_id = common_ancestor(self.branch.last_revision(),
1689
 
                                              old_tip,
1690
 
                                              self.branch.repository)
1691
 
            except errors.NoCommonAncestor:
1692
 
                base_rev_id = None
1693
 
            base_tree = self.branch.repository.revision_tree(base_rev_id)
1694
 
            other_tree = self.branch.repository.revision_tree(old_tip)
1695
 
            result += merge.merge_inner(
1696
 
                                  self.branch,
1697
 
                                  other_tree,
1698
 
                                  base_tree,
1699
 
                                  this_tree=self)
1700
 
        return result
1701
 
 
1702
 
    def _write_hashcache_if_dirty(self):
1703
 
        """Write out the hashcache if it is dirty."""
1704
 
        if self._hashcache.needs_write:
1705
 
            try:
1706
 
                self._hashcache.write()
1707
 
            except OSError, e:
1708
 
                if e.errno not in (errno.EPERM, errno.EACCES):
1709
 
                    raise
1710
 
                # TODO: jam 20061219 Should this be a warning? A single line
1711
 
                #       warning might be sufficient to let the user know what
1712
 
                #       is going on.
1713
 
                mutter('Could not write hashcache for %s\nError: %s',
1714
 
                       self._hashcache.cache_file_name(), e)
1715
 
 
1716
 
    @needs_tree_write_lock
1717
 
    def _write_inventory(self, inv):
1718
 
        """Write inventory as the current inventory."""
1719
 
        self._set_inventory(inv, dirty=True)
1720
 
        self.flush()
1721
 
 
1722
 
    def set_conflicts(self, arg):
1723
 
        raise UnsupportedOperation(self.set_conflicts, self)
1724
 
 
1725
 
    def add_conflicts(self, arg):
1726
 
        raise UnsupportedOperation(self.add_conflicts, self)
1727
 
 
1728
 
    @needs_read_lock
1729
 
    def conflicts(self):
1730
 
        conflicts = _mod_conflicts.ConflictList()
1731
 
        for conflicted in self._iter_conflicts():
1732
 
            text = True
1733
 
            try:
1734
 
                if file_kind(self.abspath(conflicted)) != "file":
1735
 
                    text = False
1736
 
            except errors.NoSuchFile:
1737
 
                text = False
1738
 
            if text is True:
1739
 
                for suffix in ('.THIS', '.OTHER'):
1740
 
                    try:
1741
 
                        kind = file_kind(self.abspath(conflicted+suffix))
1742
 
                        if kind != "file":
1743
 
                            text = False
1744
 
                    except errors.NoSuchFile:
1745
 
                        text = False
1746
 
                    if text == False:
1747
 
                        break
1748
 
            ctype = {True: 'text conflict', False: 'contents conflict'}[text]
1749
 
            conflicts.append(_mod_conflicts.Conflict.factory(ctype,
1750
 
                             path=conflicted,
1751
 
                             file_id=self.path2id(conflicted)))
1752
 
        return conflicts
1753
 
 
1754
 
 
1755
 
class WorkingTree2(WorkingTree):
1756
 
    """This is the Format 2 working tree.
1757
 
 
1758
 
    This was the first weave based working tree. 
1759
 
     - uses os locks for locking.
1760
 
     - uses the branch last-revision.
1761
 
    """
1762
 
 
1763
 
    def lock_tree_write(self):
1764
 
        """See WorkingTree.lock_tree_write().
1765
 
 
1766
 
        In Format2 WorkingTrees we have a single lock for the branch and tree
1767
 
        so lock_tree_write() degrades to lock_write().
1768
 
        """
1769
 
        self.branch.lock_write()
1770
 
        try:
1771
 
            return self._control_files.lock_write()
1772
 
        except:
1773
 
            self.branch.unlock()
1774
 
            raise
1775
 
 
1776
 
    def unlock(self):
1777
 
        # we share control files:
1778
 
        if self._control_files._lock_count == 3:
1779
 
            # _inventory_is_modified is always False during a read lock.
1780
 
            if self._inventory_is_modified:
1781
 
                self.flush()
1782
 
            self._write_hashcache_if_dirty()
1783
 
                    
1784
 
        # reverse order of locking.
1785
 
        try:
1786
 
            return self._control_files.unlock()
1787
 
        finally:
1788
 
            self.branch.unlock()
1789
 
 
1790
 
 
1791
 
class WorkingTree3(WorkingTree):
1792
 
    """This is the Format 3 working tree.
1793
 
 
1794
 
    This differs from the base WorkingTree by:
1795
 
     - having its own file lock
1796
 
     - having its own last-revision property.
1797
 
 
1798
 
    This is new in bzr 0.8
1799
 
    """
1800
 
 
1801
 
    @needs_read_lock
1802
 
    def _last_revision(self):
1803
 
        """See Mutable.last_revision."""
1804
 
        try:
1805
 
            return self._control_files.get_utf8('last-revision').read()
1806
 
        except NoSuchFile:
1807
 
            return None
1808
 
 
1809
 
    def _change_last_revision(self, revision_id):
1810
 
        """See WorkingTree._change_last_revision."""
1811
 
        if revision_id is None or revision_id == NULL_REVISION:
1812
 
            try:
1813
 
                self._control_files._transport.delete('last-revision')
1814
 
            except errors.NoSuchFile:
1815
 
                pass
1816
 
            return False
1817
 
        else:
1818
 
            self._control_files.put_utf8('last-revision', revision_id)
1819
 
            return True
1820
 
 
1821
 
    @needs_tree_write_lock
1822
 
    def set_conflicts(self, conflicts):
1823
 
        self._put_rio('conflicts', conflicts.to_stanzas(), 
1824
 
                      CONFLICT_HEADER_1)
1825
 
 
1826
 
    @needs_tree_write_lock
1827
 
    def add_conflicts(self, new_conflicts):
1828
 
        conflict_set = set(self.conflicts())
1829
 
        conflict_set.update(set(list(new_conflicts)))
1830
 
        self.set_conflicts(_mod_conflicts.ConflictList(sorted(conflict_set,
1831
 
                                       key=_mod_conflicts.Conflict.sort_key)))
1832
 
 
1833
 
    @needs_read_lock
1834
 
    def conflicts(self):
1835
 
        try:
1836
 
            confile = self._control_files.get('conflicts')
1837
 
        except NoSuchFile:
1838
 
            return _mod_conflicts.ConflictList()
1839
 
        try:
1840
 
            if confile.next() != CONFLICT_HEADER_1 + '\n':
1841
 
                raise ConflictFormatError()
1842
 
        except StopIteration:
1843
 
            raise ConflictFormatError()
1844
 
        return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
1845
 
 
1846
 
    def unlock(self):
1847
 
        if self._control_files._lock_count == 1:
1848
 
            # _inventory_is_modified is always False during a read lock.
1849
 
            if self._inventory_is_modified:
1850
 
                self.flush()
1851
 
            self._write_hashcache_if_dirty()
1852
 
        # reverse order of locking.
1853
 
        try:
1854
 
            return self._control_files.unlock()
1855
 
        finally:
1856
 
            self.branch.unlock()
1857
 
 
1858
 
 
1859
 
def get_conflicted_stem(path):
1860
 
    for suffix in _mod_conflicts.CONFLICT_SUFFIXES:
1861
 
        if path.endswith(suffix):
1862
 
            return path[:-len(suffix)]
1863
 
 
1864
 
@deprecated_function(zero_eight)
1865
 
def is_control_file(filename):
1866
 
    """See WorkingTree.is_control_filename(filename)."""
1867
 
    ## FIXME: better check
1868
 
    filename = normpath(filename)
1869
 
    while filename != '':
1870
 
        head, tail = os.path.split(filename)
1871
 
        ## mutter('check %r for control file' % ((head, tail),))
1872
 
        if tail == '.bzr':
1873
 
            return True
1874
 
        if filename == head:
1875
 
            break
1876
 
        filename = head
1877
 
    return False
1878
 
 
1879
 
 
1880
 
class WorkingTreeFormat(object):
1881
 
    """An encapsulation of the initialization and open routines for a format.
1882
 
 
1883
 
    Formats provide three things:
1884
 
     * An initialization routine,
1885
 
     * a format string,
1886
 
     * an open routine.
1887
 
 
1888
 
    Formats are placed in an dict by their format string for reference 
1889
 
    during workingtree opening. Its not required that these be instances, they
1890
 
    can be classes themselves with class methods - it simply depends on 
1891
 
    whether state is needed for a given format or not.
1892
 
 
1893
 
    Once a format is deprecated, just deprecate the initialize and open
1894
 
    methods on the format class. Do not deprecate the object, as the 
1895
 
    object will be created every time regardless.
1896
 
    """
1897
 
 
1898
 
    _default_format = None
1899
 
    """The default format used for new trees."""
1900
 
 
1901
 
    _formats = {}
1902
 
    """The known formats."""
1903
 
 
1904
 
    @classmethod
1905
 
    def find_format(klass, a_bzrdir):
1906
 
        """Return the format for the working tree object in a_bzrdir."""
1907
 
        try:
1908
 
            transport = a_bzrdir.get_workingtree_transport(None)
1909
 
            format_string = transport.get("format").read()
1910
 
            return klass._formats[format_string]
1911
 
        except NoSuchFile:
1912
 
            raise errors.NoWorkingTree(base=transport.base)
1913
 
        except KeyError:
1914
 
            raise errors.UnknownFormatError(format=format_string)
1915
 
 
1916
 
    @classmethod
1917
 
    def get_default_format(klass):
1918
 
        """Return the current default format."""
1919
 
        return klass._default_format
1920
 
 
1921
 
    def get_format_string(self):
1922
 
        """Return the ASCII format string that identifies this format."""
1923
 
        raise NotImplementedError(self.get_format_string)
1924
 
 
1925
 
    def get_format_description(self):
1926
 
        """Return the short description for this format."""
1927
 
        raise NotImplementedError(self.get_format_description)
1928
 
 
1929
 
    def is_supported(self):
1930
 
        """Is this format supported?
1931
 
 
1932
 
        Supported formats can be initialized and opened.
1933
 
        Unsupported formats may not support initialization or committing or 
1934
 
        some other features depending on the reason for not being supported.
1935
 
        """
1936
 
        return True
1937
 
 
1938
 
    @classmethod
1939
 
    def register_format(klass, format):
1940
 
        klass._formats[format.get_format_string()] = format
1941
 
 
1942
 
    @classmethod
1943
 
    def set_default_format(klass, format):
1944
 
        klass._default_format = format
1945
 
 
1946
 
    @classmethod
1947
 
    def unregister_format(klass, format):
1948
 
        assert klass._formats[format.get_format_string()] is format
1949
 
        del klass._formats[format.get_format_string()]
1950
 
 
1951
 
 
1952
 
 
1953
 
class WorkingTreeFormat2(WorkingTreeFormat):
1954
 
    """The second working tree format. 
1955
 
 
1956
 
    This format modified the hash cache from the format 1 hash cache.
1957
 
    """
1958
 
 
1959
 
    def get_format_description(self):
1960
 
        """See WorkingTreeFormat.get_format_description()."""
1961
 
        return "Working tree format 2"
1962
 
 
1963
 
    def stub_initialize_remote(self, control_files):
1964
 
        """As a special workaround create critical control files for a remote working tree
1965
 
        
1966
 
        This ensures that it can later be updated and dealt with locally,
1967
 
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with 
1968
 
        no working tree.  (See bug #43064).
1969
 
        """
1970
 
        sio = StringIO()
1971
 
        inv = Inventory()
1972
 
        xml5.serializer_v5.write_inventory(inv, sio)
1973
 
        sio.seek(0)
1974
 
        control_files.put('inventory', sio)
1975
 
 
1976
 
        control_files.put_utf8('pending-merges', '')
1977
 
        
1978
 
 
1979
 
    def initialize(self, a_bzrdir, revision_id=None):
1980
 
        """See WorkingTreeFormat.initialize()."""
1981
 
        if not isinstance(a_bzrdir.transport, LocalTransport):
1982
 
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1983
 
        branch = a_bzrdir.open_branch()
1984
 
        if revision_id is not None:
1985
 
            branch.lock_write()
1986
 
            try:
1987
 
                revision_history = branch.revision_history()
1988
 
                try:
1989
 
                    position = revision_history.index(revision_id)
1990
 
                except ValueError:
1991
 
                    raise errors.NoSuchRevision(branch, revision_id)
1992
 
                branch.set_revision_history(revision_history[:position + 1])
1993
 
            finally:
1994
 
                branch.unlock()
1995
 
        revision = branch.last_revision()
1996
 
        inv = Inventory()
1997
 
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
1998
 
                         branch,
1999
 
                         inv,
2000
 
                         _internal=True,
2001
 
                         _format=self,
2002
 
                         _bzrdir=a_bzrdir)
2003
 
        basis_tree = branch.repository.revision_tree(revision)
2004
 
        if basis_tree.inventory.root is not None:
2005
 
            wt.set_root_id(basis_tree.inventory.root.file_id)
2006
 
        # set the parent list and cache the basis tree.
2007
 
        wt.set_parent_trees([(revision, basis_tree)])
2008
 
        transform.build_tree(basis_tree, wt)
2009
 
        return wt
2010
 
 
2011
 
    def __init__(self):
2012
 
        super(WorkingTreeFormat2, self).__init__()
2013
 
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
2014
 
 
2015
 
    def open(self, a_bzrdir, _found=False):
2016
 
        """Return the WorkingTree object for a_bzrdir
2017
 
 
2018
 
        _found is a private parameter, do not use it. It is used to indicate
2019
 
               if format probing has already been done.
2020
 
        """
2021
 
        if not _found:
2022
 
            # we are being called directly and must probe.
2023
 
            raise NotImplementedError
2024
 
        if not isinstance(a_bzrdir.transport, LocalTransport):
2025
 
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2026
 
        return WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2027
 
                           _internal=True,
2028
 
                           _format=self,
2029
 
                           _bzrdir=a_bzrdir)
2030
 
 
2031
 
 
2032
 
class WorkingTreeFormat3(WorkingTreeFormat):
2033
 
    """The second working tree format updated to record a format marker.
2034
 
 
2035
 
    This format:
2036
 
        - exists within a metadir controlling .bzr
2037
 
        - includes an explicit version marker for the workingtree control
2038
 
          files, separate from the BzrDir format
2039
 
        - modifies the hash cache format
2040
 
        - is new in bzr 0.8
2041
 
        - uses a LockDir to guard access for writes.
2042
 
    """
2043
 
 
2044
 
    def get_format_string(self):
2045
 
        """See WorkingTreeFormat.get_format_string()."""
2046
 
        return "Bazaar-NG Working Tree format 3"
2047
 
 
2048
 
    def get_format_description(self):
2049
 
        """See WorkingTreeFormat.get_format_description()."""
2050
 
        return "Working tree format 3"
2051
 
 
2052
 
    _lock_file_name = 'lock'
2053
 
    _lock_class = LockDir
2054
 
 
2055
 
    def _open_control_files(self, a_bzrdir):
2056
 
        transport = a_bzrdir.get_workingtree_transport(None)
2057
 
        return LockableFiles(transport, self._lock_file_name, 
2058
 
                             self._lock_class)
2059
 
 
2060
 
    def initialize(self, a_bzrdir, revision_id=None):
2061
 
        """See WorkingTreeFormat.initialize().
2062
 
        
2063
 
        revision_id allows creating a working tree at a different
2064
 
        revision than the branch is at.
2065
 
        """
2066
 
        if not isinstance(a_bzrdir.transport, LocalTransport):
2067
 
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2068
 
        transport = a_bzrdir.get_workingtree_transport(self)
2069
 
        control_files = self._open_control_files(a_bzrdir)
2070
 
        control_files.create_lock()
2071
 
        control_files.lock_write()
2072
 
        control_files.put_utf8('format', self.get_format_string())
2073
 
        branch = a_bzrdir.open_branch()
2074
 
        if revision_id is None:
2075
 
            revision_id = branch.last_revision()
2076
 
        # WorkingTree3 can handle an inventory which has a unique root id.
2077
 
        # as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
2078
 
        # those trees. And because there isn't a format bump inbetween, we
2079
 
        # are maintaining compatibility with older clients.
2080
 
        # inv = Inventory(root_id=gen_root_id())
2081
 
        inv = Inventory()
2082
 
        wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2083
 
                         branch,
2084
 
                         inv,
2085
 
                         _internal=True,
2086
 
                         _format=self,
2087
 
                         _bzrdir=a_bzrdir,
2088
 
                         _control_files=control_files)
2089
 
        wt.lock_tree_write()
2090
 
        try:
2091
 
            basis_tree = branch.repository.revision_tree(revision_id)
2092
 
            # only set an explicit root id if there is one to set.
2093
 
            if basis_tree.inventory.root is not None:
2094
 
                wt.set_root_id(basis_tree.inventory.root.file_id)
2095
 
            if revision_id == NULL_REVISION:
2096
 
                wt.set_parent_trees([])
2097
 
            else:
2098
 
                wt.set_parent_trees([(revision_id, basis_tree)])
2099
 
            transform.build_tree(basis_tree, wt)
2100
 
        finally:
2101
 
            # Unlock in this order so that the unlock-triggers-flush in
2102
 
            # WorkingTree is given a chance to fire.
2103
 
            control_files.unlock()
2104
 
            wt.unlock()
2105
 
        return wt
2106
 
 
2107
 
    def __init__(self):
2108
 
        super(WorkingTreeFormat3, self).__init__()
2109
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2110
 
 
2111
 
    def open(self, a_bzrdir, _found=False):
2112
 
        """Return the WorkingTree object for a_bzrdir
2113
 
 
2114
 
        _found is a private parameter, do not use it. It is used to indicate
2115
 
               if format probing has already been done.
2116
 
        """
2117
 
        if not _found:
2118
 
            # we are being called directly and must probe.
2119
 
            raise NotImplementedError
2120
 
        if not isinstance(a_bzrdir.transport, LocalTransport):
2121
 
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2122
 
        return self._open(a_bzrdir, self._open_control_files(a_bzrdir))
2123
 
 
2124
 
    def _open(self, a_bzrdir, control_files):
2125
 
        """Open the tree itself.
2126
 
        
2127
 
        :param a_bzrdir: the dir for the tree.
2128
 
        :param control_files: the control files for the tree.
2129
 
        """
2130
 
        return WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2131
 
                           _internal=True,
2132
 
                           _format=self,
2133
 
                           _bzrdir=a_bzrdir,
2134
 
                           _control_files=control_files)
2135
 
 
2136
 
    def __str__(self):
2137
 
        return self.get_format_string()
2138
 
 
2139
 
 
2140
 
# formats which have no format string are not discoverable
2141
 
# and not independently creatable, so are not registered.
2142
 
__default_format = WorkingTreeFormat3()
2143
 
WorkingTreeFormat.register_format(__default_format)
2144
 
WorkingTreeFormat.set_default_format(__default_format)
2145
 
_legacy_formats = [WorkingTreeFormat2(),
2146
 
                   ]
2147
 
 
2148
 
 
2149
 
class WorkingTreeTestProviderAdapter(object):
2150
 
    """A tool to generate a suite testing multiple workingtree formats at once.
2151
 
 
2152
 
    This is done by copying the test once for each transport and injecting
2153
 
    the transport_server, transport_readonly_server, and workingtree_format
2154
 
    classes into each copy. Each copy is also given a new id() to make it
2155
 
    easy to identify.
2156
 
    """
2157
 
 
2158
 
    def __init__(self, transport_server, transport_readonly_server, formats):
2159
 
        self._transport_server = transport_server
2160
 
        self._transport_readonly_server = transport_readonly_server
2161
 
        self._formats = formats
2162
 
    
2163
 
    def _clone_test(self, test, bzrdir_format, workingtree_format, variation):
2164
 
        """Clone test for adaption."""
2165
 
        new_test = deepcopy(test)
2166
 
        new_test.transport_server = self._transport_server
2167
 
        new_test.transport_readonly_server = self._transport_readonly_server
2168
 
        new_test.bzrdir_format = bzrdir_format
2169
 
        new_test.workingtree_format = workingtree_format
2170
 
        def make_new_test_id():
2171
 
            new_id = "%s(%s)" % (test.id(), variation)
2172
 
            return lambda: new_id
2173
 
        new_test.id = make_new_test_id()
2174
 
        return new_test
2175
 
    
2176
 
    def adapt(self, test):
2177
 
        from bzrlib.tests import TestSuite
2178
 
        result = TestSuite()
2179
 
        for workingtree_format, bzrdir_format in self._formats:
2180
 
            new_test = self._clone_test(
2181
 
                test,
2182
 
                bzrdir_format,
2183
 
                workingtree_format, workingtree_format.__class__.__name__)
2184
 
            result.addTest(new_test)
2185
 
        return result