194
207
(branch.base is not cross checked, because for remote branches that
195
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
197
226
from bzrlib.hashcache import HashCache
198
227
from bzrlib.trace import note, mutter
199
228
assert isinstance(basedir, basestring), \
200
229
"base directory %r is not a string" % basedir
201
230
basedir = safe_unicode(basedir)
202
mutter("openeing working tree %r", basedir)
204
branch = Branch.open(basedir)
205
assert isinstance(branch, Branch), \
206
"branch %r is not a Branch" % branch
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
208
244
self.basedir = realpath(basedir)
209
245
# if branch is at our basedir and is a format 6 or less
210
if (isinstance(self.branch._branch_format,
211
(BzrBranchFormat4, BzrBranchFormat5, BzrBranchFormat6))
212
# might be able to share control object
213
and self.branch.base.split('/')[-2] == self.basedir.split('/')[-1]):
246
if isinstance(self._format, WorkingTreeFormat2):
247
# share control object
214
248
self._control_files = self.branch.control_files
215
249
elif _control_files is not None:
216
250
assert False, "not done yet"
217
251
# self._control_files = _control_files
253
# only ready for format 3
254
assert isinstance(self._format, WorkingTreeFormat3)
219
255
self._control_files = LockableFiles(
220
get_transport(self.basedir).clone(bzrlib.BZRDIR), 'branch-lock')
256
self.bzrdir.get_workingtree_transport(None),
222
259
# update the whole cache up front and write to disk if anything changed;
223
260
# in the future we might want to do this more selectively
861
952
def kind(self, file_id):
862
953
return file_kind(self.id2abspath(file_id))
956
def last_revision(self):
957
"""Return the last revision id of this working tree.
959
In early branch formats this was == the branch last_revision,
960
but that cannot be relied upon - for working tree operations,
961
always use tree.last_revision().
963
return self.branch.last_revision()
864
965
def lock_read(self):
865
966
"""See Branch.lock_read, and WorkingTree.unlock."""
866
return self.branch.lock_read()
967
self.branch.lock_read()
969
return self._control_files.lock_read()
868
974
def lock_write(self):
869
975
"""See Branch.lock_write, and WorkingTree.unlock."""
870
return self.branch.lock_write()
976
self.branch.lock_write()
978
return self._control_files.lock_write()
872
983
def _basis_inventory_name(self, revision_id):
873
984
return 'basis-inventory.%s' % revision_id
875
987
def set_last_revision(self, new_revision, old_revision=None):
876
if old_revision is not None:
878
path = self._basis_inventory_name(old_revision)
879
path = self.branch.control_files._escape(path)
880
self.branch.control_files._transport.delete(path)
988
"""Change the last revision in the working tree."""
989
self._remove_old_basis(old_revision)
990
if self._change_last_revision(new_revision):
991
self._cache_basis_inventory(new_revision)
993
def _change_last_revision(self, new_revision):
994
"""Template method part of set_last_revision to perform the change."""
995
if new_revision is None:
996
self.branch.set_revision_history([])
998
# current format is locked in with the branch
999
revision_history = self.branch.revision_history()
1001
position = revision_history.index(new_revision)
1003
raise errors.NoSuchRevision(self.branch, new_revision)
1004
self.branch.set_revision_history(revision_history[:position + 1])
1007
def _cache_basis_inventory(self, new_revision):
1008
"""Cache new_revision as the basis inventory."""
884
1010
xml = self.branch.repository.get_inventory_xml(new_revision)
885
1011
path = self._basis_inventory_name(new_revision)
886
self.branch.control_files.put_utf8(path, xml)
1012
self._control_files.put_utf8(path, xml)
887
1013
except WeaveRevisionNotPresent:
1016
def _remove_old_basis(self, old_revision):
1017
"""Remove the old basis inventory 'old_revision'."""
1018
if old_revision is not None:
1020
path = self._basis_inventory_name(old_revision)
1021
path = self._control_files._escape(path)
1022
self._control_files._transport.delete(path)
890
1026
def read_basis_inventory(self, revision_id):
891
1027
"""Read the cached basis inventory."""
892
1028
path = self._basis_inventory_name(revision_id)
893
return self.branch.control_files.get_utf8(path).read()
1029
return self._control_files.get_utf8(path).read()
895
1031
@needs_read_lock
896
1032
def read_working_inventory(self):
897
1033
"""Read the working inventory."""
898
1034
# ElementTree does its own conversion from UTF-8, so open in
900
return bzrlib.xml5.serializer_v5.read_inventory(
1036
result = bzrlib.xml5.serializer_v5.read_inventory(
901
1037
self._control_files.get('inventory'))
1038
self._set_inventory(result)
903
1041
@needs_write_lock
904
1042
def remove(self, files, verbose=False):
1018
1184
self._control_files.put('inventory', sio)
1019
1185
self._set_inventory(inv)
1020
1186
mutter('wrote working inventory')
1189
class WorkingTree3(WorkingTree):
1190
"""This is the Format 3 working tree.
1192
This differs from the base WorkingTree by:
1193
- having its own file lock
1194
- having its own last-revision property.
1198
def last_revision(self):
1199
"""See WorkingTree.last_revision."""
1201
return self._control_files.get_utf8('last-revision').read()
1205
def _change_last_revision(self, revision_id):
1206
"""See WorkingTree._change_last_revision."""
1207
if revision_id is None or revision_id == NULL_REVISION:
1209
self._control_files._transport.delete('last-revision')
1210
except errors.NoSuchFile:
1215
self.branch.revision_history().index(revision_id)
1217
raise errors.NoSuchRevision(self.branch, revision_id)
1218
self._control_files.put_utf8('last-revision', revision_id)
1023
1222
CONFLICT_SUFFIXES = ('.THIS', '.BASE', '.OTHER')
1024
1223
def get_conflicted_stem(path):
1025
1224
for suffix in CONFLICT_SUFFIXES:
1026
1225
if path.endswith(suffix):
1027
1226
return path[:-len(suffix)]
1228
@deprecated_function(zero_eight)
1229
def is_control_file(filename):
1230
"""See WorkingTree.is_control_filename(filename)."""
1231
## FIXME: better check
1232
filename = normpath(filename)
1233
while filename != '':
1234
head, tail = os.path.split(filename)
1235
## mutter('check %r for control file' % ((head, tail),))
1238
if filename == head:
1244
class WorkingTreeFormat(object):
1245
"""An encapsulation of the initialization and open routines for a format.
1247
Formats provide three things:
1248
* An initialization routine,
1252
Formats are placed in an dict by their format string for reference
1253
during workingtree opening. Its not required that these be instances, they
1254
can be classes themselves with class methods - it simply depends on
1255
whether state is needed for a given format or not.
1257
Once a format is deprecated, just deprecate the initialize and open
1258
methods on the format class. Do not deprecate the object, as the
1259
object will be created every time regardless.
1262
_default_format = None
1263
"""The default format used for new trees."""
1266
"""The known formats."""
1269
def find_format(klass, a_bzrdir):
1270
"""Return the format for the working tree object in a_bzrdir."""
1272
transport = a_bzrdir.get_workingtree_transport(None)
1273
format_string = transport.get("format").read()
1274
return klass._formats[format_string]
1276
raise errors.NoWorkingTree(base=transport.base)
1278
raise errors.UnknownFormatError(format_string)
1281
def get_default_format(klass):
1282
"""Return the current default format."""
1283
return klass._default_format
1285
def get_format_string(self):
1286
"""Return the ASCII format string that identifies this format."""
1287
raise NotImplementedError(self.get_format_string)
1289
def is_supported(self):
1290
"""Is this format supported?
1292
Supported formats can be initialized and opened.
1293
Unsupported formats may not support initialization or committing or
1294
some other features depending on the reason for not being supported.
1299
def register_format(klass, format):
1300
klass._formats[format.get_format_string()] = format
1303
def set_default_format(klass, format):
1304
klass._default_format = format
1307
def unregister_format(klass, format):
1308
assert klass._formats[format.get_format_string()] is format
1309
del klass._formats[format.get_format_string()]
1313
class WorkingTreeFormat2(WorkingTreeFormat):
1314
"""The second working tree format.
1316
This format modified the hash cache from the format 1 hash cache.
1319
def initialize(self, a_bzrdir, revision_id=None):
1320
"""See WorkingTreeFormat.initialize()."""
1321
if not isinstance(a_bzrdir.transport, LocalTransport):
1322
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1323
branch = a_bzrdir.open_branch()
1324
if revision_id is not None:
1327
revision_history = branch.revision_history()
1329
position = revision_history.index(revision_id)
1331
raise errors.NoSuchRevision(branch, revision_id)
1332
branch.set_revision_history(revision_history[:position + 1])
1335
revision = branch.last_revision()
1337
wt = WorkingTree(a_bzrdir.root_transport.base,
1343
wt._write_inventory(inv)
1344
wt.set_root_id(inv.root.file_id)
1345
wt.set_last_revision(revision)
1346
wt.set_pending_merges([])
1347
build_tree(wt.basis_tree(), wt)
1351
super(WorkingTreeFormat2, self).__init__()
1352
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1354
def open(self, a_bzrdir, _found=False):
1355
"""Return the WorkingTree object for a_bzrdir
1357
_found is a private parameter, do not use it. It is used to indicate
1358
if format probing has already been done.
1361
# we are being called directly and must probe.
1362
raise NotImplementedError
1363
if not isinstance(a_bzrdir.transport, LocalTransport):
1364
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1365
return WorkingTree(a_bzrdir.root_transport.base,
1371
class WorkingTreeFormat3(WorkingTreeFormat):
1372
"""The second working tree format updated to record a format marker.
1374
This format modified the hash cache from the format 1 hash cache.
1377
def get_format_string(self):
1378
"""See WorkingTreeFormat.get_format_string()."""
1379
return "Bazaar-NG Working Tree format 3"
1381
def initialize(self, a_bzrdir, revision_id=None):
1382
"""See WorkingTreeFormat.initialize().
1384
revision_id allows creating a working tree at a differnet
1385
revision than the branch is at.
1387
if not isinstance(a_bzrdir.transport, LocalTransport):
1388
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1389
transport = a_bzrdir.get_workingtree_transport(self)
1390
control_files = LockableFiles(transport, 'lock')
1391
control_files.put_utf8('format', self.get_format_string())
1392
branch = a_bzrdir.open_branch()
1393
if revision_id is None:
1394
revision_id = branch.last_revision()
1396
wt = WorkingTree3(a_bzrdir.root_transport.base,
1402
wt._write_inventory(inv)
1403
wt.set_root_id(inv.root.file_id)
1404
wt.set_last_revision(revision_id)
1405
wt.set_pending_merges([])
1406
build_tree(wt.basis_tree(), wt)
1410
super(WorkingTreeFormat3, self).__init__()
1411
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1413
def open(self, a_bzrdir, _found=False):
1414
"""Return the WorkingTree object for a_bzrdir
1416
_found is a private parameter, do not use it. It is used to indicate
1417
if format probing has already been done.
1420
# we are being called directly and must probe.
1421
raise NotImplementedError
1422
if not isinstance(a_bzrdir.transport, LocalTransport):
1423
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1424
return WorkingTree3(a_bzrdir.root_transport.base,
1430
return self.get_format_string()
1433
# formats which have no format string are not discoverable
1434
# and not independently creatable, so are not registered.
1435
__default_format = WorkingTreeFormat3()
1436
WorkingTreeFormat.register_format(__default_format)
1437
WorkingTreeFormat.set_default_format(__default_format)
1438
_legacy_formats = [WorkingTreeFormat2(),
1442
class WorkingTreeTestProviderAdapter(object):
1443
"""A tool to generate a suite testing multiple workingtree formats at once.
1445
This is done by copying the test once for each transport and injecting
1446
the transport_server, transport_readonly_server, and workingtree_format
1447
classes into each copy. Each copy is also given a new id() to make it
1451
def __init__(self, transport_server, transport_readonly_server, formats):
1452
self._transport_server = transport_server
1453
self._transport_readonly_server = transport_readonly_server
1454
self._formats = formats
1456
def adapt(self, test):
1457
from bzrlib.tests import TestSuite
1458
result = TestSuite()
1459
for workingtree_format, bzrdir_format in self._formats:
1460
new_test = deepcopy(test)
1461
new_test.transport_server = self._transport_server
1462
new_test.transport_readonly_server = self._transport_readonly_server
1463
new_test.bzrdir_format = bzrdir_format
1464
new_test.workingtree_format = workingtree_format
1465
def make_new_test_id():
1466
new_id = "%s(%s)" % (new_test.id(), workingtree_format.__class__.__name__)
1467
return lambda: new_id
1468
new_test.id = make_new_test_id()
1469
result.addTest(new_test)