14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
from cStringIO import StringIO
19
from bzrlib.lazy_import import lazy_import
20
lazy_import(globals(), """
17
21
from binascii import hexlify
18
22
from copy import deepcopy
19
from cStringIO import StringIO
22
from unittest import TestSuite
24
27
from bzrlib import (
38
revision as _mod_revision,
47
from bzrlib.osutils import (
52
from bzrlib.revisiontree import RevisionTree
53
from bzrlib.store.versioned import VersionedFileStore
54
from bzrlib.store.text import TextStore
55
from bzrlib.testament import Testament
36
58
from bzrlib.decorators import needs_read_lock, needs_write_lock
37
from bzrlib.errors import InvalidRevisionId
38
from bzrlib.graph import Graph
39
59
from bzrlib.inter import InterObject
40
60
from bzrlib.inventory import Inventory, InventoryDirectory, ROOT_ID
41
from bzrlib.knit import KnitVersionedFile, KnitPlainFactory
42
from bzrlib.lockable_files import LockableFiles, TransportLock
43
from bzrlib.lockdir import LockDir
44
from bzrlib.osutils import (safe_unicode, rand_bytes, compact_date,
46
from bzrlib.revision import NULL_REVISION, Revision
47
from bzrlib.revisiontree import RevisionTree
48
from bzrlib.store.versioned import VersionedFileStore, WeaveStore
49
from bzrlib.store.text import TextStore
50
from bzrlib import symbol_versioning
51
from bzrlib.symbol_versioning import (deprecated_method,
61
from bzrlib.symbol_versioning import (
54
from bzrlib.testament import Testament
55
65
from bzrlib.trace import mutter, note, warning
56
from bzrlib.tsort import topo_sort
57
from bzrlib.weave import WeaveFile
60
68
# Old formats display a warning, but only once
317
325
or testing the revision graph.
319
327
if not revision_id or not isinstance(revision_id, basestring):
320
raise InvalidRevisionId(revision_id=revision_id, branch=self)
328
raise errors.InvalidRevisionId(revision_id=revision_id,
321
330
return self._revision_store.get_revisions([revision_id],
322
331
self.get_transaction())[0]
426
435
# revisions. We don't need to see all lines in the inventory because
427
436
# only those added in an inventory in rev X can contain a revision=X
429
for line in w.iter_lines_added_or_present_in_versions(selected_revision_ids):
430
start = line.find('file_id="')+9
431
if start < 9: continue
432
end = line.find('"', start)
434
file_id = _unescape_xml(line[start:end])
438
pb = ui.ui_factory.nested_progress_bar()
440
for line in w.iter_lines_added_or_present_in_versions(
441
selected_revision_ids, pb=pb):
442
start = line.find('file_id="')+9
443
if start < 9: continue
444
end = line.find('"', start)
446
file_id = _unescape_xml(line[start:end])
436
start = line.find('revision="')+10
437
if start < 10: continue
438
end = line.find('"', start)
440
revision_id = _unescape_xml(line[start:end])
441
if revision_id in selected_revision_ids:
442
result.setdefault(file_id, set()).add(revision_id)
448
start = line.find('revision="')+10
449
if start < 10: continue
450
end = line.find('"', start)
452
revision_id = _unescape_xml(line[start:end])
453
if revision_id in selected_revision_ids:
454
result.setdefault(file_id, set()).add(revision_id)
492
506
:return: a dictionary of revision_id->revision_parents_list.
494
508
# special case NULL_REVISION
495
if revision_id == NULL_REVISION:
509
if revision_id == _mod_revision.NULL_REVISION:
497
weave = self.get_inventory_weave()
498
all_revisions = self._eliminate_revisions_not_present(weave.versions())
499
entire_graph = dict([(node, weave.get_parents(node)) for
511
a_weave = self.get_inventory_weave()
512
all_revisions = self._eliminate_revisions_not_present(
514
entire_graph = dict([(node, a_weave.get_parents(node)) for
500
515
node in all_revisions])
501
516
if revision_id is None:
502
517
return entire_graph
521
536
:param revision_ids: an iterable of revisions to graph or None for all.
522
537
:return: a Graph object with the graph reachable from revision_ids.
539
result = graph.Graph()
525
540
if not revision_ids:
526
541
pending = set(self.all_revision_ids())
527
542
required = set([])
529
544
pending = set(revision_ids)
530
545
# special case NULL_REVISION
531
if NULL_REVISION in pending:
532
pending.remove(NULL_REVISION)
546
if _mod_revision.NULL_REVISION in pending:
547
pending.remove(_mod_revision.NULL_REVISION)
533
548
required = set(pending)
535
550
while len(pending):
590
605
# TODO: refactor this to use an existing revision object
591
606
# so we don't need to read it in twice.
592
if revision_id is None or revision_id == NULL_REVISION:
593
return RevisionTree(self, Inventory(), NULL_REVISION)
607
if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
608
return RevisionTree(self, Inventory(root_id=None),
609
_mod_revision.NULL_REVISION)
595
611
inv = self.get_revision_inventory(revision_id)
596
612
return RevisionTree(self, inv, revision_id)
602
618
`revision_id` may not be None or 'null:'"""
603
619
assert None not in revision_ids
604
assert NULL_REVISION not in revision_ids
620
assert _mod_revision.NULL_REVISION not in revision_ids
605
621
texts = self.get_inventory_weave().get_texts(revision_ids)
606
622
for text, revision_id in zip(texts, revision_ids):
607
623
inv = self.deserialise_inventory(revision_id, text)
709
725
warning("Format %s for %s is deprecated - please use 'bzr upgrade' to get better performance"
710
726
% (self._format, self.bzrdir.transport.base))
728
def supports_rich_root(self):
729
return self._format.rich_root_data
713
732
class AllInOneRepository(Repository):
714
733
"""Legacy support - the repository behaviour for all-in-one branches."""
777
796
parent_trees[p_id] = repository.revision_tree(None)
779
798
inv = revision_tree.inventory
799
entries = inv.iter_entries()
781
800
# backwards compatability hack: skip the root id.
782
entries = inv.iter_entries()
801
if not repository.supports_rich_root():
802
path, root = entries.next()
803
if root.revision != rev.revision_id:
804
raise errors.IncompatibleRevision(repr(repository))
784
805
# Add the texts that are not already present
785
806
for path, ie in entries:
786
807
w = repository.weave_store.get_weave_or_empty(ie.file_id,
923
944
:return: a dictionary of revision_id->revision_parents_list.
925
946
# special case NULL_REVISION
926
if revision_id == NULL_REVISION:
947
if revision_id == _mod_revision.NULL_REVISION:
928
weave = self._get_revision_vf()
929
entire_graph = weave.get_graph()
949
a_weave = self._get_revision_vf()
950
entire_graph = a_weave.get_graph()
930
951
if revision_id is None:
931
return weave.get_graph()
932
elif revision_id not in weave:
952
return a_weave.get_graph()
953
elif revision_id not in a_weave:
933
954
raise errors.NoSuchRevision(self, revision_id)
935
956
# add what can be reached from revision_id
960
981
pending = set(revision_ids)
961
982
# special case NULL_REVISION
962
if NULL_REVISION in pending:
963
pending.remove(NULL_REVISION)
983
if _mod_revision.NULL_REVISION in pending:
984
pending.remove(_mod_revision.NULL_REVISION)
964
985
required = set(pending)
966
987
while len(pending):
1214
1235
TODO: when creating split out bzr branch formats, move this to a common
1215
1236
base for Format5, Format6. or something like that.
1217
from bzrlib.weavefile import write_weave_v5
1218
from bzrlib.weave import Weave
1221
1239
raise errors.IncompatibleFormat(self, a_bzrdir._format)
1237
1255
# FIXME: RBC 20060125 don't peek under the covers
1238
1256
# NB: no need to escape relative paths that are url safe.
1239
control_files = LockableFiles(a_bzrdir.transport, 'branch-lock',
1257
control_files = lockable_files.LockableFiles(a_bzrdir.transport,
1258
'branch-lock', lockable_files.TransportLock)
1241
1259
control_files.create_lock()
1242
1260
control_files.lock_write()
1243
1261
control_files._transport.mkdir_multi(dirs,
1407
1425
# FIXME: RBC 20060125 don't peek under the covers
1408
1426
# NB: no need to escape relative paths that are url safe.
1409
1427
repository_transport = a_bzrdir.get_repository_transport(self)
1410
control_files = LockableFiles(repository_transport, 'lock', LockDir)
1428
control_files = lockable_files.LockableFiles(repository_transport,
1429
'lock', lockdir.LockDir)
1411
1430
control_files.create_lock()
1412
1431
return control_files
1479
1498
:param shared: If true the repository will be initialized as a shared
1482
from bzrlib.weavefile import write_weave_v5
1483
from bzrlib.weave import Weave
1485
1501
# Create an empty weave
1486
1502
sio = StringIO()
1487
write_weave_v5(Weave(), sio)
1503
weavefile.write_weave_v5(weave.Weave(), sio)
1488
1504
empty_weave = sio.getvalue()
1490
1506
mutter('creating repository in %s.', a_bzrdir.transport.base)
1510
1526
repo_transport = _override_transport
1512
1528
repo_transport = a_bzrdir.get_repository_transport(None)
1513
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1529
control_files = lockable_files.LockableFiles(repo_transport,
1530
'lock', lockdir.LockDir)
1514
1531
text_store = self._get_text_store(repo_transport, control_files)
1515
1532
control_store = self._get_control_store(repo_transport, control_files)
1516
1533
_revision_store = self._get_revision_store(repo_transport, control_files)
1542
1559
repo_transport,
1543
1560
prefixed=False,
1544
1561
file_mode=control_files._file_mode,
1545
versionedfile_class=KnitVersionedFile,
1546
versionedfile_kwargs={'factory':KnitPlainFactory()},
1562
versionedfile_class=knit.KnitVersionedFile,
1563
versionedfile_kwargs={'factory':knit.KnitPlainFactory()},
1549
1566
def _get_revision_store(self, repo_transport, control_files):
1554
1571
file_mode=control_files._file_mode,
1555
1572
prefixed=False,
1557
versionedfile_class=KnitVersionedFile,
1558
versionedfile_kwargs={'delta':False, 'factory':KnitPlainFactory(),},
1574
versionedfile_class=knit.KnitVersionedFile,
1575
versionedfile_kwargs={'delta':False,
1576
'factory':knit.KnitPlainFactory(),
1561
1580
return KnitRevisionStore(versioned_file_store)
1563
1582
def _get_text_store(self, transport, control_files):
1564
1583
"""See RepositoryFormat._get_text_store()."""
1565
1584
return self._get_versioned_file_store('knits',
1568
versionedfile_class=KnitVersionedFile,
1569
versionedfile_kwargs={
1570
'create_parent_dir':True,
1571
'delay_create':True,
1572
'dir_mode':control_files._dir_mode,
1587
versionedfile_class=knit.KnitVersionedFile,
1588
versionedfile_kwargs={
1589
'create_parent_dir':True,
1590
'delay_create':True,
1591
'dir_mode':control_files._dir_mode,
1576
1595
def initialize(self, a_bzrdir, shared=False):
1577
1596
"""Create a knit format 1 repository.
1589
1608
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1590
1609
repo_transport = a_bzrdir.get_repository_transport(None)
1591
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1610
control_files = lockable_files.LockableFiles(repo_transport,
1611
'lock', lockdir.LockDir)
1592
1612
control_store = self._get_control_store(repo_transport, control_files)
1593
1613
transaction = transactions.WriteTransaction()
1594
1614
# trigger a write of the inventory store.
1612
1632
repo_transport = _override_transport
1614
1634
repo_transport = a_bzrdir.get_repository_transport(None)
1615
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1635
control_files = lockable_files.LockableFiles(repo_transport,
1636
'lock', lockdir.LockDir)
1616
1637
text_store = self._get_text_store(repo_transport, control_files)
1617
1638
control_store = self._get_control_store(repo_transport, control_files)
1618
1639
_revision_store = self._get_revision_store(repo_transport, control_files)
1697
1718
repo_transport = _override_transport
1699
1720
repo_transport = a_bzrdir.get_repository_transport(None)
1700
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1721
control_files = lockable_files.LockableFiles(repo_transport, 'lock',
1701
1723
text_store = self._get_text_store(repo_transport, control_files)
1702
1724
control_store = self._get_control_store(repo_transport, control_files)
1703
1725
_revision_store = self._get_revision_store(repo_transport, control_files)
1818
1840
if basis is not None:
1819
1841
self.target.fetch(basis, revision_id=revision_id)
1820
1842
# but don't bother fetching if we have the needed data now.
1821
if (revision_id not in (None, NULL_REVISION) and
1843
if (revision_id not in (None, _mod_revision.NULL_REVISION) and
1822
1844
self.target.has_revision(revision_id)):
1824
1846
self.target.fetch(self.source, revision_id=revision_id)
2062
2084
if basis is not None:
2063
2085
self.target.fetch(basis, revision_id=revision_id)
2064
2086
# but don't bother fetching if we have the needed data now.
2065
if (revision_id not in (None, NULL_REVISION) and
2087
if (revision_id not in (None, _mod_revision.NULL_REVISION) and
2066
2088
self.target.has_revision(revision_id)):
2068
2090
self.target.fetch(self.source, revision_id=revision_id)
2297
2319
:return: The revision id of the recorded revision.
2299
rev = Revision(timestamp=self._timestamp,
2321
rev = _mod_revision.Revision(
2322
timestamp=self._timestamp,
2300
2323
timezone=self._timezone,
2301
2324
committer=self._committer,
2302
2325
message=message,
2308
2331
self.new_inventory, self._config)
2309
2332
return self._new_revision_id
2334
def revision_tree(self):
2335
"""Return the tree that was just committed.
2337
After calling commit() this can be called to get a RevisionTree
2338
representing the newly committed tree. This is preferred to
2339
calling Repository.revision_tree() because that may require
2340
deserializing the inventory, while we already have a copy in
2343
return RevisionTree(self.repository, self.new_inventory,
2344
self._new_revision_id)
2311
2346
def finish_inventory(self):
2312
2347
"""Tell the builder that the inventory is finished."""
2313
2348
if self.new_inventory.root is None:
2370
2405
# In this revision format, root entries have no knit or weave
2371
2406
if ie is self.new_inventory.root:
2372
if len(parent_invs):
2373
ie.revision = parent_invs[0].root.revision
2407
# When serializing out to disk and back in
2408
# root.revision is always _new_revision_id
2409
ie.revision = self._new_revision_id
2377
2411
previous_entries = ie.find_previous_heads(