72
83
count_copied -- number of revisions copied
74
This should not be used directly, it's essential a object to encapsulate
85
This should not be used directory, its essential a object to encapsulate
75
86
the logic in InterRepository.fetch().
78
def __init__(self, to_repository, from_repository, last_revision=None, pb=None,
80
"""Create a repo fetcher.
82
:param find_ghosts: If True search the entire history for ghosts.
88
def __init__(self, to_repository, from_repository, last_revision=None, pb=None):
84
89
# result variables.
85
90
self.failed_revisions = []
86
91
self.count_copied = 0
87
if to_repository.has_same_location(from_repository):
88
# repository.fetch should be taking care of this case.
89
raise errors.BzrError('RepoFetcher run '
90
'between two objects at the same location: '
91
'%r and %r' % (to_repository, from_repository))
92
if to_repository.control_files._transport.base == from_repository.control_files._transport.base:
93
# check that last_revision is in 'from' and then return a no-operation.
94
if last_revision not in (None, NULL_REVISION):
95
from_repository.get_revision(last_revision)
92
97
self.to_repository = to_repository
93
98
self.from_repository = from_repository
94
99
# must not mutate self._last_revision as its potentially a shared instance
95
100
self._last_revision = last_revision
96
self.find_ghosts = find_ghosts
98
102
self.pb = bzrlib.ui.ui_factory.nested_progress_bar()
99
103
self.nested_pb = self.pb
126
123
requested revisions, finally clearing the progress bar.
128
125
self.to_weaves = self.to_repository.weave_store
126
self.to_control = self.to_repository.control_weaves
129
127
self.from_weaves = self.from_repository.weave_store
128
self.from_control = self.from_repository.control_weaves
130
129
self.count_total = 0
131
130
self.file_ids_names = {}
132
pp = ProgressPhase('Transferring', 4, self.pb)
131
pp = ProgressPhase('Fetch phase', 4, self.pb)
135
search = self._revids_to_fetch()
138
if getattr(self, '_fetch_everything_for_search', None) is not None:
139
self._fetch_everything_for_search(search, pp)
141
# backward compatibility
142
self._fetch_everything_for_revisions(search.get_keys, pp)
134
revs = self._revids_to_fetch()
138
self._fetch_weave_texts(revs)
140
self._fetch_inventory_weave(revs)
142
self._fetch_revision_texts(revs)
143
self.count_copied += len(revs)
146
def _fetch_everything_for_search(self, search, pp):
147
"""Fetch all data for the given set of revisions."""
148
# The first phase is "file". We pass the progress bar for it directly
149
# into item_keys_introduced_by, which has more information about how
150
# that phase is progressing than we do. Progress updates for the other
151
# phases are taken care of in this function.
152
# XXX: there should be a clear owner of the progress reporting. Perhaps
153
# item_keys_introduced_by should have a richer API than it does at the
154
# moment, so that it can feed the progress information back to this
157
pb = bzrlib.ui.ui_factory.nested_progress_bar()
159
revs = search.get_keys()
160
data_to_fetch = self.from_repository.item_keys_introduced_by(revs, pb)
161
for knit_kind, file_id, revisions in data_to_fetch:
162
if knit_kind != phase:
164
# Make a new progress bar for this phase
167
pb = bzrlib.ui.ui_factory.nested_progress_bar()
168
if knit_kind == "file":
169
self._fetch_weave_text(file_id, revisions)
170
elif knit_kind == "inventory":
172
# Once we've processed all the files, then we generate the root
173
# texts (if necessary), then we process the inventory. It's a
174
# bit distasteful to have knit_kind == "inventory" mean this,
175
# perhaps it should happen on the first non-"file" knit, in case
176
# it's not always inventory?
177
self._generate_root_texts(revs)
178
self._fetch_inventory_weave(revs, pb)
179
elif knit_kind == "signatures":
180
# Nothing to do here; this will be taken care of when
181
# _fetch_revision_texts happens.
183
elif knit_kind == "revisions":
184
self._fetch_revision_texts(revs, pb)
186
raise AssertionError("Unknown knit kind %r" % knit_kind)
190
self.count_copied += len(revs)
192
147
def _revids_to_fetch(self):
193
"""Determines the exact revisions needed from self.from_repository to
194
install self._last_revision in self.to_repository.
196
If no revisions need to be fetched, then this just returns None.
198
148
mutter('fetch up to rev {%s}', self._last_revision)
199
149
if self._last_revision is NULL_REVISION:
200
150
# explicit limit of no revisions needed
202
152
if (self._last_revision is not None and
203
153
self.to_repository.has_revision(self._last_revision)):
206
return self.to_repository.search_missing_revision_ids(
207
self.from_repository, self._last_revision,
208
find_ghosts=self.find_ghosts)
157
return self.to_repository.missing_revision_ids(self.from_repository,
209
159
except errors.NoSuchRevision:
210
160
raise InstallFailed([self._last_revision])
212
def _fetch_weave_text(self, file_id, required_versions):
213
to_weave = self.to_weaves.get_weave_or_empty(file_id,
214
self.to_repository.get_transaction())
215
from_weave = self.from_weaves.get_weave(file_id,
216
self.from_repository.get_transaction())
217
# we fetch all the texts, because texts do
218
# not reference anything, and its cheap enough
219
to_weave.join(from_weave, version_ids=required_versions)
220
# we don't need *all* of this data anymore, but we dont know
221
# what we do. This cache clearing will result in a new read
222
# of the knit data when we do the checkout, but probably we
223
# want to emit the needed data on the fly rather than at the
225
# the from weave should know not to cache data being joined,
226
# but its ok to ask it to clear.
227
from_weave.clear_cache()
228
to_weave.clear_cache()
230
def _fetch_inventory_weave(self, revs, pb):
231
pb.update("fetch inventory", 0, 2)
232
to_weave = self.to_repository.get_inventory_weave()
233
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
235
# just merge, this is optimisable and its means we don't
236
# copy unreferenced data such as not-needed inventories.
237
pb.update("fetch inventory", 1, 3)
238
from_weave = self.from_repository.get_inventory_weave()
239
pb.update("fetch inventory", 2, 3)
240
# we fetch only the referenced inventories because we do not
241
# know for unselected inventories whether all their required
242
# texts are present in the other repository - it could be
244
to_weave.join(from_weave, pb=child_pb, msg='merge inventory',
246
from_weave.clear_cache()
250
def _generate_root_texts(self, revs):
251
"""This will be called by __fetch between fetching weave texts and
252
fetching the inventory weave.
254
Subclasses should override this if they need to generate root texts
255
after fetching weave texts.
162
def _fetch_weave_texts(self, revs):
163
texts_pb = bzrlib.ui.ui_factory.nested_progress_bar()
165
# fileids_altered_by_revision_ids requires reading the inventory
166
# weave, we will need to read the inventory weave again when
167
# all this is done, so enable caching for that specific weave
168
inv_w = self.from_repository.get_inventory_weave()
170
file_ids = self.from_repository.fileids_altered_by_revision_ids(revs)
172
num_file_ids = len(file_ids)
173
for file_id, required_versions in file_ids.items():
174
texts_pb.update("fetch texts", count, num_file_ids)
176
to_weave = self.to_weaves.get_weave_or_empty(file_id,
177
self.to_repository.get_transaction())
178
from_weave = self.from_weaves.get_weave(file_id,
179
self.from_repository.get_transaction())
180
# we fetch all the texts, because texts do
181
# not reference anything, and its cheap enough
182
to_weave.join(from_weave, version_ids=required_versions)
183
# we don't need *all* of this data anymore, but we dont know
184
# what we do. This cache clearing will result in a new read
185
# of the knit data when we do the checkout, but probably we
186
# want to emit the needed data on the fly rather than at the
188
# the from weave should know not to cache data being joined,
189
# but its ok to ask it to clear.
190
from_weave.clear_cache()
191
to_weave.clear_cache()
195
def _fetch_inventory_weave(self, revs):
196
pb = bzrlib.ui.ui_factory.nested_progress_bar()
198
pb.update("fetch inventory", 0, 2)
199
to_weave = self.to_control.get_weave('inventory',
200
self.to_repository.get_transaction())
202
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
204
# just merge, this is optimisable and its means we don't
205
# copy unreferenced data such as not-needed inventories.
206
pb.update("fetch inventory", 1, 3)
207
from_weave = self.from_repository.get_inventory_weave()
208
pb.update("fetch inventory", 2, 3)
209
# we fetch only the referenced inventories because we do not
210
# know for unselected inventories whether all their required
211
# texts are present in the other repository - it could be
213
to_weave.join(from_weave, pb=child_pb, msg='merge inventory',
215
from_weave.clear_cache()
260
222
class GenericRepoFetcher(RepoFetcher):
264
226
It triggers a reconciliation after fetching to ensure integrity.
267
def _fetch_revision_texts(self, revs, pb):
229
def _fetch_revision_texts(self, revs):
268
230
"""Fetch revision object texts"""
269
to_txn = self.to_transaction = self.to_repository.get_transaction()
272
to_store = self.to_repository._revision_store
274
pb.update('copying revisions', count, total)
276
sig_text = self.from_repository.get_signature_text(rev)
277
to_store.add_revision_signature_text(rev, sig_text, to_txn)
278
except errors.NoSuchRevision:
281
to_store.add_revision(self.from_repository.get_revision(rev),
284
# fixup inventory if needed:
285
# this is expensive because we have no inverse index to current ghosts.
286
# but on local disk its a few seconds and sftp push is already insane.
288
# FIXME: repository should inform if this is needed.
289
self.to_repository.reconcile()
231
rev_pb = bzrlib.ui.ui_factory.nested_progress_bar()
233
to_txn = self.to_transaction = self.to_repository.get_transaction()
236
to_store = self.to_repository._revision_store
238
pb = bzrlib.ui.ui_factory.nested_progress_bar()
240
pb.update('copying revisions', count, total)
242
sig_text = self.from_repository.get_signature_text(rev)
243
to_store.add_revision_signature_text(rev, sig_text, to_txn)
244
except errors.NoSuchRevision:
247
to_store.add_revision(self.from_repository.get_revision(rev),
252
# fixup inventory if needed:
253
# this is expensive because we have no inverse index to current ghosts.
254
# but on local disk its a few seconds and sftp push is already insane.
256
# FIXME: repository should inform if this is needed.
257
self.to_repository.reconcile()
292
262
class KnitRepoFetcher(RepoFetcher):
356
324
parent_texts = {}
357
325
versionedfile = {}
358
326
to_store = self.target.weave_store
359
parent_map = self.source.get_graph().get_parent_map(revs)
360
327
for tree in self.iter_rev_trees(revs):
361
328
revision_id = tree.inventory.root.revision
362
root_id = tree.get_root_id()
363
parents = parent_map[revision_id]
364
if parents[0] == NULL_REVISION:
329
root_id = tree.inventory.root.file_id
330
parents = inventory_weave.get_parents(revision_id)
366
331
if root_id not in versionedfile:
367
versionedfile[root_id] = to_store.get_weave_or_empty(root_id,
332
versionedfile[root_id] = to_store.get_weave_or_empty(root_id,
368
333
self.target.get_transaction())
369
_, _, parent_texts[root_id] = versionedfile[root_id].add_lines(
334
parent_texts[root_id] = versionedfile[root_id].add_lines(
370
335
revision_id, parents, [], parent_texts)
372
337
def regenerate_inventory(self, revs):
385
351
class Model1toKnit2Fetcher(GenericRepoFetcher):
386
352
"""Fetch from a Model1 repository into a Knit2 repository
388
def __init__(self, to_repository, from_repository, last_revision=None,
389
pb=None, find_ghosts=True):
354
def __init__(self, to_repository, from_repository, last_revision=None,
390
356
self.helper = Inter1and2Helper(from_repository, to_repository)
391
357
GenericRepoFetcher.__init__(self, to_repository, from_repository,
392
last_revision, pb, find_ghosts)
394
def _generate_root_texts(self, revs):
360
def _fetch_weave_texts(self, revs):
361
GenericRepoFetcher._fetch_weave_texts(self, revs)
362
# Now generate a weave for the tree root
395
363
self.helper.generate_root_texts(revs)
397
def _fetch_inventory_weave(self, revs, pb):
365
def _fetch_inventory_weave(self, revs):
398
366
self.helper.regenerate_inventory(revs)
402
370
"""Fetch from a Knit1 repository into a Knit2 repository"""
404
372
def __init__(self, to_repository, from_repository, last_revision=None,
405
pb=None, find_ghosts=True):
406
374
self.helper = Inter1and2Helper(from_repository, to_repository)
407
375
KnitRepoFetcher.__init__(self, to_repository, from_repository,
408
last_revision, pb, find_ghosts)
410
def _generate_root_texts(self, revs):
378
def _fetch_weave_texts(self, revs):
379
KnitRepoFetcher._fetch_weave_texts(self, revs)
380
# Now generate a weave for the tree root
411
381
self.helper.generate_root_texts(revs)
413
def _fetch_inventory_weave(self, revs, pb):
383
def _fetch_inventory_weave(self, revs):
414
384
self.helper.regenerate_inventory(revs)
417
class RemoteToOtherFetcher(GenericRepoFetcher):
419
def _fetch_everything_for_search(self, search, pp):
420
data_stream = self.from_repository.get_data_stream_for_search(search)
421
self.to_repository.insert_data_stream(data_stream)
387
class Fetcher(object):
388
"""Backwards compatibility glue for branch.fetch()."""
390
@deprecated_method(zero_eight)
391
def __init__(self, to_branch, from_branch, last_revision=None, pb=None):
392
"""Please see branch.fetch()."""
393
to_branch.fetch(from_branch, last_revision, pb)