186
205
(branch.base is not cross checked, because for remote branches that
187
206
would be meaningless).
208
self._format = _format
209
self.bzrdir = _bzrdir
211
# not created via open etc.
212
warn("WorkingTree() is deprecated as of bzr version 0.8. "
213
"Please use bzrdir.open_workingtree or WorkingTree.open().",
216
wt = WorkingTree.open(basedir)
217
self.branch = wt.branch
218
self.basedir = wt.basedir
219
self._control_files = wt._control_files
220
self._hashcache = wt._hashcache
221
self._set_inventory(wt._inventory)
222
self._format = wt._format
223
self.bzrdir = wt.bzrdir
189
224
from bzrlib.hashcache import HashCache
190
225
from bzrlib.trace import note, mutter
191
226
assert isinstance(basedir, basestring), \
192
227
"base directory %r is not a string" % basedir
194
branch = Branch.open(basedir)
195
assert isinstance(branch, Branch), \
196
"branch %r is not a Branch" % branch
228
basedir = safe_unicode(basedir)
229
mutter("opening working tree %r", basedir)
230
if deprecated_passed(branch):
232
warn("WorkingTree(..., branch=XXX) is deprecated as of bzr 0.8."
233
" Please use bzrdir.open_workingtree() or WorkingTree.open().",
239
self.branch = self.bzrdir.open_branch()
240
assert isinstance(self.branch, Branch), \
241
"branch %r is not a Branch" % self.branch
198
242
self.basedir = realpath(basedir)
243
# if branch is at our basedir and is a format 6 or less
244
if isinstance(self._format, WorkingTreeFormat2):
245
# share control object
246
self._control_files = self.branch.control_files
247
elif _control_files is not None:
248
assert False, "not done yet"
249
# self._control_files = _control_files
251
# only ready for format 3
252
assert isinstance(self._format, WorkingTreeFormat3)
253
self._control_files = LockableFiles(
254
self.bzrdir.get_workingtree_transport(None),
200
257
# update the whole cache up front and write to disk if anything changed;
201
258
# in the future we might want to do this more selectively
291
417
## XXX: badly named; this is not in the store at all
292
418
return self.abspath(self.id2path(file_id))
421
def clone(self, to_bzrdir, revision_id=None, basis=None):
422
"""Duplicate this working tree into to_bzr, including all state.
424
Specifically modified files are kept as modified, but
425
ignored and unknown files are discarded.
427
If you want to make a new line of development, see bzrdir.sprout()
430
If not None, the cloned tree will have its last revision set to
431
revision, and and difference between the source trees last revision
432
and this one merged in.
435
If not None, a closer copy of a tree which may have some files in
436
common, and which file content should be preferentially copied from.
438
# assumes the target bzr dir format is compatible.
439
result = self._format.initialize(to_bzrdir)
440
self.copy_content_into(result, revision_id)
444
def copy_content_into(self, tree, revision_id=None):
445
"""Copy the current content and user files of this tree into tree."""
446
if revision_id is None:
447
transform_tree(tree, self)
449
# TODO now merge from tree.last_revision to revision
450
transform_tree(tree, self)
451
tree.set_last_revision(revision_id)
294
453
@needs_write_lock
295
def commit(self, *args, **kw):
454
def commit(self, *args, **kwargs):
296
455
from bzrlib.commit import Commit
297
Commit().commit(self.branch, *args, **kw)
456
# args for wt.commit start at message from the Commit.commit method,
457
# but with branch a kwarg now, passing in args as is results in the
458
#message being used for the branch
459
args = (DEPRECATED_PARAMETER, ) + args
460
Commit().commit(working_tree=self, *args, **kwargs)
298
461
self._set_inventory(self.read_working_inventory())
300
463
def id2abspath(self, file_id):
776
949
def kind(self, file_id):
777
950
return file_kind(self.id2abspath(file_id))
953
def last_revision(self):
954
"""Return the last revision id of this working tree.
956
In early branch formats this was == the branch last_revision,
957
but that cannot be relied upon - for working tree operations,
958
always use tree.last_revision().
960
return self.branch.last_revision()
779
962
def lock_read(self):
780
963
"""See Branch.lock_read, and WorkingTree.unlock."""
781
return self.branch.lock_read()
964
self.branch.lock_read()
966
return self._control_files.lock_read()
783
971
def lock_write(self):
784
972
"""See Branch.lock_write, and WorkingTree.unlock."""
785
return self.branch.lock_write()
973
self.branch.lock_write()
975
return self._control_files.lock_write()
787
980
def _basis_inventory_name(self, revision_id):
788
981
return 'basis-inventory.%s' % revision_id
790
984
def set_last_revision(self, new_revision, old_revision=None):
793
path = self._basis_inventory_name(old_revision)
794
path = self.branch._rel_controlfilename(path)
795
self.branch._transport.delete(path)
799
xml = self.branch.get_inventory_xml(new_revision)
985
"""Change the last revision in the working tree."""
986
self._remove_old_basis(old_revision)
987
if self._change_last_revision(new_revision):
988
self._cache_basis_inventory(new_revision)
990
def _change_last_revision(self, new_revision):
991
"""Template method part of set_last_revision to perform the change."""
992
if new_revision is None:
993
self.branch.set_revision_history([])
995
# current format is locked in with the branch
996
revision_history = self.branch.revision_history()
998
position = revision_history.index(new_revision)
1000
raise errors.NoSuchRevision(self.branch, new_revision)
1001
self.branch.set_revision_history(revision_history[:position + 1])
1004
def _cache_basis_inventory(self, new_revision):
1005
"""Cache new_revision as the basis inventory."""
1007
xml = self.branch.repository.get_inventory_xml(new_revision)
800
1008
path = self._basis_inventory_name(new_revision)
801
self.branch.put_controlfile(path, xml)
1009
self._control_files.put_utf8(path, xml)
802
1010
except WeaveRevisionNotPresent:
1013
def _remove_old_basis(self, old_revision):
1014
"""Remove the old basis inventory 'old_revision'."""
1015
if old_revision is not None:
1017
path = self._basis_inventory_name(old_revision)
1018
path = self._control_files._escape(path)
1019
self._control_files._transport.delete(path)
805
1023
def read_basis_inventory(self, revision_id):
806
1024
"""Read the cached basis inventory."""
807
1025
path = self._basis_inventory_name(revision_id)
808
return self.branch.controlfile(path, 'r').read()
1026
return self._control_files.get_utf8(path).read()
810
1028
@needs_read_lock
811
1029
def read_working_inventory(self):
812
1030
"""Read the working inventory."""
813
1031
# ElementTree does its own conversion from UTF-8, so open in
815
f = self.branch.controlfile('inventory', 'rb')
816
return bzrlib.xml5.serializer_v5.read_inventory(f)
1033
result = bzrlib.xml5.serializer_v5.read_inventory(
1034
self._control_files.get('inventory'))
1035
self._set_inventory(result)
818
1038
@needs_write_lock
819
1039
def remove(self, files, verbose=False):
914
1131
between multiple working trees, i.e. via shared storage, then we
915
1132
would probably want to lock both the local tree, and the branch.
917
if self._hashcache.needs_write:
1134
# FIXME: We want to write out the hashcache only when the last lock on
1135
# this working copy is released. Peeking at the lock count is a bit
1136
# of a nasty hack; probably it's better to have a transaction object,
1137
# which can do some finalization when it's either successfully or
1138
# unsuccessfully completed. (Denys's original patch did that.)
1139
# RBC 20060206 hookinhg into transaction will couple lock and transaction
1140
# wrongly. Hookinh into unllock on the control files object is fine though.
1142
# TODO: split this per format so there is no ugly if block
1143
if self._hashcache.needs_write and (
1144
# dedicated lock files
1145
self._control_files._lock_count==1 or
1147
(self._control_files is self.branch.control_files and
1148
self._control_files._lock_count==3)):
918
1149
self._hashcache.write()
919
return self.branch.unlock()
1150
# reverse order of locking.
1151
result = self._control_files.unlock()
1153
self.branch.unlock()
1159
self.branch.lock_read()
1161
if self.last_revision() == self.branch.last_revision():
1163
basis = self.basis_tree()
1164
to_tree = self.branch.basis_tree()
1165
result = merge_inner(self.branch,
1169
self.set_last_revision(self.branch.last_revision())
1172
self.branch.unlock()
921
1174
@needs_write_lock
922
1175
def _write_inventory(self, inv):
923
1176
"""Write inventory as the current inventory."""
924
from cStringIO import StringIO
925
from bzrlib.atomicfile import AtomicFile
926
1177
sio = StringIO()
927
1178
bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
929
f = AtomicFile(self.branch.controlfilename('inventory'))
1180
self._control_files.put('inventory', sio)
935
1181
self._set_inventory(inv)
936
1182
mutter('wrote working inventory')
1185
class WorkingTree3(WorkingTree):
1186
"""This is the Format 3 working tree.
1188
This differs from the base WorkingTree by:
1189
- having its own file lock
1190
- having its own last-revision property.
1194
def last_revision(self):
1195
"""See WorkingTree.last_revision."""
1197
return self._control_files.get_utf8('last-revision').read()
1201
def _change_last_revision(self, revision_id):
1202
"""See WorkingTree._change_last_revision."""
1203
if revision_id is None or revision_id == NULL_REVISION:
1205
self._control_files._transport.delete('last-revision')
1206
except errors.NoSuchFile:
1211
self.branch.revision_history().index(revision_id)
1213
raise errors.NoSuchRevision(self.branch, revision_id)
1214
self._control_files.put_utf8('last-revision', revision_id)
939
1218
CONFLICT_SUFFIXES = ('.THIS', '.BASE', '.OTHER')
940
1219
def get_conflicted_stem(path):
941
1220
for suffix in CONFLICT_SUFFIXES:
942
1221
if path.endswith(suffix):
943
1222
return path[:-len(suffix)]
1224
@deprecated_function(zero_eight)
1225
def is_control_file(filename):
1226
"""See WorkingTree.is_control_filename(filename)."""
1227
## FIXME: better check
1228
filename = normpath(filename)
1229
while filename != '':
1230
head, tail = os.path.split(filename)
1231
## mutter('check %r for control file' % ((head, tail),))
1234
if filename == head:
1240
class WorkingTreeFormat(object):
1241
"""An encapsulation of the initialization and open routines for a format.
1243
Formats provide three things:
1244
* An initialization routine,
1248
Formats are placed in an dict by their format string for reference
1249
during workingtree opening. Its not required that these be instances, they
1250
can be classes themselves with class methods - it simply depends on
1251
whether state is needed for a given format or not.
1253
Once a format is deprecated, just deprecate the initialize and open
1254
methods on the format class. Do not deprecate the object, as the
1255
object will be created every time regardless.
1258
_default_format = None
1259
"""The default format used for new trees."""
1262
"""The known formats."""
1265
def find_format(klass, a_bzrdir):
1266
"""Return the format for the working tree object in a_bzrdir."""
1268
transport = a_bzrdir.get_workingtree_transport(None)
1269
format_string = transport.get("format").read()
1270
return klass._formats[format_string]
1272
raise errors.NoWorkingTree(base=transport.base)
1274
raise errors.UnknownFormatError(format_string)
1277
def get_default_format(klass):
1278
"""Return the current default format."""
1279
return klass._default_format
1281
def get_format_string(self):
1282
"""Return the ASCII format string that identifies this format."""
1283
raise NotImplementedError(self.get_format_string)
1285
def is_supported(self):
1286
"""Is this format supported?
1288
Supported formats can be initialized and opened.
1289
Unsupported formats may not support initialization or committing or
1290
some other features depending on the reason for not being supported.
1295
def register_format(klass, format):
1296
klass._formats[format.get_format_string()] = format
1299
def set_default_format(klass, format):
1300
klass._default_format = format
1303
def unregister_format(klass, format):
1304
assert klass._formats[format.get_format_string()] is format
1305
del klass._formats[format.get_format_string()]
1309
class WorkingTreeFormat2(WorkingTreeFormat):
1310
"""The second working tree format.
1312
This format modified the hash cache from the format 1 hash cache.
1315
def initialize(self, a_bzrdir, revision_id=None):
1316
"""See WorkingTreeFormat.initialize()."""
1317
if not isinstance(a_bzrdir.transport, LocalTransport):
1318
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1319
branch = a_bzrdir.open_branch()
1320
if revision_id is not None:
1323
revision_history = branch.revision_history()
1325
position = revision_history.index(revision_id)
1327
raise errors.NoSuchRevision(branch, revision_id)
1328
branch.set_revision_history(revision_history[:position + 1])
1331
revision = branch.last_revision()
1333
wt = WorkingTree(a_bzrdir.root_transport.base,
1339
wt._write_inventory(inv)
1340
wt.set_root_id(inv.root.file_id)
1341
wt.set_last_revision(revision)
1342
wt.set_pending_merges([])
1343
build_tree(wt.basis_tree(), wt)
1347
super(WorkingTreeFormat2, self).__init__()
1348
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1350
def open(self, a_bzrdir, _found=False):
1351
"""Return the WorkingTree object for a_bzrdir
1353
_found is a private parameter, do not use it. It is used to indicate
1354
if format probing has already been done.
1357
# we are being called directly and must probe.
1358
raise NotImplementedError
1359
if not isinstance(a_bzrdir.transport, LocalTransport):
1360
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1361
return WorkingTree(a_bzrdir.root_transport.base,
1367
class WorkingTreeFormat3(WorkingTreeFormat):
1368
"""The second working tree format updated to record a format marker.
1370
This format modified the hash cache from the format 1 hash cache.
1373
def get_format_string(self):
1374
"""See WorkingTreeFormat.get_format_string()."""
1375
return "Bazaar-NG Working Tree format 3"
1377
def initialize(self, a_bzrdir, revision_id=None):
1378
"""See WorkingTreeFormat.initialize().
1380
revision_id allows creating a working tree at a differnet
1381
revision than the branch is at.
1383
if not isinstance(a_bzrdir.transport, LocalTransport):
1384
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1385
transport = a_bzrdir.get_workingtree_transport(self)
1386
control_files = LockableFiles(transport, 'lock')
1387
control_files.put_utf8('format', self.get_format_string())
1388
branch = a_bzrdir.open_branch()
1389
if revision_id is None:
1390
revision_id = branch.last_revision()
1392
wt = WorkingTree3(a_bzrdir.root_transport.base,
1398
wt._write_inventory(inv)
1399
wt.set_root_id(inv.root.file_id)
1400
wt.set_last_revision(revision_id)
1401
wt.set_pending_merges([])
1402
build_tree(wt.basis_tree(), wt)
1406
super(WorkingTreeFormat3, self).__init__()
1407
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1409
def open(self, a_bzrdir, _found=False):
1410
"""Return the WorkingTree object for a_bzrdir
1412
_found is a private parameter, do not use it. It is used to indicate
1413
if format probing has already been done.
1416
# we are being called directly and must probe.
1417
raise NotImplementedError
1418
if not isinstance(a_bzrdir.transport, LocalTransport):
1419
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1420
return WorkingTree3(a_bzrdir.root_transport.base,
1426
return self.get_format_string()
1429
# formats which have no format string are not discoverable
1430
# and not independently creatable, so are not registered.
1431
__default_format = WorkingTreeFormat3()
1432
WorkingTreeFormat.register_format(__default_format)
1433
WorkingTreeFormat.set_default_format(__default_format)
1434
_legacy_formats = [WorkingTreeFormat2(),
1438
class WorkingTreeTestProviderAdapter(object):
1439
"""A tool to generate a suite testing multiple workingtree formats at once.
1441
This is done by copying the test once for each transport and injecting
1442
the transport_server, transport_readonly_server, and workingtree_format
1443
classes into each copy. Each copy is also given a new id() to make it
1447
def __init__(self, transport_server, transport_readonly_server, formats):
1448
self._transport_server = transport_server
1449
self._transport_readonly_server = transport_readonly_server
1450
self._formats = formats
1452
def adapt(self, test):
1453
from bzrlib.tests import TestSuite
1454
result = TestSuite()
1455
for workingtree_format, bzrdir_format in self._formats:
1456
new_test = deepcopy(test)
1457
new_test.transport_server = self._transport_server
1458
new_test.transport_readonly_server = self._transport_readonly_server
1459
new_test.bzrdir_format = bzrdir_format
1460
new_test.workingtree_format = workingtree_format
1461
def make_new_test_id():
1462
new_id = "%s(%s)" % (new_test.id(), workingtree_format.__class__.__name__)
1463
return lambda: new_id
1464
new_test.id = make_new_test_id()
1465
result.addTest(new_test)