815
818
self._require_root_change(tree)
816
819
self.basis_delta_revision = basis_revision_id
818
def _add_text_to_weave(self, file_id, new_text, parents, nostore_sha):
819
parent_keys = tuple([(file_id, parent) for parent in parents])
820
return self.repository.texts._add_text(
821
(file_id, self._new_revision_id), parent_keys, new_text,
822
nostore_sha=nostore_sha, random_id=self.random_revid)[0:2]
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]
825
834
class RootCommitBuilder(CommitBuilder):
1911
1920
yield line, revid
1913
1922
def _find_file_ids_from_xml_inventory_lines(self, line_iterator,
1915
1924
"""Helper routine for fileids_altered_by_revision_ids.
1917
1926
This performs the translation of xml lines to revision ids.
1919
1928
:param line_iterator: An iterator of lines, origin_version_id
1920
:param revision_keys: The revision ids to filter for. This should be a
1929
:param revision_ids: The revision ids to filter for. This should be a
1921
1930
set or other type which supports efficient __contains__ lookups, as
1922
the revision key from each parsed line will be looked up in the
1923
revision_keys filter.
1931
the revision id from each parsed line will be looked up in the
1932
revision_ids filter.
1924
1933
:return: a dictionary mapping altered file-ids to an iterable of
1925
1934
revision_ids. Each altered file-ids has the exact revision_ids that
1926
1935
altered it listed explicitly.
1928
1937
seen = set(self._find_text_key_references_from_xml_inventory_lines(
1929
1938
line_iterator).iterkeys())
1930
parent_keys = self._find_parent_keys_of_revisions(revision_keys)
1939
# Note that revision_ids are revision keys.
1940
parent_maps = self.revisions.get_parent_map(revision_ids)
1942
map(parents.update, parent_maps.itervalues())
1943
parents.difference_update(revision_ids)
1931
1944
parent_seen = set(self._find_text_key_references_from_xml_inventory_lines(
1932
self._inventory_xml_lines_for_keys(parent_keys)))
1945
self._inventory_xml_lines_for_keys(parents)))
1933
1946
new_keys = seen - parent_seen
1935
1948
setdefault = result.setdefault
1937
1950
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)
1967
1953
def fileids_altered_by_revision_ids(self, revision_ids, _inv_weave=None):
1968
1954
"""Find the file ids and versions affected by revisions.
2250
2236
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])
2287
2238
def iter_reverse_revision_history(self, revision_id):
2288
2239
"""Iterate backwards through revision ids in the lefthand history
3480
3431
return self.source.revision_ids_to_search_result(result_set)
3434
class InterPackRepo(InterSameDataRepository):
3435
"""Optimised code paths between Pack based repositories."""
3438
def _get_repo_format_to_test(self):
3439
from bzrlib.repofmt import pack_repo
3440
return pack_repo.RepositoryFormatKnitPack6RichRoot()
3443
def is_compatible(source, target):
3444
"""Be compatible with known Pack formats.
3446
We don't test for the stores being of specific types because that
3447
could lead to confusing results, and there is no need to be
3450
InterPackRepo does not support CHK based repositories.
3452
from bzrlib.repofmt.pack_repo import RepositoryFormatPack
3453
from bzrlib.repofmt.groupcompress_repo import RepositoryFormatCHK1
3455
are_packs = (isinstance(source._format, RepositoryFormatPack) and
3456
isinstance(target._format, RepositoryFormatPack))
3457
not_packs = (isinstance(source._format, RepositoryFormatCHK1) or
3458
isinstance(target._format, RepositoryFormatCHK1))
3459
except AttributeError:
3461
if not_packs or not are_packs:
3463
return InterRepository._same_model(source, target)
3466
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
3468
"""See InterRepository.fetch()."""
3469
if (len(self.source._fallback_repositories) > 0 or
3470
len(self.target._fallback_repositories) > 0):
3471
# The pack layer is not aware of fallback repositories, so when
3472
# fetching from a stacked repository or into a stacked repository
3473
# we use the generic fetch logic which uses the VersionedFiles
3474
# attributes on repository.
3475
from bzrlib.fetch import RepoFetcher
3476
fetcher = RepoFetcher(self.target, self.source, revision_id,
3477
pb, find_ghosts, fetch_spec=fetch_spec)
3478
if fetch_spec is not None:
3479
if len(list(fetch_spec.heads)) != 1:
3480
raise AssertionError(
3481
"InterPackRepo.fetch doesn't support "
3482
"fetching multiple heads yet.")
3483
revision_id = list(fetch_spec.heads)[0]
3485
if revision_id is None:
3487
# everything to do - use pack logic
3488
# to fetch from all packs to one without
3489
# inventory parsing etc, IFF nothing to be copied is in the target.
3491
source_revision_ids = frozenset(self.source.all_revision_ids())
3492
revision_ids = source_revision_ids - \
3493
frozenset(self.target.get_parent_map(source_revision_ids))
3494
revision_keys = [(revid,) for revid in revision_ids]
3495
index = self.target._pack_collection.revision_index.combined_index
3496
present_revision_ids = set(item[1][0] for item in
3497
index.iter_entries(revision_keys))
3498
revision_ids = set(revision_ids) - present_revision_ids
3499
# implementing the TODO will involve:
3500
# - detecting when all of a pack is selected
3501
# - avoiding as much as possible pre-selection, so the
3502
# more-core routines such as create_pack_from_packs can filter in
3503
# a just-in-time fashion. (though having a HEADS list on a
3504
# repository might make this a lot easier, because we could
3505
# sensibly detect 'new revisions' without doing a full index scan.
3506
elif _mod_revision.is_null(revision_id):
3510
revision_ids = self.search_missing_revision_ids(revision_id,
3511
find_ghosts=find_ghosts).get_keys()
3512
if len(revision_ids) == 0:
3514
return self._pack(self.source, self.target, revision_ids)
3516
def _pack(self, source, target, revision_ids):
3517
from bzrlib.repofmt.pack_repo import Packer
3518
packs = source._pack_collection.all_packs()
3519
pack = Packer(self.target._pack_collection, packs, '.fetch',
3520
revision_ids).pack()
3521
if pack is not None:
3522
self.target._pack_collection._save_pack_names()
3523
copied_revs = pack.get_revision_count()
3524
# Trigger an autopack. This may duplicate effort as we've just done
3525
# a pack creation, but for now it is simpler to think about as
3526
# 'upload data, then repack if needed'.
3527
self.target._pack_collection.autopack()
3528
return (copied_revs, [])
3533
def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
3534
"""See InterRepository.missing_revision_ids().
3536
:param find_ghosts: Find ghosts throughout the ancestry of
3539
if not find_ghosts and revision_id is not None:
3540
return self._walk_to_common_revisions([revision_id])
3541
elif revision_id is not None:
3542
# Find ghosts: search for revisions pointing from one repository to
3543
# the other, and vice versa, anywhere in the history of revision_id.
3544
graph = self.target.get_graph(other_repository=self.source)
3545
searcher = graph._make_breadth_first_searcher([revision_id])
3549
next_revs, ghosts = searcher.next_with_ghosts()
3550
except StopIteration:
3552
if revision_id in ghosts:
3553
raise errors.NoSuchRevision(self.source, revision_id)
3554
found_ids.update(next_revs)
3555
found_ids.update(ghosts)
3556
found_ids = frozenset(found_ids)
3557
# Double query here: should be able to avoid this by changing the
3558
# graph api further.
3559
result_set = found_ids - frozenset(
3560
self.target.get_parent_map(found_ids))
3562
source_ids = self.source.all_revision_ids()
3563
# source_ids is the worst possible case we may need to pull.
3564
# now we want to filter source_ids against what we actually
3565
# have in target, but don't try to check for existence where we know
3566
# we do not have a revision as that would be pointless.
3567
target_ids = set(self.target.all_revision_ids())
3568
result_set = set(source_ids).difference(target_ids)
3569
return self.source.revision_ids_to_search_result(result_set)
3483
3572
class InterDifferingSerializer(InterRepository):
4348
4438
yield versionedfile.FulltextContentFactory(
4349
4439
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: