1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
"""WorkingTree object and friends.
19
19
A WorkingTree represents the editable working copy of a branch.
20
Operations which represent the WorkingTree are also done here,
21
such as renaming or adding files. The WorkingTree has an inventory
22
which is updated by these operations. A commit produces a
20
Operations which represent the WorkingTree are also done here,
21
such as renaming or adding files. The WorkingTree has an inventory
22
which is updated by these operations. A commit produces a
23
23
new revision based on the workingtree and its inventory.
25
25
At the moment every WorkingTree has its own branch. Remote
80
79
import bzrlib.branch
81
80
from bzrlib.transport import get_transport
83
from bzrlib.workingtree_4 import (
82
from bzrlib.workingtree_4 import WorkingTreeFormat4
90
85
from bzrlib import symbol_versioning
91
86
from bzrlib.decorators import needs_read_lock, needs_write_lock
92
87
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, TreeReference
93
from bzrlib.lockable_files import LockableFiles
88
from bzrlib.lockable_files import LockableFiles, TransportLock
94
89
from bzrlib.lockdir import LockDir
95
90
import bzrlib.mutabletree
96
91
from bzrlib.mutabletree import needs_tree_write_lock
126
123
ERROR_PATH_NOT_FOUND = 3 # WindowsError errno code, equivalent to ENOENT
126
@deprecated_function(zero_thirteen)
127
def gen_file_id(name):
128
"""Return new file id for the basename 'name'.
130
Use bzrlib.generate_ids.gen_file_id() instead
132
return generate_ids.gen_file_id(name)
135
@deprecated_function(zero_thirteen)
137
"""Return a new tree-root file id.
139
This has been deprecated in favor of bzrlib.generate_ids.gen_root_id()
141
return generate_ids.gen_root_id()
129
144
class TreeEntry(object):
130
145
"""An entry that implements the minimum interface used by commands.
132
This needs further inspection, it may be better to have
147
This needs further inspection, it may be better to have
133
148
InventoryEntries without ids - though that seems wrong. For now,
134
149
this is a parallel hierarchy to InventoryEntry, and needs to become
135
150
one of several things: decorates to that hierarchy, children of, or
138
153
no InventoryEntry available - i.e. for unversioned objects.
139
154
Perhaps they should be UnversionedEntry et al. ? - RBC 20051003
142
157
def __eq__(self, other):
143
158
# yes, this us ugly, TODO: best practice __eq__ style.
144
159
return (isinstance(other, TreeEntry)
145
160
and other.__class__ == self.__class__)
147
162
def kind_character(self):
191
206
not listed in the Inventory and vice versa.
194
# override this to set the strategy for storing views
195
def _make_views(self):
196
return views.DisabledViews(self)
198
209
def __init__(self, basedir='.',
199
210
branch=DEPRECATED_PARAMETER,
211
222
if not _internal:
212
223
raise errors.BzrError("Please use bzrdir.open_workingtree or "
213
224
"WorkingTree.open() to obtain a WorkingTree.")
225
assert isinstance(basedir, basestring), \
226
"base directory %r is not a string" % basedir
214
227
basedir = safe_unicode(basedir)
215
228
mutter("opening working tree %r", basedir)
216
229
if deprecated_passed(branch):
224
237
self._control_files = self.branch.control_files
226
239
# assume all other formats have their own control files.
240
assert isinstance(_control_files, LockableFiles), \
241
"_control_files must be a LockableFiles, not %r" \
227
243
self._control_files = _control_files
228
self._transport = self._control_files._transport
229
244
# update the whole cache up front and write to disk if anything changed;
230
245
# in the future we might want to do this more selectively
231
246
# two possible ways offer themselves : in self._unlock, write the cache
235
250
wt_trans = self.bzrdir.get_workingtree_transport(None)
236
251
cache_filename = wt_trans.local_abspath('stat-cache')
237
252
self._hashcache = hashcache.HashCache(basedir, cache_filename,
238
self.bzrdir._get_file_mode(),
239
self._content_filter_stack_provider())
253
self._control_files._file_mode)
240
254
hc = self._hashcache
242
256
# is this scan needed ? it makes things kinda slow.
257
271
# permitted to do this.
258
272
self._set_inventory(_inventory, dirty=False)
259
273
self._detect_case_handling()
260
self._rules_searcher = None
261
self.views = self._make_views()
263
275
def _detect_case_handling(self):
264
276
wt_trans = self.bzrdir.get_workingtree_transport(None)
296
308
def supports_tree_reference(self):
299
def supports_content_filtering(self):
300
return self._format.supports_content_filtering()
302
def supports_views(self):
303
return self.views.supports_views()
305
311
def _set_inventory(self, inv, dirty):
306
312
"""Set the internal cached inventory.
324
path = osutils.getcwd()
331
path = os.path.getcwdu()
325
332
control = bzrdir.BzrDir.open(path, _unsupported)
326
333
return control.open_workingtree(_unsupported)
329
336
def open_containing(path=None):
330
337
"""Open an existing working tree which has its root about path.
332
339
This probes for a working tree at path and searches upwards from there.
334
341
Basically we keep looking up until we find the control directory or
396
403
def basis_tree(self):
397
404
"""Return RevisionTree for the current last revision.
399
406
If the left most parent is a ghost then the returned tree will be an
400
empty tree - one obtained by calling
401
repository.revision_tree(NULL_REVISION).
407
empty tree - one obtained by calling repository.revision_tree(None).
404
410
revision_id = self.get_parent_ids()[0]
406
412
# no parents, return an empty revision tree.
407
413
# in the future this should return the tree for
408
414
# 'empty:' - the implicit root empty tree.
409
return self.branch.repository.revision_tree(
410
_mod_revision.NULL_REVISION)
415
return self.branch.repository.revision_tree(None)
412
417
return self.revision_tree(revision_id)
413
418
except errors.NoSuchRevision:
417
422
# at this point ?
419
424
return self.branch.repository.revision_tree(revision_id)
420
except (errors.RevisionNotPresent, errors.NoSuchRevision):
425
except errors.RevisionNotPresent:
421
426
# the basis tree *may* be a ghost or a low level error may have
422
# occurred. If the revision is present, its a problem, if its not
427
# occured. If the revision is present, its a problem, if its not
424
429
if self.branch.repository.has_revision(revision_id):
426
431
# the basis tree is a ghost so return an empty tree.
427
return self.branch.repository.revision_tree(
428
_mod_revision.NULL_REVISION)
432
return self.branch.repository.revision_tree(None)
430
434
def _cleanup(self):
431
435
self._flush_ignore_list_cache()
438
@deprecated_method(zero_eight)
439
def create(branch, directory):
440
"""Create a workingtree for branch at directory.
442
If existing_directory already exists it must have a .bzr directory.
443
If it does not exist, it will be created.
445
This returns a new WorkingTree object for the new checkout.
447
TODO FIXME RBC 20060124 when we have checkout formats in place this
448
should accept an optional revisionid to checkout [and reject this if
449
checking out into the same dir as a pre-checkout-aware branch format.]
451
XXX: When BzrDir is present, these should be created through that
454
warnings.warn('delete WorkingTree.create', stacklevel=3)
455
transport = get_transport(directory)
456
if branch.bzrdir.root_transport.base == transport.base:
458
return branch.bzrdir.create_workingtree()
459
# different directory,
460
# create a branch reference
461
# and now a working tree.
462
raise NotImplementedError
465
@deprecated_method(zero_eight)
466
def create_standalone(directory):
467
"""Create a checkout and a branch and a repo at directory.
469
Directory must exist and be empty.
471
please use BzrDir.create_standalone_workingtree
473
return bzrdir.BzrDir.create_standalone_workingtree(directory)
433
475
def relpath(self, path):
434
476
"""Return the local path portion from a given path.
436
The path may be absolute or relative. If its a relative path it is
478
The path may be absolute or relative. If its a relative path it is
437
479
interpreted relative to the python current working directory.
439
481
return osutils.relpath(self.basedir, path)
441
483
def has_filename(self, filename):
442
484
return osutils.lexists(self.abspath(filename))
444
def get_file(self, file_id, path=None, filtered=True):
445
return self.get_file_with_stat(file_id, path, filtered=filtered)[0]
447
def get_file_with_stat(self, file_id, path=None, filtered=True,
449
"""See MutableTree.get_file_with_stat."""
486
def get_file(self, file_id, path=None):
451
488
path = self.id2path(file_id)
452
file_obj = self.get_file_byname(path, filtered=False)
453
stat_value = _fstat(file_obj.fileno())
454
if self.supports_content_filtering() and filtered:
455
filters = self._content_filter_stack(path)
456
file_obj = filtered_input_file(file_obj, filters)
457
return (file_obj, stat_value)
459
def get_file_text(self, file_id, path=None, filtered=True):
460
return self.get_file(file_id, path=path, filtered=filtered).read()
462
def get_file_byname(self, filename, filtered=True):
463
path = self.abspath(filename)
465
if self.supports_content_filtering() and filtered:
466
filters = self._content_filter_stack(filename)
467
return filtered_input_file(f, filters)
471
def get_file_lines(self, file_id, path=None, filtered=True):
472
"""See Tree.get_file_lines()"""
473
file = self.get_file(file_id, path, filtered=filtered)
475
return file.readlines()
489
return self.get_file_byname(path)
491
def get_file_text(self, file_id):
492
return self.get_file(file_id).read()
494
def get_file_byname(self, filename):
495
return file(self.abspath(filename), 'rb')
480
498
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
554
572
def clone(self, to_bzrdir, revision_id=None):
555
573
"""Duplicate this working tree into to_bzr, including all state.
557
575
Specifically modified files are kept as modified, but
558
576
ignored and unknown files are discarded.
560
578
If you want to make a new line of development, see bzrdir.sprout()
563
If not None, the cloned tree will have its last revision set to
564
revision, and difference between the source trees last revision
581
If not None, the cloned tree will have its last revision set to
582
revision, and and difference between the source trees last revision
565
583
and this one merged in.
567
585
# assumes the target bzr dir format is compatible.
568
result = to_bzrdir.create_workingtree()
586
result = self._format.initialize(to_bzrdir)
569
587
self.copy_content_into(result, revision_id)
600
618
__contains__ = has_id
602
620
def get_file_size(self, file_id):
603
"""See Tree.get_file_size"""
605
return os.path.getsize(self.id2abspath(file_id))
607
if e.errno != errno.ENOENT:
621
return os.path.getsize(self.id2abspath(file_id))
613
624
def get_file_sha1(self, file_id, path=None, stat_value=None):
649
660
"""See MutableTree._add."""
650
661
# TODO: Re-adding a file that is removed in the working copy
651
662
# should probably put it back with the previous ID.
652
# the read and write working inventory should not occur in this
663
# the read and write working inventory should not occur in this
653
664
# function - they should be part of lock_write and unlock.
654
665
inv = self.inventory
655
666
for f, file_id, kind in zip(files, ids, kinds):
667
assert kind is not None
656
668
if file_id is None:
657
669
inv.add_path(f, kind=kind)
749
761
kind = 'tree-reference'
750
762
return kind, None, None, None
751
763
elif kind == 'symlink':
752
return ('symlink', None, None,
753
os.readlink(abspath.encode(osutils._fs_enc)
754
).decode(osutils._fs_enc))
764
return ('symlink', None, None, os.readlink(abspath))
756
766
return (kind, None, None, None)
768
@deprecated_method(zero_eleven)
770
def pending_merges(self):
771
"""Return a list of pending merges.
773
These are revisions that have been merged into the working
774
directory but not yet committed.
776
As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
777
instead - which is available on all tree objects.
779
return self.get_parent_ids()[1:]
758
781
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
759
782
"""Common ghost checking functionality from set_parent_*.
770
793
def _set_merges_from_parent_ids(self, parent_ids):
771
794
merges = parent_ids[1:]
772
self._transport.put_bytes('pending-merges', '\n'.join(merges),
773
mode=self.bzrdir._get_file_mode())
775
def _filter_parent_ids_by_ancestry(self, revision_ids):
776
"""Check that all merged revisions are proper 'heads'.
778
This will always return the first revision_id, and any merged revisions
781
if len(revision_ids) == 0:
783
graph = self.branch.repository.get_graph()
784
heads = graph.heads(revision_ids)
785
new_revision_ids = revision_ids[:1]
786
for revision_id in revision_ids[1:]:
787
if revision_id in heads and revision_id not in new_revision_ids:
788
new_revision_ids.append(revision_id)
789
if new_revision_ids != revision_ids:
790
trace.mutter('requested to set revision_ids = %s,'
791
' but filtered to %s', revision_ids, new_revision_ids)
792
return new_revision_ids
795
self._control_files.put_bytes('pending-merges', '\n'.join(merges))
794
797
@needs_tree_write_lock
795
798
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
796
799
"""Set the parent ids to revision_ids.
798
801
See also set_parent_trees. This api will try to retrieve the tree data
799
802
for each element of revision_ids from the trees repository. If you have
800
803
tree data already available, it is more efficient to use
875
874
def _put_rio(self, filename, stanzas, header):
876
875
self._must_be_locked()
877
876
my_file = rio_file(stanzas, header)
878
self._transport.put_file(filename, my_file,
879
mode=self.bzrdir._get_file_mode())
877
self._control_files.put(filename, my_file)
881
879
@needs_write_lock # because merge pulls data into the branch.
882
880
def merge_from_branch(self, branch, to_revision=None, from_revision=None,
933
931
def merge_modified(self):
934
932
"""Return a dictionary of files modified by a merge.
936
The list is initialized by WorkingTree.set_merge_modified, which is
934
The list is initialized by WorkingTree.set_merge_modified, which is
937
935
typically called after we make some automatic updates to the tree
938
936
because of a merge.
941
939
still in the working inventory and have that text hash.
944
hashfile = self._transport.get('merge-hashes')
942
hashfile = self._control_files.get('merge-hashes')
945
943
except errors.NoSuchFile:
950
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
951
raise errors.MergeModifiedFormatError()
952
except StopIteration:
947
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
953
948
raise errors.MergeModifiedFormatError()
954
for s in RioReader(hashfile):
955
# RioReader reads in Unicode, so convert file_ids back to utf8
956
file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
957
if file_id not in self.inventory:
959
text_hash = s.get("hash")
960
if text_hash == self.get_file_sha1(file_id):
961
merge_hashes[file_id] = text_hash
949
except StopIteration:
950
raise errors.MergeModifiedFormatError()
951
for s in RioReader(hashfile):
952
# RioReader reads in Unicode, so convert file_ids back to utf8
953
file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
954
if file_id not in self.inventory:
956
text_hash = s.get("hash")
957
if text_hash == self.get_file_sha1(file_id):
958
merge_hashes[file_id] = text_hash
966
961
@needs_write_lock
967
962
def mkdir(self, path, file_id=None):
975
970
def get_symlink_target(self, file_id):
976
return os.readlink(self.id2abspath(file_id).encode(osutils._fs_enc)
977
).decode(osutils._fs_enc)
971
return os.readlink(self.id2abspath(file_id))
979
973
@needs_write_lock
980
974
def subsume(self, other_tree):
1032
1026
def _directory_may_be_tree_reference(self, relpath):
1033
# as a special case, if a directory contains control files then
1027
# as a special case, if a directory contains control files then
1034
1028
# it's a tree reference, except that the root of the tree is not
1035
1029
return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
1036
1030
# TODO: We could ask all the control formats whether they
1108
1102
sio = StringIO()
1109
1103
self._serialize(self._inventory, sio)
1111
self._transport.put_file('inventory', sio,
1112
mode=self.bzrdir._get_file_mode())
1105
self._control_files.put('inventory', sio)
1113
1106
self._inventory_is_modified = False
1115
1108
def _kind(self, relpath):
1146
1139
# directory file_id, relative path, absolute path, reverse sorted children
1147
1140
children = os.listdir(self.basedir)
1148
1141
children.sort()
1149
# jam 20060527 The kernel sized tree seems equivalent whether we
1142
# jam 20060527 The kernel sized tree seems equivalent whether we
1150
1143
# use a deque and popleft to keep them sorted, or if we use a plain
1151
1144
# list and just reverse() them.
1152
1145
children = collections.deque(children)
1233
1226
to_dir must exist in the inventory.
1235
1228
If to_dir exists and is a directory, the files are moved into
1236
it, keeping their old names.
1229
it, keeping their old names.
1238
1231
Note that to_dir is only the last component of the new name;
1239
1232
this doesn't change the directory.
1367
1359
only_change_inv = True
1368
1360
elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1369
1361
only_change_inv = False
1370
elif (not self.case_sensitive
1371
and from_rel.lower() == to_rel.lower()
1372
and self.has_filename(from_rel)):
1362
elif (sys.platform == 'win32'
1363
and from_rel.lower() == to_rel.lower()
1364
and self.has_filename(from_rel)):
1373
1365
only_change_inv = False
1375
1367
# something is wrong, so lets determine what exactly
1516
1508
These are files in the working directory that are not versioned or
1517
1509
control files or ignored.
1519
# force the extras method to be fully executed before returning, to
1511
# force the extras method to be fully executed before returning, to
1520
1512
# prevent race conditions with the lock
1522
1514
[subp for subp in self.extras() if not self.is_ignored(subp)])
1532
1524
:raises: NoSuchId if any fileid is not currently versioned.
1534
1526
for file_id in file_ids:
1535
if file_id not in self._inventory:
1536
raise errors.NoSuchId(self, file_id)
1537
for file_id in file_ids:
1538
1527
if self._inventory.has_id(file_id):
1539
1528
self._inventory.remove_recursive_id(file_id)
1530
raise errors.NoSuchId(self, file_id)
1540
1531
if len(file_ids):
1541
# in the future this should just set a dirty bit to wait for the
1532
# in the future this should just set a dirty bit to wait for the
1542
1533
# final unlock. However, until all methods of workingtree start
1543
# with the current in -memory inventory rather than triggering
1534
# with the current in -memory inventory rather than triggering
1544
1535
# a read, it is more complex - we need to teach read_inventory
1545
1536
# to know when to read, and when to not read first... and possibly
1546
1537
# to save first when the in memory one may be corrupted.
1547
1538
# so for now, we just only write it if it is indeed dirty.
1548
1539
# - RBC 20060907
1549
1540
self._write_inventory(self._inventory)
1542
@deprecated_method(zero_eight)
1543
def iter_conflicts(self):
1544
"""List all files in the tree that have text or content conflicts.
1545
DEPRECATED. Use conflicts instead."""
1546
return self._iter_conflicts()
1551
1548
def _iter_conflicts(self):
1552
1549
conflicted = set()
1596
1593
# reuse the revisiontree we merged against to set the new
1598
1595
parent_trees = [(self.branch.last_revision(), new_basis_tree)]
1599
# we have to pull the merge trees out again, because
1600
# merge_inner has set the ids. - this corner is not yet
1596
# we have to pull the merge trees out again, because
1597
# merge_inner has set the ids. - this corner is not yet
1601
1598
# layered well enough to prevent double handling.
1602
1599
# XXX TODO: Fix the double handling: telling the tree about
1603
1600
# the already known parent data is wasteful.
1644
1641
if subf == '.bzr':
1646
1643
if subf not in dir_entry.children:
1649
can_access) = osutils.normalized_filename(subf)
1650
except UnicodeDecodeError:
1651
path_os_enc = path.encode(osutils._fs_enc)
1652
relpath = path_os_enc + '/' + subf
1653
raise errors.BadFilenameEncoding(relpath,
1644
subf_norm, can_access = osutils.normalized_filename(subf)
1655
1645
if subf_norm != subf and can_access:
1656
1646
if subf_norm not in dir_entry.children:
1657
1647
fl.append(subf_norm)
1659
1649
fl.append(subf)
1662
1652
for subf in fl:
1663
1653
subp = pathjoin(path, subf)
1831
1821
def _write_basis_inventory(self, xml):
1832
1822
"""Write the basis inventory XML to the basis-inventory file"""
1823
assert isinstance(xml, str), 'serialised xml must be bytestring.'
1833
1824
path = self._basis_inventory_name()
1834
1825
sio = StringIO(xml)
1835
self._transport.put_file(path, sio,
1836
mode=self.bzrdir._get_file_mode())
1826
self._control_files.put(path, sio)
1838
1828
def _create_basis_xml_from_inventory(self, revision_id, inventory):
1839
1829
"""Create the text that will be saved in basis-inventory"""
1846
1836
# as commit already has that ready-to-use [while the format is the
1847
1837
# same, that is].
1849
# this double handles the inventory - unpack and repack -
1839
# this double handles the inventory - unpack and repack -
1850
1840
# but is easier to understand. We can/should put a conditional
1851
1841
# in here based on whether the inventory is in the latest format
1852
1842
# - perhaps we should repack all inventories on a repository
1854
1844
# the fast path is to copy the raw xml from the repository. If the
1855
# xml contains 'revision_id="', then we assume the right
1845
# xml contains 'revision_id="', then we assume the right
1856
1846
# revision_id is set. We must check for this full string, because a
1857
1847
# root node id can legitimately look like 'revision_id' but cannot
1858
1848
# contain a '"'.
1859
1849
xml = self.branch.repository.get_inventory_xml(new_revision)
1860
1850
firstline = xml.split('\n', 1)[0]
1861
if (not 'revision_id="' in firstline or
1851
if (not 'revision_id="' in firstline or
1862
1852
'format="7"' not in firstline):
1863
1853
inv = self.branch.repository.deserialise_inventory(
1864
1854
new_revision, xml)
1870
1860
def read_basis_inventory(self):
1871
1861
"""Read the cached basis inventory."""
1872
1862
path = self._basis_inventory_name()
1873
return self._transport.get_bytes(path)
1863
return self._control_files.get(path).read()
1875
1865
@needs_read_lock
1876
1866
def read_working_inventory(self):
1877
1867
"""Read the working inventory.
1879
1869
:raises errors.InventoryModified: read_working_inventory will fail
1880
1870
when the current in memory inventory has been modified.
1882
# conceptually this should be an implementation detail of the tree.
1872
# conceptually this should be an implementation detail of the tree.
1883
1873
# XXX: Deprecate this.
1884
1874
# ElementTree does its own conversion from UTF-8, so open in
1886
1876
if self._inventory_is_modified:
1887
1877
raise errors.InventoryModified(self)
1888
result = self._deserialize(self._transport.get('inventory'))
1878
result = self._deserialize(self._control_files.get('inventory'))
1889
1879
self._set_inventory(result, dirty=False)
1911
1901
# Recurse directory and add all files
1912
1902
# so we can check if they have changed.
1913
1903
for parent_info, file_infos in\
1914
self.walkdirs(directory):
1915
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1904
osutils.walkdirs(self.abspath(directory),
1906
for relpath, basename, kind, lstat, abspath in file_infos:
1916
1907
# Is it versioned or ignored?
1917
1908
if self.path2id(relpath) or self.is_ignored(relpath):
1918
1909
# Add nested content for deletion.
1967
1959
tree_delta.unversioned.extend((unknown_file,))
1968
1960
raise errors.BzrRemoveChangedFilesError(tree_delta)
1970
# Build inv_delta and delete files where applicable,
1962
# Build inv_delta and delete files where applicaple,
1971
1963
# do this before any modifications to inventory.
1972
1964
for f in files:
1973
1965
fid = self.path2id(f)
2082
2074
name = os.path.basename(path)
2085
# fixme, there should be a factory function inv,add_??
2077
# fixme, there should be a factory function inv,add_??
2086
2078
if kind == 'directory':
2087
2079
inv.add(InventoryDirectory(file_id, name, parent))
2088
2080
elif kind == 'file':
2096
2088
@needs_tree_write_lock
2097
2089
def set_root_id(self, file_id):
2098
2090
"""Set the root id for this tree."""
2100
2092
if file_id is None:
2102
'WorkingTree.set_root_id with fileid=None')
2103
file_id = osutils.safe_file_id(file_id)
2093
symbol_versioning.warn(symbol_versioning.zero_twelve
2094
% 'WorkingTree.set_root_id with fileid=None',
2099
file_id = osutils.safe_file_id(file_id)
2104
2100
self._set_root_id(file_id)
2106
2102
def _set_root_id(self, file_id):
2107
2103
"""Set the root id for this tree, in a format specific manner.
2109
:param file_id: The file id to assign to the root. It must not be
2105
:param file_id: The file id to assign to the root. It must not be
2110
2106
present in the current inventory or an error will occur. It must
2111
2107
not be None, but rather a valid file id.
2132
2128
def unlock(self):
2133
2129
"""See Branch.unlock.
2135
2131
WorkingTree locking just uses the Branch locking facilities.
2136
2132
This is current because all working trees have an embedded branch
2137
2133
within them. IF in the future, we were to make branch data shareable
2138
between multiple working trees, i.e. via shared storage, then we
2134
between multiple working trees, i.e. via shared storage, then we
2139
2135
would probably want to lock both the local tree, and the branch.
2141
2137
raise NotImplementedError(self.unlock)
2193
2189
# cant set that until we update the working trees last revision to be
2194
2190
# one from the new branch, because it will just get absorbed by the
2195
2191
# parent de-duplication logic.
2197
2193
# We MUST save it even if an error occurs, because otherwise the users
2198
2194
# local work is unreferenced and will appear to have been lost.
2202
2198
last_rev = self.get_parent_ids()[0]
2224
2220
parent_trees = [(self.branch.last_revision(), to_tree)]
2225
2221
merges = self.get_parent_ids()[1:]
2226
2222
# Ideally we ask the tree for the trees here, that way the working
2227
# tree can decide whether to give us the entire tree or give us a
2223
# tree can decide whether to give us teh entire tree or give us a
2228
2224
# lazy initialised tree. dirstate for instance will have the trees
2229
2225
# in ram already, whereas a last-revision + basis-inventory tree
2230
2226
# will not, but also does not need them when setting parents.
2373
2369
bzrdir_loc = bisect_left(cur_disk_dir_content,
2374
2370
('.bzr', '.bzr'))
2375
if (bzrdir_loc < len(cur_disk_dir_content)
2376
and cur_disk_dir_content[bzrdir_loc][0] == '.bzr'):
2371
if cur_disk_dir_content[bzrdir_loc][0] == '.bzr':
2377
2372
# we dont yield the contents of, or, .bzr itself.
2378
2373
del cur_disk_dir_content[bzrdir_loc]
2379
2374
if inv_finished:
2470
2465
# FIXME: stash the node in pending
2471
2466
entry = inv[top_id]
2472
if entry.kind == 'directory':
2473
for name, child in entry.sorted_children():
2474
dirblock.append((relroot + name, name, child.kind, None,
2475
child.file_id, child.kind
2467
for name, child in entry.sorted_children():
2468
dirblock.append((relroot + name, name, child.kind, None,
2469
child.file_id, child.kind
2477
2471
yield (currentdir[0], entry.file_id), dirblock
2478
2472
# push the user specified dirs from dirblock
2479
2473
for dir in reversed(dirblock):
2512
2506
self.set_conflicts(un_resolved)
2513
2507
return un_resolved, resolved
2517
tree_basis = self.basis_tree()
2518
tree_basis.lock_read()
2520
repo_basis = self.branch.repository.revision_tree(
2521
self.last_revision())
2522
if len(list(repo_basis.iter_changes(tree_basis))) > 0:
2523
raise errors.BzrCheckError(
2524
"Mismatched basis inventory content.")
2529
2509
def _validate(self):
2530
2510
"""Validate internal structures.
2541
def _get_rules_searcher(self, default_searcher):
2542
"""See Tree._get_rules_searcher."""
2543
if self._rules_searcher is None:
2544
self._rules_searcher = super(WorkingTree,
2545
self)._get_rules_searcher(default_searcher)
2546
return self._rules_searcher
2548
def get_shelf_manager(self):
2549
"""Return the ShelfManager for this WorkingTree."""
2550
from bzrlib.shelf import ShelfManager
2551
return ShelfManager(self, self._transport)
2554
2521
class WorkingTree2(WorkingTree):
2555
2522
"""This is the Format 2 working tree.
2557
This was the first weave based working tree.
2524
This was the first weave based working tree.
2558
2525
- uses os locks for locking.
2559
2526
- uses the branch last-revision.
2615
2582
def _last_revision(self):
2616
2583
"""See Mutable.last_revision."""
2618
return self._transport.get_bytes('last-revision')
2585
return self._control_files.get('last-revision').read()
2619
2586
except errors.NoSuchFile:
2620
2587
return _mod_revision.NULL_REVISION
2623
2590
"""See WorkingTree._change_last_revision."""
2624
2591
if revision_id is None or revision_id == NULL_REVISION:
2626
self._transport.delete('last-revision')
2593
self._control_files._transport.delete('last-revision')
2627
2594
except errors.NoSuchFile:
2631
self._transport.put_bytes('last-revision', revision_id,
2632
mode=self.bzrdir._get_file_mode())
2598
self._control_files.put_bytes('last-revision', revision_id)
2635
2601
@needs_tree_write_lock
2636
2602
def set_conflicts(self, conflicts):
2637
self._put_rio('conflicts', conflicts.to_stanzas(),
2603
self._put_rio('conflicts', conflicts.to_stanzas(),
2638
2604
CONFLICT_HEADER_1)
2640
2606
@needs_tree_write_lock
2647
2613
@needs_read_lock
2648
2614
def conflicts(self):
2650
confile = self._transport.get('conflicts')
2616
confile = self._control_files.get('conflicts')
2651
2617
except errors.NoSuchFile:
2652
2618
return _mod_conflicts.ConflictList()
2655
if confile.next() != CONFLICT_HEADER_1 + '\n':
2656
raise errors.ConflictFormatError()
2657
except StopIteration:
2620
if confile.next() != CONFLICT_HEADER_1 + '\n':
2658
2621
raise errors.ConflictFormatError()
2659
return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
2622
except StopIteration:
2623
raise errors.ConflictFormatError()
2624
return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
2663
2626
def unlock(self):
2664
2627
# do non-implementation specific cleanup
2681
2644
return path[:-len(suffix)]
2647
@deprecated_function(zero_eight)
2648
def is_control_file(filename):
2649
"""See WorkingTree.is_control_filename(filename)."""
2650
## FIXME: better check
2651
filename = normpath(filename)
2652
while filename != '':
2653
head, tail = os.path.split(filename)
2654
## mutter('check %r for control file' % ((head, tail),))
2657
if filename == head:
2684
2663
class WorkingTreeFormat(object):
2685
2664
"""An encapsulation of the initialization and open routines for a format.
2689
2668
* a format string,
2690
2669
* an open routine.
2692
Formats are placed in an dict by their format string for reference
2671
Formats are placed in an dict by their format string for reference
2693
2672
during workingtree opening. Its not required that these be instances, they
2694
can be classes themselves with class methods - it simply depends on
2673
can be classes themselves with class methods - it simply depends on
2695
2674
whether state is needed for a given format or not.
2697
2676
Once a format is deprecated, just deprecate the initialize and open
2698
methods on the format class. Do not deprecate the object, as the
2677
methods on the format class. Do not deprecate the object, as the
2699
2678
object will be created every time regardless.
2745
2724
"""Is this format supported?
2747
2726
Supported formats can be initialized and opened.
2748
Unsupported formats may not support initialization or committing or
2727
Unsupported formats may not support initialization or committing or
2749
2728
some other features depending on the reason for not being supported.
2753
def supports_content_filtering(self):
2754
"""True if this format supports content filtering."""
2757
def supports_views(self):
2758
"""True if this format supports stored views."""
2762
2733
def register_format(klass, format):
2763
2734
klass._formats[format.get_format_string()] = format
2770
2741
def unregister_format(klass, format):
2742
assert klass._formats[format.get_format_string()] is format
2771
2743
del klass._formats[format.get_format_string()]
2774
2746
class WorkingTreeFormat2(WorkingTreeFormat):
2775
"""The second working tree format.
2747
"""The second working tree format.
2777
2749
This format modified the hash cache from the format 1 hash cache.
2783
2755
"""See WorkingTreeFormat.get_format_description()."""
2784
2756
return "Working tree format 2"
2786
def _stub_initialize_on_transport(self, transport, file_mode):
2787
"""Workaround: create control files for a remote working tree.
2758
def stub_initialize_remote(self, control_files):
2759
"""As a special workaround create critical control files for a remote working tree
2789
2761
This ensures that it can later be updated and dealt with locally,
2790
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2762
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2791
2763
no working tree. (See bug #43064).
2793
2765
sio = StringIO()
2794
2766
inv = Inventory()
2795
2767
xml5.serializer_v5.write_inventory(inv, sio, working=True)
2797
transport.put_file('inventory', sio, file_mode)
2798
transport.put_bytes('pending-merges', '', file_mode)
2769
control_files.put('inventory', sio)
2771
control_files.put_bytes('pending-merges', '')
2800
2774
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2801
2775
accelerator_tree=None, hardlink=False):
2888
2862
def _open_control_files(self, a_bzrdir):
2889
2863
transport = a_bzrdir.get_workingtree_transport(None)
2890
return LockableFiles(transport, self._lock_file_name,
2864
return LockableFiles(transport, self._lock_file_name,
2891
2865
self._lock_class)
2893
2867
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2894
2868
accelerator_tree=None, hardlink=False):
2895
2869
"""See WorkingTreeFormat.initialize().
2897
2871
:param revision_id: if supplied, create a working tree at a different
2898
2872
revision than the branch is at.
2899
2873
:param accelerator_tree: A tree which can be used for retrieving file
2909
2883
control_files = self._open_control_files(a_bzrdir)
2910
2884
control_files.create_lock()
2911
2885
control_files.lock_write()
2912
transport.put_bytes('format', self.get_format_string(),
2913
mode=a_bzrdir._get_file_mode())
2886
control_files.put_utf8('format', self.get_format_string())
2914
2887
if from_branch is not None:
2915
2888
branch = from_branch
2987
2960
__default_format = WorkingTreeFormat4()
2988
2961
WorkingTreeFormat.register_format(__default_format)
2989
WorkingTreeFormat.register_format(WorkingTreeFormat6())
2990
WorkingTreeFormat.register_format(WorkingTreeFormat5())
2991
2962
WorkingTreeFormat.register_format(WorkingTreeFormat3())
2992
2963
WorkingTreeFormat.set_default_format(__default_format)
2993
2964
# formats which have no format string are not discoverable