420
418
# implementation could give us bad output from readlines() so this is
421
419
# not a guarantee of safety. What would be better is always checking
422
420
# the content during test suite execution. RBC 20070912
424
return versionedfile.add_lines_with_ghosts(
425
self._new_revision_id, parents, new_lines,
426
nostore_sha=nostore_sha, random_id=self.random_revid,
427
check_content=False)[0:2]
429
versionedfile.clear_cache()
421
return versionedfile.add_lines_with_ghosts(
422
self._new_revision_id, parents, new_lines,
423
nostore_sha=nostore_sha, random_id=self.random_revid,
424
check_content=False)[0:2]
432
427
class RootCommitBuilder(CommitBuilder):
504
502
:returns: The validator(which is a sha1 digest, though what is sha'd is
505
503
repository format specific) of the serialized inventory.
507
assert self.is_in_write_group()
505
if not self.is_in_write_group():
506
raise AssertionError("%r not in write group" % (self,))
508
507
_mod_revision.check_not_reserved_id(revision_id)
509
assert inv.revision_id is None or inv.revision_id == revision_id, \
510
"Mismatch between inventory revision" \
511
" id and insertion revid (%r, %r)" % (inv.revision_id, revision_id)
512
assert inv.root is not None
508
if not (inv.revision_id is None or inv.revision_id == revision_id):
509
raise AssertionError(
510
"Mismatch between inventory revision"
511
" id and insertion revid (%r, %r)"
512
% (inv.revision_id, revision_id))
514
raise AssertionError()
513
515
inv_lines = self._serialise_inventory_to_lines(inv)
514
516
inv_vf = self.get_inventory_weave()
515
517
return self._inventory_add_lines(inv_vf, revision_id, parents,
545
547
plaintext = Testament(rev, inv).as_short_text()
546
548
self.store_revision_signature(
547
549
gpg.GPGStrategy(config), plaintext, revision_id)
548
if not revision_id in self.get_inventory_weave():
550
inventory_vf = self.get_inventory_weave()
551
if not revision_id in inventory_vf:
550
553
raise errors.WeaveRevisionNotPresent(revision_id,
551
self.get_inventory_weave())
553
556
# yes, this is not suitable for adding with ghosts.
554
self.add_inventory(revision_id, inv, rev.parent_ids)
557
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
560
rev.inventory_sha1 = inventory_vf.get_sha1s([revision_id])[0]
555
561
self._revision_store.add_revision(rev, self.get_transaction())
557
563
def _add_revision_text(self, revision_id, text):
594
600
Returns a set of the present revisions.
597
for id in revision_ids:
598
if self.has_revision(id):
603
graph = self.get_graph()
604
parent_map = graph.get_parent_map(revision_ids)
605
# The old API returned a list, should this actually be a set?
606
return parent_map.keys()
603
609
def create(a_bzrdir):
604
610
"""Construct the current default format repository in a_bzrdir."""
605
611
return RepositoryFormat.get_default_format().initialize(a_bzrdir)
607
def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
613
def __init__(self, _format, a_bzrdir, control_files,
614
_revision_store, control_store, text_store):
608
615
"""instantiate a Repository.
610
617
:param _format: The format of the repository on disk.
1280
1282
setdefault(file_id, set()).add(revision_id)
1283
def fileids_altered_by_revision_ids(self, revision_ids):
1285
def fileids_altered_by_revision_ids(self, revision_ids, _inv_weave=None):
1284
1286
"""Find the file ids and versions affected by revisions.
1286
1288
:param revisions: an iterable containing revision ids.
1289
:param _inv_weave: The inventory weave from this repository or None.
1290
If None, the inventory weave will be opened automatically.
1287
1291
:return: a dictionary mapping altered file-ids to an iterable of
1288
1292
revision_ids. Each altered file-ids has the exact revision_ids that
1289
1293
altered it listed explicitly.
1291
1295
selected_revision_ids = set(revision_ids)
1292
w = self.get_inventory_weave()
1296
w = _inv_weave or self.get_inventory_weave()
1293
1297
pb = ui.ui_factory.nested_progress_bar()
1295
1299
return self._find_file_ids_from_xml_inventory_lines(
1447
1451
# maybe this generator should explicitly have the contract that it
1448
1452
# should not be iterated until the previously yielded item has been
1451
1454
inv_w = self.get_inventory_weave()
1452
inv_w.enable_cache()
1454
1456
# file ids that changed
1455
file_ids = self.fileids_altered_by_revision_ids(revision_ids)
1457
file_ids = self.fileids_altered_by_revision_ids(revision_ids, inv_w)
1457
1459
num_file_ids = len(file_ids)
1458
1460
for file_id, altered_versions in file_ids.iteritems():
1504
1504
:return: An iterator of inventories.
1506
assert None not in revision_ids
1507
assert _mod_revision.NULL_REVISION not in revision_ids
1506
if ((None in revision_ids)
1507
or (_mod_revision.NULL_REVISION in revision_ids)):
1508
raise ValueError('cannot get null revision inventory')
1508
1509
return self._iter_inventories(revision_ids)
1510
1511
def _iter_inventories(self, revision_ids):
1551
1551
return self.get_revision(revision_id).inventory_sha1
1554
def get_revision_graph(self, revision_id=None):
1555
"""Return a dictionary containing the revision graph.
1557
NB: This method should not be used as it accesses the entire graph all
1558
at once, which is much more data than most operations should require.
1560
:param revision_id: The revision_id to get a graph from. If None, then
1561
the entire revision graph is returned. This is a deprecated mode of
1562
operation and will be removed in the future.
1563
:return: a dictionary of revision_id->revision_parents_list.
1565
raise NotImplementedError(self.get_revision_graph)
1568
def get_revision_graph_with_ghosts(self, revision_ids=None):
1569
"""Return a graph of the revisions with ghosts marked as applicable.
1571
:param revision_ids: an iterable of revisions to graph or None for all.
1572
:return: a Graph object with the graph reachable from revision_ids.
1574
if 'evil' in debug.debug_flags:
1576
"get_revision_graph_with_ghosts scales with size of history.")
1577
result = deprecated_graph.Graph()
1578
if not revision_ids:
1579
pending = set(self.all_revision_ids())
1582
pending = set(revision_ids)
1583
# special case NULL_REVISION
1584
if _mod_revision.NULL_REVISION in pending:
1585
pending.remove(_mod_revision.NULL_REVISION)
1586
required = set(pending)
1589
revision_id = pending.pop()
1591
rev = self.get_revision(revision_id)
1592
except errors.NoSuchRevision:
1593
if revision_id in required:
1596
result.add_ghost(revision_id)
1598
for parent_id in rev.parent_ids:
1599
# is this queued or done ?
1600
if (parent_id not in pending and
1601
parent_id not in done):
1603
pending.add(parent_id)
1604
result.add_node(revision_id, rev.parent_ids)
1605
done.add(revision_id)
1608
def _get_history_vf(self):
1609
"""Get a versionedfile whose history graph reflects all revisions.
1611
For weave repositories, this is the inventory weave.
1613
return self.get_inventory_weave()
1615
1553
def iter_reverse_revision_history(self, revision_id):
1616
1554
"""Iterate backwards through revision ids in the lefthand history
1618
1556
:param revision_id: The revision id to start with. All its lefthand
1619
1557
ancestors will be traversed.
1621
if revision_id in (None, _mod_revision.NULL_REVISION):
1559
graph = self.get_graph()
1623
1560
next_id = revision_id
1624
versionedfile = self._get_history_vf()
1562
if next_id in (None, _mod_revision.NULL_REVISION):
1627
parents = versionedfile.get_parents(next_id)
1565
# Note: The following line may raise KeyError in the event of
1566
# truncated history. We decided not to have a try:except:raise
1567
# RevisionNotPresent here until we see a use for it, because of the
1568
# cost in an inner loop that is by its very nature O(history).
1569
# Robert Collins 20080326
1570
parents = graph.get_parent_map([next_id])[next_id]
1628
1571
if len(parents) == 0:
1743
1685
def get_transaction(self):
1744
1686
return self.control_files.get_transaction()
1746
def revision_parents(self, revision_id):
1747
return self.get_inventory_weave().parent_names(revision_id)
1749
1688
@deprecated_method(symbol_versioning.one_one)
1750
1689
def get_parents(self, revision_ids):
1751
1690
"""See StackedParentsProvider.get_parents"""
1999
1940
class MetaDirRepository(Repository):
2000
"""Repositories in the new meta-dir layout."""
1941
"""Repositories in the new meta-dir layout.
1943
:ivar _transport: Transport for access to repository control files,
1944
typically pointing to .bzr/repository.
2002
1947
def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
2003
1948
super(MetaDirRepository, self).__init__(_format,
2006
1951
_revision_store,
2009
dir_mode = self.control_files._dir_mode
2010
file_mode = self.control_files._file_mode
1954
self._transport = control_files._transport
2012
1956
@needs_read_lock
2013
1957
def is_shared(self):
2014
1958
"""Return True if this repository is flagged as a shared repository."""
2015
return self.control_files._transport.has('shared-storage')
1959
return self._transport.has('shared-storage')
2017
1961
@needs_write_lock
2018
1962
def set_make_working_trees(self, new_value):
2029
self.control_files._transport.delete('no-working-trees')
1973
self._transport.delete('no-working-trees')
2030
1974
except errors.NoSuchFile:
2033
self.control_files.put_utf8('no-working-trees', '')
1977
self._transport.put_bytes('no-working-trees', '',
1978
mode=self.bzrdir._get_file_mode())
2035
1980
def make_working_trees(self):
2036
1981
"""Returns the policy for making working trees on new branches."""
2037
return not self.control_files._transport.has('no-working-trees')
1982
return not self._transport.has('no-working-trees')
1985
class MetaDirVersionedFileRepository(MetaDirRepository):
1986
"""Repositories in a meta-dir, that work via versioned file objects."""
1988
def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
1989
super(MetaDirVersionedFileRepository, self).__init__(_format, a_bzrdir,
1990
control_files, _revision_store, control_store, text_store)
1991
_revision_store.get_scope = self.get_transaction
1992
control_store.get_scope = self.get_transaction
1993
text_store.get_scope = self.get_transaction
2040
1996
class RepositoryFormatRegistry(registry.Registry):
2257
2214
"""Upload the initial blank content."""
2258
2215
control_files = self._create_control_files(a_bzrdir)
2259
2216
control_files.lock_write()
2217
transport = control_files._transport
2219
utf8_files += [('shared-storage', '')]
2261
control_files._transport.mkdir_multi(dirs,
2262
mode=control_files._dir_mode)
2263
for file, content in files:
2264
control_files.put(file, content)
2265
for file, content in utf8_files:
2266
control_files.put_utf8(file, content)
2268
control_files.put_utf8('shared-storage', '')
2221
transport.mkdir_multi(dirs, mode=a_bzrdir._get_dir_mode())
2222
for (filename, content_stream) in files:
2223
transport.put_file(filename, content_stream,
2224
mode=a_bzrdir._get_file_mode())
2225
for (filename, content_bytes) in utf8_files:
2226
transport.put_bytes_non_atomic(filename, content_bytes,
2227
mode=a_bzrdir._get_file_mode())
2270
2229
control_files.unlock()
2374
2333
:param revision_ids: The start point for the search.
2375
2334
:return: A set of revision ids.
2377
graph = self.source.get_graph()
2336
target_graph = self.target.get_graph()
2337
revision_ids = frozenset(revision_ids)
2338
if set(target_graph.get_parent_map(revision_ids)) == revision_ids:
2339
return graph.SearchResult(revision_ids, set(), 0, set())
2378
2340
missing_revs = set()
2341
source_graph = self.source.get_graph()
2379
2342
# ensure we don't pay silly lookup costs.
2380
revision_ids = frozenset(revision_ids)
2381
searcher = graph._make_breadth_first_searcher(revision_ids)
2343
searcher = source_graph._make_breadth_first_searcher(revision_ids)
2382
2344
null_set = frozenset([_mod_revision.NULL_REVISION])
2389
2351
absent_ids = set(revision_ids.intersection(ghosts))
2390
2352
# If all absent_ids are present in target, no error is needed.
2391
2353
absent_ids.difference_update(
2392
self.target.has_revisions(absent_ids))
2354
set(target_graph.get_parent_map(absent_ids)))
2394
2356
raise errors.NoSuchRevision(self.source, absent_ids.pop())
2395
2357
# we don't care about other ghosts as we can't fetch them and
2396
2358
# haven't been asked to.
2397
2359
next_revs = set(next_revs)
2398
2360
# we always have NULL_REVISION present.
2399
have_revs = self.target.has_revisions(next_revs).union(null_set)
2361
have_revs = set(target_graph.get_parent_map(next_revs)).union(null_set)
2400
2362
missing_revs.update(next_revs - have_revs)
2401
2363
searcher.stop_searching_any(have_revs)
2402
2364
return searcher.get_result()
2548
2511
# weave specific optimised path:
2550
2513
self.target.set_make_working_trees(self.source.make_working_trees())
2551
except NotImplementedError:
2514
except (errors.RepositoryUpgradeRequired, NotImplemented):
2553
2516
# FIXME do not peek!
2554
if self.source.control_files._transport.listable():
2517
if self.source._transport.listable():
2555
2518
pb = ui.ui_factory.nested_progress_bar()
2557
2520
self.target.weave_store.copy_all_ids(
2735
2700
# inventory parsing etc, IFF nothing to be copied is in the target.
2737
2702
revision_ids = self.source.all_revision_ids()
2703
revision_keys = [(revid,) for revid in revision_ids]
2704
index = self.target._pack_collection.revision_index.combined_index
2705
present_revision_ids = set(item[1][0] for item in
2706
index.iter_entries(revision_keys))
2707
revision_ids = set(revision_ids) - present_revision_ids
2738
2708
# implementing the TODO will involve:
2739
2709
# - detecting when all of a pack is selected
2740
2710
# - avoiding as much as possible pre-selection, so the
2942
2915
# Is source's model compatible with target's model?
2943
2916
source._ensure_real()
2944
2917
real_source = source._real_repository
2945
assert not isinstance(real_source, remote.RemoteRepository), (
2946
"We don't support remote repos backed by remote repos yet.")
2918
if isinstance(real_source, remote.RemoteRepository):
2919
raise NotImplementedError(
2920
"We don't support remote repos backed by remote repos yet.")
2947
2921
return InterRepository._same_model(real_source, target)
2949
2923
@needs_write_lock
3130
3106
unused_versions.add(revision_id)
3133
knit_parents = tuple(weave.get_parents(revision_id))
3109
knit_parents = tuple(parent_map[revision_id])
3134
3110
except errors.RevisionNotPresent:
3135
3111
knit_parents = None
3136
3112
if correct_parents != knit_parents:
3137
3113
wrong_parents[revision_id] = (knit_parents, correct_parents)
3138
3114
return wrong_parents, unused_versions
3117
def _old_get_graph(repository, revision_id):
3118
"""DO NOT USE. That is all. I'm serious."""
3119
graph = repository.get_graph()
3120
revision_graph = dict(((key, value) for key, value in
3121
graph.iter_ancestry([revision_id]) if value is not None))
3122
return _strip_NULL_ghosts(revision_graph)
3125
def _strip_NULL_ghosts(revision_graph):
3126
"""Also don't use this. more compatibility code for unmigrated clients."""
3127
# Filter ghosts, and null:
3128
if _mod_revision.NULL_REVISION in revision_graph:
3129
del revision_graph[_mod_revision.NULL_REVISION]
3130
for key, parents in revision_graph.items():
3131
revision_graph[key] = tuple(parent for parent in parents if parent
3133
return revision_graph