186
207
(branch.base is not cross checked, because for remote branches that
187
208
would be meaningless).
210
self._format = _format
211
self.bzrdir = _bzrdir
213
# not created via open etc.
214
warn("WorkingTree() is deprecated as of bzr version 0.8. "
215
"Please use bzrdir.open_workingtree or WorkingTree.open().",
218
wt = WorkingTree.open(basedir)
219
self.branch = wt.branch
220
self.basedir = wt.basedir
221
self._control_files = wt._control_files
222
self._hashcache = wt._hashcache
223
self._set_inventory(wt._inventory)
224
self._format = wt._format
225
self.bzrdir = wt.bzrdir
189
226
from bzrlib.hashcache import HashCache
190
227
from bzrlib.trace import note, mutter
191
228
assert isinstance(basedir, basestring), \
192
229
"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
230
basedir = safe_unicode(basedir)
231
mutter("opening working tree %r", basedir)
232
if deprecated_passed(branch):
234
warn("WorkingTree(..., branch=XXX) is deprecated as of bzr 0.8."
235
" Please use bzrdir.open_workingtree() or WorkingTree.open().",
241
self.branch = self.bzrdir.open_branch()
242
assert isinstance(self.branch, Branch), \
243
"branch %r is not a Branch" % self.branch
198
244
self.basedir = realpath(basedir)
245
# if branch is at our basedir and is a format 6 or less
246
if isinstance(self._format, WorkingTreeFormat2):
247
# share control object
248
self._control_files = self.branch.control_files
249
elif _control_files is not None:
250
assert False, "not done yet"
251
# self._control_files = _control_files
253
# only ready for format 3
254
assert isinstance(self._format, WorkingTreeFormat3)
255
self._control_files = LockableFiles(
256
self.bzrdir.get_workingtree_transport(None),
200
259
# update the whole cache up front and write to disk if anything changed;
201
260
# in the future we might want to do this more selectively
292
419
## XXX: badly named; this is not in the store at all
293
420
return self.abspath(self.id2path(file_id))
423
def clone(self, to_bzrdir, revision_id=None, basis=None):
424
"""Duplicate this working tree into to_bzr, including all state.
426
Specifically modified files are kept as modified, but
427
ignored and unknown files are discarded.
429
If you want to make a new line of development, see bzrdir.sprout()
432
If not None, the cloned tree will have its last revision set to
433
revision, and and difference between the source trees last revision
434
and this one merged in.
437
If not None, a closer copy of a tree which may have some files in
438
common, and which file content should be preferentially copied from.
440
# assumes the target bzr dir format is compatible.
441
result = self._format.initialize(to_bzrdir)
442
self.copy_content_into(result, revision_id)
446
def copy_content_into(self, tree, revision_id=None):
447
"""Copy the current content and user files of this tree into tree."""
448
if revision_id is None:
449
transform_tree(tree, self)
451
# TODO now merge from tree.last_revision to revision
452
transform_tree(tree, self)
453
tree.set_last_revision(revision_id)
295
455
@needs_write_lock
296
def commit(self, *args, **kw):
456
def commit(self, *args, **kwargs):
297
457
from bzrlib.commit import Commit
298
Commit().commit(self.branch, *args, **kw)
458
# args for wt.commit start at message from the Commit.commit method,
459
# but with branch a kwarg now, passing in args as is results in the
460
#message being used for the branch
461
args = (DEPRECATED_PARAMETER, ) + args
462
Commit().commit(working_tree=self, *args, **kwargs)
299
463
self._set_inventory(self.read_working_inventory())
301
465
def id2abspath(self, file_id):
780
953
def kind(self, file_id):
781
954
return file_kind(self.id2abspath(file_id))
957
def last_revision(self):
958
"""Return the last revision id of this working tree.
960
In early branch formats this was == the branch last_revision,
961
but that cannot be relied upon - for working tree operations,
962
always use tree.last_revision().
964
return self.branch.last_revision()
783
966
def lock_read(self):
784
967
"""See Branch.lock_read, and WorkingTree.unlock."""
785
return self.branch.lock_read()
968
self.branch.lock_read()
970
return self._control_files.lock_read()
787
975
def lock_write(self):
788
976
"""See Branch.lock_write, and WorkingTree.unlock."""
789
return self.branch.lock_write()
977
self.branch.lock_write()
979
return self._control_files.lock_write()
791
984
def _basis_inventory_name(self, revision_id):
792
985
return 'basis-inventory.%s' % revision_id
794
988
def set_last_revision(self, new_revision, old_revision=None):
795
if old_revision is not None:
797
path = self._basis_inventory_name(old_revision)
798
path = self.branch.control_files._escape(path)
799
self.branch.control_files._transport.delete(path)
989
"""Change the last revision in the working tree."""
990
self._remove_old_basis(old_revision)
991
if self._change_last_revision(new_revision):
992
self._cache_basis_inventory(new_revision)
994
def _change_last_revision(self, new_revision):
995
"""Template method part of set_last_revision to perform the change."""
996
if new_revision is None:
997
self.branch.set_revision_history([])
999
# current format is locked in with the branch
1000
revision_history = self.branch.revision_history()
1002
position = revision_history.index(new_revision)
1004
raise errors.NoSuchRevision(self.branch, new_revision)
1005
self.branch.set_revision_history(revision_history[:position + 1])
1008
def _cache_basis_inventory(self, new_revision):
1009
"""Cache new_revision as the basis inventory."""
803
1011
xml = self.branch.repository.get_inventory_xml(new_revision)
804
1012
path = self._basis_inventory_name(new_revision)
805
self.branch.control_files.put_utf8(path, xml)
1013
self._control_files.put_utf8(path, xml)
806
1014
except WeaveRevisionNotPresent:
1017
def _remove_old_basis(self, old_revision):
1018
"""Remove the old basis inventory 'old_revision'."""
1019
if old_revision is not None:
1021
path = self._basis_inventory_name(old_revision)
1022
path = self._control_files._escape(path)
1023
self._control_files._transport.delete(path)
809
1027
def read_basis_inventory(self, revision_id):
810
1028
"""Read the cached basis inventory."""
811
1029
path = self._basis_inventory_name(revision_id)
812
return self.branch.control_files.get_utf8(path).read()
1030
return self._control_files.get_utf8(path).read()
814
1032
@needs_read_lock
815
1033
def read_working_inventory(self):
816
1034
"""Read the working inventory."""
817
1035
# ElementTree does its own conversion from UTF-8, so open in
819
f = self.branch.control_files.get('inventory')
820
return bzrlib.xml5.serializer_v5.read_inventory(f)
1037
result = bzrlib.xml5.serializer_v5.read_inventory(
1038
self._control_files.get('inventory'))
1039
self._set_inventory(result)
822
1042
@needs_write_lock
823
1043
def remove(self, files, verbose=False):
923
1141
# of a nasty hack; probably it's better to have a transaction object,
924
1142
# which can do some finalization when it's either successfully or
925
1143
# unsuccessfully completed. (Denys's original patch did that.)
926
if self._hashcache.needs_write and self.branch.control_files._lock_count==1:
1144
# RBC 20060206 hookinhg into transaction will couple lock and transaction
1145
# wrongly. Hookinh into unllock on the control files object is fine though.
1147
# TODO: split this per format so there is no ugly if block
1148
if self._hashcache.needs_write and (
1149
# dedicated lock files
1150
self._control_files._lock_count==1 or
1152
(self._control_files is self.branch.control_files and
1153
self._control_files._lock_count==3)):
927
1154
self._hashcache.write()
928
return self.branch.unlock()
1155
# reverse order of locking.
1156
result = self._control_files.unlock()
1158
self.branch.unlock()
1164
self.branch.lock_read()
1166
if self.last_revision() == self.branch.last_revision():
1168
basis = self.basis_tree()
1169
to_tree = self.branch.basis_tree()
1170
result = merge_inner(self.branch,
1174
self.set_last_revision(self.branch.last_revision())
1177
self.branch.unlock()
930
1179
@needs_write_lock
931
1180
def _write_inventory(self, inv):
932
1181
"""Write inventory as the current inventory."""
933
from cStringIO import StringIO
934
from bzrlib.atomicfile import AtomicFile
935
1182
sio = StringIO()
936
1183
bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
938
f = AtomicFile(self.branch.control_files.controlfilename('inventory'))
1185
self._control_files.put('inventory', sio)
944
1186
self._set_inventory(inv)
945
1187
mutter('wrote working inventory')
1190
class WorkingTree3(WorkingTree):
1191
"""This is the Format 3 working tree.
1193
This differs from the base WorkingTree by:
1194
- having its own file lock
1195
- having its own last-revision property.
1199
def last_revision(self):
1200
"""See WorkingTree.last_revision."""
1202
return self._control_files.get_utf8('last-revision').read()
1206
def _change_last_revision(self, revision_id):
1207
"""See WorkingTree._change_last_revision."""
1208
if revision_id is None or revision_id == NULL_REVISION:
1210
self._control_files._transport.delete('last-revision')
1211
except errors.NoSuchFile:
1216
self.branch.revision_history().index(revision_id)
1218
raise errors.NoSuchRevision(self.branch, revision_id)
1219
self._control_files.put_utf8('last-revision', revision_id)
948
1223
CONFLICT_SUFFIXES = ('.THIS', '.BASE', '.OTHER')
949
1224
def get_conflicted_stem(path):
950
1225
for suffix in CONFLICT_SUFFIXES:
951
1226
if path.endswith(suffix):
952
1227
return path[:-len(suffix)]
1229
@deprecated_function(zero_eight)
1230
def is_control_file(filename):
1231
"""See WorkingTree.is_control_filename(filename)."""
1232
## FIXME: better check
1233
filename = normpath(filename)
1234
while filename != '':
1235
head, tail = os.path.split(filename)
1236
## mutter('check %r for control file' % ((head, tail),))
1239
if filename == head:
1245
class WorkingTreeFormat(object):
1246
"""An encapsulation of the initialization and open routines for a format.
1248
Formats provide three things:
1249
* An initialization routine,
1253
Formats are placed in an dict by their format string for reference
1254
during workingtree opening. Its not required that these be instances, they
1255
can be classes themselves with class methods - it simply depends on
1256
whether state is needed for a given format or not.
1258
Once a format is deprecated, just deprecate the initialize and open
1259
methods on the format class. Do not deprecate the object, as the
1260
object will be created every time regardless.
1263
_default_format = None
1264
"""The default format used for new trees."""
1267
"""The known formats."""
1270
def find_format(klass, a_bzrdir):
1271
"""Return the format for the working tree object in a_bzrdir."""
1273
transport = a_bzrdir.get_workingtree_transport(None)
1274
format_string = transport.get("format").read()
1275
return klass._formats[format_string]
1277
raise errors.NoWorkingTree(base=transport.base)
1279
raise errors.UnknownFormatError(format_string)
1282
def get_default_format(klass):
1283
"""Return the current default format."""
1284
return klass._default_format
1286
def get_format_string(self):
1287
"""Return the ASCII format string that identifies this format."""
1288
raise NotImplementedError(self.get_format_string)
1290
def is_supported(self):
1291
"""Is this format supported?
1293
Supported formats can be initialized and opened.
1294
Unsupported formats may not support initialization or committing or
1295
some other features depending on the reason for not being supported.
1300
def register_format(klass, format):
1301
klass._formats[format.get_format_string()] = format
1304
def set_default_format(klass, format):
1305
klass._default_format = format
1308
def unregister_format(klass, format):
1309
assert klass._formats[format.get_format_string()] is format
1310
del klass._formats[format.get_format_string()]
1314
class WorkingTreeFormat2(WorkingTreeFormat):
1315
"""The second working tree format.
1317
This format modified the hash cache from the format 1 hash cache.
1320
def initialize(self, a_bzrdir, revision_id=None):
1321
"""See WorkingTreeFormat.initialize()."""
1322
if not isinstance(a_bzrdir.transport, LocalTransport):
1323
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1324
branch = a_bzrdir.open_branch()
1325
if revision_id is not None:
1328
revision_history = branch.revision_history()
1330
position = revision_history.index(revision_id)
1332
raise errors.NoSuchRevision(branch, revision_id)
1333
branch.set_revision_history(revision_history[:position + 1])
1336
revision = branch.last_revision()
1338
wt = WorkingTree(a_bzrdir.root_transport.base,
1344
wt._write_inventory(inv)
1345
wt.set_root_id(inv.root.file_id)
1346
wt.set_last_revision(revision)
1347
wt.set_pending_merges([])
1348
build_tree(wt.basis_tree(), wt)
1352
super(WorkingTreeFormat2, self).__init__()
1353
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1355
def open(self, a_bzrdir, _found=False):
1356
"""Return the WorkingTree object for a_bzrdir
1358
_found is a private parameter, do not use it. It is used to indicate
1359
if format probing has already been done.
1362
# we are being called directly and must probe.
1363
raise NotImplementedError
1364
if not isinstance(a_bzrdir.transport, LocalTransport):
1365
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1366
return WorkingTree(a_bzrdir.root_transport.base,
1372
class WorkingTreeFormat3(WorkingTreeFormat):
1373
"""The second working tree format updated to record a format marker.
1375
This format modified the hash cache from the format 1 hash cache.
1378
def get_format_string(self):
1379
"""See WorkingTreeFormat.get_format_string()."""
1380
return "Bazaar-NG Working Tree format 3"
1382
def initialize(self, a_bzrdir, revision_id=None):
1383
"""See WorkingTreeFormat.initialize().
1385
revision_id allows creating a working tree at a differnet
1386
revision than the branch is at.
1388
if not isinstance(a_bzrdir.transport, LocalTransport):
1389
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1390
transport = a_bzrdir.get_workingtree_transport(self)
1391
control_files = LockableFiles(transport, 'lock')
1392
control_files.put_utf8('format', self.get_format_string())
1393
branch = a_bzrdir.open_branch()
1394
if revision_id is None:
1395
revision_id = branch.last_revision()
1397
wt = WorkingTree3(a_bzrdir.root_transport.base,
1403
wt._write_inventory(inv)
1404
wt.set_root_id(inv.root.file_id)
1405
wt.set_last_revision(revision_id)
1406
wt.set_pending_merges([])
1407
build_tree(wt.basis_tree(), wt)
1411
super(WorkingTreeFormat3, self).__init__()
1412
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1414
def open(self, a_bzrdir, _found=False):
1415
"""Return the WorkingTree object for a_bzrdir
1417
_found is a private parameter, do not use it. It is used to indicate
1418
if format probing has already been done.
1421
# we are being called directly and must probe.
1422
raise NotImplementedError
1423
if not isinstance(a_bzrdir.transport, LocalTransport):
1424
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1425
return WorkingTree3(a_bzrdir.root_transport.base,
1431
return self.get_format_string()
1434
# formats which have no format string are not discoverable
1435
# and not independently creatable, so are not registered.
1436
__default_format = WorkingTreeFormat3()
1437
WorkingTreeFormat.register_format(__default_format)
1438
WorkingTreeFormat.set_default_format(__default_format)
1439
_legacy_formats = [WorkingTreeFormat2(),
1443
class WorkingTreeTestProviderAdapter(object):
1444
"""A tool to generate a suite testing multiple workingtree formats at once.
1446
This is done by copying the test once for each transport and injecting
1447
the transport_server, transport_readonly_server, and workingtree_format
1448
classes into each copy. Each copy is also given a new id() to make it
1452
def __init__(self, transport_server, transport_readonly_server, formats):
1453
self._transport_server = transport_server
1454
self._transport_readonly_server = transport_readonly_server
1455
self._formats = formats
1457
def adapt(self, test):
1458
from bzrlib.tests import TestSuite
1459
result = TestSuite()
1460
for workingtree_format, bzrdir_format in self._formats:
1461
new_test = deepcopy(test)
1462
new_test.transport_server = self._transport_server
1463
new_test.transport_readonly_server = self._transport_readonly_server
1464
new_test.bzrdir_format = bzrdir_format
1465
new_test.workingtree_format = workingtree_format
1466
def make_new_test_id():
1467
new_id = "%s(%s)" % (new_test.id(), workingtree_format.__class__.__name__)
1468
return lambda: new_id
1469
new_test.id = make_new_test_id()
1470
result.addTest(new_test)