818
812
self._require_root_change(tree)
819
813
self.basis_delta_revision = basis_revision_id
821
def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
822
# Note: as we read the content directly from the tree, we know its not
823
# been turned into unicode or badly split - but a broken tree
824
# implementation could give us bad output from readlines() so this is
825
# not a guarantee of safety. What would be better is always checking
826
# the content during test suite execution. RBC 20070912
827
parent_keys = tuple((file_id, parent) for parent in parents)
828
return self.repository.texts.add_lines(
829
(file_id, self._new_revision_id), parent_keys, new_lines,
830
nostore_sha=nostore_sha, random_id=self.random_revid,
831
check_content=False)[0:2]
815
def _add_text_to_weave(self, file_id, new_text, parents, nostore_sha):
816
parent_keys = tuple([(file_id, parent) for parent in parents])
817
return self.repository.texts._add_text(
818
(file_id, self._new_revision_id), parent_keys, new_text,
819
nostore_sha=nostore_sha, random_id=self.random_revid)[0:2]
834
822
class RootCommitBuilder(CommitBuilder):
1912
1911
yield line, revid
1914
1913
def _find_file_ids_from_xml_inventory_lines(self, line_iterator,
1916
1915
"""Helper routine for fileids_altered_by_revision_ids.
1918
1917
This performs the translation of xml lines to revision ids.
1920
1919
:param line_iterator: An iterator of lines, origin_version_id
1921
:param revision_ids: The revision ids to filter for. This should be a
1920
:param revision_keys: The revision ids to filter for. This should be a
1922
1921
set or other type which supports efficient __contains__ lookups, as
1923
the revision id from each parsed line will be looked up in the
1924
revision_ids filter.
1922
the revision key from each parsed line will be looked up in the
1923
revision_keys filter.
1925
1924
:return: a dictionary mapping altered file-ids to an iterable of
1926
1925
revision_ids. Each altered file-ids has the exact revision_ids that
1927
1926
altered it listed explicitly.
1929
1928
seen = set(self._find_text_key_references_from_xml_inventory_lines(
1930
1929
line_iterator).iterkeys())
1931
# Note that revision_ids are revision keys.
1932
parent_maps = self.revisions.get_parent_map(revision_ids)
1934
map(parents.update, parent_maps.itervalues())
1935
parents.difference_update(revision_ids)
1930
parent_keys = self._find_parent_keys_of_revisions(revision_keys)
1936
1931
parent_seen = set(self._find_text_key_references_from_xml_inventory_lines(
1937
self._inventory_xml_lines_for_keys(parents)))
1932
self._inventory_xml_lines_for_keys(parent_keys)))
1938
1933
new_keys = seen - parent_seen
1940
1935
setdefault = result.setdefault
1942
1937
setdefault(key[0], set()).add(key[-1])
1940
def _find_parent_ids_of_revisions(self, revision_ids):
1941
"""Find all parent ids that are mentioned in the revision graph.
1943
:return: set of revisions that are parents of revision_ids which are
1944
not part of revision_ids themselves
1946
parent_map = self.get_parent_map(revision_ids)
1948
map(parent_ids.update, parent_map.itervalues())
1949
parent_ids.difference_update(revision_ids)
1950
parent_ids.discard(_mod_revision.NULL_REVISION)
1953
def _find_parent_keys_of_revisions(self, revision_keys):
1954
"""Similar to _find_parent_ids_of_revisions, but used with keys.
1956
:param revision_keys: An iterable of revision_keys.
1957
:return: The parents of all revision_keys that are not already in
1960
parent_map = self.revisions.get_parent_map(revision_keys)
1962
map(parent_keys.update, parent_map.itervalues())
1963
parent_keys.difference_update(revision_keys)
1964
parent_keys.discard(_mod_revision.NULL_REVISION)
1945
1967
def fileids_altered_by_revision_ids(self, revision_ids, _inv_weave=None):
1946
1968
"""Find the file ids and versions affected by revisions.
2228
2250
return self.get_revision(revision_id).inventory_sha1
2252
def get_rev_id_for_revno(self, revno, known_pair):
2253
"""Return the revision id of a revno, given a later (revno, revid)
2254
pair in the same history.
2256
:return: if found (True, revid). If the available history ran out
2257
before reaching the revno, then this returns
2258
(False, (closest_revno, closest_revid)).
2260
known_revno, known_revid = known_pair
2261
partial_history = [known_revid]
2262
distance_from_known = known_revno - revno
2263
if distance_from_known < 0:
2265
'requested revno (%d) is later than given known revno (%d)'
2266
% (revno, known_revno))
2269
self, partial_history, stop_index=distance_from_known)
2270
except errors.RevisionNotPresent, err:
2271
if err.revision_id == known_revid:
2272
# The start revision (known_revid) wasn't found.
2274
# This is a stacked repository with no fallbacks, or a there's a
2275
# left-hand ghost. Either way, even though the revision named in
2276
# the error isn't in this repo, we know it's the next step in this
2277
# left-hand history.
2278
partial_history.append(err.revision_id)
2279
if len(partial_history) <= distance_from_known:
2280
# Didn't find enough history to get a revid for the revno.
2281
earliest_revno = known_revno - len(partial_history) + 1
2282
return (False, (earliest_revno, partial_history[-1]))
2283
if len(partial_history) - 1 > distance_from_known:
2284
raise AssertionError('_iter_for_revno returned too much history')
2285
return (True, partial_history[-1])
2230
2287
def iter_reverse_revision_history(self, revision_id):
2231
2288
"""Iterate backwards through revision ids in the lefthand history
3401
3480
return self.source.revision_ids_to_search_result(result_set)
3404
class InterPackRepo(InterSameDataRepository):
3405
"""Optimised code paths between Pack based repositories."""
3408
def _get_repo_format_to_test(self):
3409
from bzrlib.repofmt import pack_repo
3410
return pack_repo.RepositoryFormatKnitPack6RichRoot()
3413
def is_compatible(source, target):
3414
"""Be compatible with known Pack formats.
3416
We don't test for the stores being of specific types because that
3417
could lead to confusing results, and there is no need to be
3420
InterPackRepo does not support CHK based repositories.
3422
from bzrlib.repofmt.pack_repo import RepositoryFormatPack
3423
from bzrlib.repofmt.groupcompress_repo import RepositoryFormatCHK1
3425
are_packs = (isinstance(source._format, RepositoryFormatPack) and
3426
isinstance(target._format, RepositoryFormatPack))
3427
not_packs = (isinstance(source._format, RepositoryFormatCHK1) or
3428
isinstance(target._format, RepositoryFormatCHK1))
3429
except AttributeError:
3431
if not_packs or not are_packs:
3433
return InterRepository._same_model(source, target)
3436
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
3438
"""See InterRepository.fetch()."""
3439
if (len(self.source._fallback_repositories) > 0 or
3440
len(self.target._fallback_repositories) > 0):
3441
# The pack layer is not aware of fallback repositories, so when
3442
# fetching from a stacked repository or into a stacked repository
3443
# we use the generic fetch logic which uses the VersionedFiles
3444
# attributes on repository.
3445
from bzrlib.fetch import RepoFetcher
3446
fetcher = RepoFetcher(self.target, self.source, revision_id,
3447
pb, find_ghosts, fetch_spec=fetch_spec)
3448
if fetch_spec is not None:
3449
if len(list(fetch_spec.heads)) != 1:
3450
raise AssertionError(
3451
"InterPackRepo.fetch doesn't support "
3452
"fetching multiple heads yet.")
3453
revision_id = list(fetch_spec.heads)[0]
3455
if revision_id is None:
3457
# everything to do - use pack logic
3458
# to fetch from all packs to one without
3459
# inventory parsing etc, IFF nothing to be copied is in the target.
3461
source_revision_ids = frozenset(self.source.all_revision_ids())
3462
revision_ids = source_revision_ids - \
3463
frozenset(self.target.get_parent_map(source_revision_ids))
3464
revision_keys = [(revid,) for revid in revision_ids]
3465
index = self.target._pack_collection.revision_index.combined_index
3466
present_revision_ids = set(item[1][0] for item in
3467
index.iter_entries(revision_keys))
3468
revision_ids = set(revision_ids) - present_revision_ids
3469
# implementing the TODO will involve:
3470
# - detecting when all of a pack is selected
3471
# - avoiding as much as possible pre-selection, so the
3472
# more-core routines such as create_pack_from_packs can filter in
3473
# a just-in-time fashion. (though having a HEADS list on a
3474
# repository might make this a lot easier, because we could
3475
# sensibly detect 'new revisions' without doing a full index scan.
3476
elif _mod_revision.is_null(revision_id):
3480
revision_ids = self.search_missing_revision_ids(revision_id,
3481
find_ghosts=find_ghosts).get_keys()
3482
if len(revision_ids) == 0:
3484
return self._pack(self.source, self.target, revision_ids)
3486
def _pack(self, source, target, revision_ids):
3487
from bzrlib.repofmt.pack_repo import Packer
3488
packs = source._pack_collection.all_packs()
3489
pack = Packer(self.target._pack_collection, packs, '.fetch',
3490
revision_ids).pack()
3491
if pack is not None:
3492
self.target._pack_collection._save_pack_names()
3493
copied_revs = pack.get_revision_count()
3494
# Trigger an autopack. This may duplicate effort as we've just done
3495
# a pack creation, but for now it is simpler to think about as
3496
# 'upload data, then repack if needed'.
3497
self.target._pack_collection.autopack()
3498
return (copied_revs, [])
3503
def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
3504
"""See InterRepository.missing_revision_ids().
3506
:param find_ghosts: Find ghosts throughout the ancestry of
3509
if not find_ghosts and revision_id is not None:
3510
return self._walk_to_common_revisions([revision_id])
3511
elif revision_id is not None:
3512
# Find ghosts: search for revisions pointing from one repository to
3513
# the other, and vice versa, anywhere in the history of revision_id.
3514
graph = self.target.get_graph(other_repository=self.source)
3515
searcher = graph._make_breadth_first_searcher([revision_id])
3519
next_revs, ghosts = searcher.next_with_ghosts()
3520
except StopIteration:
3522
if revision_id in ghosts:
3523
raise errors.NoSuchRevision(self.source, revision_id)
3524
found_ids.update(next_revs)
3525
found_ids.update(ghosts)
3526
found_ids = frozenset(found_ids)
3527
# Double query here: should be able to avoid this by changing the
3528
# graph api further.
3529
result_set = found_ids - frozenset(
3530
self.target.get_parent_map(found_ids))
3532
source_ids = self.source.all_revision_ids()
3533
# source_ids is the worst possible case we may need to pull.
3534
# now we want to filter source_ids against what we actually
3535
# have in target, but don't try to check for existence where we know
3536
# we do not have a revision as that would be pointless.
3537
target_ids = set(self.target.all_revision_ids())
3538
result_set = set(source_ids).difference(target_ids)
3539
return self.source.revision_ids_to_search_result(result_set)
3542
3483
class InterDifferingSerializer(InterRepository):
4070
4017
if new_pack is not None:
4071
4018
new_pack._write_data('', flush=True)
4072
4019
# Find all the new revisions (including ones from resume_tokens)
4073
missing_keys = self.target_repo.get_missing_parent_inventories()
4020
missing_keys = self.target_repo.get_missing_parent_inventories(
4021
check_for_missing_texts=is_resume)
4075
4023
for prefix, versioned_file in (
4076
4024
('texts', self.target_repo.texts),
4077
4025
('inventories', self.target_repo.inventories),
4078
4026
('revisions', self.target_repo.revisions),
4079
4027
('signatures', self.target_repo.signatures),
4028
('chk_bytes', self.target_repo.chk_bytes),
4030
if versioned_file is None:
4081
4032
missing_keys.update((prefix,) + key for key in
4082
4033
versioned_file.get_missing_compression_parent_keys())
4083
4034
except NotImplementedError:
4382
4348
yield versionedfile.FulltextContentFactory(
4383
4349
key, parent_keys, None, as_bytes)
4352
def _iter_for_revno(repo, partial_history_cache, stop_index=None,
4353
stop_revision=None):
4354
"""Extend the partial history to include a given index
4356
If a stop_index is supplied, stop when that index has been reached.
4357
If a stop_revision is supplied, stop when that revision is
4358
encountered. Otherwise, stop when the beginning of history is
4361
:param stop_index: The index which should be present. When it is
4362
present, history extension will stop.
4363
:param stop_revision: The revision id which should be present. When
4364
it is encountered, history extension will stop.
4366
start_revision = partial_history_cache[-1]
4367
iterator = repo.iter_reverse_revision_history(start_revision)
4369
#skip the last revision in the list
4372
if (stop_index is not None and
4373
len(partial_history_cache) > stop_index):
4375
if partial_history_cache[-1] == stop_revision:
4377
revision_id = iterator.next()
4378
partial_history_cache.append(revision_id)
4379
except StopIteration: