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 (
39
revision as _mod_revision,
48
from bzrlib.osutils import (
53
from bzrlib.revisiontree import RevisionTree
54
from bzrlib.store.versioned import VersionedFileStore
55
from bzrlib.store.text import TextStore
56
from bzrlib.testament import Testament
36
59
from bzrlib.decorators import needs_read_lock, needs_write_lock
37
from bzrlib.errors import InvalidRevisionId
38
from bzrlib.graph import Graph
39
60
from bzrlib.inter import InterObject
40
61
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,
62
from bzrlib.symbol_versioning import (
54
from bzrlib.testament import Testament
55
66
from bzrlib.trace import mutter, note, warning
56
from bzrlib.tsort import topo_sort
57
from bzrlib.weave import WeaveFile
60
69
# Old formats display a warning, but only once
317
326
or testing the revision graph.
319
328
if not revision_id or not isinstance(revision_id, basestring):
320
raise InvalidRevisionId(revision_id=revision_id, branch=self)
329
raise errors.InvalidRevisionId(revision_id=revision_id,
321
331
return self._revision_store.get_revisions([revision_id],
322
332
self.get_transaction())[0]
426
436
# revisions. We don't need to see all lines in the inventory because
427
437
# 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])
439
pb = ui.ui_factory.nested_progress_bar()
441
for line in w.iter_lines_added_or_present_in_versions(
442
selected_revision_ids, pb=pb):
443
start = line.find('file_id="')+9
444
if start < 9: continue
445
end = line.find('"', start)
447
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)
449
start = line.find('revision="')+10
450
if start < 10: continue
451
end = line.find('"', start)
453
revision_id = _unescape_xml(line[start:end])
454
if revision_id in selected_revision_ids:
455
result.setdefault(file_id, set()).add(revision_id)
492
507
:return: a dictionary of revision_id->revision_parents_list.
494
509
# special case NULL_REVISION
495
if revision_id == NULL_REVISION:
510
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
512
a_weave = self.get_inventory_weave()
513
all_revisions = self._eliminate_revisions_not_present(
515
entire_graph = dict([(node, a_weave.get_parents(node)) for
500
516
node in all_revisions])
501
517
if revision_id is None:
502
518
return entire_graph
521
537
:param revision_ids: an iterable of revisions to graph or None for all.
522
538
:return: a Graph object with the graph reachable from revision_ids.
540
result = graph.Graph()
525
541
if not revision_ids:
526
542
pending = set(self.all_revision_ids())
527
543
required = set([])
529
545
pending = set(revision_ids)
530
546
# special case NULL_REVISION
531
if NULL_REVISION in pending:
532
pending.remove(NULL_REVISION)
547
if _mod_revision.NULL_REVISION in pending:
548
pending.remove(_mod_revision.NULL_REVISION)
533
549
required = set(pending)
535
551
while len(pending):
590
606
# TODO: refactor this to use an existing revision object
591
607
# 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)
608
if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
609
return RevisionTree(self, Inventory(root_id=None),
610
_mod_revision.NULL_REVISION)
595
612
inv = self.get_revision_inventory(revision_id)
596
613
return RevisionTree(self, inv, revision_id)
602
619
`revision_id` may not be None or 'null:'"""
603
620
assert None not in revision_ids
604
assert NULL_REVISION not in revision_ids
621
assert _mod_revision.NULL_REVISION not in revision_ids
605
622
texts = self.get_inventory_weave().get_texts(revision_ids)
606
623
for text, revision_id in zip(texts, revision_ids):
607
624
inv = self.deserialise_inventory(revision_id, text)
709
726
warning("Format %s for %s is deprecated - please use 'bzr upgrade' to get better performance"
710
727
% (self._format, self.bzrdir.transport.base))
729
def supports_rich_root(self):
730
return self._format.rich_root_data
713
733
class AllInOneRepository(Repository):
714
734
"""Legacy support - the repository behaviour for all-in-one branches."""
777
797
parent_trees[p_id] = repository.revision_tree(None)
779
799
inv = revision_tree.inventory
800
entries = inv.iter_entries()
781
801
# backwards compatability hack: skip the root id.
782
entries = inv.iter_entries()
802
if not repository.supports_rich_root():
803
path, root = entries.next()
804
if root.revision != rev.revision_id:
805
raise errors.IncompatibleRevision(repr(repository))
784
806
# Add the texts that are not already present
785
807
for path, ie in entries:
786
808
w = repository.weave_store.get_weave_or_empty(ie.file_id,
923
945
:return: a dictionary of revision_id->revision_parents_list.
925
947
# special case NULL_REVISION
926
if revision_id == NULL_REVISION:
948
if revision_id == _mod_revision.NULL_REVISION:
928
weave = self._get_revision_vf()
929
entire_graph = weave.get_graph()
950
a_weave = self._get_revision_vf()
951
entire_graph = a_weave.get_graph()
930
952
if revision_id is None:
931
return weave.get_graph()
932
elif revision_id not in weave:
953
return a_weave.get_graph()
954
elif revision_id not in a_weave:
933
955
raise errors.NoSuchRevision(self, revision_id)
935
957
# add what can be reached from revision_id
960
982
pending = set(revision_ids)
961
983
# special case NULL_REVISION
962
if NULL_REVISION in pending:
963
pending.remove(NULL_REVISION)
984
if _mod_revision.NULL_REVISION in pending:
985
pending.remove(_mod_revision.NULL_REVISION)
964
986
required = set(pending)
966
988
while len(pending):
1212
1234
TODO: when creating split out bzr branch formats, move this to a common
1213
1235
base for Format5, Format6. or something like that.
1215
from bzrlib.weavefile import write_weave_v5
1216
from bzrlib.weave import Weave
1219
1238
raise errors.IncompatibleFormat(self, a_bzrdir._format)
1235
1254
# FIXME: RBC 20060125 don't peek under the covers
1236
1255
# NB: no need to escape relative paths that are url safe.
1237
control_files = LockableFiles(a_bzrdir.transport, 'branch-lock',
1256
control_files = lockable_files.LockableFiles(a_bzrdir.transport,
1257
'branch-lock', lockable_files.TransportLock)
1239
1258
control_files.create_lock()
1240
1259
control_files.lock_write()
1241
1260
control_files._transport.mkdir_multi(dirs,
1405
1424
# FIXME: RBC 20060125 don't peek under the covers
1406
1425
# NB: no need to escape relative paths that are url safe.
1407
1426
repository_transport = a_bzrdir.get_repository_transport(self)
1408
control_files = LockableFiles(repository_transport, 'lock', LockDir)
1427
control_files = lockable_files.LockableFiles(repository_transport,
1428
'lock', lockdir.LockDir)
1409
1429
control_files.create_lock()
1410
1430
return control_files
1477
1497
:param shared: If true the repository will be initialized as a shared
1480
from bzrlib.weavefile import write_weave_v5
1481
from bzrlib.weave import Weave
1483
1500
# Create an empty weave
1484
1501
sio = StringIO()
1485
write_weave_v5(Weave(), sio)
1502
weavefile.write_weave_v5(weave.Weave(), sio)
1486
1503
empty_weave = sio.getvalue()
1488
1505
mutter('creating repository in %s.', a_bzrdir.transport.base)
1508
1525
repo_transport = _override_transport
1510
1527
repo_transport = a_bzrdir.get_repository_transport(None)
1511
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1528
control_files = lockable_files.LockableFiles(repo_transport,
1529
'lock', lockdir.LockDir)
1512
1530
text_store = self._get_text_store(repo_transport, control_files)
1513
1531
control_store = self._get_control_store(repo_transport, control_files)
1514
1532
_revision_store = self._get_revision_store(repo_transport, control_files)
1540
1558
repo_transport,
1541
1559
prefixed=False,
1542
1560
file_mode=control_files._file_mode,
1543
versionedfile_class=KnitVersionedFile,
1544
versionedfile_kwargs={'factory':KnitPlainFactory()},
1561
versionedfile_class=knit.KnitVersionedFile,
1562
versionedfile_kwargs={'factory':knit.KnitPlainFactory()},
1547
1565
def _get_revision_store(self, repo_transport, control_files):
1552
1570
file_mode=control_files._file_mode,
1553
1571
prefixed=False,
1555
versionedfile_class=KnitVersionedFile,
1556
versionedfile_kwargs={'delta':False, 'factory':KnitPlainFactory(),},
1573
versionedfile_class=knit.KnitVersionedFile,
1574
versionedfile_kwargs={'delta':False,
1575
'factory':knit.KnitPlainFactory(),
1559
1579
return KnitRevisionStore(versioned_file_store)
1561
1581
def _get_text_store(self, transport, control_files):
1562
1582
"""See RepositoryFormat._get_text_store()."""
1563
1583
return self._get_versioned_file_store('knits',
1566
versionedfile_class=KnitVersionedFile,
1567
versionedfile_kwargs={
1568
'create_parent_dir':True,
1569
'delay_create':True,
1570
'dir_mode':control_files._dir_mode,
1586
versionedfile_class=knit.KnitVersionedFile,
1587
versionedfile_kwargs={
1588
'create_parent_dir':True,
1589
'delay_create':True,
1590
'dir_mode':control_files._dir_mode,
1574
1594
def initialize(self, a_bzrdir, shared=False):
1575
1595
"""Create a knit format 1 repository.
1587
1607
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1588
1608
repo_transport = a_bzrdir.get_repository_transport(None)
1589
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1609
control_files = lockable_files.LockableFiles(repo_transport,
1610
'lock', lockdir.LockDir)
1590
1611
control_store = self._get_control_store(repo_transport, control_files)
1591
1612
transaction = transactions.WriteTransaction()
1592
1613
# trigger a write of the inventory store.
1610
1631
repo_transport = _override_transport
1612
1633
repo_transport = a_bzrdir.get_repository_transport(None)
1613
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1634
control_files = lockable_files.LockableFiles(repo_transport,
1635
'lock', lockdir.LockDir)
1614
1636
text_store = self._get_text_store(repo_transport, control_files)
1615
1637
control_store = self._get_control_store(repo_transport, control_files)
1616
1638
_revision_store = self._get_revision_store(repo_transport, control_files)
1695
1717
repo_transport = _override_transport
1697
1719
repo_transport = a_bzrdir.get_repository_transport(None)
1698
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1720
control_files = lockable_files.LockableFiles(repo_transport, 'lock',
1699
1722
text_store = self._get_text_store(repo_transport, control_files)
1700
1723
control_store = self._get_control_store(repo_transport, control_files)
1701
1724
_revision_store = self._get_revision_store(repo_transport, control_files)
1816
1839
if basis is not None:
1817
1840
self.target.fetch(basis, revision_id=revision_id)
1818
1841
# but don't bother fetching if we have the needed data now.
1819
if (revision_id not in (None, NULL_REVISION) and
1842
if (revision_id not in (None, _mod_revision.NULL_REVISION) and
1820
1843
self.target.has_revision(revision_id)):
1822
1845
self.target.fetch(self.source, revision_id=revision_id)
2060
2083
if basis is not None:
2061
2084
self.target.fetch(basis, revision_id=revision_id)
2062
2085
# but don't bother fetching if we have the needed data now.
2063
if (revision_id not in (None, NULL_REVISION) and
2086
if (revision_id not in (None, _mod_revision.NULL_REVISION) and
2064
2087
self.target.has_revision(revision_id)):
2066
2089
self.target.fetch(self.source, revision_id=revision_id)
2295
2318
:return: The revision id of the recorded revision.
2297
rev = Revision(timestamp=self._timestamp,
2320
rev = _mod_revision.Revision(
2321
timestamp=self._timestamp,
2298
2322
timezone=self._timezone,
2299
2323
committer=self._committer,
2300
2324
message=message,
2306
2330
self.new_inventory, self._config)
2307
2331
return self._new_revision_id
2333
def revision_tree(self):
2334
"""Return the tree that was just committed.
2336
After calling commit() this can be called to get a RevisionTree
2337
representing the newly committed tree. This is preferred to
2338
calling Repository.revision_tree() because that may require
2339
deserializing the inventory, while we already have a copy in
2342
return RevisionTree(self.repository, self.new_inventory,
2343
self._new_revision_id)
2309
2345
def finish_inventory(self):
2310
2346
"""Tell the builder that the inventory is finished."""
2311
2347
if self.new_inventory.root is None:
2323
2359
def _gen_revision_id(self):
2324
2360
"""Return new revision-id."""
2325
s = '%s-%s-' % (self._config.user_email(),
2326
compact_date(self._timestamp))
2327
s += hexlify(rand_bytes(8))
2361
return generate_ids.gen_revision_id(self._config.username(),
2330
2364
def _generate_revision_if_needed(self):
2331
2365
"""Create a revision id if None was supplied.
2368
2402
# In this revision format, root entries have no knit or weave
2369
2403
if ie is self.new_inventory.root:
2370
if len(parent_invs):
2371
ie.revision = parent_invs[0].root.revision
2404
# When serializing out to disk and back in
2405
# root.revision is always _new_revision_id
2406
ie.revision = self._new_revision_id
2375
2408
previous_entries = ie.find_previous_heads(