329
334
def _set_inventory(self, inv):
330
335
assert inv.root is not None
331
336
self._inventory = inv
332
self.path2id = self._inventory.path2id
334
def is_control_filename(self, filename):
335
"""True if filename is the name of a control file in this tree.
337
:param filename: A filename within the tree. This is a relative path
338
from the root of this tree.
340
This is true IF and ONLY IF the filename is part of the meta data
341
that bzr controls in this tree. I.E. a random .bzr directory placed
342
on disk will not be a control file for this tree.
344
return self.bzrdir.is_control_filename(filename)
347
339
def open(path=None, _unsupported=False):
416
408
xml = self.read_basis_inventory()
417
inv = xml5.serializer_v5.read_inventory_from_string(xml)
418
inv.root.revision = revision_id
421
if inv is not None and inv.revision_id == revision_id:
422
return bzrlib.tree.RevisionTree(self.branch.repository, inv,
409
inv = xml6.serializer_v6.read_inventory_from_string(xml)
410
if inv is not None and inv.revision_id == revision_id:
411
return bzrlib.tree.RevisionTree(self.branch.repository,
413
except (NoSuchFile, errors.BadInventoryFormat):
424
415
# No cached copy available, retrieve from the repository.
425
416
# FIXME? RBC 20060403 should we cache the inventory locally
426
417
# at this point ?
556
547
merge.transform_tree(tree, self)
557
548
tree.set_parent_ids([revision_id])
560
def commit(self, message=None, revprops=None, *args, **kwargs):
561
# avoid circular imports
562
from bzrlib.commit import Commit
565
if not 'branch-nick' in revprops:
566
revprops['branch-nick'] = self.branch.nick
567
# args for wt.commit start at message from the Commit.commit method,
568
# but with branch a kwarg now, passing in args as is results in the
569
#message being used for the branch
570
args = (DEPRECATED_PARAMETER, message, ) + args
571
committed_id = Commit().commit( working_tree=self, revprops=revprops,
575
550
def id2abspath(self, file_id):
576
551
return self.abspath(self.id2path(file_id))
615
590
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
617
592
@needs_write_lock
618
def add(self, files, ids=None):
619
"""Make files versioned.
621
Note that the command line normally calls smart_add instead,
622
which can automatically recurse.
624
This adds the files to the inventory, so that they will be
625
recorded by the next commit.
628
List of paths to add, relative to the base of the tree.
631
If set, use these instead of automatically generated ids.
632
Must be the same length as the list of files, but may
633
contain None for ids that are to be autogenerated.
635
TODO: Perhaps have an option to add the ids even if the files do
638
TODO: Perhaps callback with the ids and paths as they're added.
593
def _add(self, files, ids, kinds):
594
"""See MutableTree._add."""
640
595
# TODO: Re-adding a file that is removed in the working copy
641
596
# should probably put it back with the previous ID.
642
if isinstance(files, basestring):
643
assert(ids is None or isinstance(ids, basestring))
649
ids = [None] * len(files)
651
assert(len(ids) == len(files))
597
# the read and write working inventory should not occur in this
598
# function - they should be part of lock_write and unlock.
653
599
inv = self.read_working_inventory()
654
for f,file_id in zip(files, ids):
655
if self.is_control_filename(f):
656
raise errors.ForbiddenControlFileError(filename=f)
661
raise BzrError("cannot add top-level %r" % f)
663
fullpath = normpath(self.abspath(f))
665
kind = file_kind(fullpath)
667
if e.errno == errno.ENOENT:
668
raise NoSuchFile(fullpath)
669
if not InventoryEntry.versionable_kind(kind):
670
raise errors.BadFileKindError(filename=f, kind=kind)
600
for f, file_id, kind in zip(files, ids, kinds):
601
assert kind is not None
671
602
if file_id is None:
672
603
inv.add_path(f, kind=kind)
674
605
inv.add_path(f, kind=kind, file_id=file_id)
676
606
self._write_inventory(inv)
608
@needs_tree_write_lock
609
def _gather_kinds(self, files, kinds):
610
"""See MutableTree._gather_kinds."""
611
for pos, f in enumerate(files):
612
if kinds[pos] is None:
613
fullpath = normpath(self.abspath(f))
615
kinds[pos] = file_kind(fullpath)
617
if e.errno == errno.ENOENT:
618
raise NoSuchFile(fullpath)
678
620
@needs_write_lock
679
621
def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
680
622
"""Add revision_id as a parent.
741
683
return self.get_parent_ids()[1:]
685
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
686
"""Common ghost checking functionality from set_parent_*.
688
This checks that the left hand-parent exists if there are any
691
if len(revision_ids) > 0:
692
leftmost_id = revision_ids[0]
693
if (not allow_leftmost_as_ghost and not
694
self.branch.repository.has_revision(leftmost_id)):
695
raise errors.GhostRevisionUnusableHere(leftmost_id)
697
def _set_merges_from_parent_ids(self, parent_ids):
698
merges = parent_ids[1:]
699
self._control_files.put_utf8('pending-merges', '\n'.join(merges))
701
@needs_tree_write_lock
744
702
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
745
703
"""Set the parent ids to revision_ids.
753
711
:param revision_ids: The revision_ids to set as the parent ids of this
754
712
working tree. Any of these may be ghosts.
714
self._check_parents_for_ghosts(revision_ids,
715
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
756
717
if len(revision_ids) > 0:
757
leftmost_id = revision_ids[0]
758
if (not allow_leftmost_as_ghost and not
759
self.branch.repository.has_revision(leftmost_id)):
760
raise errors.GhostRevisionUnusableHere(leftmost_id)
761
self.set_last_revision(leftmost_id)
718
self.set_last_revision(revision_ids[0])
763
720
self.set_last_revision(None)
764
merges = revision_ids[1:]
765
self._control_files.put_utf8('pending-merges', '\n'.join(merges))
722
self._set_merges_from_parent_ids(revision_ids)
724
@needs_tree_write_lock
768
725
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
769
"""Set the parents of the working tree.
726
"""See MutableTree.set_parent_trees."""
727
parent_ids = [rev for (rev, tree) in parents_list]
771
:param parents_list: A list of (revision_id, tree) tuples.
772
If tree is None, then that element is treated as an unreachable
773
parent tree - i.e. a ghost.
775
# parent trees are not used in current format trees, delegate to
777
self.set_parent_ids([rev for (rev, tree) in parents_list],
729
self._check_parents_for_ghosts(parent_ids,
778
730
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
732
if len(parent_ids) == 0:
733
leftmost_parent_id = None
734
leftmost_parent_tree = None
736
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
738
if self._change_last_revision(leftmost_parent_id):
739
if leftmost_parent_tree is None:
740
# If we don't have a tree, fall back to reading the
741
# parent tree from the repository.
742
self._cache_basis_inventory(leftmost_parent_id)
744
inv = leftmost_parent_tree.inventory
745
xml = self._create_basis_xml_from_inventory(
746
leftmost_parent_id, inv)
747
self._write_basis_inventory(xml)
748
self._set_merges_from_parent_ids(parent_ids)
750
@needs_tree_write_lock
781
751
def set_pending_merges(self, rev_list):
782
752
parents = self.get_parent_ids()
783
753
leftmost = parents[:1]
784
754
new_parents = leftmost + rev_list
785
755
self.set_parent_ids(new_parents)
757
@needs_tree_write_lock
788
758
def set_merge_modified(self, modified_hashes):
789
759
def iter_stanzas():
790
760
for file_id, hash in modified_hashes.iteritems():
791
761
yield Stanza(file_id=file_id, hash=hash)
792
762
self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
764
@needs_tree_write_lock
795
765
def _put_rio(self, filename, stanzas, header):
796
766
my_file = rio_file(stanzas, header)
797
767
self._control_files.put(filename, my_file)
769
@needs_write_lock # because merge pulls data into the branch.
800
770
def merge_from_branch(self, branch, to_revision=None):
801
771
"""Merge from a branch into this working tree.
860
830
merge_hashes[file_id] = hash
861
831
return merge_hashes
834
def mkdir(self, path, file_id=None):
835
"""See MutableTree.mkdir()."""
837
file_id = gen_file_id(os.path.basename(path))
838
os.mkdir(self.abspath(path))
839
self.add(path, file_id, 'directory')
863
842
def get_symlink_target(self, file_id):
864
843
return os.readlink(self.id2abspath(file_id))
1364
1354
return file_kind(self.id2abspath(file_id))
1366
1356
def last_revision(self):
1367
"""Return the last revision id of this working tree.
1369
In early branch formats this was the same as the branch last_revision,
1370
but that cannot be relied upon - for working tree operations,
1371
always use tree.last_revision(). This returns the left most parent id,
1372
or None if there are no parents.
1374
This was deprecated as of 0.11. Please use get_parent_ids instead.
1357
"""Return the last revision of the branch for this tree.
1359
This format tree does not support a separate marker for last-revision
1360
compared to the branch.
1362
See MutableTree.last_revision
1376
1364
return self._last_revision()
1429
1426
self.branch.set_revision_history([new_revision])
1429
def _write_basis_inventory(self, xml):
1430
"""Write the basis inventory XML to the basis-inventory file"""
1431
assert isinstance(xml, str), 'serialised xml must be bytestring.'
1432
path = self._basis_inventory_name()
1434
self._control_files.put(path, sio)
1436
def _create_basis_xml_from_inventory(self, revision_id, inventory):
1437
"""Create the text that will be saved in basis-inventory"""
1438
inventory.revision_id = revision_id
1439
return xml6.serializer_v6.write_inventory_to_string(inventory)
1432
1441
def _cache_basis_inventory(self, new_revision):
1433
1442
"""Cache new_revision as the basis inventory."""
1434
1443
# TODO: this should allow the ready-to-use inventory to be passed in,
1446
1455
# root node id can legitimately look like 'revision_id' but cannot
1447
1456
# contain a '"'.
1448
1457
xml = self.branch.repository.get_inventory_xml(new_revision)
1449
if not 'revision_id="' in xml.split('\n', 1)[0]:
1458
firstline = xml.split('\n', 1)[0]
1459
if (not 'revision_id="' in firstline or
1460
'format="6"' not in firstline):
1450
1461
inv = self.branch.repository.deserialise_inventory(
1451
1462
new_revision, xml)
1452
inv.revision_id = new_revision
1453
xml = xml5.serializer_v5.write_inventory_to_string(inv)
1454
assert isinstance(xml, str), 'serialised xml must be bytestring.'
1455
path = self._basis_inventory_name()
1457
self._control_files.put(path, sio)
1463
xml = self._create_basis_xml_from_inventory(new_revision, inv)
1464
self._write_basis_inventory(xml)
1458
1465
except (errors.NoSuchRevision, errors.RevisionNotPresent):