28
28
import bzrlib.errors
29
29
from bzrlib.textui import show_status
30
30
from bzrlib.revision import Revision
31
from bzrlib.xml import unpack_xml
32
31
from bzrlib.delta import compare_trees
33
32
from bzrlib.tree import EmptyTree, RevisionTree
321
320
# if we want per-tree root ids then this is the place to set
322
321
# them; they're not needed for now and so ommitted for
324
pack_xml(Inventory(), self.controlfile('inventory','w'))
323
f = self.controlfile('inventory','w')
324
bzrlib.xml.serializer_v4.write_inventory(Inventory(), f)
326
327
def _check_format(self):
327
328
"""Check this branch format is supported.
335
336
# on Windows from Linux and so on. I think it might be better
336
337
# to always make all internal files in unix format.
337
338
fmt = self.controlfile('branch-format', 'r').read()
338
fmt.replace('\r\n', '')
339
fmt = fmt.replace('\r\n', '\n')
339
340
if fmt != BZR_BRANCH_FORMAT:
340
341
raise BzrError('sorry, branch format %r not supported' % fmt,
341
342
['use a different bzr version',
361
362
def read_working_inventory(self):
362
363
"""Read the working inventory."""
363
364
from bzrlib.inventory import Inventory
364
from bzrlib.xml import unpack_xml
365
from time import time
369
367
# ElementTree does its own conversion from UTF-8, so open in
371
inv = unpack_xml(Inventory,
372
self.controlfile('inventory', 'rb'))
373
mutter("loaded inventory of %d items in %f"
374
% (len(inv), time() - before))
369
f = self.controlfile('inventory', 'rb')
370
return bzrlib.xml.serializer_v4.read_inventory(f)
384
379
will be committed to the next revision.
386
381
from bzrlib.atomicfile import AtomicFile
387
from bzrlib.xml import pack_xml
389
383
self.lock_write()
391
385
f = AtomicFile(self.controlfilename('inventory'), 'wb')
387
bzrlib.xml.serializer_v4.write_inventory(inv, f)
584
def get_revision_xml(self, revision_id):
578
def get_revision_xml_file(self, revision_id):
585
579
"""Return XML file object for revision object."""
586
580
if not revision_id or not isinstance(revision_id, basestring):
587
581
raise InvalidRevisionId(revision_id)
594
get_revision_xml = get_revision_xml_file
599
597
def get_revision(self, revision_id):
600
598
"""Return the Revision object for a named revision"""
601
xml_file = self.get_revision_xml(revision_id)
599
xml_file = self.get_revision_xml_file(revision_id)
604
r = unpack_xml(Revision, xml_file)
602
r = bzrlib.xml.serializer_v4.read_revision(xml_file)
605
603
except SyntaxError, e:
606
604
raise bzrlib.errors.BzrError('failed to unpack revision_xml',
652
650
parameter which can be either an integer revno or a
654
652
from bzrlib.inventory import Inventory
655
from bzrlib.xml import unpack_xml
657
return unpack_xml(Inventory, self.get_inventory_xml(inventory_id))
654
f = self.get_inventory_xml_file(inventory_id)
655
return bzrlib.xml.serializer_v4.read_inventory(f)
660
658
def get_inventory_xml(self, inventory_id):
661
659
"""Get inventory XML as a file object."""
662
660
return self.inventory_store[inventory_id]
662
get_inventory_xml_file = get_inventory_xml
665
665
def get_inventory_sha1(self, inventory_id):
877
877
def lookup_revision(self, revision):
878
878
"""Return the revision identifier for a given revision information."""
879
revno, info = self.get_revision_info(revision)
879
revno, info = self._get_revision_info(revision)
897
897
revision can also be a string, in which case it is parsed for something like
898
898
'date:' or 'revid:' etc.
900
revno, rev_id = self._get_revision_info(revision)
902
raise bzrlib.errors.NoSuchRevision(self, revision)
905
def get_rev_id(self, revno, history=None):
906
"""Find the revision id of the specified revno."""
910
history = self.revision_history()
911
elif revno <= 0 or revno > len(history):
912
raise bzrlib.errors.NoSuchRevision(self, revno)
913
return history[revno - 1]
915
def _get_revision_info(self, revision):
916
"""Return (revno, revision id) for revision specifier.
918
revision can be an integer, in which case it is assumed to be revno
919
(though this will translate negative values into positive ones)
920
revision can also be a string, in which case it is parsed for something
921
like 'date:' or 'revid:' etc.
923
A revid is always returned. If it is None, the specifier referred to
924
the null revision. If the revid does not occur in the revision
925
history, revno will be None.
900
928
if revision is None:
907
935
revs = self.revision_history()
908
936
if isinstance(revision, int):
911
# Mabye we should do this first, but we don't need it if revision == 0
913
938
revno = len(revs) + revision + 1
941
rev_id = self.get_rev_id(revno, revs)
916
942
elif isinstance(revision, basestring):
917
943
for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
918
944
if revision.startswith(prefix):
919
revno = func(self, revs, revision)
945
result = func(self, revs, revision)
947
revno, rev_id = result
950
rev_id = self.get_rev_id(revno, revs)
922
raise BzrError('No namespace registered for string: %r' % revision)
953
raise BzrError('No namespace registered for string: %r' %
956
raise TypeError('Unhandled revision type %s' % revision)
924
if revno is None or revno <= 0 or revno > len(revs):
925
raise BzrError("no such revision %s" % revision)
926
return revno, revs[revno-1]
960
raise bzrlib.errors.NoSuchRevision(self, revision)
928
963
def _namespace_revno(self, revs, revision):
929
964
"""Lookup a revision by revision number"""
930
965
assert revision.startswith('revno:')
932
return int(revision[6:])
967
return (int(revision[6:]),)
933
968
except ValueError:
935
970
REVISION_NAMESPACES['revno:'] = _namespace_revno
937
972
def _namespace_revid(self, revs, revision):
938
973
assert revision.startswith('revid:')
974
rev_id = revision[len('revid:'):]
940
return revs.index(revision[6:]) + 1
976
return revs.index(rev_id) + 1, rev_id
941
977
except ValueError:
943
979
REVISION_NAMESPACES['revid:'] = _namespace_revid
945
981
def _namespace_last(self, revs, revision):
948
984
offset = int(revision[5:])
949
985
except ValueError:
953
989
raise BzrError('You must supply a positive value for --revision last:XXX')
954
return len(revs) - offset + 1
990
return (len(revs) - offset + 1,)
955
991
REVISION_NAMESPACES['last:'] = _namespace_last
957
993
def _namespace_tag(self, revs, revision):
1032
1068
# TODO: Handle timezone.
1033
1069
dt = datetime.datetime.fromtimestamp(r.timestamp)
1034
1070
if first >= dt and (last is None or dt >= last):
1037
1073
for i in range(len(revs)):
1038
1074
r = self.get_revision(revs[i])
1039
1075
# TODO: Handle timezone.
1040
1076
dt = datetime.datetime.fromtimestamp(r.timestamp)
1041
1077
if first <= dt and (last is None or dt <= last):
1043
1079
REVISION_NAMESPACES['date:'] = _namespace_date
1045
1081
def revision_tree(self, revision_id):
1319
def get_parent(self):
1320
"""Return the parent location of the branch.
1322
This is the default location for push/pull/missing. The usual
1323
pattern is that the user can override it by specifying a
1327
_locs = ['parent', 'pull', 'x-pull']
1330
return self.controlfile(l, 'r').read().strip('\n')
1332
if e.errno != errno.ENOENT:
1337
def set_parent(self, url):
1338
# TODO: Maybe delete old location files?
1339
from bzrlib.atomicfile import AtomicFile
1342
f = AtomicFile(self.controlfilename('parent'))
1351
def check_revno(self, revno):
1353
Check whether a revno corresponds to any revision.
1354
Zero (the NULL revision) is considered valid.
1357
self.check_real_revno(revno)
1359
def check_real_revno(self, revno):
1361
Check whether a revno corresponds to a real revision.
1362
Zero (the NULL revision) is considered invalid
1364
if revno < 1 or revno > self.revno():
1365
raise InvalidRevisionNumber(revno)
1284
1370
class ScratchBranch(Branch):
1285
1371
"""Special test class: a branch that cleans up after itself.
1415
1503
def copy_branch(branch_from, to_location, revision=None):
1416
1504
"""Copy branch_from into the existing directory to_location.
1418
If revision is not None, the head of the new branch will be revision.
1507
If not None, only revisions up to this point will be copied.
1508
The head of the new branch will be that revision.
1511
The name of a local directory that exists but is empty.
1420
1513
from bzrlib.merge import merge
1421
1514
from bzrlib.branch import Branch
1516
assert isinstance(branch_from, Branch)
1517
assert isinstance(to_location, basestring)
1422
1519
br_to = Branch(to_location, init=True)
1423
1520
br_to.set_root_id(branch_from.get_root_id())
1424
1521
if revision is None:
1428
1525
br_to.update_revisions(branch_from, stop_revision=revno)
1429
1526
merge((to_location, -1), (to_location, 0), this_dir=to_location,
1430
1527
check_clean=False, ignore_zero=True)
1431
1529
from_location = pull_loc(branch_from)
1432
br_to.controlfile("x-pull", "wb").write(from_location + "\n")
1530
br_to.set_parent(pull_loc(branch_from))