26
25
sha_file, appendpath, file_kind
28
from bzrlib.errors import (BzrError, InvalidRevisionNumber, InvalidRevisionId,
27
from bzrlib.errors import BzrError, InvalidRevisionNumber, InvalidRevisionId
30
29
from bzrlib.textui import show_status
31
30
from bzrlib.revision import Revision
31
from bzrlib.xml import unpack_xml
32
32
from bzrlib.delta import compare_trees
33
33
from bzrlib.tree import EmptyTree, RevisionTree
34
from bzrlib.inventory import Inventory
35
from bzrlib.weavestore import WeaveStore
36
from bzrlib.store import ImmutableStore
41
INVENTORY_FILEID = '__inventory'
42
ANCESTRY_FILEID = '__ancestry'
45
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
46
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
38
BZR_BRANCH_FORMAT = "Bazaar-NG branch, format 0.0.4\n"
47
39
## TODO: Maybe include checks for common corruption of newlines, etc?
50
42
# TODO: Some operations like log might retrieve the same revisions
51
43
# repeatedly to calculate deltas. We could perhaps have a weakref
52
# cache in memory to make this faster. In general anything can be
53
# cached in memory between lock and unlock operations.
44
# cache in memory to make this faster.
55
46
# TODO: please move the revision-string syntax stuff out of the branch
56
47
# object; it's clutter
309
301
raise BzrError("invalid controlfile mode %r" % mode)
311
303
def _make_control(self):
304
from bzrlib.inventory import Inventory
305
from bzrlib.xml import pack_xml
312
307
os.mkdir(self.controlfilename([]))
313
308
self.controlfile('README', 'w').write(
314
309
"This is a Bazaar-NG control directory.\n"
315
310
"Do not change any files in this directory.\n")
316
self.controlfile('branch-format', 'w').write(BZR_BRANCH_FORMAT_5)
317
for d in ('text-store', 'revision-store',
311
self.controlfile('branch-format', 'w').write(BZR_BRANCH_FORMAT)
312
for d in ('text-store', 'inventory-store', 'revision-store'):
319
313
os.mkdir(self.controlfilename(d))
320
314
for f in ('revision-history', 'merged-patches',
321
315
'pending-merged-patches', 'branch-name',
327
321
# if we want per-tree root ids then this is the place to set
328
322
# them; they're not needed for now and so ommitted for
330
f = self.controlfile('inventory','w')
331
bzrlib.xml5.serializer_v5.write_inventory(Inventory(), f)
324
pack_xml(Inventory(), self.controlfile('inventory','w'))
335
326
def _check_format(self):
336
327
"""Check this branch format is supported.
338
The format level is stored, as an integer, in
339
self._branch_format for code that needs to check it later.
329
The current tool only supports the current unstable format.
341
331
In the future, we might need different in-memory Branch
342
332
classes to support downlevel branches. But not yet.
334
# This ignores newlines so that we can open branches created
335
# on Windows from Linux and so on. I think it might be better
336
# to always make all internal files in unix format.
344
337
fmt = self.controlfile('branch-format', 'r').read()
345
if fmt == BZR_BRANCH_FORMAT_5:
346
self._branch_format = 5
348
raise BzrError('sorry, branch format "%s" not supported; '
349
'use a different bzr version, '
350
'or run "bzr upgrade", '
351
'or remove the .bzr directory and "bzr init" again'
352
% fmt.rstrip('\n\r'))
338
fmt.replace('\r\n', '')
339
if fmt != BZR_BRANCH_FORMAT:
340
raise BzrError('sorry, branch format %r not supported' % fmt,
341
['use a different bzr version',
342
'or remove the .bzr directory and "bzr init" again'])
354
344
def get_root_id(self):
355
345
"""Return the id of this branches root"""
371
361
def read_working_inventory(self):
372
362
"""Read the working inventory."""
363
from bzrlib.inventory import Inventory
364
from bzrlib.xml import unpack_xml
365
from time import time
375
369
# ElementTree does its own conversion from UTF-8, so open in
377
f = self.controlfile('inventory', 'rb')
378
return bzrlib.xml5.serializer_v5.read_inventory(f)
371
inv = unpack_xml(Inventory,
372
self.controlfile('inventory', 'rb'))
373
mutter("loaded inventory of %d items in %f"
374
% (len(inv), time() - before))
586
def get_revision_xml_file(self, revision_id):
584
def get_revision_xml(self, revision_id):
587
585
"""Return XML file object for revision object."""
588
586
if not revision_id or not isinstance(revision_id, basestring):
589
587
raise InvalidRevisionId(revision_id)
602
get_revision_xml = get_revision_xml_file
605
599
def get_revision(self, revision_id):
606
600
"""Return the Revision object for a named revision"""
607
xml_file = self.get_revision_xml_file(revision_id)
601
xml_file = self.get_revision_xml(revision_id)
610
r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
604
r = unpack_xml(Revision, xml_file)
611
605
except SyntaxError, e:
612
606
raise bzrlib.errors.BzrError('failed to unpack revision_xml',
651
645
return bzrlib.osutils.sha_file(self.get_revision_xml(revision_id))
654
def get_ancestry(self, revision_id):
655
"""Return a list of revision-ids integrated by a revision.
657
w = self.weave_store.get_weave(ANCESTRY_FILEID)
659
return [l[:-1] for l in w.get_iter(w.lookup(revision_id))]
662
def get_inventory_weave(self):
663
return self.weave_store.get_weave(INVENTORY_FILEID)
666
def get_inventory(self, revision_id):
667
"""Get Inventory object by hash."""
668
# FIXME: The text gets passed around a lot coming from the weave.
669
f = StringIO(self.get_inventory_xml(revision_id))
670
return bzrlib.xml5.serializer_v5.read_inventory(f)
673
def get_inventory_xml(self, revision_id):
648
def get_inventory(self, inventory_id):
649
"""Get Inventory object by hash.
651
TODO: Perhaps for this and similar methods, take a revision
652
parameter which can be either an integer revno or a
654
from bzrlib.inventory import Inventory
655
from bzrlib.xml import unpack_xml
657
return unpack_xml(Inventory, self.get_inventory_xml(inventory_id))
660
def get_inventory_xml(self, inventory_id):
674
661
"""Get inventory XML as a file object."""
676
assert isinstance(revision_id, basestring), type(revision_id)
677
iw = self.get_inventory_weave()
678
return iw.get_text(iw.lookup(revision_id))
680
raise bzrlib.errors.HistoryMissing(self, 'inventory', revision_id)
683
def get_inventory_sha1(self, revision_id):
662
return self.inventory_store[inventory_id]
665
def get_inventory_sha1(self, inventory_id):
684
666
"""Return the sha1 hash of the inventory entry
686
return self.get_revision(revision_id).inventory_sha1
668
return sha_file(self.get_inventory_xml(inventory_id))
689
671
def get_revision_inventory(self, revision_id):
690
672
"""Return inventory of a past revision."""
691
# bzr 0.0.6 and later imposes the constraint that the inventory_id
673
# bzr 0.0.6 imposes the constraint that the inventory_id
692
674
# must be the same as its revision, so this is trivial.
693
675
if revision_id == None:
676
from bzrlib.inventory import Inventory
694
677
return Inventory(self.get_root_id())
696
679
return self.get_inventory(revision_id)
716
699
>>> sb = ScratchBranch(files=['foo', 'foo~'])
717
700
>>> sb.common_ancestor(sb) == (None, None)
719
>>> commit.commit(sb, "Committing first revision")
702
>>> commit.commit(sb, "Committing first revision", verbose=False)
720
703
>>> sb.common_ancestor(sb)[0]
722
705
>>> clone = sb.clone()
723
>>> commit.commit(sb, "Committing second revision")
706
>>> commit.commit(sb, "Committing second revision", verbose=False)
724
707
>>> sb.common_ancestor(sb)[0]
726
709
>>> sb.common_ancestor(clone)[0]
728
>>> commit.commit(clone, "Committing divergent second revision")
711
>>> commit.commit(clone, "Committing divergent second revision",
729
713
>>> sb.common_ancestor(clone)[0]
731
715
>>> sb.common_ancestor(clone) == clone.common_ancestor(sb)
835
819
## note("Added %d revisions." % count)
822
def install_revisions(self, other, revision_ids, pb):
823
if hasattr(other.revision_store, "prefetch"):
824
other.revision_store.prefetch(revision_ids)
825
if hasattr(other.inventory_store, "prefetch"):
826
inventory_ids = [other.get_revision(r).inventory_id
827
for r in revision_ids]
828
other.inventory_store.prefetch(inventory_ids)
831
pb = bzrlib.ui.ui_factory.progress_bar()
838
for i, rev_id in enumerate(revision_ids):
839
pb.update('fetching revision', i+1, len(revision_ids))
841
rev = other.get_revision(rev_id)
842
except bzrlib.errors.NoSuchRevision:
846
revisions.append(rev)
847
inv = other.get_inventory(str(rev.inventory_id))
848
for key, entry in inv.iter_entries():
849
if entry.text_id is None:
851
if entry.text_id not in self.text_store:
852
needed_texts.add(entry.text_id)
856
count, cp_fail = self.text_store.copy_multi(other.text_store,
858
#print "Added %d texts." % count
859
inventory_ids = [ f.inventory_id for f in revisions ]
860
count, cp_fail = self.inventory_store.copy_multi(other.inventory_store,
862
#print "Added %d inventories." % count
863
revision_ids = [ f.revision_id for f in revisions]
865
count, cp_fail = self.revision_store.copy_multi(other.revision_store,
868
assert len(cp_fail) == 0
869
return count, failures
839
872
def commit(self, *args, **kw):
840
from bzrlib.commit import Commit
841
Commit().commit(self, *args, **kw)
873
from bzrlib.commit import commit
874
commit(self, *args, **kw)
844
877
def lookup_revision(self, revision):