325
327
def _set_inventory(self, inv):
326
328
assert inv.root is not None
327
329
self._inventory = inv
328
self.path2id = self._inventory.path2id
330
def is_control_filename(self, filename):
331
"""True if filename is the name of a control file in this tree.
333
:param filename: A filename within the tree. This is a relative path
334
from the root of this tree.
336
This is true IF and ONLY IF the filename is part of the meta data
337
that bzr controls in this tree. I.E. a random .bzr directory placed
338
on disk will not be a control file for this tree.
340
return self.bzrdir.is_control_filename(filename)
343
332
def open(path=None, _unsupported=False):
545
534
transform_tree(tree, self)
546
535
tree.set_parent_ids([revision_id])
549
def commit(self, message=None, revprops=None, *args, **kwargs):
550
# avoid circular imports
551
from bzrlib.commit import Commit
554
if not 'branch-nick' in revprops:
555
revprops['branch-nick'] = self.branch.nick
556
# args for wt.commit start at message from the Commit.commit method,
557
# but with branch a kwarg now, passing in args as is results in the
558
#message being used for the branch
559
args = (DEPRECATED_PARAMETER, message, ) + args
560
committed_id = Commit().commit( working_tree=self, revprops=revprops,
564
537
def id2abspath(self, file_id):
565
538
return self.abspath(self.id2path(file_id))
604
577
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
606
579
@needs_write_lock
607
def add(self, files, ids=None):
608
"""Make files versioned.
610
Note that the command line normally calls smart_add instead,
611
which can automatically recurse.
613
This adds the files to the inventory, so that they will be
614
recorded by the next commit.
617
List of paths to add, relative to the base of the tree.
620
If set, use these instead of automatically generated ids.
621
Must be the same length as the list of files, but may
622
contain None for ids that are to be autogenerated.
624
TODO: Perhaps have an option to add the ids even if the files do
627
TODO: Perhaps callback with the ids and paths as they're added.
580
def _add(self, files, ids, kinds):
581
"""See MutableTree._add."""
629
582
# TODO: Re-adding a file that is removed in the working copy
630
583
# should probably put it back with the previous ID.
631
if isinstance(files, basestring):
632
assert(ids is None or isinstance(ids, basestring))
638
ids = [None] * len(files)
640
assert(len(ids) == len(files))
584
# the read and write working inventory should not occur in this
585
# function - they should be part of lock_write and unlock.
642
586
inv = self.read_working_inventory()
643
for f,file_id in zip(files, ids):
644
if self.is_control_filename(f):
645
raise errors.ForbiddenControlFileError(filename=f)
650
raise BzrError("cannot add top-level %r" % f)
652
fullpath = normpath(self.abspath(f))
654
kind = file_kind(fullpath)
656
if e.errno == errno.ENOENT:
657
raise NoSuchFile(fullpath)
658
if not InventoryEntry.versionable_kind(kind):
659
raise errors.BadFileKindError(filename=f, kind=kind)
587
for f, file_id, kind in zip(files, ids, kinds):
588
assert kind is not None
660
589
if file_id is None:
661
590
inv.add_path(f, kind=kind)
663
592
inv.add_path(f, kind=kind, file_id=file_id)
665
593
self._write_inventory(inv)
595
@needs_tree_write_lock
596
def _gather_kinds(self, files, kinds):
597
"""See MutableTree._gather_kinds."""
598
for pos, f in enumerate(files):
599
if kinds[pos] is None:
600
fullpath = normpath(self.abspath(f))
602
kinds[pos] = file_kind(fullpath)
604
if e.errno == errno.ENOENT:
605
raise NoSuchFile(fullpath)
667
607
@needs_write_lock
668
608
def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
669
609
"""Add revision_id as a parent.
730
670
return self.get_parent_ids()[1:]
672
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
673
"""Common ghost checking functionality from set_parent_*.
675
This checks that the left hand-parent exists if there are any
678
if len(revision_ids) > 0:
679
leftmost_id = revision_ids[0]
680
if (not allow_leftmost_as_ghost and not
681
self.branch.repository.has_revision(leftmost_id)):
682
raise errors.GhostRevisionUnusableHere(leftmost_id)
684
def _set_merges_from_parent_ids(self, parent_ids):
685
merges = parent_ids[1:]
686
self._control_files.put_utf8('pending-merges', '\n'.join(merges))
688
@needs_tree_write_lock
733
689
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
734
690
"""Set the parent ids to revision_ids.
742
698
:param revision_ids: The revision_ids to set as the parent ids of this
743
699
working tree. Any of these may be ghosts.
701
self._check_parents_for_ghosts(revision_ids,
702
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
745
704
if len(revision_ids) > 0:
746
leftmost_id = revision_ids[0]
747
if (not allow_leftmost_as_ghost and not
748
self.branch.repository.has_revision(leftmost_id)):
749
raise errors.GhostRevisionUnusableHere(leftmost_id)
750
self.set_last_revision(leftmost_id)
705
self.set_last_revision(revision_ids[0])
752
707
self.set_last_revision(None)
753
merges = revision_ids[1:]
754
self._control_files.put_utf8('pending-merges', '\n'.join(merges))
709
self._set_merges_from_parent_ids(revision_ids)
711
@needs_tree_write_lock
757
712
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
758
"""Set the parents of the working tree.
713
"""See MutableTree.set_parent_trees."""
714
parent_ids = [rev for (rev, tree) in parents_list]
760
:param parents_list: A list of (revision_id, tree) tuples.
761
If tree is None, then that element is treated as an unreachable
762
parent tree - i.e. a ghost.
764
# parent trees are not used in current format trees, delegate to
766
self.set_parent_ids([rev for (rev, tree) in parents_list],
716
self._check_parents_for_ghosts(parent_ids,
767
717
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
719
if len(parent_ids) == 0:
720
leftmost_parent_id = None
721
leftmost_parent_tree = None
723
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
725
if self._change_last_revision(leftmost_parent_id):
726
if leftmost_parent_tree is None:
727
# If we don't have a tree, fall back to reading the
728
# parent tree from the repository.
729
self._cache_basis_inventory(leftmost_parent_id)
731
inv = leftmost_parent_tree.inventory
732
xml = self._create_basis_xml_from_inventory(
733
leftmost_parent_id, inv)
734
self._write_basis_inventory(xml)
735
self._set_merges_from_parent_ids(parent_ids)
737
@needs_tree_write_lock
770
738
def set_pending_merges(self, rev_list):
771
739
parents = self.get_parent_ids()
772
740
leftmost = parents[:1]
773
741
new_parents = leftmost + rev_list
774
742
self.set_parent_ids(new_parents)
744
@needs_tree_write_lock
777
745
def set_merge_modified(self, modified_hashes):
778
746
def iter_stanzas():
779
747
for file_id, hash in modified_hashes.iteritems():
780
748
yield Stanza(file_id=file_id, hash=hash)
781
749
self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
751
@needs_tree_write_lock
784
752
def _put_rio(self, filename, stanzas, header):
785
753
my_file = rio_file(stanzas, header)
786
754
self._control_files.put(filename, my_file)
756
@needs_write_lock # because merge pulls data into the branch.
789
757
def merge_from_branch(self, branch, to_revision=None):
790
758
"""Merge from a branch into this working tree.
849
817
merge_hashes[file_id] = hash
850
818
return merge_hashes
821
def mkdir(self, path, file_id=None):
822
"""See MutableTree.mkdir()."""
824
file_id = gen_file_id(os.path.basename(path))
825
os.mkdir(self.abspath(path))
826
self.add(path, file_id, 'directory')
852
829
def get_symlink_target(self, file_id):
853
830
return os.readlink(self.id2abspath(file_id))
1352
1340
return file_kind(self.id2abspath(file_id))
1354
1342
def last_revision(self):
1355
"""Return the last revision id of this working tree.
1357
In early branch formats this was the same as the branch last_revision,
1358
but that cannot be relied upon - for working tree operations,
1359
always use tree.last_revision(). This returns the left most parent id,
1360
or None if there are no parents.
1362
This was deprecated as of 0.11. Please use get_parent_ids instead.
1343
"""Return the last revision of the branch for this tree.
1345
This format tree does not support a separate marker for last-revision
1346
compared to the branch.
1348
See MutableTree.last_revision
1364
1350
return self._last_revision()
1417
1412
self.branch.set_revision_history([new_revision])
1415
def _write_basis_inventory(self, xml):
1416
"""Write the basis inventory XML to the basis-inventory file"""
1417
assert isinstance(xml, str), 'serialised xml must be bytestring.'
1418
path = self._basis_inventory_name()
1420
self._control_files.put(path, sio)
1422
def _create_basis_xml_from_inventory(self, revision_id, inventory):
1423
"""Create the text that will be saved in basis-inventory"""
1424
inventory.revision_id = revision_id
1425
return bzrlib.xml6.serializer_v6.write_inventory_to_string(inventory)
1420
1427
def _cache_basis_inventory(self, new_revision):
1421
1428
"""Cache new_revision as the basis inventory."""
1422
1429
# TODO: this should allow the ready-to-use inventory to be passed in,
1434
1441
# root node id can legitimately look like 'revision_id' but cannot
1435
1442
# contain a '"'.
1436
1443
xml = self.branch.repository.get_inventory_xml(new_revision)
1437
if not 'revision_id="' in xml.split('\n', 1)[0]:
1444
firstline = xml.split('\n', 1)[0]
1445
if (not 'revision_id="' in firstline or
1446
'format="6"' not in firstline):
1438
1447
inv = self.branch.repository.deserialise_inventory(
1439
1448
new_revision, xml)
1440
inv.revision_id = new_revision
1441
xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1442
assert isinstance(xml, str), 'serialised xml must be bytestring.'
1443
path = self._basis_inventory_name()
1445
self._control_files.put(path, sio)
1449
xml = self._create_basis_xml_from_inventory(new_revision, inv)
1450
self._write_basis_inventory(xml)
1446
1451
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1528
1533
except NoSuchFile:
1531
inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
1532
# Fixup old inventory serialization that has a missing root
1533
# revision_id attribute.
1534
inv.root.revision = revision_id
1535
# dont use the repository revision_tree api because we want
1536
# to supply the inventory.
1537
if inv.revision_id == revision_id:
1538
return bzrlib.tree.RevisionTree(self.branch.repository,
1537
inv = bzrlib.xml6.serializer_v6.read_inventory_from_string(xml)
1538
# dont use the repository revision_tree api because we want
1539
# to supply the inventory.
1540
if inv.revision_id == revision_id:
1541
return bzrlib.tree.RevisionTree(self.branch.repository,
1543
except errors.BadInventoryFormat:
1540
1545
# raise if there was no inventory, or if we read the wrong inventory.
1541
1546
raise errors.NoSuchRevisionInTree(self, revision_id)
1543
1548
# XXX: This method should be deprecated in favour of taking in a proper
1544
1549
# new Inventory object.
1550
@needs_tree_write_lock
1546
1551
def set_inventory(self, new_inventory_list):
1547
1552
from bzrlib.inventory import (Inventory,
1548
1553
InventoryDirectory,