68
359
inv = self._inventory
69
360
for path, ie in inv.iter_entries():
70
if os.path.exists(self.abspath(path)):
361
if osutils.lexists(self.abspath(path)):
74
364
def __repr__(self):
75
365
return "<%s of %s>" % (self.__class__.__name__,
76
366
getattr(self, 'basedir', None))
80
368
def abspath(self, filename):
81
return os.path.join(self.basedir, filename)
369
return pathjoin(self.basedir, filename)
371
def basis_tree(self):
372
"""Return RevisionTree for the current last revision.
374
If the left most parent is a ghost then the returned tree will be an
375
empty tree - one obtained by calling repository.revision_tree(None).
378
revision_id = self.get_parent_ids()[0]
380
# no parents, return an empty revision tree.
381
# in the future this should return the tree for
382
# 'empty:' - the implicit root empty tree.
383
return self.branch.repository.revision_tree(None)
385
return self.revision_tree(revision_id)
386
except errors.NoSuchRevision:
388
# No cached copy available, retrieve from the repository.
389
# FIXME? RBC 20060403 should we cache the inventory locally
392
return self.branch.repository.revision_tree(revision_id)
393
except errors.RevisionNotPresent:
394
# the basis tree *may* be a ghost or a low level error may have
395
# occured. If the revision is present, its a problem, if its not
397
if self.branch.repository.has_revision(revision_id):
399
# the basis tree is a ghost so return an empty tree.
400
return self.branch.repository.revision_tree(None)
403
self._flush_ignore_list_cache()
406
@deprecated_method(zero_eight)
407
def create(branch, directory):
408
"""Create a workingtree for branch at directory.
410
If existing_directory already exists it must have a .bzr directory.
411
If it does not exist, it will be created.
413
This returns a new WorkingTree object for the new checkout.
415
TODO FIXME RBC 20060124 when we have checkout formats in place this
416
should accept an optional revisionid to checkout [and reject this if
417
checking out into the same dir as a pre-checkout-aware branch format.]
419
XXX: When BzrDir is present, these should be created through that
422
warnings.warn('delete WorkingTree.create', stacklevel=3)
423
transport = get_transport(directory)
424
if branch.bzrdir.root_transport.base == transport.base:
426
return branch.bzrdir.create_workingtree()
427
# different directory,
428
# create a branch reference
429
# and now a working tree.
430
raise NotImplementedError
433
@deprecated_method(zero_eight)
434
def create_standalone(directory):
435
"""Create a checkout and a branch and a repo at directory.
437
Directory must exist and be empty.
439
please use BzrDir.create_standalone_workingtree
441
return bzrdir.BzrDir.create_standalone_workingtree(directory)
443
def relpath(self, path):
444
"""Return the local path portion from a given path.
446
The path may be absolute or relative. If its a relative path it is
447
interpreted relative to the python current working directory.
449
return osutils.relpath(self.basedir, path)
83
451
def has_filename(self, filename):
84
return os.path.exists(self.abspath(filename))
452
return osutils.lexists(self.abspath(filename))
86
454
def get_file(self, file_id):
455
file_id = osutils.safe_file_id(file_id)
87
456
return self.get_file_byname(self.id2path(file_id))
458
def get_file_text(self, file_id):
459
file_id = osutils.safe_file_id(file_id)
460
return self.get_file(file_id).read()
89
462
def get_file_byname(self, filename):
90
463
return file(self.abspath(filename), 'rb')
466
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
467
"""See Tree.annotate_iter
469
This implementation will use the basis tree implementation if possible.
470
Lines not in the basis are attributed to CURRENT_REVISION
472
If there are pending merges, lines added by those merges will be
473
incorrectly attributed to CURRENT_REVISION (but after committing, the
474
attribution will be correct).
476
file_id = osutils.safe_file_id(file_id)
477
basis = self.basis_tree()
480
changes = self._iter_changes(basis, True, [self.id2path(file_id)],
481
require_versioned=True).next()
482
changed_content, kind = changes[2], changes[6]
483
if not changed_content:
484
return basis.annotate_iter(file_id)
488
if kind[0] != 'file':
491
old_lines = list(basis.annotate_iter(file_id))
493
for tree in self.branch.repository.revision_trees(
494
self.get_parent_ids()[1:]):
495
if file_id not in tree:
497
old.append(list(tree.annotate_iter(file_id)))
498
return annotate.reannotate(old, self.get_file(file_id).readlines(),
503
def _get_ancestors(self, default_revision):
504
ancestors = set([default_revision])
505
for parent_id in self.get_parent_ids():
506
ancestors.update(self.branch.repository.get_ancestry(
507
parent_id, topo_sorted=False))
510
def get_parent_ids(self):
511
"""See Tree.get_parent_ids.
513
This implementation reads the pending merges list and last_revision
514
value and uses that to decide what the parents list should be.
516
last_rev = _mod_revision.ensure_null(self._last_revision())
517
if _mod_revision.NULL_REVISION == last_rev:
522
merges_file = self._control_files.get('pending-merges')
523
except errors.NoSuchFile:
526
for l in merges_file.readlines():
527
revision_id = osutils.safe_revision_id(l.rstrip('\n'))
528
parents.append(revision_id)
532
def get_root_id(self):
533
"""Return the id of this trees root"""
534
return self._inventory.root.file_id
92
536
def _get_store_filename(self, file_id):
93
## XXX: badly named; this isn't in the store at all
94
return self.abspath(self.id2path(file_id))
537
## XXX: badly named; this is not in the store at all
538
file_id = osutils.safe_file_id(file_id)
539
return self.abspath(self.id2path(file_id))
542
def clone(self, to_bzrdir, revision_id=None):
543
"""Duplicate this working tree into to_bzr, including all state.
545
Specifically modified files are kept as modified, but
546
ignored and unknown files are discarded.
548
If you want to make a new line of development, see bzrdir.sprout()
551
If not None, the cloned tree will have its last revision set to
552
revision, and and difference between the source trees last revision
553
and this one merged in.
555
# assumes the target bzr dir format is compatible.
556
result = self._format.initialize(to_bzrdir)
557
self.copy_content_into(result, revision_id)
561
def copy_content_into(self, tree, revision_id=None):
562
"""Copy the current content and user files of this tree into tree."""
563
tree.set_root_id(self.get_root_id())
564
if revision_id is None:
565
merge.transform_tree(tree, self)
567
# TODO now merge from tree.last_revision to revision (to preserve
568
# user local changes)
569
merge.transform_tree(tree, self)
570
tree.set_parent_ids([revision_id])
572
def id2abspath(self, file_id):
573
file_id = osutils.safe_file_id(file_id)
574
return self.abspath(self.id2path(file_id))
97
576
def has_id(self, file_id):
98
577
# files that have been deleted are excluded
578
file_id = osutils.safe_file_id(file_id)
100
580
if not inv.has_id(file_id):
102
582
path = inv.id2path(file_id)
103
return os.path.exists(self.abspath(path))
583
return osutils.lexists(self.abspath(path))
585
def has_or_had_id(self, file_id):
586
file_id = osutils.safe_file_id(file_id)
587
if file_id == self.inventory.root.file_id:
589
return self.inventory.has_id(file_id)
106
591
__contains__ = has_id
109
593
def get_file_size(self, file_id):
110
# is this still called?
111
raise NotImplementedError()
114
def get_file_sha1(self, file_id):
115
path = self._inventory.id2path(file_id)
116
return self._hashcache.get_sha1(path)
119
def file_class(self, filename):
120
if self.path2id(filename):
122
elif self.is_ignored(filename):
128
def list_files(self):
129
"""Recursively list all files as (path, class, kind, id).
594
file_id = osutils.safe_file_id(file_id)
595
return os.path.getsize(self.id2abspath(file_id))
598
def get_file_sha1(self, file_id, path=None, stat_value=None):
599
file_id = osutils.safe_file_id(file_id)
601
path = self._inventory.id2path(file_id)
602
return self._hashcache.get_sha1(path, stat_value)
604
def get_file_mtime(self, file_id, path=None):
605
file_id = osutils.safe_file_id(file_id)
607
path = self.inventory.id2path(file_id)
608
return os.lstat(self.abspath(path)).st_mtime
610
if not supports_executable():
611
def is_executable(self, file_id, path=None):
612
file_id = osutils.safe_file_id(file_id)
613
return self._inventory[file_id].executable
615
def is_executable(self, file_id, path=None):
617
file_id = osutils.safe_file_id(file_id)
618
path = self.id2path(file_id)
619
mode = os.lstat(self.abspath(path)).st_mode
620
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
622
@needs_tree_write_lock
623
def _add(self, files, ids, kinds):
624
"""See MutableTree._add."""
625
# TODO: Re-adding a file that is removed in the working copy
626
# should probably put it back with the previous ID.
627
# the read and write working inventory should not occur in this
628
# function - they should be part of lock_write and unlock.
630
for f, file_id, kind in zip(files, ids, kinds):
631
assert kind is not None
633
inv.add_path(f, kind=kind)
635
file_id = osutils.safe_file_id(file_id)
636
inv.add_path(f, kind=kind, file_id=file_id)
637
self._inventory_is_modified = True
639
@needs_tree_write_lock
640
def _gather_kinds(self, files, kinds):
641
"""See MutableTree._gather_kinds."""
642
for pos, f in enumerate(files):
643
if kinds[pos] is None:
644
fullpath = normpath(self.abspath(f))
646
kinds[pos] = file_kind(fullpath)
648
if e.errno == errno.ENOENT:
649
raise errors.NoSuchFile(fullpath)
652
def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
653
"""Add revision_id as a parent.
655
This is equivalent to retrieving the current list of parent ids
656
and setting the list to its value plus revision_id.
658
:param revision_id: The revision id to add to the parent list. It may
659
be a ghost revision as long as its not the first parent to be added,
660
or the allow_leftmost_as_ghost parameter is set True.
661
:param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
663
parents = self.get_parent_ids() + [revision_id]
664
self.set_parent_ids(parents, allow_leftmost_as_ghost=len(parents) > 1
665
or allow_leftmost_as_ghost)
667
@needs_tree_write_lock
668
def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
669
"""Add revision_id, tree tuple as a parent.
671
This is equivalent to retrieving the current list of parent trees
672
and setting the list to its value plus parent_tuple. See also
673
add_parent_tree_id - if you only have a parent id available it will be
674
simpler to use that api. If you have the parent already available, using
675
this api is preferred.
677
:param parent_tuple: The (revision id, tree) to add to the parent list.
678
If the revision_id is a ghost, pass None for the tree.
679
:param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
681
parent_ids = self.get_parent_ids() + [parent_tuple[0]]
682
if len(parent_ids) > 1:
683
# the leftmost may have already been a ghost, preserve that if it
685
allow_leftmost_as_ghost = True
686
self.set_parent_ids(parent_ids,
687
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
689
@needs_tree_write_lock
690
def add_pending_merge(self, *revision_ids):
691
# TODO: Perhaps should check at this point that the
692
# history of the revision is actually present?
693
parents = self.get_parent_ids()
695
for rev_id in revision_ids:
696
if rev_id in parents:
698
parents.append(rev_id)
701
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
703
@deprecated_method(zero_eleven)
705
def pending_merges(self):
706
"""Return a list of pending merges.
708
These are revisions that have been merged into the working
709
directory but not yet committed.
711
As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
712
instead - which is available on all tree objects.
714
return self.get_parent_ids()[1:]
716
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
717
"""Common ghost checking functionality from set_parent_*.
719
This checks that the left hand-parent exists if there are any
722
if len(revision_ids) > 0:
723
leftmost_id = revision_ids[0]
724
if (not allow_leftmost_as_ghost and not
725
self.branch.repository.has_revision(leftmost_id)):
726
raise errors.GhostRevisionUnusableHere(leftmost_id)
728
def _set_merges_from_parent_ids(self, parent_ids):
729
merges = parent_ids[1:]
730
self._control_files.put_bytes('pending-merges', '\n'.join(merges))
732
@needs_tree_write_lock
733
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
734
"""Set the parent ids to revision_ids.
736
See also set_parent_trees. This api will try to retrieve the tree data
737
for each element of revision_ids from the trees repository. If you have
738
tree data already available, it is more efficient to use
739
set_parent_trees rather than set_parent_ids. set_parent_ids is however
740
an easier API to use.
742
:param revision_ids: The revision_ids to set as the parent ids of this
743
working tree. Any of these may be ghosts.
745
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
746
self._check_parents_for_ghosts(revision_ids,
747
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
748
for revision_id in revision_ids:
749
_mod_revision.check_not_reserved_id(revision_id)
751
if len(revision_ids) > 0:
752
self.set_last_revision(revision_ids[0])
754
self.set_last_revision(_mod_revision.NULL_REVISION)
756
self._set_merges_from_parent_ids(revision_ids)
758
@needs_tree_write_lock
759
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
760
"""See MutableTree.set_parent_trees."""
761
parent_ids = [osutils.safe_revision_id(rev) for (rev, tree) in parents_list]
762
for revision_id in parent_ids:
763
_mod_revision.check_not_reserved_id(revision_id)
765
self._check_parents_for_ghosts(parent_ids,
766
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
768
if len(parent_ids) == 0:
769
leftmost_parent_id = _mod_revision.NULL_REVISION
770
leftmost_parent_tree = None
772
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
774
if self._change_last_revision(leftmost_parent_id):
775
if leftmost_parent_tree is None:
776
# If we don't have a tree, fall back to reading the
777
# parent tree from the repository.
778
self._cache_basis_inventory(leftmost_parent_id)
780
inv = leftmost_parent_tree.inventory
781
xml = self._create_basis_xml_from_inventory(
782
leftmost_parent_id, inv)
783
self._write_basis_inventory(xml)
784
self._set_merges_from_parent_ids(parent_ids)
786
@needs_tree_write_lock
787
def set_pending_merges(self, rev_list):
788
parents = self.get_parent_ids()
789
leftmost = parents[:1]
790
new_parents = leftmost + rev_list
791
self.set_parent_ids(new_parents)
793
@needs_tree_write_lock
794
def set_merge_modified(self, modified_hashes):
796
for file_id, hash in modified_hashes.iteritems():
797
yield Stanza(file_id=file_id.decode('utf8'), hash=hash)
798
self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
800
def _put_rio(self, filename, stanzas, header):
801
self._must_be_locked()
802
my_file = rio_file(stanzas, header)
803
self._control_files.put(filename, my_file)
805
@needs_write_lock # because merge pulls data into the branch.
806
def merge_from_branch(self, branch, to_revision=None, from_revision=None,
808
"""Merge from a branch into this working tree.
810
:param branch: The branch to merge from.
811
:param to_revision: If non-None, the merge will merge to to_revision,
812
but not beyond it. to_revision does not need to be in the history
813
of the branch when it is supplied. If None, to_revision defaults to
814
branch.last_revision().
816
from bzrlib.merge import Merger, Merge3Merger
817
pb = bzrlib.ui.ui_factory.nested_progress_bar()
819
merger = Merger(self.branch, this_tree=self, pb=pb)
820
merger.pp = ProgressPhase("Merge phase", 5, pb)
821
merger.pp.next_phase()
822
# check that there are no
824
merger.check_basis(check_clean=True, require_commits=False)
825
if to_revision is None:
826
to_revision = _mod_revision.ensure_null(branch.last_revision())
828
to_revision = osutils.safe_revision_id(to_revision)
829
merger.other_rev_id = to_revision
830
if _mod_revision.is_null(merger.other_rev_id):
831
raise errors.NoCommits(branch)
832
self.branch.fetch(branch, last_revision=merger.other_rev_id)
833
merger.other_basis = merger.other_rev_id
834
merger.other_tree = self.branch.repository.revision_tree(
836
merger.other_branch = branch
837
merger.pp.next_phase()
838
if from_revision is None:
841
merger.set_base_revision(from_revision, branch)
842
if merger.base_rev_id == merger.other_rev_id:
843
raise errors.PointlessMerge
844
merger.backup_files = False
845
if merge_type is None:
846
merger.merge_type = Merge3Merger
848
merger.merge_type = merge_type
849
merger.set_interesting_files(None)
850
merger.show_base = False
851
merger.reprocess = False
852
conflicts = merger.do_merge()
859
def merge_modified(self):
860
"""Return a dictionary of files modified by a merge.
862
The list is initialized by WorkingTree.set_merge_modified, which is
863
typically called after we make some automatic updates to the tree
866
This returns a map of file_id->sha1, containing only files which are
867
still in the working inventory and have that text hash.
870
hashfile = self._control_files.get('merge-hashes')
871
except errors.NoSuchFile:
875
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
876
raise errors.MergeModifiedFormatError()
877
except StopIteration:
878
raise errors.MergeModifiedFormatError()
879
for s in RioReader(hashfile):
880
# RioReader reads in Unicode, so convert file_ids back to utf8
881
file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
882
if file_id not in self.inventory:
884
text_hash = s.get("hash")
885
if text_hash == self.get_file_sha1(file_id):
886
merge_hashes[file_id] = text_hash
890
def mkdir(self, path, file_id=None):
891
"""See MutableTree.mkdir()."""
893
file_id = generate_ids.gen_file_id(os.path.basename(path))
894
os.mkdir(self.abspath(path))
895
self.add(path, file_id, 'directory')
898
def get_symlink_target(self, file_id):
899
file_id = osutils.safe_file_id(file_id)
900
return os.readlink(self.id2abspath(file_id))
903
def subsume(self, other_tree):
904
def add_children(inventory, entry):
905
for child_entry in entry.children.values():
906
inventory._byid[child_entry.file_id] = child_entry
907
if child_entry.kind == 'directory':
908
add_children(inventory, child_entry)
909
if other_tree.get_root_id() == self.get_root_id():
910
raise errors.BadSubsumeSource(self, other_tree,
911
'Trees have the same root')
913
other_tree_path = self.relpath(other_tree.basedir)
914
except errors.PathNotChild:
915
raise errors.BadSubsumeSource(self, other_tree,
916
'Tree is not contained by the other')
917
new_root_parent = self.path2id(osutils.dirname(other_tree_path))
918
if new_root_parent is None:
919
raise errors.BadSubsumeSource(self, other_tree,
920
'Parent directory is not versioned.')
921
# We need to ensure that the result of a fetch will have a
922
# versionedfile for the other_tree root, and only fetching into
923
# RepositoryKnit2 guarantees that.
924
if not self.branch.repository.supports_rich_root():
925
raise errors.SubsumeTargetNeedsUpgrade(other_tree)
926
other_tree.lock_tree_write()
928
new_parents = other_tree.get_parent_ids()
929
other_root = other_tree.inventory.root
930
other_root.parent_id = new_root_parent
931
other_root.name = osutils.basename(other_tree_path)
932
self.inventory.add(other_root)
933
add_children(self.inventory, other_root)
934
self._write_inventory(self.inventory)
935
# normally we don't want to fetch whole repositories, but i think
936
# here we really do want to consolidate the whole thing.
937
for parent_id in other_tree.get_parent_ids():
938
self.branch.fetch(other_tree.branch, parent_id)
939
self.add_parent_tree_id(parent_id)
942
other_tree.bzrdir.retire_bzrdir()
944
@needs_tree_write_lock
945
def extract(self, file_id, format=None):
946
"""Extract a subtree from this tree.
948
A new branch will be created, relative to the path for this tree.
952
segments = osutils.splitpath(path)
953
transport = self.branch.bzrdir.root_transport
954
for name in segments:
955
transport = transport.clone(name)
956
transport.ensure_base()
959
sub_path = self.id2path(file_id)
960
branch_transport = mkdirs(sub_path)
962
format = bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
963
branch_transport.ensure_base()
964
branch_bzrdir = format.initialize_on_transport(branch_transport)
966
repo = branch_bzrdir.find_repository()
967
except errors.NoRepositoryPresent:
968
repo = branch_bzrdir.create_repository()
969
assert repo.supports_rich_root()
971
if not repo.supports_rich_root():
972
raise errors.RootNotRich()
973
new_branch = branch_bzrdir.create_branch()
974
new_branch.pull(self.branch)
975
for parent_id in self.get_parent_ids():
976
new_branch.fetch(self.branch, parent_id)
977
tree_transport = self.bzrdir.root_transport.clone(sub_path)
978
if tree_transport.base != branch_transport.base:
979
tree_bzrdir = format.initialize_on_transport(tree_transport)
980
branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
982
tree_bzrdir = branch_bzrdir
983
wt = tree_bzrdir.create_workingtree(NULL_REVISION)
984
wt.set_parent_ids(self.get_parent_ids())
985
my_inv = self.inventory
986
child_inv = Inventory(root_id=None)
987
new_root = my_inv[file_id]
988
my_inv.remove_recursive_id(file_id)
989
new_root.parent_id = None
990
child_inv.add(new_root)
991
self._write_inventory(my_inv)
992
wt._write_inventory(child_inv)
995
def _serialize(self, inventory, out_file):
996
xml5.serializer_v5.write_inventory(self._inventory, out_file)
998
def _deserialize(selt, in_file):
999
return xml5.serializer_v5.read_inventory(in_file)
1002
"""Write the in memory inventory to disk."""
1003
# TODO: Maybe this should only write on dirty ?
1004
if self._control_files._lock_mode != 'w':
1005
raise errors.NotWriteLocked(self)
1007
self._serialize(self._inventory, sio)
1009
self._control_files.put('inventory', sio)
1010
self._inventory_is_modified = False
1012
def _kind(self, relpath):
1013
return osutils.file_kind(self.abspath(relpath))
1015
def list_files(self, include_root=False):
1016
"""Recursively list all files as (path, class, kind, id, entry).
131
1018
Lists, but does not descend into unversioned directories.
136
1023
Skips the control directory.
138
from osutils import appendpath, file_kind
141
inv = self._inventory
143
def descend(from_dir_relpath, from_dir_id, dp):
1025
# list_files is an iterator, so @needs_read_lock doesn't work properly
1026
# with it. So callers should be careful to always read_lock the tree.
1027
if not self.is_locked():
1028
raise errors.ObjectNotLocked(self)
1030
inv = self.inventory
1031
if include_root is True:
1032
yield ('', 'V', 'directory', inv.root.file_id, inv.root)
1033
# Convert these into local objects to save lookup times
1034
pathjoin = osutils.pathjoin
1035
file_kind = self._kind
1037
# transport.base ends in a slash, we want the piece
1038
# between the last two slashes
1039
transport_base_dir = self.bzrdir.transport.base.rsplit('/', 2)[1]
1041
fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
1043
# directory file_id, relative path, absolute path, reverse sorted children
1044
children = os.listdir(self.basedir)
1046
# jam 20060527 The kernel sized tree seems equivalent whether we
1047
# use a deque and popleft to keep them sorted, or if we use a plain
1048
# list and just reverse() them.
1049
children = collections.deque(children)
1050
stack = [(inv.root.file_id, u'', self.basedir, children)]
1052
from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
1055
f = children.popleft()
147
1056
## TODO: If we find a subdirectory with its own .bzr
148
1057
## directory, then that is a separate tree and we
149
1058
## should exclude it.
150
if bzrlib.BZRDIR == f:
1060
# the bzrdir for this tree
1061
if transport_base_dir == f:
154
fp = appendpath(from_dir_relpath, f)
1064
# we know that from_dir_relpath and from_dir_abspath never end in a slash
1065
# and 'f' doesn't begin with one, we can do a string op, rather
1066
# than the checks of pathjoin(), all relative paths will have an extra slash
1068
fp = from_dir_relpath + '/' + f
157
fap = appendpath(dp, f)
1071
fap = from_dir_abspath + '/' + f
159
1073
f_ie = inv.get_child(from_dir_id, f)
162
elif self.is_ignored(fp):
1076
elif self.is_ignored(fp[1:]):
1079
# we may not have found this file, because of a unicode issue
1080
f_norm, can_access = osutils.normalized_filename(f)
1081
if f == f_norm or not can_access:
1082
# No change, so treat this file normally
1085
# this file can be accessed by a normalized path
1086
# check again if it is versioned
1087
# these lines are repeated here for performance
1089
fp = from_dir_relpath + '/' + f
1090
fap = from_dir_abspath + '/' + f
1091
f_ie = inv.get_child(from_dir_id, f)
1094
elif self.is_ignored(fp[1:]):
167
1099
fk = file_kind(fap)
1101
# make a last minute entry
171
raise BzrCheckError("file %r entered as kind %r id %r, "
173
% (fap, f_ie.kind, f_ie.file_id, fk))
175
yield fp, c, fk, (f_ie and f_ie.file_id)
1103
yield fp[1:], c, fk, f_ie.file_id, f_ie
1106
yield fp[1:], c, fk, None, fk_entries[fk]()
1108
yield fp[1:], c, fk, None, TreeEntry()
177
1111
if fk != 'directory':
181
# don't descend unversioned directories
184
for ff in descend(fp, f_ie.file_id, fap):
187
for f in descend('', inv.root.file_id, self.basedir):
1114
# But do this child first
1115
new_children = os.listdir(fap)
1117
new_children = collections.deque(new_children)
1118
stack.append((f_ie.file_id, fp, fap, new_children))
1119
# Break out of inner loop,
1120
# so that we start outer loop with child
1123
# if we finished all children, pop it off the stack
1126
@needs_tree_write_lock
1127
def move(self, from_paths, to_dir=None, after=False, **kwargs):
1130
to_dir must exist in the inventory.
1132
If to_dir exists and is a directory, the files are moved into
1133
it, keeping their old names.
1135
Note that to_dir is only the last component of the new name;
1136
this doesn't change the directory.
1138
For each entry in from_paths the move mode will be determined
1141
The first mode moves the file in the filesystem and updates the
1142
inventory. The second mode only updates the inventory without
1143
touching the file on the filesystem. This is the new mode introduced
1146
move uses the second mode if 'after == True' and the target is not
1147
versioned but present in the working tree.
1149
move uses the second mode if 'after == False' and the source is
1150
versioned but no longer in the working tree, and the target is not
1151
versioned but present in the working tree.
1153
move uses the first mode if 'after == False' and the source is
1154
versioned and present in the working tree, and the target is not
1155
versioned and not present in the working tree.
1157
Everything else results in an error.
1159
This returns a list of (from_path, to_path) pairs for each
1160
entry that is moved.
1165
# check for deprecated use of signature
1167
to_dir = kwargs.get('to_name', None)
1169
raise TypeError('You must supply a target directory')
1171
symbol_versioning.warn('The parameter to_name was deprecated'
1172
' in version 0.13. Use to_dir instead',
1175
# check destination directory
1176
assert not isinstance(from_paths, basestring)
1177
inv = self.inventory
1178
to_abs = self.abspath(to_dir)
1179
if not isdir(to_abs):
1180
raise errors.BzrMoveFailedError('',to_dir,
1181
errors.NotADirectory(to_abs))
1182
if not self.has_filename(to_dir):
1183
raise errors.BzrMoveFailedError('',to_dir,
1184
errors.NotInWorkingDirectory(to_dir))
1185
to_dir_id = inv.path2id(to_dir)
1186
if to_dir_id is None:
1187
raise errors.BzrMoveFailedError('',to_dir,
1188
errors.NotVersionedError(path=str(to_dir)))
1190
to_dir_ie = inv[to_dir_id]
1191
if to_dir_ie.kind != 'directory':
1192
raise errors.BzrMoveFailedError('',to_dir,
1193
errors.NotADirectory(to_abs))
1195
# create rename entries and tuples
1196
for from_rel in from_paths:
1197
from_tail = splitpath(from_rel)[-1]
1198
from_id = inv.path2id(from_rel)
1200
raise errors.BzrMoveFailedError(from_rel,to_dir,
1201
errors.NotVersionedError(path=str(from_rel)))
1203
from_entry = inv[from_id]
1204
from_parent_id = from_entry.parent_id
1205
to_rel = pathjoin(to_dir, from_tail)
1206
rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
1208
from_tail=from_tail,
1209
from_parent_id=from_parent_id,
1210
to_rel=to_rel, to_tail=from_tail,
1211
to_parent_id=to_dir_id)
1212
rename_entries.append(rename_entry)
1213
rename_tuples.append((from_rel, to_rel))
1215
# determine which move mode to use. checks also for movability
1216
rename_entries = self._determine_mv_mode(rename_entries, after)
1218
original_modified = self._inventory_is_modified
1221
self._inventory_is_modified = True
1222
self._move(rename_entries)
1224
# restore the inventory on error
1225
self._inventory_is_modified = original_modified
1227
self._write_inventory(inv)
1228
return rename_tuples
1230
def _determine_mv_mode(self, rename_entries, after=False):
1231
"""Determines for each from-to pair if both inventory and working tree
1232
or only the inventory has to be changed.
1234
Also does basic plausability tests.
1236
inv = self.inventory
1238
for rename_entry in rename_entries:
1239
# store to local variables for easier reference
1240
from_rel = rename_entry.from_rel
1241
from_id = rename_entry.from_id
1242
to_rel = rename_entry.to_rel
1243
to_id = inv.path2id(to_rel)
1244
only_change_inv = False
1246
# check the inventory for source and destination
1248
raise errors.BzrMoveFailedError(from_rel,to_rel,
1249
errors.NotVersionedError(path=str(from_rel)))
1250
if to_id is not None:
1251
raise errors.BzrMoveFailedError(from_rel,to_rel,
1252
errors.AlreadyVersionedError(path=str(to_rel)))
1254
# try to determine the mode for rename (only change inv or change
1255
# inv and file system)
1257
if not self.has_filename(to_rel):
1258
raise errors.BzrMoveFailedError(from_id,to_rel,
1259
errors.NoSuchFile(path=str(to_rel),
1260
extra="New file has not been created yet"))
1261
only_change_inv = True
1262
elif not self.has_filename(from_rel) and self.has_filename(to_rel):
1263
only_change_inv = True
1264
elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1265
only_change_inv = False
1267
# something is wrong, so lets determine what exactly
1268
if not self.has_filename(from_rel) and \
1269
not self.has_filename(to_rel):
1270
raise errors.BzrRenameFailedError(from_rel,to_rel,
1271
errors.PathsDoNotExist(paths=(str(from_rel),
1274
raise errors.RenameFailedFilesExist(from_rel, to_rel,
1275
extra="(Use --after to update the Bazaar id)")
1276
rename_entry.only_change_inv = only_change_inv
1277
return rename_entries
1279
def _move(self, rename_entries):
1280
"""Moves a list of files.
1282
Depending on the value of the flag 'only_change_inv', the
1283
file will be moved on the file system or not.
1285
inv = self.inventory
1288
for entry in rename_entries:
1290
self._move_entry(entry)
1292
self._rollback_move(moved)
1296
def _rollback_move(self, moved):
1297
"""Try to rollback a previous move in case of an filesystem error."""
1298
inv = self.inventory
1301
self._move_entry(_RenameEntry(entry.to_rel, entry.from_id,
1302
entry.to_tail, entry.to_parent_id, entry.from_rel,
1303
entry.from_tail, entry.from_parent_id,
1304
entry.only_change_inv))
1305
except errors.BzrMoveFailedError, e:
1306
raise errors.BzrMoveFailedError( '', '', "Rollback failed."
1307
" The working tree is in an inconsistent state."
1308
" Please consider doing a 'bzr revert'."
1309
" Error message is: %s" % e)
1311
def _move_entry(self, entry):
1312
inv = self.inventory
1313
from_rel_abs = self.abspath(entry.from_rel)
1314
to_rel_abs = self.abspath(entry.to_rel)
1315
if from_rel_abs == to_rel_abs:
1316
raise errors.BzrMoveFailedError(entry.from_rel, entry.to_rel,
1317
"Source and target are identical.")
1319
if not entry.only_change_inv:
1321
osutils.rename(from_rel_abs, to_rel_abs)
1323
raise errors.BzrMoveFailedError(entry.from_rel,
1325
inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
1327
@needs_tree_write_lock
1328
def rename_one(self, from_rel, to_rel, after=False):
1331
This can change the directory or the filename or both.
1333
rename_one has several 'modes' to work. First, it can rename a physical
1334
file and change the file_id. That is the normal mode. Second, it can
1335
only change the file_id without touching any physical file. This is
1336
the new mode introduced in version 0.15.
1338
rename_one uses the second mode if 'after == True' and 'to_rel' is not
1339
versioned but present in the working tree.
1341
rename_one uses the second mode if 'after == False' and 'from_rel' is
1342
versioned but no longer in the working tree, and 'to_rel' is not
1343
versioned but present in the working tree.
1345
rename_one uses the first mode if 'after == False' and 'from_rel' is
1346
versioned and present in the working tree, and 'to_rel' is not
1347
versioned and not present in the working tree.
1349
Everything else results in an error.
1351
inv = self.inventory
1354
# create rename entries and tuples
1355
from_tail = splitpath(from_rel)[-1]
1356
from_id = inv.path2id(from_rel)
1358
raise errors.BzrRenameFailedError(from_rel,to_rel,
1359
errors.NotVersionedError(path=str(from_rel)))
1360
from_entry = inv[from_id]
1361
from_parent_id = from_entry.parent_id
1362
to_dir, to_tail = os.path.split(to_rel)
1363
to_dir_id = inv.path2id(to_dir)
1364
rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
1366
from_tail=from_tail,
1367
from_parent_id=from_parent_id,
1368
to_rel=to_rel, to_tail=to_tail,
1369
to_parent_id=to_dir_id)
1370
rename_entries.append(rename_entry)
1372
# determine which move mode to use. checks also for movability
1373
rename_entries = self._determine_mv_mode(rename_entries, after)
1375
# check if the target changed directory and if the target directory is
1377
if to_dir_id is None:
1378
raise errors.BzrMoveFailedError(from_rel,to_rel,
1379
errors.NotVersionedError(path=str(to_dir)))
1381
# all checks done. now we can continue with our actual work
1382
mutter('rename_one:\n'
1387
' to_dir_id {%s}\n',
1388
from_id, from_rel, to_rel, to_dir, to_dir_id)
1390
self._move(rename_entries)
1391
self._write_inventory(inv)
1393
class _RenameEntry(object):
1394
def __init__(self, from_rel, from_id, from_tail, from_parent_id,
1395
to_rel, to_tail, to_parent_id, only_change_inv=False):
1396
self.from_rel = from_rel
1397
self.from_id = from_id
1398
self.from_tail = from_tail
1399
self.from_parent_id = from_parent_id
1400
self.to_rel = to_rel
1401
self.to_tail = to_tail
1402
self.to_parent_id = to_parent_id
1403
self.only_change_inv = only_change_inv
192
1406
def unknowns(self):
193
for subp in self.extras():
194
if not self.is_ignored(subp):
1407
"""Return all unknown files.
1409
These are files in the working directory that are not versioned or
1410
control files or ignored.
1412
# force the extras method to be fully executed before returning, to
1413
# prevent race conditions with the lock
1415
[subp for subp in self.extras() if not self.is_ignored(subp)])
1417
@needs_tree_write_lock
1418
def unversion(self, file_ids):
1419
"""Remove the file ids in file_ids from the current versioned set.
1421
When a file_id is unversioned, all of its children are automatically
1424
:param file_ids: The file ids to stop versioning.
1425
:raises: NoSuchId if any fileid is not currently versioned.
1427
for file_id in file_ids:
1428
file_id = osutils.safe_file_id(file_id)
1429
if self._inventory.has_id(file_id):
1430
self._inventory.remove_recursive_id(file_id)
1432
raise errors.NoSuchId(self, file_id)
1434
# in the future this should just set a dirty bit to wait for the
1435
# final unlock. However, until all methods of workingtree start
1436
# with the current in -memory inventory rather than triggering
1437
# a read, it is more complex - we need to teach read_inventory
1438
# to know when to read, and when to not read first... and possibly
1439
# to save first when the in memory one may be corrupted.
1440
# so for now, we just only write it if it is indeed dirty.
1442
self._write_inventory(self._inventory)
1444
@deprecated_method(zero_eight)
1445
def iter_conflicts(self):
1446
"""List all files in the tree that have text or content conflicts.
1447
DEPRECATED. Use conflicts instead."""
1448
return self._iter_conflicts()
1450
def _iter_conflicts(self):
1452
for info in self.list_files():
1454
stem = get_conflicted_stem(path)
1457
if stem not in conflicted:
1458
conflicted.add(stem)
1462
def pull(self, source, overwrite=False, stop_revision=None,
1463
change_reporter=None):
1464
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1467
pp = ProgressPhase("Pull phase", 2, top_pb)
1469
old_revision_info = self.branch.last_revision_info()
1470
basis_tree = self.basis_tree()
1471
count = self.branch.pull(source, overwrite, stop_revision)
1472
new_revision_info = self.branch.last_revision_info()
1473
if new_revision_info != old_revision_info:
1475
repository = self.branch.repository
1476
pb = bzrlib.ui.ui_factory.nested_progress_bar()
1477
basis_tree.lock_read()
1479
new_basis_tree = self.branch.basis_tree()
1486
change_reporter=change_reporter)
1487
if (basis_tree.inventory.root is None and
1488
new_basis_tree.inventory.root is not None):
1489
self.set_root_id(new_basis_tree.inventory.root.file_id)
1493
# TODO - dedup parents list with things merged by pull ?
1494
# reuse the revisiontree we merged against to set the new
1496
parent_trees = [(self.branch.last_revision(), new_basis_tree)]
1497
# we have to pull the merge trees out again, because
1498
# merge_inner has set the ids. - this corner is not yet
1499
# layered well enough to prevent double handling.
1500
# XXX TODO: Fix the double handling: telling the tree about
1501
# the already known parent data is wasteful.
1502
merges = self.get_parent_ids()[1:]
1503
parent_trees.extend([
1504
(parent, repository.revision_tree(parent)) for
1506
self.set_parent_trees(parent_trees)
1513
def put_file_bytes_non_atomic(self, file_id, bytes):
1514
"""See MutableTree.put_file_bytes_non_atomic."""
1515
file_id = osutils.safe_file_id(file_id)
1516
stream = file(self.id2abspath(file_id), 'wb')
1521
# TODO: update the hashcache here ?
198
1523
def extras(self):
199
"""Yield all unknown files in this WorkingTree.
1524
"""Yield all unversioned files in this WorkingTree.
201
If there are any unknown directories then only the directory is
202
returned, not all its children. But if there are unknown files
1526
If there are any unversioned directories then only the directory is
1527
returned, not all its children. But if there are unversioned files
203
1528
under a versioned subdirectory, they are returned.
205
1530
Currently returned depth-first, sorted by name within directories.
1531
This is the same order used by 'osutils.walkdirs'.
207
1533
## TODO: Work from given directory downwards
208
from osutils import isdir, appendpath
210
1534
for path, dir_entry in self.inventory.directories():
211
mutter("search for unknowns in %r" % path)
1535
# mutter("search for unknowns in %r", path)
212
1536
dirabs = self.abspath(path)
213
1537
if not isdir(dirabs):
214
1538
# e.g. directory deleted
259
1597
If the file is ignored, returns the pattern which caused it to
260
1598
be ignored, otherwise None. So this can simply be used as a
261
1599
boolean if desired."""
263
# TODO: Use '**' to match directories, and other extended
264
# globbing stuff from cvs/rsync.
266
# XXX: fnmatch is actually not quite what we want: it's only
267
# approximately the same as real Unix fnmatch, and doesn't
268
# treat dotfiles correctly and allows * to match /.
269
# Eventually it should be replaced with something more
273
from osutils import splitpath
275
for pat in self.get_ignore_list():
276
if '/' in pat or '\\' in pat:
278
# as a special case, you can put ./ at the start of a
279
# pattern; this is good to match in the top-level
282
if (pat[:2] == './') or (pat[:2] == '.\\'):
1600
if getattr(self, '_ignoreglobster', None) is None:
1601
self._ignoreglobster = globbing.Globster(self.get_ignore_list())
1602
return self._ignoreglobster.match(filename)
1604
def kind(self, file_id):
1605
return file_kind(self.id2abspath(file_id))
1607
def _comparison_data(self, entry, path):
1608
abspath = self.abspath(path)
1610
stat_value = os.lstat(abspath)
1612
if getattr(e, 'errno', None) == errno.ENOENT:
1619
mode = stat_value.st_mode
1620
kind = osutils.file_kind_from_stat_mode(mode)
1621
if not supports_executable():
1622
executable = entry is not None and entry.executable
1624
executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1625
return kind, executable, stat_value
1627
def _file_size(self, entry, stat_value):
1628
return stat_value.st_size
1630
def last_revision(self):
1631
"""Return the last revision of the branch for this tree.
1633
This format tree does not support a separate marker for last-revision
1634
compared to the branch.
1636
See MutableTree.last_revision
1638
return self._last_revision()
1641
def _last_revision(self):
1642
"""helper for get_parent_ids."""
1643
return self.branch.last_revision()
1645
def is_locked(self):
1646
return self._control_files.is_locked()
1648
def _must_be_locked(self):
1649
if not self.is_locked():
1650
raise errors.ObjectNotLocked(self)
1652
def lock_read(self):
1653
"""See Branch.lock_read, and WorkingTree.unlock."""
1654
if not self.is_locked():
1656
self.branch.lock_read()
1658
return self._control_files.lock_read()
1660
self.branch.unlock()
1663
def lock_tree_write(self):
1664
"""See MutableTree.lock_tree_write, and WorkingTree.unlock."""
1665
if not self.is_locked():
1667
self.branch.lock_read()
1669
return self._control_files.lock_write()
1671
self.branch.unlock()
1674
def lock_write(self):
1675
"""See MutableTree.lock_write, and WorkingTree.unlock."""
1676
if not self.is_locked():
1678
self.branch.lock_write()
1680
return self._control_files.lock_write()
1682
self.branch.unlock()
1685
def get_physical_lock_status(self):
1686
return self._control_files.get_physical_lock_status()
1688
def _basis_inventory_name(self):
1689
return 'basis-inventory-cache'
1691
def _reset_data(self):
1692
"""Reset transient data that cannot be revalidated."""
1693
self._inventory_is_modified = False
1694
result = self._deserialize(self._control_files.get('inventory'))
1695
self._set_inventory(result, dirty=False)
1697
@needs_tree_write_lock
1698
def set_last_revision(self, new_revision):
1699
"""Change the last revision in the working tree."""
1700
new_revision = osutils.safe_revision_id(new_revision)
1701
if self._change_last_revision(new_revision):
1702
self._cache_basis_inventory(new_revision)
1704
def _change_last_revision(self, new_revision):
1705
"""Template method part of set_last_revision to perform the change.
1707
This is used to allow WorkingTree3 instances to not affect branch
1708
when their last revision is set.
1710
if _mod_revision.is_null(new_revision):
1711
self.branch.set_revision_history([])
1714
self.branch.generate_revision_history(new_revision)
1715
except errors.NoSuchRevision:
1716
# not present in the repo - dont try to set it deeper than the tip
1717
self.branch.set_revision_history([new_revision])
1720
def _write_basis_inventory(self, xml):
1721
"""Write the basis inventory XML to the basis-inventory file"""
1722
assert isinstance(xml, str), 'serialised xml must be bytestring.'
1723
path = self._basis_inventory_name()
1725
self._control_files.put(path, sio)
1727
def _create_basis_xml_from_inventory(self, revision_id, inventory):
1728
"""Create the text that will be saved in basis-inventory"""
1729
# TODO: jam 20070209 This should be redundant, as the revision_id
1730
# as all callers should have already converted the revision_id to
1732
inventory.revision_id = osutils.safe_revision_id(revision_id)
1733
return xml7.serializer_v7.write_inventory_to_string(inventory)
1735
def _cache_basis_inventory(self, new_revision):
1736
"""Cache new_revision as the basis inventory."""
1737
# TODO: this should allow the ready-to-use inventory to be passed in,
1738
# as commit already has that ready-to-use [while the format is the
1741
# this double handles the inventory - unpack and repack -
1742
# but is easier to understand. We can/should put a conditional
1743
# in here based on whether the inventory is in the latest format
1744
# - perhaps we should repack all inventories on a repository
1746
# the fast path is to copy the raw xml from the repository. If the
1747
# xml contains 'revision_id="', then we assume the right
1748
# revision_id is set. We must check for this full string, because a
1749
# root node id can legitimately look like 'revision_id' but cannot
1751
xml = self.branch.repository.get_inventory_xml(new_revision)
1752
firstline = xml.split('\n', 1)[0]
1753
if (not 'revision_id="' in firstline or
1754
'format="7"' not in firstline):
1755
inv = self.branch.repository.deserialise_inventory(
1757
xml = self._create_basis_xml_from_inventory(new_revision, inv)
1758
self._write_basis_inventory(xml)
1759
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1762
def read_basis_inventory(self):
1763
"""Read the cached basis inventory."""
1764
path = self._basis_inventory_name()
1765
return self._control_files.get(path).read()
1768
def read_working_inventory(self):
1769
"""Read the working inventory.
1771
:raises errors.InventoryModified: read_working_inventory will fail
1772
when the current in memory inventory has been modified.
1774
# conceptually this should be an implementation detail of the tree.
1775
# XXX: Deprecate this.
1776
# ElementTree does its own conversion from UTF-8, so open in
1778
if self._inventory_is_modified:
1779
raise errors.InventoryModified(self)
1780
result = self._deserialize(self._control_files.get('inventory'))
1781
self._set_inventory(result, dirty=False)
1784
@needs_tree_write_lock
1785
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1787
"""Remove nominated files from the working inventory.
1789
:files: File paths relative to the basedir.
1790
:keep_files: If true, the files will also be kept.
1791
:force: Delete files and directories, even if they are changed and
1792
even if the directories are not empty.
1794
## TODO: Normalize names
1796
if isinstance(files, basestring):
1802
unknown_files_in_directory=set()
1804
def recurse_directory_to_add_files(directory):
1805
# recurse directory and add all files
1806
# so we can check if they have changed.
1807
for parent_info, file_infos in\
1808
osutils.walkdirs(self.abspath(directory),
1810
for relpath, basename, kind, lstat, abspath in file_infos:
1812
if self.path2id(relpath): #is it versioned?
1813
new_files.add(relpath)
1815
unknown_files_in_directory.add(
1816
(relpath, None, kind))
1818
for filename in files:
1819
# Get file name into canonical form.
1820
abspath = self.abspath(filename)
1821
filename = self.relpath(abspath)
1822
if len(filename) > 0:
1823
new_files.add(filename)
1824
if osutils.isdir(abspath):
1825
recurse_directory_to_add_files(filename)
1826
files = [f for f in new_files]
1829
return # nothing to do
1831
# Sort needed to first handle directory content before the directory
1832
files.sort(reverse=True)
1833
if not keep_files and not force:
1834
has_changed_files = len(unknown_files_in_directory) > 0
1835
if not has_changed_files:
1836
for (file_id, path, content_change, versioned, parent_id, name,
1837
kind, executable) in self._iter_changes(self.basis_tree(),
1838
include_unchanged=True, require_versioned=False,
1839
want_unversioned=True, specific_files=files):
1840
# check if it's unknown OR changed but not deleted:
1841
if (versioned == (False, False)
1842
or (content_change and kind[1] != None)):
1843
has_changed_files = True
1846
if has_changed_files:
1847
# make delta to show ALL applicable changes in error message.
1848
tree_delta = self.changes_from(self.basis_tree(),
1849
specific_files=files)
1850
for unknown_file in unknown_files_in_directory:
1851
tree_delta.unversioned.extend((unknown_file,))
1852
raise errors.BzrRemoveChangedFilesError(tree_delta)
1854
# do this before any modifications
1856
fid = self.path2id(f)
1859
message="%s is not versioned." % (f,)
1862
# having removed it, it must be either ignored or unknown
1863
if self.is_ignored(f):
1867
textui.show_status(new_status, self.kind(fid), f,
1870
inv_delta.append((f, None, fid, None))
1871
message="removed %s" % (f,)
1874
abs_path = self.abspath(f)
1875
if osutils.lexists(abs_path):
1876
if (osutils.isdir(abs_path) and
1877
len(os.listdir(abs_path)) > 0):
1878
message="%s is not empty directory "\
1879
"and won't be deleted." % (f,)
1881
osutils.delete_any(abs_path)
1882
message="deleted %s" % (f,)
1883
elif message is not None:
1884
# only care if we haven't done anything yet.
1885
message="%s does not exist." % (f,)
1887
# print only one message (if any) per file.
1888
if message is not None:
1890
self.apply_inventory_delta(inv_delta)
1892
@needs_tree_write_lock
1893
def revert(self, filenames, old_tree=None, backups=True,
1894
pb=DummyProgress(), report_changes=False):
1895
from bzrlib.conflicts import resolve
1896
if old_tree is None:
1897
old_tree = self.basis_tree()
1898
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1900
if not len(filenames):
1901
self.set_parent_ids(self.get_parent_ids()[:1])
1904
resolve(self, filenames, ignore_misses=True)
1907
def revision_tree(self, revision_id):
1908
"""See Tree.revision_tree.
1910
WorkingTree can supply revision_trees for the basis revision only
1911
because there is only one cached inventory in the bzr directory.
1913
if revision_id == self.last_revision():
1915
xml = self.read_basis_inventory()
1916
except errors.NoSuchFile:
1920
inv = xml7.serializer_v7.read_inventory_from_string(xml)
1921
# dont use the repository revision_tree api because we want
1922
# to supply the inventory.
1923
if inv.revision_id == revision_id:
1924
return revisiontree.RevisionTree(self.branch.repository,
1926
except errors.BadInventoryFormat:
1928
# raise if there was no inventory, or if we read the wrong inventory.
1929
raise errors.NoSuchRevisionInTree(self, revision_id)
1931
# XXX: This method should be deprecated in favour of taking in a proper
1932
# new Inventory object.
1933
@needs_tree_write_lock
1934
def set_inventory(self, new_inventory_list):
1935
from bzrlib.inventory import (Inventory,
1940
inv = Inventory(self.get_root_id())
1941
for path, file_id, parent, kind in new_inventory_list:
1942
name = os.path.basename(path)
1945
# fixme, there should be a factory function inv,add_??
1946
if kind == 'directory':
1947
inv.add(InventoryDirectory(file_id, name, parent))
1948
elif kind == 'file':
1949
inv.add(InventoryFile(file_id, name, parent))
1950
elif kind == 'symlink':
1951
inv.add(InventoryLink(file_id, name, parent))
1953
raise errors.BzrError("unknown kind %r" % kind)
1954
self._write_inventory(inv)
1956
@needs_tree_write_lock
1957
def set_root_id(self, file_id):
1958
"""Set the root id for this tree."""
1961
symbol_versioning.warn(symbol_versioning.zero_twelve
1962
% 'WorkingTree.set_root_id with fileid=None',
1967
file_id = osutils.safe_file_id(file_id)
1968
self._set_root_id(file_id)
1970
def _set_root_id(self, file_id):
1971
"""Set the root id for this tree, in a format specific manner.
1973
:param file_id: The file id to assign to the root. It must not be
1974
present in the current inventory or an error will occur. It must
1975
not be None, but rather a valid file id.
1977
inv = self._inventory
1978
orig_root_id = inv.root.file_id
1979
# TODO: it might be nice to exit early if there was nothing
1980
# to do, saving us from trigger a sync on unlock.
1981
self._inventory_is_modified = True
1982
# we preserve the root inventory entry object, but
1983
# unlinkit from the byid index
1984
del inv._byid[inv.root.file_id]
1985
inv.root.file_id = file_id
1986
# and link it into the index with the new changed id.
1987
inv._byid[inv.root.file_id] = inv.root
1988
# and finally update all children to reference the new id.
1989
# XXX: this should be safe to just look at the root.children
1990
# list, not the WHOLE INVENTORY.
1993
if entry.parent_id == orig_root_id:
1994
entry.parent_id = inv.root.file_id
1997
"""See Branch.unlock.
1999
WorkingTree locking just uses the Branch locking facilities.
2000
This is current because all working trees have an embedded branch
2001
within them. IF in the future, we were to make branch data shareable
2002
between multiple working trees, i.e. via shared storage, then we
2003
would probably want to lock both the local tree, and the branch.
2005
raise NotImplementedError(self.unlock)
2007
def update(self, change_reporter=None):
2008
"""Update a working tree along its branch.
2010
This will update the branch if its bound too, which means we have
2011
multiple trees involved:
2013
- The new basis tree of the master.
2014
- The old basis tree of the branch.
2015
- The old basis tree of the working tree.
2016
- The current working tree state.
2018
Pathologically, all three may be different, and non-ancestors of each
2019
other. Conceptually we want to:
2021
- Preserve the wt.basis->wt.state changes
2022
- Transform the wt.basis to the new master basis.
2023
- Apply a merge of the old branch basis to get any 'local' changes from
2025
- Restore the wt.basis->wt.state changes.
2027
There isn't a single operation at the moment to do that, so we:
2028
- Merge current state -> basis tree of the master w.r.t. the old tree
2030
- Do a 'normal' merge of the old branch basis if it is relevant.
2032
if self.branch.get_master_branch() is not None:
2034
update_branch = True
2036
self.lock_tree_write()
2037
update_branch = False
2040
old_tip = self.branch.update()
2043
return self._update_tree(old_tip, change_reporter)
2047
@needs_tree_write_lock
2048
def _update_tree(self, old_tip=None, change_reporter=None):
2049
"""Update a tree to the master branch.
2051
:param old_tip: if supplied, the previous tip revision the branch,
2052
before it was changed to the master branch's tip.
2054
# here if old_tip is not None, it is the old tip of the branch before
2055
# it was updated from the master branch. This should become a pending
2056
# merge in the working tree to preserve the user existing work. we
2057
# cant set that until we update the working trees last revision to be
2058
# one from the new branch, because it will just get absorbed by the
2059
# parent de-duplication logic.
2061
# We MUST save it even if an error occurs, because otherwise the users
2062
# local work is unreferenced and will appear to have been lost.
2066
last_rev = self.get_parent_ids()[0]
2068
last_rev = _mod_revision.NULL_REVISION
2069
if last_rev != _mod_revision.ensure_null(self.branch.last_revision()):
2070
# merge tree state up to new branch tip.
2071
basis = self.basis_tree()
2074
to_tree = self.branch.basis_tree()
2075
if basis.inventory.root is None:
2076
self.set_root_id(to_tree.inventory.root.file_id)
2078
result += merge.merge_inner(
2083
change_reporter=change_reporter)
2086
# TODO - dedup parents list with things merged by pull ?
2087
# reuse the tree we've updated to to set the basis:
2088
parent_trees = [(self.branch.last_revision(), to_tree)]
2089
merges = self.get_parent_ids()[1:]
2090
# Ideally we ask the tree for the trees here, that way the working
2091
# tree can decide whether to give us teh entire tree or give us a
2092
# lazy initialised tree. dirstate for instance will have the trees
2093
# in ram already, whereas a last-revision + basis-inventory tree
2094
# will not, but also does not need them when setting parents.
2095
for parent in merges:
2096
parent_trees.append(
2097
(parent, self.branch.repository.revision_tree(parent)))
2098
if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2099
parent_trees.append(
2100
(old_tip, self.branch.repository.revision_tree(old_tip)))
2101
self.set_parent_trees(parent_trees)
2102
last_rev = parent_trees[0][0]
2104
# the working tree had the same last-revision as the master
2105
# branch did. We may still have pivot local work from the local
2106
# branch into old_tip:
2107
if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2108
self.add_parent_tree_id(old_tip)
2109
if (old_tip is not None and not _mod_revision.is_null(old_tip)
2110
and old_tip != last_rev):
2111
# our last revision was not the prior branch last revision
2112
# and we have converted that last revision to a pending merge.
2113
# base is somewhere between the branch tip now
2114
# and the now pending merge
2116
# Since we just modified the working tree and inventory, flush out
2117
# the current state, before we modify it again.
2118
# TODO: jam 20070214 WorkingTree3 doesn't require this, dirstate
2119
# requires it only because TreeTransform directly munges the
2120
# inventory and calls tree._write_inventory(). Ultimately we
2121
# should be able to remove this extra flush.
2123
graph = self.branch.repository.get_graph()
2124
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
2126
base_tree = self.branch.repository.revision_tree(base_rev_id)
2127
other_tree = self.branch.repository.revision_tree(old_tip)
2128
result += merge.merge_inner(
2133
change_reporter=change_reporter)
2136
def _write_hashcache_if_dirty(self):
2137
"""Write out the hashcache if it is dirty."""
2138
if self._hashcache.needs_write:
2140
self._hashcache.write()
2142
if e.errno not in (errno.EPERM, errno.EACCES):
2144
# TODO: jam 20061219 Should this be a warning? A single line
2145
# warning might be sufficient to let the user know what
2147
mutter('Could not write hashcache for %s\nError: %s',
2148
self._hashcache.cache_file_name(), e)
2150
@needs_tree_write_lock
2151
def _write_inventory(self, inv):
2152
"""Write inventory as the current inventory."""
2153
self._set_inventory(inv, dirty=True)
2156
def set_conflicts(self, arg):
2157
raise errors.UnsupportedOperation(self.set_conflicts, self)
2159
def add_conflicts(self, arg):
2160
raise errors.UnsupportedOperation(self.add_conflicts, self)
2163
def conflicts(self):
2164
conflicts = _mod_conflicts.ConflictList()
2165
for conflicted in self._iter_conflicts():
2168
if file_kind(self.abspath(conflicted)) != "file":
2170
except errors.NoSuchFile:
2173
for suffix in ('.THIS', '.OTHER'):
2175
kind = file_kind(self.abspath(conflicted+suffix))
2178
except errors.NoSuchFile:
2182
ctype = {True: 'text conflict', False: 'contents conflict'}[text]
2183
conflicts.append(_mod_conflicts.Conflict.factory(ctype,
2185
file_id=self.path2id(conflicted)))
2188
def walkdirs(self, prefix=""):
2189
"""Walk the directories of this tree.
2191
returns a generator which yields items in the form:
2192
((curren_directory_path, fileid),
2193
[(file1_path, file1_name, file1_kind, (lstat), file1_id,
2196
This API returns a generator, which is only valid during the current
2197
tree transaction - within a single lock_read or lock_write duration.
2199
If the tree is not locked, it may cause an error to be raised,
2200
depending on the tree implementation.
2202
disk_top = self.abspath(prefix)
2203
if disk_top.endswith('/'):
2204
disk_top = disk_top[:-1]
2205
top_strip_len = len(disk_top) + 1
2206
inventory_iterator = self._walkdirs(prefix)
2207
disk_iterator = osutils.walkdirs(disk_top, prefix)
2209
current_disk = disk_iterator.next()
2210
disk_finished = False
2212
if not (e.errno == errno.ENOENT or
2213
(sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
2216
disk_finished = True
2218
current_inv = inventory_iterator.next()
2219
inv_finished = False
2220
except StopIteration:
2223
while not inv_finished or not disk_finished:
2224
if not disk_finished:
2225
# strip out .bzr dirs
2226
if current_disk[0][1][top_strip_len:] == '':
2227
# osutils.walkdirs can be made nicer -
2228
# yield the path-from-prefix rather than the pathjoined
2230
bzrdir_loc = bisect_left(current_disk[1], ('.bzr', '.bzr'))
2231
if current_disk[1][bzrdir_loc][0] == '.bzr':
2232
# we dont yield the contents of, or, .bzr itself.
2233
del current_disk[1][bzrdir_loc]
2235
# everything is unknown
2238
# everything is missing
2241
direction = cmp(current_inv[0][0], current_disk[0][0])
2243
# disk is before inventory - unknown
2244
dirblock = [(relpath, basename, kind, stat, None, None) for
2245
relpath, basename, kind, stat, top_path in current_disk[1]]
2246
yield (current_disk[0][0], None), dirblock
2248
current_disk = disk_iterator.next()
2249
except StopIteration:
2250
disk_finished = True
2252
# inventory is before disk - missing.
2253
dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
2254
for relpath, basename, dkind, stat, fileid, kind in
2256
yield (current_inv[0][0], current_inv[0][1]), dirblock
2258
current_inv = inventory_iterator.next()
2259
except StopIteration:
2262
# versioned present directory
2263
# merge the inventory and disk data together
2265
for relpath, subiterator in itertools.groupby(sorted(
2266
current_inv[1] + current_disk[1], key=operator.itemgetter(0)), operator.itemgetter(1)):
2267
path_elements = list(subiterator)
2268
if len(path_elements) == 2:
2269
inv_row, disk_row = path_elements
2270
# versioned, present file
2271
dirblock.append((inv_row[0],
2272
inv_row[1], disk_row[2],
2273
disk_row[3], inv_row[4],
2275
elif len(path_elements[0]) == 5:
2277
dirblock.append((path_elements[0][0],
2278
path_elements[0][1], path_elements[0][2],
2279
path_elements[0][3], None, None))
2280
elif len(path_elements[0]) == 6:
2281
# versioned, absent file.
2282
dirblock.append((path_elements[0][0],
2283
path_elements[0][1], 'unknown', None,
2284
path_elements[0][4], path_elements[0][5]))
2286
raise NotImplementedError('unreachable code')
2287
yield current_inv[0], dirblock
2289
current_inv = inventory_iterator.next()
2290
except StopIteration:
2293
current_disk = disk_iterator.next()
2294
except StopIteration:
2295
disk_finished = True
2297
def _walkdirs(self, prefix=""):
2298
"""Walk the directories of this tree.
2300
:prefix: is used as the directrory to start with.
2301
returns a generator which yields items in the form:
2302
((curren_directory_path, fileid),
2303
[(file1_path, file1_name, file1_kind, None, file1_id,
2306
_directory = 'directory'
2307
# get the root in the inventory
2308
inv = self.inventory
2309
top_id = inv.path2id(prefix)
2313
pending = [(prefix, '', _directory, None, top_id, None)]
2316
currentdir = pending.pop()
2317
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-id, 5-kind
2318
top_id = currentdir[4]
2320
relroot = currentdir[0] + '/'
2323
# FIXME: stash the node in pending
2325
for name, child in entry.sorted_children():
2326
dirblock.append((relroot + name, name, child.kind, None,
2327
child.file_id, child.kind
2329
yield (currentdir[0], entry.file_id), dirblock
2330
# push the user specified dirs from dirblock
2331
for dir in reversed(dirblock):
2332
if dir[2] == _directory:
2335
@needs_tree_write_lock
2336
def auto_resolve(self):
2337
"""Automatically resolve text conflicts according to contents.
2339
Only text conflicts are auto_resolvable. Files with no conflict markers
2340
are considered 'resolved', because bzr always puts conflict markers
2341
into files that have text conflicts. The corresponding .THIS .BASE and
2342
.OTHER files are deleted, as per 'resolve'.
2343
:return: a tuple of ConflictLists: (un_resolved, resolved).
2345
un_resolved = _mod_conflicts.ConflictList()
2346
resolved = _mod_conflicts.ConflictList()
2347
conflict_re = re.compile('^(<{7}|={7}|>{7})')
2348
for conflict in self.conflicts():
2349
if (conflict.typestring != 'text conflict' or
2350
self.kind(conflict.file_id) != 'file'):
2351
un_resolved.append(conflict)
2353
my_file = open(self.id2abspath(conflict.file_id), 'rb')
2355
for line in my_file:
2356
if conflict_re.search(line):
2357
un_resolved.append(conflict)
286
if fnmatch.fnmatchcase(filename, newpat):
289
if fnmatch.fnmatchcase(splitpath(filename)[-1], pat):
2360
resolved.append(conflict)
2363
resolved.remove_files(self)
2364
self.set_conflicts(un_resolved)
2365
return un_resolved, resolved
2367
def _validate(self):
2368
"""Validate internal structures.
2370
This is meant mostly for the test suite. To give it a chance to detect
2371
corruption after actions have occurred. The default implementation is a
2374
:return: None. An exception should be raised if there is an error.
2379
class WorkingTree2(WorkingTree):
2380
"""This is the Format 2 working tree.
2382
This was the first weave based working tree.
2383
- uses os locks for locking.
2384
- uses the branch last-revision.
2387
def __init__(self, *args, **kwargs):
2388
super(WorkingTree2, self).__init__(*args, **kwargs)
2389
# WorkingTree2 has more of a constraint that self._inventory must
2390
# exist. Because this is an older format, we don't mind the overhead
2391
# caused by the extra computation here.
2393
# Newer WorkingTree's should only have self._inventory set when they
2395
if self._inventory is None:
2396
self.read_working_inventory()
2398
def lock_tree_write(self):
2399
"""See WorkingTree.lock_tree_write().
2401
In Format2 WorkingTrees we have a single lock for the branch and tree
2402
so lock_tree_write() degrades to lock_write().
2404
self.branch.lock_write()
2406
return self._control_files.lock_write()
2408
self.branch.unlock()
2412
# do non-implementation specific cleanup
2415
# we share control files:
2416
if self._control_files._lock_count == 3:
2417
# _inventory_is_modified is always False during a read lock.
2418
if self._inventory_is_modified:
2420
self._write_hashcache_if_dirty()
2422
# reverse order of locking.
2424
return self._control_files.unlock()
2426
self.branch.unlock()
2429
class WorkingTree3(WorkingTree):
2430
"""This is the Format 3 working tree.
2432
This differs from the base WorkingTree by:
2433
- having its own file lock
2434
- having its own last-revision property.
2436
This is new in bzr 0.8
2440
def _last_revision(self):
2441
"""See Mutable.last_revision."""
2443
return osutils.safe_revision_id(
2444
self._control_files.get('last-revision').read())
2445
except errors.NoSuchFile:
b'\\ No newline at end of file'
2448
def _change_last_revision(self, revision_id):
2449
"""See WorkingTree._change_last_revision."""
2450
if revision_id is None or revision_id == NULL_REVISION:
2452
self._control_files._transport.delete('last-revision')
2453
except errors.NoSuchFile:
2457
self._control_files.put_bytes('last-revision', revision_id)
2460
@needs_tree_write_lock
2461
def set_conflicts(self, conflicts):
2462
self._put_rio('conflicts', conflicts.to_stanzas(),
2465
@needs_tree_write_lock
2466
def add_conflicts(self, new_conflicts):
2467
conflict_set = set(self.conflicts())
2468
conflict_set.update(set(list(new_conflicts)))
2469
self.set_conflicts(_mod_conflicts.ConflictList(sorted(conflict_set,
2470
key=_mod_conflicts.Conflict.sort_key)))
2473
def conflicts(self):
2475
confile = self._control_files.get('conflicts')
2476
except errors.NoSuchFile:
2477
return _mod_conflicts.ConflictList()
2479
if confile.next() != CONFLICT_HEADER_1 + '\n':
2480
raise errors.ConflictFormatError()
2481
except StopIteration:
2482
raise errors.ConflictFormatError()
2483
return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
2486
# do non-implementation specific cleanup
2488
if self._control_files._lock_count == 1:
2489
# _inventory_is_modified is always False during a read lock.
2490
if self._inventory_is_modified:
2492
self._write_hashcache_if_dirty()
2493
# reverse order of locking.
2495
return self._control_files.unlock()
2497
self.branch.unlock()
2500
def get_conflicted_stem(path):
2501
for suffix in _mod_conflicts.CONFLICT_SUFFIXES:
2502
if path.endswith(suffix):
2503
return path[:-len(suffix)]
2506
@deprecated_function(zero_eight)
2507
def is_control_file(filename):
2508
"""See WorkingTree.is_control_filename(filename)."""
2509
## FIXME: better check
2510
filename = normpath(filename)
2511
while filename != '':
2512
head, tail = os.path.split(filename)
2513
## mutter('check %r for control file' % ((head, tail),))
2516
if filename == head:
2522
class WorkingTreeFormat(object):
2523
"""An encapsulation of the initialization and open routines for a format.
2525
Formats provide three things:
2526
* An initialization routine,
2530
Formats are placed in an dict by their format string for reference
2531
during workingtree opening. Its not required that these be instances, they
2532
can be classes themselves with class methods - it simply depends on
2533
whether state is needed for a given format or not.
2535
Once a format is deprecated, just deprecate the initialize and open
2536
methods on the format class. Do not deprecate the object, as the
2537
object will be created every time regardless.
2540
_default_format = None
2541
"""The default format used for new trees."""
2544
"""The known formats."""
2546
requires_rich_root = False
2548
upgrade_recommended = False
2551
def find_format(klass, a_bzrdir):
2552
"""Return the format for the working tree object in a_bzrdir."""
2554
transport = a_bzrdir.get_workingtree_transport(None)
2555
format_string = transport.get("format").read()
2556
return klass._formats[format_string]
2557
except errors.NoSuchFile:
2558
raise errors.NoWorkingTree(base=transport.base)
2560
raise errors.UnknownFormatError(format=format_string)
2562
def __eq__(self, other):
2563
return self.__class__ is other.__class__
2565
def __ne__(self, other):
2566
return not (self == other)
2569
def get_default_format(klass):
2570
"""Return the current default format."""
2571
return klass._default_format
2573
def get_format_string(self):
2574
"""Return the ASCII format string that identifies this format."""
2575
raise NotImplementedError(self.get_format_string)
2577
def get_format_description(self):
2578
"""Return the short description for this format."""
2579
raise NotImplementedError(self.get_format_description)
2581
def is_supported(self):
2582
"""Is this format supported?
2584
Supported formats can be initialized and opened.
2585
Unsupported formats may not support initialization or committing or
2586
some other features depending on the reason for not being supported.
2591
def register_format(klass, format):
2592
klass._formats[format.get_format_string()] = format
2595
def set_default_format(klass, format):
2596
klass._default_format = format
2599
def unregister_format(klass, format):
2600
assert klass._formats[format.get_format_string()] is format
2601
del klass._formats[format.get_format_string()]
2604
class WorkingTreeFormat2(WorkingTreeFormat):
2605
"""The second working tree format.
2607
This format modified the hash cache from the format 1 hash cache.
2610
upgrade_recommended = True
2612
def get_format_description(self):
2613
"""See WorkingTreeFormat.get_format_description()."""
2614
return "Working tree format 2"
2616
def stub_initialize_remote(self, control_files):
2617
"""As a special workaround create critical control files for a remote working tree
2619
This ensures that it can later be updated and dealt with locally,
2620
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2621
no working tree. (See bug #43064).
2625
xml5.serializer_v5.write_inventory(inv, sio)
2627
control_files.put('inventory', sio)
2629
control_files.put_bytes('pending-merges', '')
2632
def initialize(self, a_bzrdir, revision_id=None):
2633
"""See WorkingTreeFormat.initialize()."""
2634
if not isinstance(a_bzrdir.transport, LocalTransport):
2635
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2636
branch = a_bzrdir.open_branch()
2637
if revision_id is None:
2638
revision_id = _mod_revision.ensure_null(branch.last_revision())
2640
revision_id = osutils.safe_revision_id(revision_id)
2643
branch.generate_revision_history(revision_id)
2647
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2653
basis_tree = branch.repository.revision_tree(revision_id)
2654
if basis_tree.inventory.root is not None:
2655
wt.set_root_id(basis_tree.inventory.root.file_id)
2656
# set the parent list and cache the basis tree.
2657
if _mod_revision.is_null(revision_id):
2660
parent_trees = [(revision_id, basis_tree)]
2661
wt.set_parent_trees(parent_trees)
2662
transform.build_tree(basis_tree, wt)
2666
super(WorkingTreeFormat2, self).__init__()
2667
self._matchingbzrdir = bzrdir.BzrDirFormat6()
2669
def open(self, a_bzrdir, _found=False):
2670
"""Return the WorkingTree object for a_bzrdir
2672
_found is a private parameter, do not use it. It is used to indicate
2673
if format probing has already been done.
2676
# we are being called directly and must probe.
2677
raise NotImplementedError
2678
if not isinstance(a_bzrdir.transport, LocalTransport):
2679
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2680
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2686
class WorkingTreeFormat3(WorkingTreeFormat):
2687
"""The second working tree format updated to record a format marker.
2690
- exists within a metadir controlling .bzr
2691
- includes an explicit version marker for the workingtree control
2692
files, separate from the BzrDir format
2693
- modifies the hash cache format
2695
- uses a LockDir to guard access for writes.
2698
upgrade_recommended = True
2700
def get_format_string(self):
2701
"""See WorkingTreeFormat.get_format_string()."""
2702
return "Bazaar-NG Working Tree format 3"
2704
def get_format_description(self):
2705
"""See WorkingTreeFormat.get_format_description()."""
2706
return "Working tree format 3"
2708
_lock_file_name = 'lock'
2709
_lock_class = LockDir
2711
_tree_class = WorkingTree3
2713
def __get_matchingbzrdir(self):
2714
return bzrdir.BzrDirMetaFormat1()
2716
_matchingbzrdir = property(__get_matchingbzrdir)
2718
def _open_control_files(self, a_bzrdir):
2719
transport = a_bzrdir.get_workingtree_transport(None)
2720
return LockableFiles(transport, self._lock_file_name,
2723
def initialize(self, a_bzrdir, revision_id=None):
2724
"""See WorkingTreeFormat.initialize().
2726
revision_id allows creating a working tree at a different
2727
revision than the branch is at.
2729
if not isinstance(a_bzrdir.transport, LocalTransport):
2730
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2731
transport = a_bzrdir.get_workingtree_transport(self)
2732
control_files = self._open_control_files(a_bzrdir)
2733
control_files.create_lock()
2734
control_files.lock_write()
2735
control_files.put_utf8('format', self.get_format_string())
2736
branch = a_bzrdir.open_branch()
2737
if revision_id is None:
2738
revision_id = _mod_revision.ensure_null(branch.last_revision())
2740
revision_id = osutils.safe_revision_id(revision_id)
2741
# WorkingTree3 can handle an inventory which has a unique root id.
2742
# as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
2743
# those trees. And because there isn't a format bump inbetween, we
2744
# are maintaining compatibility with older clients.
2745
# inv = Inventory(root_id=gen_root_id())
2746
inv = self._initial_inventory()
2747
wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
2753
_control_files=control_files)
2754
wt.lock_tree_write()
2756
basis_tree = branch.repository.revision_tree(revision_id)
2757
# only set an explicit root id if there is one to set.
2758
if basis_tree.inventory.root is not None:
2759
wt.set_root_id(basis_tree.inventory.root.file_id)
2760
if revision_id == NULL_REVISION:
2761
wt.set_parent_trees([])
2763
wt.set_parent_trees([(revision_id, basis_tree)])
2764
transform.build_tree(basis_tree, wt)
2766
# Unlock in this order so that the unlock-triggers-flush in
2767
# WorkingTree is given a chance to fire.
2768
control_files.unlock()
2772
def _initial_inventory(self):
2776
super(WorkingTreeFormat3, self).__init__()
2778
def open(self, a_bzrdir, _found=False):
2779
"""Return the WorkingTree object for a_bzrdir
2781
_found is a private parameter, do not use it. It is used to indicate
2782
if format probing has already been done.
2785
# we are being called directly and must probe.
2786
raise NotImplementedError
2787
if not isinstance(a_bzrdir.transport, LocalTransport):
2788
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2789
wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
2792
def _open(self, a_bzrdir, control_files):
2793
"""Open the tree itself.
2795
:param a_bzrdir: the dir for the tree.
2796
:param control_files: the control files for the tree.
2798
return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
2802
_control_files=control_files)
2805
return self.get_format_string()
2808
__default_format = WorkingTreeFormat4()
2809
WorkingTreeFormat.register_format(__default_format)
2810
WorkingTreeFormat.register_format(WorkingTreeFormat3())
2811
WorkingTreeFormat.set_default_format(__default_format)
2812
# formats which have no format string are not discoverable
2813
# and not independently creatable, so are not registered.
2814
_legacy_formats = [WorkingTreeFormat2(),