1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
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.
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.
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
17
"""WorkingTree object and friends.
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.
25
At the moment every WorkingTree has its own branch. Remote
26
WorkingTrees aren't supported.
28
To get a WorkingTree, call bzrdir.open_workingtree() or
29
WorkingTree.open(dir).
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)
39
from cStringIO import StringIO
42
from bzrlib.lazy_import import lazy_import
43
lazy_import(globals(), """
44
from bisect import bisect_left
46
from copy import deepcopy
58
conflicts as _mod_conflicts,
77
from bzrlib.transport import get_transport
79
from bzrlib.workingtree_4 import WorkingTreeFormat4
82
from bzrlib import symbol_versioning
83
from bzrlib.decorators import needs_read_lock, needs_write_lock
84
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, TreeReference
85
from bzrlib.lockable_files import LockableFiles, TransportLock
86
from bzrlib.lockdir import LockDir
87
import bzrlib.mutabletree
88
from bzrlib.mutabletree import needs_tree_write_lock
89
from bzrlib.osutils import (
101
from bzrlib.trace import mutter, note
102
from bzrlib.transport.local import LocalTransport
103
from bzrlib.progress import DummyProgress, ProgressPhase
104
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
105
from bzrlib.rio import RioReader, rio_file, Stanza
106
from bzrlib.symbol_versioning import (deprecated_passed,
109
DEPRECATED_PARAMETER,
116
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
117
CONFLICT_HEADER_1 = "BZR conflict list format 1"
120
@deprecated_function(zero_thirteen)
121
def gen_file_id(name):
122
"""Return new file id for the basename 'name'.
124
Use bzrlib.generate_ids.gen_file_id() instead
126
return generate_ids.gen_file_id(name)
129
@deprecated_function(zero_thirteen)
131
"""Return a new tree-root file id.
133
This has been deprecated in favor of bzrlib.generate_ids.gen_root_id()
135
return generate_ids.gen_root_id()
138
class TreeEntry(object):
139
"""An entry that implements the minimum interface used by commands.
141
This needs further inspection, it may be better to have
142
InventoryEntries without ids - though that seems wrong. For now,
143
this is a parallel hierarchy to InventoryEntry, and needs to become
144
one of several things: decorates to that hierarchy, children of, or
146
Another note is that these objects are currently only used when there is
147
no InventoryEntry available - i.e. for unversioned objects.
148
Perhaps they should be UnversionedEntry et al. ? - RBC 20051003
151
def __eq__(self, other):
152
# yes, this us ugly, TODO: best practice __eq__ style.
153
return (isinstance(other, TreeEntry)
154
and other.__class__ == self.__class__)
156
def kind_character(self):
160
class TreeDirectory(TreeEntry):
161
"""See TreeEntry. This is a directory in a working tree."""
163
def __eq__(self, other):
164
return (isinstance(other, TreeDirectory)
165
and other.__class__ == self.__class__)
167
def kind_character(self):
171
class TreeFile(TreeEntry):
172
"""See TreeEntry. This is a regular file in a working tree."""
174
def __eq__(self, other):
175
return (isinstance(other, TreeFile)
176
and other.__class__ == self.__class__)
178
def kind_character(self):
182
class TreeLink(TreeEntry):
183
"""See TreeEntry. This is a symlink in a working tree."""
185
def __eq__(self, other):
186
return (isinstance(other, TreeLink)
187
and other.__class__ == self.__class__)
189
def kind_character(self):
193
class WorkingTree(bzrlib.mutabletree.MutableTree):
194
"""Working copy tree.
196
The inventory is held in the `Branch` working-inventory, and the
197
files are in a directory on disk.
199
It is possible for a `WorkingTree` to have a filename which is
200
not listed in the Inventory and vice versa.
203
def __init__(self, basedir='.',
204
branch=DEPRECATED_PARAMETER,
210
"""Construct a WorkingTree for basedir.
212
If the branch is not supplied, it is opened automatically.
213
If the branch is supplied, it must be the branch for this basedir.
214
(branch.base is not cross checked, because for remote branches that
215
would be meaningless).
217
self._format = _format
218
self.bzrdir = _bzrdir
220
# not created via open etc.
221
warnings.warn("WorkingTree() is deprecated as of bzr version 0.8. "
222
"Please use bzrdir.open_workingtree or WorkingTree.open().",
225
wt = WorkingTree.open(basedir)
226
self._branch = wt.branch
227
self.basedir = wt.basedir
228
self._control_files = wt._control_files
229
self._hashcache = wt._hashcache
230
self._set_inventory(wt._inventory, dirty=False)
231
self._format = wt._format
232
self.bzrdir = wt.bzrdir
233
assert isinstance(basedir, basestring), \
234
"base directory %r is not a string" % basedir
235
basedir = safe_unicode(basedir)
236
mutter("opening working tree %r", basedir)
237
if deprecated_passed(branch):
239
warnings.warn("WorkingTree(..., branch=XXX) is deprecated"
240
" as of bzr 0.8. Please use bzrdir.open_workingtree() or"
241
" WorkingTree.open().",
245
self._branch = branch
247
self._branch = self.bzrdir.open_branch()
248
self.basedir = realpath(basedir)
249
# if branch is at our basedir and is a format 6 or less
250
if isinstance(self._format, WorkingTreeFormat2):
251
# share control object
252
self._control_files = self.branch.control_files
254
# assume all other formats have their own control files.
255
assert isinstance(_control_files, LockableFiles), \
256
"_control_files must be a LockableFiles, not %r" \
258
self._control_files = _control_files
259
# update the whole cache up front and write to disk if anything changed;
260
# in the future we might want to do this more selectively
261
# two possible ways offer themselves : in self._unlock, write the cache
262
# if needed, or, when the cache sees a change, append it to the hash
263
# cache file, and have the parser take the most recent entry for a
265
wt_trans = self.bzrdir.get_workingtree_transport(None)
266
cache_filename = wt_trans.local_abspath('stat-cache')
267
self._hashcache = hashcache.HashCache(basedir, cache_filename,
268
self._control_files._file_mode)
271
# is this scan needed ? it makes things kinda slow.
278
if _inventory is None:
279
self._inventory_is_modified = False
280
self.read_working_inventory()
282
# the caller of __init__ has provided an inventory,
283
# we assume they know what they are doing - as its only
284
# the Format factory and creation methods that are
285
# permitted to do this.
286
self._set_inventory(_inventory, dirty=False)
289
fget=lambda self: self._branch,
290
doc="""The branch this WorkingTree is connected to.
292
This cannot be set - it is reflective of the actual disk structure
293
the working tree has been constructed from.
296
def break_lock(self):
297
"""Break a lock if one is present from another instance.
299
Uses the ui factory to ask for confirmation if the lock may be from
302
This will probe the repository for its lock as well.
304
self._control_files.break_lock()
305
self.branch.break_lock()
307
def requires_rich_root(self):
308
return self._format.requires_rich_root
310
def supports_tree_reference(self):
311
return getattr(self._format, 'supports_tree_reference', False)
313
def _set_inventory(self, inv, dirty):
314
"""Set the internal cached inventory.
316
:param inv: The inventory to set.
317
:param dirty: A boolean indicating whether the inventory is the same
318
logical inventory as whats on disk. If True the inventory is not
319
the same and should be written to disk or data will be lost, if
320
False then the inventory is the same as that on disk and any
321
serialisation would be unneeded overhead.
323
assert inv.root is not None
324
self._inventory = inv
325
self._inventory_is_modified = dirty
328
def open(path=None, _unsupported=False):
329
"""Open an existing working tree at path.
333
path = os.path.getcwdu()
334
control = bzrdir.BzrDir.open(path, _unsupported)
335
return control.open_workingtree(_unsupported)
338
def open_containing(path=None):
339
"""Open an existing working tree which has its root about path.
341
This probes for a working tree at path and searches upwards from there.
343
Basically we keep looking up until we find the control directory or
344
run into /. If there isn't one, raises NotBranchError.
345
TODO: give this a new exception.
346
If there is one, it is returned, along with the unused portion of path.
348
:return: The WorkingTree that contains 'path', and the rest of path
351
path = osutils.getcwd()
352
control, relpath = bzrdir.BzrDir.open_containing(path)
354
return control.open_workingtree(), relpath
357
def open_downlevel(path=None):
358
"""Open an unsupported working tree.
360
Only intended for advanced situations like upgrading part of a bzrdir.
362
return WorkingTree.open(path, _unsupported=True)
365
"""Iterate through file_ids for this tree.
367
file_ids are in a WorkingTree if they are in the working inventory
368
and the working file exists.
370
inv = self._inventory
371
for path, ie in inv.iter_entries():
372
if osutils.lexists(self.abspath(path)):
376
return "<%s of %s>" % (self.__class__.__name__,
377
getattr(self, 'basedir', None))
379
def abspath(self, filename):
380
return pathjoin(self.basedir, filename)
382
def basis_tree(self):
383
"""Return RevisionTree for the current last revision.
385
If the left most parent is a ghost then the returned tree will be an
386
empty tree - one obtained by calling repository.revision_tree(None).
389
revision_id = self.get_parent_ids()[0]
391
# no parents, return an empty revision tree.
392
# in the future this should return the tree for
393
# 'empty:' - the implicit root empty tree.
394
return self.branch.repository.revision_tree(None)
396
return self.revision_tree(revision_id)
397
except errors.NoSuchRevision:
399
# No cached copy available, retrieve from the repository.
400
# FIXME? RBC 20060403 should we cache the inventory locally
403
return self.branch.repository.revision_tree(revision_id)
404
except errors.RevisionNotPresent:
405
# the basis tree *may* be a ghost or a low level error may have
406
# occured. If the revision is present, its a problem, if its not
408
if self.branch.repository.has_revision(revision_id):
410
# the basis tree is a ghost so return an empty tree.
411
return self.branch.repository.revision_tree(None)
414
@deprecated_method(zero_eight)
415
def create(branch, directory):
416
"""Create a workingtree for branch at directory.
418
If existing_directory already exists it must have a .bzr directory.
419
If it does not exist, it will be created.
421
This returns a new WorkingTree object for the new checkout.
423
TODO FIXME RBC 20060124 when we have checkout formats in place this
424
should accept an optional revisionid to checkout [and reject this if
425
checking out into the same dir as a pre-checkout-aware branch format.]
427
XXX: When BzrDir is present, these should be created through that
430
warnings.warn('delete WorkingTree.create', stacklevel=3)
431
transport = get_transport(directory)
432
if branch.bzrdir.root_transport.base == transport.base:
434
return branch.bzrdir.create_workingtree()
435
# different directory,
436
# create a branch reference
437
# and now a working tree.
438
raise NotImplementedError
441
@deprecated_method(zero_eight)
442
def create_standalone(directory):
443
"""Create a checkout and a branch and a repo at directory.
445
Directory must exist and be empty.
447
please use BzrDir.create_standalone_workingtree
449
return bzrdir.BzrDir.create_standalone_workingtree(directory)
451
def relpath(self, path):
452
"""Return the local path portion from a given path.
454
The path may be absolute or relative. If its a relative path it is
455
interpreted relative to the python current working directory.
457
return osutils.relpath(self.basedir, path)
459
def has_filename(self, filename):
460
return osutils.lexists(self.abspath(filename))
462
def get_file(self, file_id):
463
file_id = osutils.safe_file_id(file_id)
464
return self.get_file_byname(self.id2path(file_id))
466
def get_file_text(self, file_id):
467
file_id = osutils.safe_file_id(file_id)
468
return self.get_file(file_id).read()
470
def get_file_byname(self, filename):
471
return file(self.abspath(filename), 'rb')
474
def annotate_iter(self, file_id):
475
"""See Tree.annotate_iter
477
This implementation will use the basis tree implementation if possible.
478
Lines not in the basis are attributed to CURRENT_REVISION
480
If there are pending merges, lines added by those merges will be
481
incorrectly attributed to CURRENT_REVISION (but after committing, the
482
attribution will be correct).
484
file_id = osutils.safe_file_id(file_id)
485
basis = self.basis_tree()
486
changes = iter(self._iter_changes(basis, True, [self.id2path(file_id)],
487
require_versioned=True)).next()
488
changed_content, kind = changes[2], changes[6]
489
if not changed_content:
490
return basis.annotate_iter(file_id)
494
if kind[0] != 'file':
497
old_lines = list(basis.annotate_iter(file_id))
499
for tree in self.branch.repository.revision_trees(
500
self.get_parent_ids()[1:]):
501
if file_id not in tree:
503
old.append(list(tree.annotate_iter(file_id)))
504
return annotate.reannotate(old, self.get_file(file_id).readlines(),
507
def get_parent_ids(self):
508
"""See Tree.get_parent_ids.
510
This implementation reads the pending merges list and last_revision
511
value and uses that to decide what the parents list should be.
513
last_rev = self._last_revision()
519
merges_file = self._control_files.get('pending-merges')
520
except errors.NoSuchFile:
523
for l in merges_file.readlines():
524
revision_id = osutils.safe_revision_id(l.rstrip('\n'))
525
parents.append(revision_id)
529
def get_root_id(self):
530
"""Return the id of this trees root"""
531
return self._inventory.root.file_id
533
def _get_store_filename(self, file_id):
534
## XXX: badly named; this is not in the store at all
535
file_id = osutils.safe_file_id(file_id)
536
return self.abspath(self.id2path(file_id))
539
def clone(self, to_bzrdir, revision_id=None, basis=None):
540
"""Duplicate this working tree into to_bzr, including all state.
542
Specifically modified files are kept as modified, but
543
ignored and unknown files are discarded.
545
If you want to make a new line of development, see bzrdir.sprout()
548
If not None, the cloned tree will have its last revision set to
549
revision, and and difference between the source trees last revision
550
and this one merged in.
553
If not None, a closer copy of a tree which may have some files in
554
common, and which file content should be preferentially copied from.
556
# assumes the target bzr dir format is compatible.
557
result = self._format.initialize(to_bzrdir)
558
self.copy_content_into(result, revision_id)
562
def copy_content_into(self, tree, revision_id=None):
563
"""Copy the current content and user files of this tree into tree."""
564
tree.set_root_id(self.get_root_id())
565
if revision_id is None:
566
merge.transform_tree(tree, self)
568
# TODO now merge from tree.last_revision to revision (to preserve
569
# user local changes)
570
merge.transform_tree(tree, self)
571
tree.set_parent_ids([revision_id])
573
def id2abspath(self, file_id):
574
file_id = osutils.safe_file_id(file_id)
575
return self.abspath(self.id2path(file_id))
577
def has_id(self, file_id):
578
# files that have been deleted are excluded
579
file_id = osutils.safe_file_id(file_id)
581
if not inv.has_id(file_id):
583
path = inv.id2path(file_id)
584
return osutils.lexists(self.abspath(path))
586
def has_or_had_id(self, file_id):
587
file_id = osutils.safe_file_id(file_id)
588
if file_id == self.inventory.root.file_id:
590
return self.inventory.has_id(file_id)
592
__contains__ = has_id
594
def get_file_size(self, file_id):
595
file_id = osutils.safe_file_id(file_id)
596
return os.path.getsize(self.id2abspath(file_id))
599
def get_file_sha1(self, file_id, path=None, stat_value=None):
600
file_id = osutils.safe_file_id(file_id)
602
path = self._inventory.id2path(file_id)
603
return self._hashcache.get_sha1(path, stat_value)
605
def get_file_mtime(self, file_id, path=None):
606
file_id = osutils.safe_file_id(file_id)
608
path = self.inventory.id2path(file_id)
609
return os.lstat(self.abspath(path)).st_mtime
611
if not supports_executable():
612
def is_executable(self, file_id, path=None):
613
file_id = osutils.safe_file_id(file_id)
614
return self._inventory[file_id].executable
616
def is_executable(self, file_id, path=None):
618
file_id = osutils.safe_file_id(file_id)
619
path = self.id2path(file_id)
620
mode = os.lstat(self.abspath(path)).st_mode
621
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
623
@needs_tree_write_lock
624
def _add(self, files, ids, kinds):
625
"""See MutableTree._add."""
626
# TODO: Re-adding a file that is removed in the working copy
627
# should probably put it back with the previous ID.
628
# the read and write working inventory should not occur in this
629
# function - they should be part of lock_write and unlock.
630
inv = self.read_working_inventory()
631
for f, file_id, kind in zip(files, ids, kinds):
632
assert kind is not None
634
inv.add_path(f, kind=kind)
636
file_id = osutils.safe_file_id(file_id)
637
inv.add_path(f, kind=kind, file_id=file_id)
638
self._write_inventory(inv)
640
@needs_tree_write_lock
641
def _gather_kinds(self, files, kinds):
642
"""See MutableTree._gather_kinds."""
643
for pos, f in enumerate(files):
644
if kinds[pos] is None:
645
fullpath = normpath(self.abspath(f))
647
kinds[pos] = file_kind(fullpath)
649
if e.errno == errno.ENOENT:
650
raise errors.NoSuchFile(fullpath)
653
def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
654
"""Add revision_id as a parent.
656
This is equivalent to retrieving the current list of parent ids
657
and setting the list to its value plus revision_id.
659
:param revision_id: The revision id to add to the parent list. It may
660
be a ghost revision as long as its not the first parent to be added,
661
or the allow_leftmost_as_ghost parameter is set True.
662
:param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
664
parents = self.get_parent_ids() + [revision_id]
665
self.set_parent_ids(parents, allow_leftmost_as_ghost=len(parents) > 1
666
or allow_leftmost_as_ghost)
668
@needs_tree_write_lock
669
def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
670
"""Add revision_id, tree tuple as a parent.
672
This is equivalent to retrieving the current list of parent trees
673
and setting the list to its value plus parent_tuple. See also
674
add_parent_tree_id - if you only have a parent id available it will be
675
simpler to use that api. If you have the parent already available, using
676
this api is preferred.
678
:param parent_tuple: The (revision id, tree) to add to the parent list.
679
If the revision_id is a ghost, pass None for the tree.
680
:param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
682
parent_ids = self.get_parent_ids() + [parent_tuple[0]]
683
if len(parent_ids) > 1:
684
# the leftmost may have already been a ghost, preserve that if it
686
allow_leftmost_as_ghost = True
687
self.set_parent_ids(parent_ids,
688
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
690
@needs_tree_write_lock
691
def add_pending_merge(self, *revision_ids):
692
# TODO: Perhaps should check at this point that the
693
# history of the revision is actually present?
694
parents = self.get_parent_ids()
696
for rev_id in revision_ids:
697
if rev_id in parents:
699
parents.append(rev_id)
702
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
704
@deprecated_method(zero_eleven)
706
def pending_merges(self):
707
"""Return a list of pending merges.
709
These are revisions that have been merged into the working
710
directory but not yet committed.
712
As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
713
instead - which is available on all tree objects.
715
return self.get_parent_ids()[1:]
717
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
718
"""Common ghost checking functionality from set_parent_*.
720
This checks that the left hand-parent exists if there are any
723
if len(revision_ids) > 0:
724
leftmost_id = revision_ids[0]
725
if (not allow_leftmost_as_ghost and not
726
self.branch.repository.has_revision(leftmost_id)):
727
raise errors.GhostRevisionUnusableHere(leftmost_id)
729
def _set_merges_from_parent_ids(self, parent_ids):
730
merges = parent_ids[1:]
731
self._control_files.put_bytes('pending-merges', '\n'.join(merges))
733
@needs_tree_write_lock
734
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
735
"""Set the parent ids to revision_ids.
737
See also set_parent_trees. This api will try to retrieve the tree data
738
for each element of revision_ids from the trees repository. If you have
739
tree data already available, it is more efficient to use
740
set_parent_trees rather than set_parent_ids. set_parent_ids is however
741
an easier API to use.
743
:param revision_ids: The revision_ids to set as the parent ids of this
744
working tree. Any of these may be ghosts.
746
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
747
self._check_parents_for_ghosts(revision_ids,
748
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
750
if len(revision_ids) > 0:
751
self.set_last_revision(revision_ids[0])
753
self.set_last_revision(None)
755
self._set_merges_from_parent_ids(revision_ids)
757
@needs_tree_write_lock
758
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
759
"""See MutableTree.set_parent_trees."""
760
parent_ids = [osutils.safe_revision_id(rev) for (rev, tree) in parents_list]
762
self._check_parents_for_ghosts(parent_ids,
763
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
765
if len(parent_ids) == 0:
766
leftmost_parent_id = None
767
leftmost_parent_tree = None
769
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
771
if self._change_last_revision(leftmost_parent_id):
772
if leftmost_parent_tree is None:
773
# If we don't have a tree, fall back to reading the
774
# parent tree from the repository.
775
self._cache_basis_inventory(leftmost_parent_id)
777
inv = leftmost_parent_tree.inventory
778
xml = self._create_basis_xml_from_inventory(
779
leftmost_parent_id, inv)
780
self._write_basis_inventory(xml)
781
self._set_merges_from_parent_ids(parent_ids)
783
@needs_tree_write_lock
784
def set_pending_merges(self, rev_list):
785
parents = self.get_parent_ids()
786
leftmost = parents[:1]
787
new_parents = leftmost + rev_list
788
self.set_parent_ids(new_parents)
790
@needs_tree_write_lock
791
def set_merge_modified(self, modified_hashes):
793
for file_id, hash in modified_hashes.iteritems():
794
yield Stanza(file_id=file_id.decode('utf8'), hash=hash)
795
self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
797
def _put_rio(self, filename, stanzas, header):
798
self._must_be_locked()
799
my_file = rio_file(stanzas, header)
800
self._control_files.put(filename, my_file)
802
@needs_write_lock # because merge pulls data into the branch.
803
def merge_from_branch(self, branch, to_revision=None):
804
"""Merge from a branch into this working tree.
806
:param branch: The branch to merge from.
807
:param to_revision: If non-None, the merge will merge to to_revision,
808
but not beyond it. to_revision does not need to be in the history
809
of the branch when it is supplied. If None, to_revision defaults to
810
branch.last_revision().
812
from bzrlib.merge import Merger, Merge3Merger
813
pb = bzrlib.ui.ui_factory.nested_progress_bar()
815
merger = Merger(self.branch, this_tree=self, pb=pb)
816
merger.pp = ProgressPhase("Merge phase", 5, pb)
817
merger.pp.next_phase()
818
# check that there are no
820
merger.check_basis(check_clean=True, require_commits=False)
821
if to_revision is None:
822
to_revision = branch.last_revision()
824
to_revision = osutils.safe_revision_id(to_revision)
825
merger.other_rev_id = to_revision
826
if merger.other_rev_id is None:
827
raise error.NoCommits(branch)
828
self.branch.fetch(branch, last_revision=merger.other_rev_id)
829
merger.other_basis = merger.other_rev_id
830
merger.other_tree = self.branch.repository.revision_tree(
832
merger.other_branch = branch
833
merger.pp.next_phase()
835
if merger.base_rev_id == merger.other_rev_id:
836
raise errors.PointlessMerge
837
merger.backup_files = False
838
merger.merge_type = Merge3Merger
839
merger.set_interesting_files(None)
840
merger.show_base = False
841
merger.reprocess = False
842
conflicts = merger.do_merge()
849
def merge_modified(self):
850
"""Return a dictionary of files modified by a merge.
852
The list is initialized by WorkingTree.set_merge_modified, which is
853
typically called after we make some automatic updates to the tree
856
This returns a map of file_id->sha1, containing only files which are
857
still in the working inventory and have that text hash.
860
hashfile = self._control_files.get('merge-hashes')
861
except errors.NoSuchFile:
865
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
866
raise errors.MergeModifiedFormatError()
867
except StopIteration:
868
raise errors.MergeModifiedFormatError()
869
for s in RioReader(hashfile):
870
file_id = s.get("file_id")
871
if file_id not in self.inventory:
873
text_hash = s.get("hash")
874
if text_hash == self.get_file_sha1(file_id):
875
merge_hashes[file_id] = text_hash
879
def mkdir(self, path, file_id=None):
880
"""See MutableTree.mkdir()."""
882
file_id = generate_ids.gen_file_id(os.path.basename(path))
883
os.mkdir(self.abspath(path))
884
self.add(path, file_id, 'directory')
887
def get_symlink_target(self, file_id):
888
file_id = osutils.safe_file_id(file_id)
889
return os.readlink(self.id2abspath(file_id))
892
def subsume(self, other_tree):
893
def add_children(inventory, entry):
894
for child_entry in entry.children.values():
895
inventory._byid[child_entry.file_id] = child_entry
896
if child_entry.kind == 'directory':
897
add_children(inventory, child_entry)
898
if other_tree.get_root_id() == self.get_root_id():
899
raise errors.BadSubsumeSource(self, other_tree,
900
'Trees have the same root')
902
other_tree_path = self.relpath(other_tree.basedir)
903
except errors.PathNotChild:
904
raise errors.BadSubsumeSource(self, other_tree,
905
'Tree is not contained by the other')
906
new_root_parent = self.path2id(osutils.dirname(other_tree_path))
907
if new_root_parent is None:
908
raise errors.BadSubsumeSource(self, other_tree,
909
'Parent directory is not versioned.')
910
# We need to ensure that the result of a fetch will have a
911
# versionedfile for the other_tree root, and only fetching into
912
# RepositoryKnit2 guarantees that.
913
if not self.branch.repository.supports_rich_root():
914
raise errors.SubsumeTargetNeedsUpgrade(other_tree)
915
other_tree.lock_tree_write()
917
for parent_id in other_tree.get_parent_ids():
918
self.branch.fetch(other_tree.branch, parent_id)
919
self.add_parent_tree_id(parent_id)
920
other_root = other_tree.inventory.root
921
other_root.parent_id = new_root_parent
922
other_root.name = osutils.basename(other_tree_path)
923
self.inventory.add(other_root)
924
add_children(self.inventory, other_root)
925
self._write_inventory(self.inventory)
928
other_tree.bzrdir.destroy_workingtree_metadata()
930
@needs_tree_write_lock
931
def extract(self, file_id, format=None):
932
"""Extract a subtree from this tree.
934
A new branch will be created, relative to the path for this tree.
937
segments = osutils.splitpath(path)
938
transport = self.branch.bzrdir.root_transport
939
for name in segments:
940
transport = transport.clone(name)
943
except errors.FileExists:
947
sub_path = self.id2path(file_id)
948
branch_transport = mkdirs(sub_path)
950
format = bzrdir.format_registry.make_bzrdir('experimental-knit2')
952
branch_transport.mkdir('.')
953
except errors.FileExists:
955
branch_bzrdir = format.initialize_on_transport(branch_transport)
957
repo = branch_bzrdir.find_repository()
958
except errors.NoRepositoryPresent:
959
repo = branch_bzrdir.create_repository()
960
assert repo.supports_rich_root()
962
if not repo.supports_rich_root():
963
raise errors.RootNotRich()
964
new_branch = branch_bzrdir.create_branch()
965
new_branch.pull(self.branch)
966
for parent_id in self.get_parent_ids():
967
new_branch.fetch(self.branch, parent_id)
968
tree_transport = self.bzrdir.root_transport.clone(sub_path)
969
if tree_transport.base != branch_transport.base:
970
tree_bzrdir = format.initialize_on_transport(tree_transport)
971
branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
973
tree_bzrdir = branch_bzrdir
974
wt = tree_bzrdir.create_workingtree(NULL_REVISION)
975
wt.set_parent_ids(self.get_parent_ids())
976
my_inv = self.inventory
977
child_inv = Inventory(root_id=None)
978
new_root = my_inv[file_id]
979
my_inv.remove_recursive_id(file_id)
980
new_root.parent_id = None
981
child_inv.add(new_root)
982
self._write_inventory(my_inv)
983
wt._write_inventory(child_inv)
986
def _serialize(self, inventory, out_file):
987
xml5.serializer_v5.write_inventory(self._inventory, out_file)
989
def _deserialize(selt, in_file):
990
return xml5.serializer_v5.read_inventory(in_file)
993
"""Write the in memory inventory to disk."""
994
# TODO: Maybe this should only write on dirty ?
995
if self._control_files._lock_mode != 'w':
996
raise errors.NotWriteLocked(self)
998
self._serialize(self._inventory, sio)
1000
self._control_files.put('inventory', sio)
1001
self._inventory_is_modified = False
1003
def list_files(self, include_root=False):
1004
"""Recursively list all files as (path, class, kind, id, entry).
1006
Lists, but does not descend into unversioned directories.
1008
This does not include files that have been deleted in this
1011
Skips the control directory.
1013
# list_files is an iterator, so @needs_read_lock doesn't work properly
1014
# with it. So callers should be careful to always read_lock the tree.
1015
if not self.is_locked():
1016
raise errors.ObjectNotLocked(self)
1018
inv = self.inventory
1019
if include_root is True:
1020
yield ('', 'V', 'directory', inv.root.file_id, inv.root)
1021
# Convert these into local objects to save lookup times
1022
pathjoin = osutils.pathjoin
1023
file_kind = osutils.file_kind
1025
# transport.base ends in a slash, we want the piece
1026
# between the last two slashes
1027
transport_base_dir = self.bzrdir.transport.base.rsplit('/', 2)[1]
1029
fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
1031
# directory file_id, relative path, absolute path, reverse sorted children
1032
children = os.listdir(self.basedir)
1034
# jam 20060527 The kernel sized tree seems equivalent whether we
1035
# use a deque and popleft to keep them sorted, or if we use a plain
1036
# list and just reverse() them.
1037
children = collections.deque(children)
1038
stack = [(inv.root.file_id, u'', self.basedir, children)]
1040
from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
1043
f = children.popleft()
1044
## TODO: If we find a subdirectory with its own .bzr
1045
## directory, then that is a separate tree and we
1046
## should exclude it.
1048
# the bzrdir for this tree
1049
if transport_base_dir == f:
1052
# we know that from_dir_relpath and from_dir_abspath never end in a slash
1053
# and 'f' doesn't begin with one, we can do a string op, rather
1054
# than the checks of pathjoin(), all relative paths will have an extra slash
1056
fp = from_dir_relpath + '/' + f
1059
fap = from_dir_abspath + '/' + f
1061
f_ie = inv.get_child(from_dir_id, f)
1064
elif self.is_ignored(fp[1:]):
1067
# we may not have found this file, because of a unicode issue
1068
f_norm, can_access = osutils.normalized_filename(f)
1069
if f == f_norm or not can_access:
1070
# No change, so treat this file normally
1073
# this file can be accessed by a normalized path
1074
# check again if it is versioned
1075
# these lines are repeated here for performance
1077
fp = from_dir_relpath + '/' + f
1078
fap = from_dir_abspath + '/' + f
1079
f_ie = inv.get_child(from_dir_id, f)
1082
elif self.is_ignored(fp[1:]):
1091
raise errors.BzrCheckError(
1092
"file %r entered as kind %r id %r, now of kind %r"
1093
% (fap, f_ie.kind, f_ie.file_id, fk))
1095
# make a last minute entry
1097
yield fp[1:], c, fk, f_ie.file_id, f_ie
1100
yield fp[1:], c, fk, None, fk_entries[fk]()
1102
yield fp[1:], c, fk, None, TreeEntry()
1105
if fk != 'directory':
1108
# But do this child first
1109
new_children = os.listdir(fap)
1111
new_children = collections.deque(new_children)
1112
stack.append((f_ie.file_id, fp, fap, new_children))
1113
# Break out of inner loop,
1114
# so that we start outer loop with child
1117
# if we finished all children, pop it off the stack
1120
@needs_tree_write_lock
1121
def move(self, from_paths, to_dir=None, after=False, **kwargs):
1124
to_dir must exist in the inventory.
1126
If to_dir exists and is a directory, the files are moved into
1127
it, keeping their old names.
1129
Note that to_dir is only the last component of the new name;
1130
this doesn't change the directory.
1132
For each entry in from_paths the move mode will be determined
1135
The first mode moves the file in the filesystem and updates the
1136
inventory. The second mode only updates the inventory without
1137
touching the file on the filesystem. This is the new mode introduced
1140
move uses the second mode if 'after == True' and the target is not
1141
versioned but present in the working tree.
1143
move uses the second mode if 'after == False' and the source is
1144
versioned but no longer in the working tree, and the target is not
1145
versioned but present in the working tree.
1147
move uses the first mode if 'after == False' and the source is
1148
versioned and present in the working tree, and the target is not
1149
versioned and not present in the working tree.
1151
Everything else results in an error.
1153
This returns a list of (from_path, to_path) pairs for each
1154
entry that is moved.
1159
# check for deprecated use of signature
1161
to_dir = kwargs.get('to_name', None)
1163
raise TypeError('You must supply a target directory')
1165
symbol_versioning.warn('The parameter to_name was deprecated'
1166
' in version 0.13. Use to_dir instead',
1169
# check destination directory
1170
assert not isinstance(from_paths, basestring)
1171
inv = self.inventory
1172
to_abs = self.abspath(to_dir)
1173
if not isdir(to_abs):
1174
raise errors.BzrMoveFailedError('',to_dir,
1175
errors.NotADirectory(to_abs))
1176
if not self.has_filename(to_dir):
1177
raise errors.BzrMoveFailedError('',to_dir,
1178
errors.NotInWorkingDirectory(to_dir))
1179
to_dir_id = inv.path2id(to_dir)
1180
if to_dir_id is None:
1181
raise errors.BzrMoveFailedError('',to_dir,
1182
errors.NotVersionedError(path=str(to_dir)))
1184
to_dir_ie = inv[to_dir_id]
1185
if to_dir_ie.kind != 'directory':
1186
raise errors.BzrMoveFailedError('',to_dir,
1187
errors.NotADirectory(to_abs))
1189
# create rename entries and tuples
1190
for from_rel in from_paths:
1191
from_tail = splitpath(from_rel)[-1]
1192
from_id = inv.path2id(from_rel)
1194
raise errors.BzrMoveFailedError(from_rel,to_dir,
1195
errors.NotVersionedError(path=str(from_rel)))
1197
from_entry = inv[from_id]
1198
from_parent_id = from_entry.parent_id
1199
to_rel = pathjoin(to_dir, from_tail)
1200
rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
1202
from_tail=from_tail,
1203
from_parent_id=from_parent_id,
1204
to_rel=to_rel, to_tail=from_tail,
1205
to_parent_id=to_dir_id)
1206
rename_entries.append(rename_entry)
1207
rename_tuples.append((from_rel, to_rel))
1209
# determine which move mode to use. checks also for movability
1210
rename_entries = self._determine_mv_mode(rename_entries, after)
1212
original_modified = self._inventory_is_modified
1215
self._inventory_is_modified = True
1216
self._move(rename_entries)
1218
# restore the inventory on error
1219
self._inventory_is_modified = original_modified
1221
self._write_inventory(inv)
1222
return rename_tuples
1224
def _determine_mv_mode(self, rename_entries, after=False):
1225
"""Determines for each from-to pair if both inventory and working tree
1226
or only the inventory has to be changed.
1228
Also does basic plausability tests.
1230
inv = self.inventory
1232
for rename_entry in rename_entries:
1233
# store to local variables for easier reference
1234
from_rel = rename_entry.from_rel
1235
from_id = rename_entry.from_id
1236
to_rel = rename_entry.to_rel
1237
to_id = inv.path2id(to_rel)
1238
only_change_inv = False
1240
# check the inventory for source and destination
1242
raise errors.BzrMoveFailedError(from_rel,to_rel,
1243
errors.NotVersionedError(path=str(from_rel)))
1244
if to_id is not None:
1245
raise errors.BzrMoveFailedError(from_rel,to_rel,
1246
errors.AlreadyVersionedError(path=str(to_rel)))
1248
# try to determine the mode for rename (only change inv or change
1249
# inv and file system)
1251
if not self.has_filename(to_rel):
1252
raise errors.BzrMoveFailedError(from_id,to_rel,
1253
errors.NoSuchFile(path=str(to_rel),
1254
extra="New file has not been created yet"))
1255
only_change_inv = True
1256
elif not self.has_filename(from_rel) and self.has_filename(to_rel):
1257
only_change_inv = True
1258
elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1259
only_change_inv = False
1261
# something is wrong, so lets determine what exactly
1262
if not self.has_filename(from_rel) and \
1263
not self.has_filename(to_rel):
1264
raise errors.BzrRenameFailedError(from_rel,to_rel,
1265
errors.PathsDoNotExist(paths=(str(from_rel),
1268
raise errors.RenameFailedFilesExist(from_rel, to_rel,
1269
extra="(Use --after to update the Bazaar id)")
1270
rename_entry.only_change_inv = only_change_inv
1271
return rename_entries
1273
def _move(self, rename_entries):
1274
"""Moves a list of files.
1276
Depending on the value of the flag 'only_change_inv', the
1277
file will be moved on the file system or not.
1279
inv = self.inventory
1282
for entry in rename_entries:
1284
self._move_entry(entry)
1286
self._rollback_move(moved)
1290
def _rollback_move(self, moved):
1291
"""Try to rollback a previous move in case of an filesystem error."""
1292
inv = self.inventory
1295
self._move_entry(_RenameEntry(entry.to_rel, entry.from_id,
1296
entry.to_tail, entry.to_parent_id, entry.from_rel,
1297
entry.from_tail, entry.from_parent_id,
1298
entry.only_change_inv))
1299
except errors.BzrMoveFailedError, e:
1300
raise errors.BzrMoveFailedError( '', '', "Rollback failed."
1301
" The working tree is in an inconsistent state."
1302
" Please consider doing a 'bzr revert'."
1303
" Error message is: %s" % e)
1305
def _move_entry(self, entry):
1306
inv = self.inventory
1307
from_rel_abs = self.abspath(entry.from_rel)
1308
to_rel_abs = self.abspath(entry.to_rel)
1309
if from_rel_abs == to_rel_abs:
1310
raise errors.BzrMoveFailedError(entry.from_rel, entry.to_rel,
1311
"Source and target are identical.")
1313
if not entry.only_change_inv:
1315
osutils.rename(from_rel_abs, to_rel_abs)
1317
raise errors.BzrMoveFailedError(entry.from_rel,
1319
inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
1321
@needs_tree_write_lock
1322
def rename_one(self, from_rel, to_rel, after=False):
1325
This can change the directory or the filename or both.
1327
rename_one has several 'modes' to work. First, it can rename a physical
1328
file and change the file_id. That is the normal mode. Second, it can
1329
only change the file_id without touching any physical file. This is
1330
the new mode introduced in version 0.15.
1332
rename_one uses the second mode if 'after == True' and 'to_rel' is not
1333
versioned but present in the working tree.
1335
rename_one uses the second mode if 'after == False' and 'from_rel' is
1336
versioned but no longer in the working tree, and 'to_rel' is not
1337
versioned but present in the working tree.
1339
rename_one uses the first mode if 'after == False' and 'from_rel' is
1340
versioned and present in the working tree, and 'to_rel' is not
1341
versioned and not present in the working tree.
1343
Everything else results in an error.
1345
inv = self.inventory
1348
# create rename entries and tuples
1349
from_tail = splitpath(from_rel)[-1]
1350
from_id = inv.path2id(from_rel)
1352
raise errors.BzrRenameFailedError(from_rel,to_rel,
1353
errors.NotVersionedError(path=str(from_rel)))
1354
from_entry = inv[from_id]
1355
from_parent_id = from_entry.parent_id
1356
to_dir, to_tail = os.path.split(to_rel)
1357
to_dir_id = inv.path2id(to_dir)
1358
rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
1360
from_tail=from_tail,
1361
from_parent_id=from_parent_id,
1362
to_rel=to_rel, to_tail=to_tail,
1363
to_parent_id=to_dir_id)
1364
rename_entries.append(rename_entry)
1366
# determine which move mode to use. checks also for movability
1367
rename_entries = self._determine_mv_mode(rename_entries, after)
1369
# check if the target changed directory and if the target directory is
1371
if to_dir_id is None:
1372
raise errors.BzrMoveFailedError(from_rel,to_rel,
1373
errors.NotVersionedError(path=str(to_dir)))
1375
# all checks done. now we can continue with our actual work
1376
mutter('rename_one:\n'
1381
' to_dir_id {%s}\n',
1382
from_id, from_rel, to_rel, to_dir, to_dir_id)
1384
self._move(rename_entries)
1385
self._write_inventory(inv)
1387
class _RenameEntry(object):
1388
def __init__(self, from_rel, from_id, from_tail, from_parent_id,
1389
to_rel, to_tail, to_parent_id, only_change_inv=False):
1390
self.from_rel = from_rel
1391
self.from_id = from_id
1392
self.from_tail = from_tail
1393
self.from_parent_id = from_parent_id
1394
self.to_rel = to_rel
1395
self.to_tail = to_tail
1396
self.to_parent_id = to_parent_id
1397
self.only_change_inv = only_change_inv
1401
"""Return all unknown files.
1403
These are files in the working directory that are not versioned or
1404
control files or ignored.
1406
# force the extras method to be fully executed before returning, to
1407
# prevent race conditions with the lock
1409
[subp for subp in self.extras() if not self.is_ignored(subp)])
1411
@needs_tree_write_lock
1412
def unversion(self, file_ids):
1413
"""Remove the file ids in file_ids from the current versioned set.
1415
When a file_id is unversioned, all of its children are automatically
1418
:param file_ids: The file ids to stop versioning.
1419
:raises: NoSuchId if any fileid is not currently versioned.
1421
for file_id in file_ids:
1422
file_id = osutils.safe_file_id(file_id)
1423
if self._inventory.has_id(file_id):
1424
self._inventory.remove_recursive_id(file_id)
1426
raise errors.NoSuchId(self, file_id)
1428
# in the future this should just set a dirty bit to wait for the
1429
# final unlock. However, until all methods of workingtree start
1430
# with the current in -memory inventory rather than triggering
1431
# a read, it is more complex - we need to teach read_inventory
1432
# to know when to read, and when to not read first... and possibly
1433
# to save first when the in memory one may be corrupted.
1434
# so for now, we just only write it if it is indeed dirty.
1436
self._write_inventory(self._inventory)
1438
@deprecated_method(zero_eight)
1439
def iter_conflicts(self):
1440
"""List all files in the tree that have text or content conflicts.
1441
DEPRECATED. Use conflicts instead."""
1442
return self._iter_conflicts()
1444
def _iter_conflicts(self):
1446
for info in self.list_files():
1448
stem = get_conflicted_stem(path)
1451
if stem not in conflicted:
1452
conflicted.add(stem)
1456
def pull(self, source, overwrite=False, stop_revision=None,
1457
change_reporter=None):
1458
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1461
pp = ProgressPhase("Pull phase", 2, top_pb)
1463
old_revision_info = self.branch.last_revision_info()
1464
basis_tree = self.basis_tree()
1465
count = self.branch.pull(source, overwrite, stop_revision)
1466
new_revision_info = self.branch.last_revision_info()
1467
if new_revision_info != old_revision_info:
1469
repository = self.branch.repository
1470
pb = bzrlib.ui.ui_factory.nested_progress_bar()
1471
basis_tree.lock_read()
1473
new_basis_tree = self.branch.basis_tree()
1480
change_reporter=change_reporter)
1481
if (basis_tree.inventory.root is None and
1482
new_basis_tree.inventory.root is not None):
1483
self.set_root_id(new_basis_tree.inventory.root.file_id)
1487
# TODO - dedup parents list with things merged by pull ?
1488
# reuse the revisiontree we merged against to set the new
1490
parent_trees = [(self.branch.last_revision(), new_basis_tree)]
1491
# we have to pull the merge trees out again, because
1492
# merge_inner has set the ids. - this corner is not yet
1493
# layered well enough to prevent double handling.
1494
# XXX TODO: Fix the double handling: telling the tree about
1495
# the already known parent data is wasteful.
1496
merges = self.get_parent_ids()[1:]
1497
parent_trees.extend([
1498
(parent, repository.revision_tree(parent)) for
1500
self.set_parent_trees(parent_trees)
1507
def put_file_bytes_non_atomic(self, file_id, bytes):
1508
"""See MutableTree.put_file_bytes_non_atomic."""
1509
file_id = osutils.safe_file_id(file_id)
1510
stream = file(self.id2abspath(file_id), 'wb')
1515
# TODO: update the hashcache here ?
1518
"""Yield all unknown files in this WorkingTree.
1520
If there are any unknown directories then only the directory is
1521
returned, not all its children. But if there are unknown files
1522
under a versioned subdirectory, they are returned.
1524
Currently returned depth-first, sorted by name within directories.
1526
## TODO: Work from given directory downwards
1527
for path, dir_entry in self.inventory.directories():
1528
# mutter("search for unknowns in %r", path)
1529
dirabs = self.abspath(path)
1530
if not isdir(dirabs):
1531
# e.g. directory deleted
1535
for subf in os.listdir(dirabs):
1538
if subf not in dir_entry.children:
1539
subf_norm, can_access = osutils.normalized_filename(subf)
1540
if subf_norm != subf and can_access:
1541
if subf_norm not in dir_entry.children:
1542
fl.append(subf_norm)
1548
subp = pathjoin(path, subf)
1551
def ignored_files(self):
1552
"""Yield list of PATH, IGNORE_PATTERN"""
1553
for subp in self.extras():
1554
pat = self.is_ignored(subp)
1558
def get_ignore_list(self):
1559
"""Return list of ignore patterns.
1561
Cached in the Tree object after the first call.
1563
ignoreset = getattr(self, '_ignoreset', None)
1564
if ignoreset is not None:
1567
ignore_globs = set(bzrlib.DEFAULT_IGNORE)
1568
ignore_globs.update(ignores.get_runtime_ignores())
1569
ignore_globs.update(ignores.get_user_ignores())
1570
if self.has_filename(bzrlib.IGNORE_FILENAME):
1571
f = self.get_file_byname(bzrlib.IGNORE_FILENAME)
1573
ignore_globs.update(ignores.parse_ignore_file(f))
1576
self._ignoreset = ignore_globs
1579
def _flush_ignore_list_cache(self):
1580
"""Resets the cached ignore list to force a cache rebuild."""
1581
self._ignoreset = None
1582
self._ignoreglobster = None
1584
def is_ignored(self, filename):
1585
r"""Check whether the filename matches an ignore pattern.
1587
Patterns containing '/' or '\' need to match the whole path;
1588
others match against only the last component.
1590
If the file is ignored, returns the pattern which caused it to
1591
be ignored, otherwise None. So this can simply be used as a
1592
boolean if desired."""
1593
if getattr(self, '_ignoreglobster', None) is None:
1594
self._ignoreglobster = globbing.Globster(self.get_ignore_list())
1595
return self._ignoreglobster.match(filename)
1597
def kind(self, file_id):
1598
return file_kind(self.id2abspath(file_id))
1600
def _comparison_data(self, entry, path):
1601
abspath = self.abspath(path)
1603
stat_value = os.lstat(abspath)
1605
if getattr(e, 'errno', None) == errno.ENOENT:
1612
mode = stat_value.st_mode
1613
kind = osutils.file_kind_from_stat_mode(mode)
1614
if not supports_executable():
1615
executable = entry.executable
1617
executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1618
return kind, executable, stat_value
1620
def _file_size(self, entry, stat_value):
1621
return stat_value.st_size
1623
def last_revision(self):
1624
"""Return the last revision of the branch for this tree.
1626
This format tree does not support a separate marker for last-revision
1627
compared to the branch.
1629
See MutableTree.last_revision
1631
return self._last_revision()
1634
def _last_revision(self):
1635
"""helper for get_parent_ids."""
1636
return self.branch.last_revision()
1638
def is_locked(self):
1639
return self._control_files.is_locked()
1641
def _must_be_locked(self):
1642
if not self.is_locked():
1643
raise errors.ObjectNotLocked(self)
1645
def lock_read(self):
1646
"""See Branch.lock_read, and WorkingTree.unlock."""
1647
self.branch.lock_read()
1649
return self._control_files.lock_read()
1651
self.branch.unlock()
1654
def lock_tree_write(self):
1655
"""See MutableTree.lock_tree_write, and WorkingTree.unlock."""
1656
self.branch.lock_read()
1658
return self._control_files.lock_write()
1660
self.branch.unlock()
1663
def lock_write(self):
1664
"""See MutableTree.lock_write, and WorkingTree.unlock."""
1665
self.branch.lock_write()
1667
return self._control_files.lock_write()
1669
self.branch.unlock()
1672
def get_physical_lock_status(self):
1673
return self._control_files.get_physical_lock_status()
1675
def _basis_inventory_name(self):
1676
return 'basis-inventory-cache'
1678
@needs_tree_write_lock
1679
def set_last_revision(self, new_revision):
1680
"""Change the last revision in the working tree."""
1681
new_revision = osutils.safe_revision_id(new_revision)
1682
if self._change_last_revision(new_revision):
1683
self._cache_basis_inventory(new_revision)
1685
def _change_last_revision(self, new_revision):
1686
"""Template method part of set_last_revision to perform the change.
1688
This is used to allow WorkingTree3 instances to not affect branch
1689
when their last revision is set.
1691
if new_revision is None:
1692
self.branch.set_revision_history([])
1695
self.branch.generate_revision_history(new_revision)
1696
except errors.NoSuchRevision:
1697
# not present in the repo - dont try to set it deeper than the tip
1698
self.branch.set_revision_history([new_revision])
1701
def _write_basis_inventory(self, xml):
1702
"""Write the basis inventory XML to the basis-inventory file"""
1703
assert isinstance(xml, str), 'serialised xml must be bytestring.'
1704
path = self._basis_inventory_name()
1706
self._control_files.put(path, sio)
1708
def _create_basis_xml_from_inventory(self, revision_id, inventory):
1709
"""Create the text that will be saved in basis-inventory"""
1710
# TODO: jam 20070209 This should be redundant, as the revision_id
1711
# as all callers should have already converted the revision_id to
1713
inventory.revision_id = osutils.safe_revision_id(revision_id)
1714
return xml7.serializer_v7.write_inventory_to_string(inventory)
1716
def _cache_basis_inventory(self, new_revision):
1717
"""Cache new_revision as the basis inventory."""
1718
# TODO: this should allow the ready-to-use inventory to be passed in,
1719
# as commit already has that ready-to-use [while the format is the
1722
# this double handles the inventory - unpack and repack -
1723
# but is easier to understand. We can/should put a conditional
1724
# in here based on whether the inventory is in the latest format
1725
# - perhaps we should repack all inventories on a repository
1727
# the fast path is to copy the raw xml from the repository. If the
1728
# xml contains 'revision_id="', then we assume the right
1729
# revision_id is set. We must check for this full string, because a
1730
# root node id can legitimately look like 'revision_id' but cannot
1732
xml = self.branch.repository.get_inventory_xml(new_revision)
1733
firstline = xml.split('\n', 1)[0]
1734
if (not 'revision_id="' in firstline or
1735
'format="7"' not in firstline):
1736
inv = self.branch.repository.deserialise_inventory(
1738
xml = self._create_basis_xml_from_inventory(new_revision, inv)
1739
self._write_basis_inventory(xml)
1740
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1743
def read_basis_inventory(self):
1744
"""Read the cached basis inventory."""
1745
path = self._basis_inventory_name()
1746
return self._control_files.get(path).read()
1749
def read_working_inventory(self):
1750
"""Read the working inventory.
1752
:raises errors.InventoryModified: read_working_inventory will fail
1753
when the current in memory inventory has been modified.
1755
# conceptually this should be an implementation detail of the tree.
1756
# XXX: Deprecate this.
1757
# ElementTree does its own conversion from UTF-8, so open in
1759
if self._inventory_is_modified:
1760
raise errors.InventoryModified(self)
1761
result = self._deserialize(self._control_files.get('inventory'))
1762
self._set_inventory(result, dirty=False)
1765
@needs_tree_write_lock
1766
def remove(self, files, verbose=False, to_file=None):
1767
"""Remove nominated files from the working inventory..
1769
This does not remove their text. This does not run on XXX on what? RBC
1771
TODO: Refuse to remove modified files unless --force is given?
1773
TODO: Do something useful with directories.
1775
TODO: Should this remove the text or not? Tough call; not
1776
removing may be useful and the user can just use use rm, and
1777
is the opposite of add. Removing it is consistent with most
1778
other tools. Maybe an option.
1780
## TODO: Normalize names
1781
## TODO: Remove nested loops; better scalability
1782
if isinstance(files, basestring):
1785
inv = self.inventory
1787
# do this before any modifications
1789
fid = inv.path2id(f)
1791
note("%s is not versioned."%f)
1794
# having remove it, it must be either ignored or unknown
1795
if self.is_ignored(f):
1799
textui.show_status(new_status, inv[fid].kind, f,
1803
self._write_inventory(inv)
1805
@needs_tree_write_lock
1806
def revert(self, filenames, old_tree=None, backups=True,
1807
pb=DummyProgress(), report_changes=False):
1808
from bzrlib.conflicts import resolve
1809
if old_tree is None:
1810
old_tree = self.basis_tree()
1811
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1813
if not len(filenames):
1814
self.set_parent_ids(self.get_parent_ids()[:1])
1817
resolve(self, filenames, ignore_misses=True)
1820
def revision_tree(self, revision_id):
1821
"""See Tree.revision_tree.
1823
WorkingTree can supply revision_trees for the basis revision only
1824
because there is only one cached inventory in the bzr directory.
1826
if revision_id == self.last_revision():
1828
xml = self.read_basis_inventory()
1829
except errors.NoSuchFile:
1833
inv = xml7.serializer_v7.read_inventory_from_string(xml)
1834
# dont use the repository revision_tree api because we want
1835
# to supply the inventory.
1836
if inv.revision_id == revision_id:
1837
return revisiontree.RevisionTree(self.branch.repository,
1839
except errors.BadInventoryFormat:
1841
# raise if there was no inventory, or if we read the wrong inventory.
1842
raise errors.NoSuchRevisionInTree(self, revision_id)
1844
# XXX: This method should be deprecated in favour of taking in a proper
1845
# new Inventory object.
1846
@needs_tree_write_lock
1847
def set_inventory(self, new_inventory_list):
1848
from bzrlib.inventory import (Inventory,
1853
inv = Inventory(self.get_root_id())
1854
for path, file_id, parent, kind in new_inventory_list:
1855
name = os.path.basename(path)
1858
# fixme, there should be a factory function inv,add_??
1859
if kind == 'directory':
1860
inv.add(InventoryDirectory(file_id, name, parent))
1861
elif kind == 'file':
1862
inv.add(InventoryFile(file_id, name, parent))
1863
elif kind == 'symlink':
1864
inv.add(InventoryLink(file_id, name, parent))
1866
raise errors.BzrError("unknown kind %r" % kind)
1867
self._write_inventory(inv)
1869
@needs_tree_write_lock
1870
def set_root_id(self, file_id):
1871
"""Set the root id for this tree."""
1874
symbol_versioning.warn(symbol_versioning.zero_twelve
1875
% 'WorkingTree.set_root_id with fileid=None',
1880
file_id = osutils.safe_file_id(file_id)
1881
self._set_root_id(file_id)
1883
def _set_root_id(self, file_id):
1884
"""Set the root id for this tree, in a format specific manner.
1886
:param file_id: The file id to assign to the root. It must not be
1887
present in the current inventory or an error will occur. It must
1888
not be None, but rather a valid file id.
1890
inv = self._inventory
1891
orig_root_id = inv.root.file_id
1892
# TODO: it might be nice to exit early if there was nothing
1893
# to do, saving us from trigger a sync on unlock.
1894
self._inventory_is_modified = True
1895
# we preserve the root inventory entry object, but
1896
# unlinkit from the byid index
1897
del inv._byid[inv.root.file_id]
1898
inv.root.file_id = file_id
1899
# and link it into the index with the new changed id.
1900
inv._byid[inv.root.file_id] = inv.root
1901
# and finally update all children to reference the new id.
1902
# XXX: this should be safe to just look at the root.children
1903
# list, not the WHOLE INVENTORY.
1906
if entry.parent_id == orig_root_id:
1907
entry.parent_id = inv.root.file_id
1910
"""See Branch.unlock.
1912
WorkingTree locking just uses the Branch locking facilities.
1913
This is current because all working trees have an embedded branch
1914
within them. IF in the future, we were to make branch data shareable
1915
between multiple working trees, i.e. via shared storage, then we
1916
would probably want to lock both the local tree, and the branch.
1918
raise NotImplementedError(self.unlock)
1921
"""Update a working tree along its branch.
1923
This will update the branch if its bound too, which means we have
1924
multiple trees involved:
1926
- The new basis tree of the master.
1927
- The old basis tree of the branch.
1928
- The old basis tree of the working tree.
1929
- The current working tree state.
1931
Pathologically, all three may be different, and non-ancestors of each
1932
other. Conceptually we want to:
1934
- Preserve the wt.basis->wt.state changes
1935
- Transform the wt.basis to the new master basis.
1936
- Apply a merge of the old branch basis to get any 'local' changes from
1938
- Restore the wt.basis->wt.state changes.
1940
There isn't a single operation at the moment to do that, so we:
1941
- Merge current state -> basis tree of the master w.r.t. the old tree
1943
- Do a 'normal' merge of the old branch basis if it is relevant.
1945
if self.branch.get_master_branch() is not None:
1947
update_branch = True
1949
self.lock_tree_write()
1950
update_branch = False
1953
old_tip = self.branch.update()
1956
return self._update_tree(old_tip)
1960
@needs_tree_write_lock
1961
def _update_tree(self, old_tip=None):
1962
"""Update a tree to the master branch.
1964
:param old_tip: if supplied, the previous tip revision the branch,
1965
before it was changed to the master branch's tip.
1967
# here if old_tip is not None, it is the old tip of the branch before
1968
# it was updated from the master branch. This should become a pending
1969
# merge in the working tree to preserve the user existing work. we
1970
# cant set that until we update the working trees last revision to be
1971
# one from the new branch, because it will just get absorbed by the
1972
# parent de-duplication logic.
1974
# We MUST save it even if an error occurs, because otherwise the users
1975
# local work is unreferenced and will appear to have been lost.
1979
last_rev = self.get_parent_ids()[0]
1982
if last_rev != self.branch.last_revision():
1983
# merge tree state up to new branch tip.
1984
basis = self.basis_tree()
1987
to_tree = self.branch.basis_tree()
1988
if basis.inventory.root is None:
1989
self.set_root_id(to_tree.inventory.root.file_id)
1991
result += merge.merge_inner(
1998
# TODO - dedup parents list with things merged by pull ?
1999
# reuse the tree we've updated to to set the basis:
2000
parent_trees = [(self.branch.last_revision(), to_tree)]
2001
merges = self.get_parent_ids()[1:]
2002
# Ideally we ask the tree for the trees here, that way the working
2003
# tree can decide whether to give us teh entire tree or give us a
2004
# lazy initialised tree. dirstate for instance will have the trees
2005
# in ram already, whereas a last-revision + basis-inventory tree
2006
# will not, but also does not need them when setting parents.
2007
for parent in merges:
2008
parent_trees.append(
2009
(parent, self.branch.repository.revision_tree(parent)))
2010
if old_tip is not None:
2011
parent_trees.append(
2012
(old_tip, self.branch.repository.revision_tree(old_tip)))
2013
self.set_parent_trees(parent_trees)
2014
last_rev = parent_trees[0][0]
2016
# the working tree had the same last-revision as the master
2017
# branch did. We may still have pivot local work from the local
2018
# branch into old_tip:
2019
if old_tip is not None:
2020
self.add_parent_tree_id(old_tip)
2021
if old_tip and old_tip != last_rev:
2022
# our last revision was not the prior branch last revision
2023
# and we have converted that last revision to a pending merge.
2024
# base is somewhere between the branch tip now
2025
# and the now pending merge
2027
# Since we just modified the working tree and inventory, flush out
2028
# the current state, before we modify it again.
2029
# TODO: jam 20070214 WorkingTree3 doesn't require this, dirstate
2030
# requires it only because TreeTransform directly munges the
2031
# inventory and calls tree._write_inventory(). Ultimately we
2032
# should be able to remove this extra flush.
2034
from bzrlib.revision import common_ancestor
2036
base_rev_id = common_ancestor(self.branch.last_revision(),
2038
self.branch.repository)
2039
except errors.NoCommonAncestor:
2041
base_tree = self.branch.repository.revision_tree(base_rev_id)
2042
other_tree = self.branch.repository.revision_tree(old_tip)
2043
result += merge.merge_inner(
2050
def _write_hashcache_if_dirty(self):
2051
"""Write out the hashcache if it is dirty."""
2052
if self._hashcache.needs_write:
2054
self._hashcache.write()
2056
if e.errno not in (errno.EPERM, errno.EACCES):
2058
# TODO: jam 20061219 Should this be a warning? A single line
2059
# warning might be sufficient to let the user know what
2061
mutter('Could not write hashcache for %s\nError: %s',
2062
self._hashcache.cache_file_name(), e)
2064
@needs_tree_write_lock
2065
def _write_inventory(self, inv):
2066
"""Write inventory as the current inventory."""
2067
self._set_inventory(inv, dirty=True)
2070
def set_conflicts(self, arg):
2071
raise errors.UnsupportedOperation(self.set_conflicts, self)
2073
def add_conflicts(self, arg):
2074
raise errors.UnsupportedOperation(self.add_conflicts, self)
2077
def conflicts(self):
2078
conflicts = _mod_conflicts.ConflictList()
2079
for conflicted in self._iter_conflicts():
2082
if file_kind(self.abspath(conflicted)) != "file":
2084
except errors.NoSuchFile:
2087
for suffix in ('.THIS', '.OTHER'):
2089
kind = file_kind(self.abspath(conflicted+suffix))
2092
except errors.NoSuchFile:
2096
ctype = {True: 'text conflict', False: 'contents conflict'}[text]
2097
conflicts.append(_mod_conflicts.Conflict.factory(ctype,
2099
file_id=self.path2id(conflicted)))
2102
def walkdirs(self, prefix=""):
2103
"""Walk the directories of this tree.
2105
This API returns a generator, which is only valid during the current
2106
tree transaction - within a single lock_read or lock_write duration.
2108
If the tree is not locked, it may cause an error to be raised, depending
2109
on the tree implementation.
2111
disk_top = self.abspath(prefix)
2112
if disk_top.endswith('/'):
2113
disk_top = disk_top[:-1]
2114
top_strip_len = len(disk_top) + 1
2115
inventory_iterator = self._walkdirs(prefix)
2116
disk_iterator = osutils.walkdirs(disk_top, prefix)
2118
current_disk = disk_iterator.next()
2119
disk_finished = False
2121
if e.errno != errno.ENOENT:
2124
disk_finished = True
2126
current_inv = inventory_iterator.next()
2127
inv_finished = False
2128
except StopIteration:
2131
while not inv_finished or not disk_finished:
2132
if not disk_finished:
2133
# strip out .bzr dirs
2134
if current_disk[0][1][top_strip_len:] == '':
2135
# osutils.walkdirs can be made nicer -
2136
# yield the path-from-prefix rather than the pathjoined
2138
bzrdir_loc = bisect_left(current_disk[1], ('.bzr', '.bzr'))
2139
if current_disk[1][bzrdir_loc][0] == '.bzr':
2140
# we dont yield the contents of, or, .bzr itself.
2141
del current_disk[1][bzrdir_loc]
2143
# everything is unknown
2146
# everything is missing
2149
direction = cmp(current_inv[0][0], current_disk[0][0])
2151
# disk is before inventory - unknown
2152
dirblock = [(relpath, basename, kind, stat, None, None) for
2153
relpath, basename, kind, stat, top_path in current_disk[1]]
2154
yield (current_disk[0][0], None), dirblock
2156
current_disk = disk_iterator.next()
2157
except StopIteration:
2158
disk_finished = True
2160
# inventory is before disk - missing.
2161
dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
2162
for relpath, basename, dkind, stat, fileid, kind in
2164
yield (current_inv[0][0], current_inv[0][1]), dirblock
2166
current_inv = inventory_iterator.next()
2167
except StopIteration:
2170
# versioned present directory
2171
# merge the inventory and disk data together
2173
for relpath, subiterator in itertools.groupby(sorted(
2174
current_inv[1] + current_disk[1], key=operator.itemgetter(0)), operator.itemgetter(1)):
2175
path_elements = list(subiterator)
2176
if len(path_elements) == 2:
2177
inv_row, disk_row = path_elements
2178
# versioned, present file
2179
dirblock.append((inv_row[0],
2180
inv_row[1], disk_row[2],
2181
disk_row[3], inv_row[4],
2183
elif len(path_elements[0]) == 5:
2185
dirblock.append((path_elements[0][0],
2186
path_elements[0][1], path_elements[0][2],
2187
path_elements[0][3], None, None))
2188
elif len(path_elements[0]) == 6:
2189
# versioned, absent file.
2190
dirblock.append((path_elements[0][0],
2191
path_elements[0][1], 'unknown', None,
2192
path_elements[0][4], path_elements[0][5]))
2194
raise NotImplementedError('unreachable code')
2195
yield current_inv[0], dirblock
2197
current_inv = inventory_iterator.next()
2198
except StopIteration:
2201
current_disk = disk_iterator.next()
2202
except StopIteration:
2203
disk_finished = True
2205
def _walkdirs(self, prefix=""):
2206
_directory = 'directory'
2207
# get the root in the inventory
2208
inv = self.inventory
2209
top_id = inv.path2id(prefix)
2213
pending = [(prefix, '', _directory, None, top_id, None)]
2216
currentdir = pending.pop()
2217
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-id, 5-kind
2218
top_id = currentdir[4]
2220
relroot = currentdir[0] + '/'
2223
# FIXME: stash the node in pending
2225
for name, child in entry.sorted_children():
2226
dirblock.append((relroot + name, name, child.kind, None,
2227
child.file_id, child.kind
2229
yield (currentdir[0], entry.file_id), dirblock
2230
# push the user specified dirs from dirblock
2231
for dir in reversed(dirblock):
2232
if dir[2] == _directory:
2236
class WorkingTree2(WorkingTree):
2237
"""This is the Format 2 working tree.
2239
This was the first weave based working tree.
2240
- uses os locks for locking.
2241
- uses the branch last-revision.
2244
def lock_tree_write(self):
2245
"""See WorkingTree.lock_tree_write().
2247
In Format2 WorkingTrees we have a single lock for the branch and tree
2248
so lock_tree_write() degrades to lock_write().
2250
self.branch.lock_write()
2252
return self._control_files.lock_write()
2254
self.branch.unlock()
2258
# we share control files:
2259
if self._control_files._lock_count == 3:
2260
# _inventory_is_modified is always False during a read lock.
2261
if self._inventory_is_modified:
2263
self._write_hashcache_if_dirty()
2265
# reverse order of locking.
2267
return self._control_files.unlock()
2269
self.branch.unlock()
2272
class WorkingTree3(WorkingTree):
2273
"""This is the Format 3 working tree.
2275
This differs from the base WorkingTree by:
2276
- having its own file lock
2277
- having its own last-revision property.
2279
This is new in bzr 0.8
2283
def _last_revision(self):
2284
"""See Mutable.last_revision."""
2286
return osutils.safe_revision_id(
2287
self._control_files.get('last-revision').read())
2288
except errors.NoSuchFile:
2291
def _change_last_revision(self, revision_id):
2292
"""See WorkingTree._change_last_revision."""
2293
if revision_id is None or revision_id == NULL_REVISION:
2295
self._control_files._transport.delete('last-revision')
2296
except errors.NoSuchFile:
2300
self._control_files.put_bytes('last-revision', revision_id)
2303
@needs_tree_write_lock
2304
def set_conflicts(self, conflicts):
2305
self._put_rio('conflicts', conflicts.to_stanzas(),
2308
@needs_tree_write_lock
2309
def add_conflicts(self, new_conflicts):
2310
conflict_set = set(self.conflicts())
2311
conflict_set.update(set(list(new_conflicts)))
2312
self.set_conflicts(_mod_conflicts.ConflictList(sorted(conflict_set,
2313
key=_mod_conflicts.Conflict.sort_key)))
2316
def conflicts(self):
2318
confile = self._control_files.get('conflicts')
2319
except errors.NoSuchFile:
2320
return _mod_conflicts.ConflictList()
2322
if confile.next() != CONFLICT_HEADER_1 + '\n':
2323
raise errors.ConflictFormatError()
2324
except StopIteration:
2325
raise errors.ConflictFormatError()
2326
return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
2329
if self._control_files._lock_count == 1:
2330
# _inventory_is_modified is always False during a read lock.
2331
if self._inventory_is_modified:
2333
self._write_hashcache_if_dirty()
2334
# reverse order of locking.
2336
return self._control_files.unlock()
2338
self.branch.unlock()
2341
class WorkingTreeAB1(WorkingTree3):
2343
def _serialize(self, inventory, out_file):
2344
xml7.serializer_v7.write_inventory(self._inventory, out_file)
2346
def _deserialize(selt, in_file):
2347
return xml7.serializer_v7.read_inventory(in_file)
2349
def _comparison_data(self, entry, path):
2350
kind, executable, stat_value = \
2351
WorkingTree3._comparison_data(self, entry, path)
2352
if kind == 'directory' and entry.kind == 'tree-reference':
2353
kind = 'tree-reference'
2354
return kind, executable, stat_value
2356
def kind(self, file_id):
2357
kind = WorkingTree3.kind(self, file_id)
2358
if kind == 'directory':
2359
entry = self.inventory[file_id]
2360
if entry.kind == 'tree-reference':
2361
kind = 'tree-reference'
2364
def add_reference(self, sub_tree):
2365
return self._add_reference(sub_tree)
2367
def get_nested_tree(self, entry, path=None):
2369
path = self.id2path(entry.file_id)
2370
return WorkingTree.open(self.abspath(path))
2372
def get_reference_revision(self, entry, path=None):
2373
return self.get_nested_tree(entry, path).last_revision()
2376
def get_conflicted_stem(path):
2377
for suffix in _mod_conflicts.CONFLICT_SUFFIXES:
2378
if path.endswith(suffix):
2379
return path[:-len(suffix)]
2382
@deprecated_function(zero_eight)
2383
def is_control_file(filename):
2384
"""See WorkingTree.is_control_filename(filename)."""
2385
## FIXME: better check
2386
filename = normpath(filename)
2387
while filename != '':
2388
head, tail = os.path.split(filename)
2389
## mutter('check %r for control file' % ((head, tail),))
2392
if filename == head:
2398
class WorkingTreeFormat(object):
2399
"""An encapsulation of the initialization and open routines for a format.
2401
Formats provide three things:
2402
* An initialization routine,
2406
Formats are placed in an dict by their format string for reference
2407
during workingtree opening. Its not required that these be instances, they
2408
can be classes themselves with class methods - it simply depends on
2409
whether state is needed for a given format or not.
2411
Once a format is deprecated, just deprecate the initialize and open
2412
methods on the format class. Do not deprecate the object, as the
2413
object will be created every time regardless.
2416
_default_format = None
2417
"""The default format used for new trees."""
2420
"""The known formats."""
2422
requires_rich_root = False
2425
def find_format(klass, a_bzrdir):
2426
"""Return the format for the working tree object in a_bzrdir."""
2428
transport = a_bzrdir.get_workingtree_transport(None)
2429
format_string = transport.get("format").read()
2430
return klass._formats[format_string]
2431
except errors.NoSuchFile:
2432
raise errors.NoWorkingTree(base=transport.base)
2434
raise errors.UnknownFormatError(format=format_string)
2436
def __eq__(self, other):
2437
return self.__class__ is other.__class__
2439
def __ne__(self, other):
2440
return not (self == other)
2443
def get_default_format(klass):
2444
"""Return the current default format."""
2445
return klass._default_format
2447
def get_format_string(self):
2448
"""Return the ASCII format string that identifies this format."""
2449
raise NotImplementedError(self.get_format_string)
2451
def get_format_description(self):
2452
"""Return the short description for this format."""
2453
raise NotImplementedError(self.get_format_description)
2455
def is_supported(self):
2456
"""Is this format supported?
2458
Supported formats can be initialized and opened.
2459
Unsupported formats may not support initialization or committing or
2460
some other features depending on the reason for not being supported.
2465
def register_format(klass, format):
2466
klass._formats[format.get_format_string()] = format
2469
def set_default_format(klass, format):
2470
klass._default_format = format
2473
def unregister_format(klass, format):
2474
assert klass._formats[format.get_format_string()] is format
2475
del klass._formats[format.get_format_string()]
2479
class WorkingTreeFormat2(WorkingTreeFormat):
2480
"""The second working tree format.
2482
This format modified the hash cache from the format 1 hash cache.
2485
def get_format_description(self):
2486
"""See WorkingTreeFormat.get_format_description()."""
2487
return "Working tree format 2"
2489
def stub_initialize_remote(self, control_files):
2490
"""As a special workaround create critical control files for a remote working tree
2492
This ensures that it can later be updated and dealt with locally,
2493
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2494
no working tree. (See bug #43064).
2498
xml5.serializer_v5.write_inventory(inv, sio)
2500
control_files.put('inventory', sio)
2502
control_files.put_bytes('pending-merges', '')
2505
def initialize(self, a_bzrdir, revision_id=None):
2506
"""See WorkingTreeFormat.initialize()."""
2507
if not isinstance(a_bzrdir.transport, LocalTransport):
2508
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2509
branch = a_bzrdir.open_branch()
2510
if revision_id is not None:
2511
revision_id = osutils.safe_revision_id(revision_id)
2514
revision_history = branch.revision_history()
2516
position = revision_history.index(revision_id)
2518
raise errors.NoSuchRevision(branch, revision_id)
2519
branch.set_revision_history(revision_history[:position + 1])
2522
revision = branch.last_revision()
2524
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2530
basis_tree = branch.repository.revision_tree(revision)
2531
if basis_tree.inventory.root is not None:
2532
wt.set_root_id(basis_tree.inventory.root.file_id)
2533
# set the parent list and cache the basis tree.
2534
wt.set_parent_trees([(revision, basis_tree)])
2535
transform.build_tree(basis_tree, wt)
2539
super(WorkingTreeFormat2, self).__init__()
2540
self._matchingbzrdir = bzrdir.BzrDirFormat6()
2542
def open(self, a_bzrdir, _found=False):
2543
"""Return the WorkingTree object for a_bzrdir
2545
_found is a private parameter, do not use it. It is used to indicate
2546
if format probing has already been done.
2549
# we are being called directly and must probe.
2550
raise NotImplementedError
2551
if not isinstance(a_bzrdir.transport, LocalTransport):
2552
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2553
return WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2559
class WorkingTreeFormat3(WorkingTreeFormat):
2560
"""The second working tree format updated to record a format marker.
2563
- exists within a metadir controlling .bzr
2564
- includes an explicit version marker for the workingtree control
2565
files, separate from the BzrDir format
2566
- modifies the hash cache format
2568
- uses a LockDir to guard access for writes.
2571
def get_format_string(self):
2572
"""See WorkingTreeFormat.get_format_string()."""
2573
return "Bazaar-NG Working Tree format 3"
2575
def get_format_description(self):
2576
"""See WorkingTreeFormat.get_format_description()."""
2577
return "Working tree format 3"
2579
_lock_file_name = 'lock'
2580
_lock_class = LockDir
2582
_tree_class = WorkingTree3
2584
def __get_matchingbzrdir(self):
2585
return bzrdir.BzrDirMetaFormat1()
2587
_matchingbzrdir = property(__get_matchingbzrdir)
2589
def _open_control_files(self, a_bzrdir):
2590
transport = a_bzrdir.get_workingtree_transport(None)
2591
return LockableFiles(transport, self._lock_file_name,
2594
def initialize(self, a_bzrdir, revision_id=None):
2595
"""See WorkingTreeFormat.initialize().
2597
revision_id allows creating a working tree at a different
2598
revision than the branch is at.
2600
if not isinstance(a_bzrdir.transport, LocalTransport):
2601
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2602
transport = a_bzrdir.get_workingtree_transport(self)
2603
control_files = self._open_control_files(a_bzrdir)
2604
control_files.create_lock()
2605
control_files.lock_write()
2606
control_files.put_utf8('format', self.get_format_string())
2607
branch = a_bzrdir.open_branch()
2608
if revision_id is None:
2609
revision_id = branch.last_revision()
2611
revision_id = osutils.safe_revision_id(revision_id)
2612
# WorkingTree3 can handle an inventory which has a unique root id.
2613
# as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
2614
# those trees. And because there isn't a format bump inbetween, we
2615
# are maintaining compatibility with older clients.
2616
# inv = Inventory(root_id=gen_root_id())
2617
inv = self._initial_inventory()
2618
wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
2624
_control_files=control_files)
2625
wt.lock_tree_write()
2627
basis_tree = branch.repository.revision_tree(revision_id)
2628
# only set an explicit root id if there is one to set.
2629
if basis_tree.inventory.root is not None:
2630
wt.set_root_id(basis_tree.inventory.root.file_id)
2631
if revision_id == NULL_REVISION:
2632
wt.set_parent_trees([])
2634
wt.set_parent_trees([(revision_id, basis_tree)])
2635
transform.build_tree(basis_tree, wt)
2637
# Unlock in this order so that the unlock-triggers-flush in
2638
# WorkingTree is given a chance to fire.
2639
control_files.unlock()
2643
def _initial_inventory(self):
2647
super(WorkingTreeFormat3, self).__init__()
2649
def open(self, a_bzrdir, _found=False):
2650
"""Return the WorkingTree object for a_bzrdir
2652
_found is a private parameter, do not use it. It is used to indicate
2653
if format probing has already been done.
2656
# we are being called directly and must probe.
2657
raise NotImplementedError
2658
if not isinstance(a_bzrdir.transport, LocalTransport):
2659
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2660
return self._open(a_bzrdir, self._open_control_files(a_bzrdir))
2662
def _open(self, a_bzrdir, control_files):
2663
"""Open the tree itself.
2665
:param a_bzrdir: the dir for the tree.
2666
:param control_files: the control files for the tree.
2668
return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
2672
_control_files=control_files)
2675
return self.get_format_string()
2678
class WorkingTreeFormatAB1(WorkingTreeFormat3):
2679
"""Working tree format that supports unique roots and nested trees"""
2681
_tree_class = WorkingTreeAB1
2683
requires_rich_root = True
2685
supports_tree_reference = True
2688
WorkingTreeFormat3.__init__(self)
2690
def __get_matchingbzrdir(self):
2691
return bzrdir.format_registry.make_bzrdir('experimental-knit3')
2693
_matchingbzrdir = property(__get_matchingbzrdir)
2695
def get_format_string(self):
2696
"""See WorkingTreeFormat.get_format_string()."""
2697
return "Bazaar-NG Working Tree format AB1"
2699
def get_format_description(self):
2700
"""See WorkingTreeFormat.get_format_description()."""
2701
return "Working tree format 4"
2703
def _initial_inventory(self):
2704
return Inventory(root_id=generate_ids.gen_root_id())
2706
# formats which have no format string are not discoverable
2707
# and not independently creatable, so are not registered.
2708
__default_format = WorkingTreeFormat3()
2709
## __default_format = WorkingTreeFormat4()
2710
WorkingTreeFormat.register_format(__default_format)
2711
WorkingTreeFormat.register_format(WorkingTreeFormat4())
2712
WorkingTreeFormat.register_format(WorkingTreeFormatAB1())
2713
## WorkingTreeFormat.register_format(WorkingTreeFormat3())
2714
WorkingTreeFormat.set_default_format(__default_format)
2715
# formats which have no format string are not discoverable
2716
# and not independently creatable, so are not registered.
2717
_legacy_formats = [WorkingTreeFormat2(),
2721
class WorkingTreeTestProviderAdapter(object):
2722
"""A tool to generate a suite testing multiple workingtree formats at once.
2724
This is done by copying the test once for each transport and injecting
2725
the transport_server, transport_readonly_server, and workingtree_format
2726
classes into each copy. Each copy is also given a new id() to make it
2730
def __init__(self, transport_server, transport_readonly_server, formats):
2731
self._transport_server = transport_server
2732
self._transport_readonly_server = transport_readonly_server
2733
self._formats = formats
2735
def _clone_test(self, test, bzrdir_format, workingtree_format, variation):
2736
"""Clone test for adaption."""
2737
new_test = deepcopy(test)
2738
new_test.transport_server = self._transport_server
2739
new_test.transport_readonly_server = self._transport_readonly_server
2740
new_test.bzrdir_format = bzrdir_format
2741
new_test.workingtree_format = workingtree_format
2742
def make_new_test_id():
2743
new_id = "%s(%s)" % (test.id(), variation)
2744
return lambda: new_id
2745
new_test.id = make_new_test_id()
2748
def adapt(self, test):
2749
from bzrlib.tests import TestSuite
2750
result = TestSuite()
2751
for workingtree_format, bzrdir_format in self._formats:
2752
new_test = self._clone_test(
2755
workingtree_format, workingtree_format.__class__.__name__)
2756
result.addTest(new_test)