121
120
requested revisions, finally clearing the progress bar.
123
122
self.to_weaves = self.to_repository.weave_store
124
self.to_control = self.to_repository.control_weaves
125
123
self.from_weaves = self.from_repository.weave_store
126
self.from_control = self.from_repository.control_weaves
127
124
self.count_total = 0
128
125
self.file_ids_names = {}
129
126
pp = ProgressPhase('Fetch phase', 4, self.pb)
132
129
revs = self._revids_to_fetch()
136
self._fetch_weave_texts(revs)
138
self._fetch_inventory_weave(revs)
140
self._fetch_revision_texts(revs)
141
self.count_copied += len(revs)
132
self._fetch_everything_for_revisions(revs, pp)
136
def _fetch_everything_for_revisions(self, revs, pp):
137
"""Fetch all data for the given set of revisions."""
138
# The first phase is "file". We pass the progress bar for it directly
139
# into item_keys_introduced_by, which has more information about how
140
# that phase is progressing than we do. Progress updates for the other
141
# phases are taken care of in this function.
142
# XXX: there should be a clear owner of the progress reporting. Perhaps
143
# item_keys_introduced_by should have a richer API than it does at the
144
# moment, so that it can feed the progress information back to this
147
pb = bzrlib.ui.ui_factory.nested_progress_bar()
149
data_to_fetch = self.from_repository.item_keys_introduced_by(revs, pb)
150
for knit_kind, file_id, revisions in data_to_fetch:
151
if knit_kind != phase:
153
# Make a new progress bar for this phase
156
pb = bzrlib.ui.ui_factory.nested_progress_bar()
157
if knit_kind == "file":
158
self._fetch_weave_text(file_id, revisions)
159
elif knit_kind == "inventory":
161
# Once we've processed all the files, then we generate the root
162
# texts (if necessary), then we process the inventory. It's a
163
# bit distasteful to have knit_kind == "inventory" mean this,
164
# perhaps it should happen on the first non-"file" knit, in case
165
# it's not always inventory?
166
self._generate_root_texts(revs)
167
self._fetch_inventory_weave(revs, pb)
168
elif knit_kind == "signatures":
169
# Nothing to do here; this will be taken care of when
170
# _fetch_revision_texts happens.
172
elif knit_kind == "revisions":
173
self._fetch_revision_texts(revs, pb)
175
raise AssertionError("Unknown knit kind %r" % knit_kind)
179
self.count_copied += len(revs)
145
181
def _revids_to_fetch(self):
182
"""Determines the exact revisions needed from self.from_repository to
183
install self._last_revision in self.to_repository.
185
If no revisions need to be fetched, then this just returns None.
146
187
mutter('fetch up to rev {%s}', self._last_revision)
147
188
if self._last_revision is NULL_REVISION:
148
189
# explicit limit of no revisions needed
150
if (self._last_revision != None and
191
if (self._last_revision is not None and
151
192
self.to_repository.has_revision(self._last_revision)):
196
# XXX: this gets the full graph on both sides, and will make sure
197
# that ghosts are filled whether or not you care about them.
155
198
return self.to_repository.missing_revision_ids(self.from_repository,
156
199
self._last_revision)
157
200
except errors.NoSuchRevision:
158
201
raise InstallFailed([self._last_revision])
160
def _fetch_weave_texts(self, revs):
161
texts_pb = bzrlib.ui.ui_factory.nested_progress_bar()
163
# fileids_altered_by_revision_ids requires reading the inventory
164
# weave, we will need to read the inventory weave again when
165
# all this is done, so enable caching for that specific weave
166
inv_w = self.from_repository.get_inventory_weave()
168
file_ids = self.from_repository.fileids_altered_by_revision_ids(revs)
170
num_file_ids = len(file_ids)
171
for file_id, required_versions in file_ids.items():
172
texts_pb.update("fetch texts", count, num_file_ids)
174
to_weave = self.to_weaves.get_weave_or_empty(file_id,
175
self.to_repository.get_transaction())
176
from_weave = self.from_weaves.get_weave(file_id,
177
self.from_repository.get_transaction())
178
# we fetch all the texts, because texts do
179
# not reference anything, and its cheap enough
180
to_weave.join(from_weave, version_ids=required_versions)
181
# we don't need *all* of this data anymore, but we dont know
182
# what we do. This cache clearing will result in a new read
183
# of the knit data when we do the checkout, but probably we
184
# want to emit the needed data on the fly rather than at the
186
# the from weave should know not to cache data being joined,
187
# but its ok to ask it to clear.
188
from_weave.clear_cache()
189
to_weave.clear_cache()
193
def _fetch_inventory_weave(self, revs):
194
pb = bzrlib.ui.ui_factory.nested_progress_bar()
196
pb.update("fetch inventory", 0, 2)
197
to_weave = self.to_control.get_weave('inventory',
198
self.to_repository.get_transaction())
200
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
202
# just merge, this is optimisable and its means we don't
203
# copy unreferenced data such as not-needed inventories.
204
pb.update("fetch inventory", 1, 3)
205
from_weave = self.from_repository.get_inventory_weave()
206
pb.update("fetch inventory", 2, 3)
207
# we fetch only the referenced inventories because we do not
208
# know for unselected inventories whether all their required
209
# texts are present in the other repository - it could be
211
to_weave.join(from_weave, pb=child_pb, msg='merge inventory',
213
from_weave.clear_cache()
203
def _fetch_weave_text(self, file_id, required_versions):
204
to_weave = self.to_weaves.get_weave_or_empty(file_id,
205
self.to_repository.get_transaction())
206
from_weave = self.from_weaves.get_weave(file_id,
207
self.from_repository.get_transaction())
208
# we fetch all the texts, because texts do
209
# not reference anything, and its cheap enough
210
to_weave.join(from_weave, version_ids=required_versions)
211
# we don't need *all* of this data anymore, but we dont know
212
# what we do. This cache clearing will result in a new read
213
# of the knit data when we do the checkout, but probably we
214
# want to emit the needed data on the fly rather than at the
216
# the from weave should know not to cache data being joined,
217
# but its ok to ask it to clear.
218
from_weave.clear_cache()
219
to_weave.clear_cache()
221
def _fetch_inventory_weave(self, revs, pb):
222
pb.update("fetch inventory", 0, 2)
223
to_weave = self.to_repository.get_inventory_weave()
224
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
226
# just merge, this is optimisable and its means we don't
227
# copy unreferenced data such as not-needed inventories.
228
pb.update("fetch inventory", 1, 3)
229
from_weave = self.from_repository.get_inventory_weave()
230
pb.update("fetch inventory", 2, 3)
231
# we fetch only the referenced inventories because we do not
232
# know for unselected inventories whether all their required
233
# texts are present in the other repository - it could be
235
to_weave.join(from_weave, pb=child_pb, msg='merge inventory',
237
from_weave.clear_cache()
241
def _generate_root_texts(self, revs):
242
"""This will be called by __fetch between fetching weave texts and
243
fetching the inventory weave.
245
Subclasses should override this if they need to generate root texts
246
after fetching weave texts.
220
251
class GenericRepoFetcher(RepoFetcher):
224
255
It triggers a reconciliation after fetching to ensure integrity.
227
def _fetch_revision_texts(self, revs):
258
def _fetch_revision_texts(self, revs, pb):
228
259
"""Fetch revision object texts"""
229
rev_pb = bzrlib.ui.ui_factory.nested_progress_bar()
231
to_txn = self.to_transaction = self.to_repository.get_transaction()
234
to_store = self.to_repository._revision_store
236
pb = bzrlib.ui.ui_factory.nested_progress_bar()
238
pb.update('copying revisions', count, total)
240
sig_text = self.from_repository.get_signature_text(rev)
241
to_store.add_revision_signature_text(rev, sig_text, to_txn)
242
except errors.NoSuchRevision:
245
to_store.add_revision(self.from_repository.get_revision(rev),
250
# fixup inventory if needed:
251
# this is expensive because we have no inverse index to current ghosts.
252
# but on local disk its a few seconds and sftp push is already insane.
254
# FIXME: repository should inform if this is needed.
255
self.to_repository.reconcile()
260
to_txn = self.to_transaction = self.to_repository.get_transaction()
263
to_store = self.to_repository._revision_store
265
pb.update('copying revisions', count, total)
267
sig_text = self.from_repository.get_signature_text(rev)
268
to_store.add_revision_signature_text(rev, sig_text, to_txn)
269
except errors.NoSuchRevision:
272
to_store.add_revision(self.from_repository.get_revision(rev),
275
# fixup inventory if needed:
276
# this is expensive because we have no inverse index to current ghosts.
277
# but on local disk its a few seconds and sftp push is already insane.
279
# FIXME: repository should inform if this is needed.
280
self.to_repository.reconcile()
260
283
class KnitRepoFetcher(RepoFetcher):
281
304
to_rf.join(from_rf, version_ids=revs)
284
class Fetcher(object):
285
"""Backwards compatibility glue for branch.fetch()."""
287
@deprecated_method(zero_eight)
288
def __init__(self, to_branch, from_branch, last_revision=None, pb=None):
289
"""Please see branch.fetch()."""
290
to_branch.fetch(from_branch, last_revision, pb)
307
class Inter1and2Helper(object):
308
"""Helper for operations that convert data from model 1 and 2
310
This is for use by fetchers and converters.
313
def __init__(self, source, target):
316
:param source: The repository data comes from
317
:param target: The repository data goes to
322
def iter_rev_trees(self, revs):
323
"""Iterate through RevisionTrees efficiently.
325
Additionally, the inventory's revision_id is set if unset.
327
Trees are retrieved in batches of 100, and then yielded in the order
330
:param revs: A list of revision ids
333
for tree in self.source.revision_trees(revs[:100]):
334
if tree.inventory.revision_id is None:
335
tree.inventory.revision_id = tree.get_revision_id()
339
def generate_root_texts(self, revs):
340
"""Generate VersionedFiles for all root ids.
342
:param revs: the revisions to include
344
inventory_weave = self.source.get_inventory_weave()
347
to_store = self.target.weave_store
348
for tree in self.iter_rev_trees(revs):
349
revision_id = tree.inventory.root.revision
350
root_id = tree.inventory.root.file_id
351
parents = inventory_weave.get_parents(revision_id)
352
if root_id not in versionedfile:
353
versionedfile[root_id] = to_store.get_weave_or_empty(root_id,
354
self.target.get_transaction())
355
_, _, parent_texts[root_id] = versionedfile[root_id].add_lines(
356
revision_id, parents, [], parent_texts)
358
def regenerate_inventory(self, revs):
359
"""Generate a new inventory versionedfile in target, convertin data.
361
The inventory is retrieved from the source, (deserializing it), and
362
stored in the target (reserializing it in a different format).
363
:param revs: The revisions to include
365
inventory_weave = self.source.get_inventory_weave()
366
for tree in self.iter_rev_trees(revs):
367
parents = inventory_weave.get_parents(tree.get_revision_id())
368
self.target.add_inventory(tree.get_revision_id(), tree.inventory,
372
class Model1toKnit2Fetcher(GenericRepoFetcher):
373
"""Fetch from a Model1 repository into a Knit2 repository
375
def __init__(self, to_repository, from_repository, last_revision=None,
377
self.helper = Inter1and2Helper(from_repository, to_repository)
378
GenericRepoFetcher.__init__(self, to_repository, from_repository,
381
def _generate_root_texts(self, revs):
382
self.helper.generate_root_texts(revs)
384
def _fetch_inventory_weave(self, revs, pb):
385
self.helper.regenerate_inventory(revs)
388
class Knit1to2Fetcher(KnitRepoFetcher):
389
"""Fetch from a Knit1 repository into a Knit2 repository"""
391
def __init__(self, to_repository, from_repository, last_revision=None,
393
self.helper = Inter1and2Helper(from_repository, to_repository)
394
KnitRepoFetcher.__init__(self, to_repository, from_repository,
397
def _generate_root_texts(self, revs):
398
self.helper.generate_root_texts(revs)
400
def _fetch_inventory_weave(self, revs, pb):
401
self.helper.regenerate_inventory(revs)
404
class RemoteToOtherFetcher(GenericRepoFetcher):
406
def _fetch_everything_for_revisions(self, revs, pp):
407
data_stream = self.from_repository.get_data_stream(revs)
408
self.to_repository.insert_data_stream(data_stream)