61
62
# and add in all file versions
65
@deprecated_function(zero_eight)
65
66
def greedy_fetch(to_branch, from_branch, revision=None, pb=None):
67
"""Legacy API, please see branch.fetch(from_branch, last_revision, pb)."""
66
68
f = Fetcher(to_branch, from_branch, revision, pb)
67
69
return f.count_copied, f.failed_revisions
71
class Fetcher(object):
72
"""Pull revisions and texts from one branch to another.
74
This doesn't update the destination's history; that can be done
75
separately if desired.
78
If set, pull only up to this revision_id.
82
last_revision -- if last_revision
83
is given it will be that, otherwise the last revision of
74
class RepoFetcher(object):
75
"""Pull revisions and texts from one repository to another.
78
if set, try to limit to the data this revision references.
86
81
count_copied -- number of revisions copied
88
count_weaves -- number of file weaves copied
83
This should not be used directory, its essential a object to encapsulate
84
the logic in InterRepository.fetch().
90
def __init__(self, to_branch, from_branch, last_revision=None, pb=None):
91
if to_branch.base == from_branch.base:
92
raise Exception("can't fetch from a branch to itself %s, %s" %
93
(from_branch.base, to_branch.base))
95
self.to_branch = to_branch
96
self.from_branch = from_branch
86
def __init__(self, to_repository, from_repository, last_revision=None, pb=None):
88
self.failed_revisions = []
90
if to_repository.control_files._transport.base == from_repository.control_files._transport.base:
91
# check that last_revision is in 'from' and then return a no-operation.
92
if last_revision not in (None, NULL_REVISION):
93
from_repository.get_revision(last_revision)
95
self.to_repository = to_repository
96
self.from_repository = from_repository
97
# must not mutate self._last_revision as its potentially a shared instance
97
98
self._last_revision = last_revision
99
self.pb = bzrlib.ui.ui_factory.progress_bar()
100
self.pb = bzrlib.ui.ui_factory.nested_progress_bar()
101
self.nested_pb = self.pb
102
self.from_branch.lock_read()
104
self.nested_pb = None
105
self.from_repository.lock_read()
104
self.to_branch.lock_write()
107
self.to_repository.lock_write()
108
self.to_branch.unlock()
111
if self.nested_pb is not None:
112
self.nested_pb.finished()
113
self.to_repository.unlock()
110
self.from_branch.unlock()
115
self.from_repository.unlock()
112
117
def __fetch(self):
113
118
"""Primary worker function.
115
120
This initialises all the needed variables, and then fetches the
116
121
requested revisions, finally clearing the progress bar.
118
self.to_repository = self.to_branch.repository
119
123
self.to_weaves = self.to_repository.weave_store
120
124
self.to_control = self.to_repository.control_weaves
121
self.from_repository = self.from_branch.repository
122
125
self.from_weaves = self.from_repository.weave_store
123
126
self.from_control = self.from_repository.control_weaves
124
self.failed_revisions = []
125
self.count_copied = 0
126
127
self.count_total = 0
127
self.count_weaves = 0
128
self.copied_file_ids = set()
129
128
self.file_ids_names = {}
129
pp = ProgressPhase('Fetch phase', 4, self.pb)
131
132
revs = self._revids_to_fetch()
134
136
self._fetch_weave_texts(revs)
135
138
self._fetch_inventory_weave(revs)
136
140
self._fetch_revision_texts(revs)
137
141
self.count_copied += len(revs)
141
145
def _revids_to_fetch(self):
142
self._find_last_revision()
143
146
mutter('fetch up to rev {%s}', self._last_revision)
144
if (self._last_revision is not None and
147
if self._last_revision is NULL_REVISION:
148
# explicit limit of no revisions needed
150
if (self._last_revision != None and
145
151
self.to_repository.has_revision(self._last_revision)):
148
branch_from_revs = set(self.from_repository.get_ancestry(self._last_revision))
155
return self.to_repository.missing_revision_ids(self.from_repository,
157
except errors.NoSuchRevision:
150
158
raise InstallFailed([self._last_revision])
152
self.dest_last_rev = self.to_branch.last_revision()
153
branch_to_revs = set(self.to_repository.get_ancestry(self.dest_last_rev))
155
return branch_from_revs.difference(branch_to_revs)
157
def _fetch_revision_texts(self, revs):
158
self.to_repository.revision_store.copy_multi(
159
self.from_repository.revision_store, revs)
161
160
def _fetch_weave_texts(self, revs):
162
file_ids = self.from_branch.fileid_involved_by_set(revs)
164
num_file_ids = len(file_ids)
165
for file_id in file_ids:
166
self.pb.update("merge weave merge", count, num_file_ids)
168
to_weave = self.to_weaves.get_weave_or_empty(file_id,
169
self.to_branch.get_transaction())
170
from_weave = self.from_weaves.get_weave(file_id,
171
self.from_branch.get_transaction())
173
if to_weave.numversions() > 0:
174
# destination has contents, must merge
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()
220
class GenericRepoFetcher(RepoFetcher):
221
"""This is a generic repo to repo fetcher.
223
This makes minimal assumptions about repo layout and contents.
224
It triggers a reconciliation after fetching to ensure integrity.
227
def _fetch_revision_texts(self, revs):
228
"""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()
176
to_weave.join(from_weave)
177
except errors.WeaveParentMismatch:
178
to_weave.reweave(from_weave)
180
# destination is empty, just replace it
181
to_weave = from_weave.copy()
183
self.to_weaves.put_weave(file_id, to_weave,
184
self.to_branch.get_transaction())
188
def _fetch_inventory_weave(self, revs):
189
self.pb.update("inventory merge", 0, 1)
190
from_weave = self.from_repository.get_inventory_weave()
191
to_weave = self.to_repository.get_inventory_weave()
193
if to_weave.numversions() > 0:
194
# destination has contents, must merge
196
to_weave.join(from_weave)
197
except errors.WeaveParentMismatch:
198
to_weave.reweave(from_weave)
200
# destination is empty, just replace it
201
to_weave = from_weave.copy()
203
self.to_control.put_weave('inventory', to_weave,
204
self.to_branch.get_transaction())
208
def _find_last_revision(self):
209
"""Find the limiting source revision.
211
Every ancestor of that revision will be merged across.
213
Returns the revision_id, or returns None if there's no history
214
in the source branch."""
215
if self._last_revision:
217
self.pb.update('get source history')
218
from_history = self.from_branch.revision_history()
219
self.pb.update('get destination history')
221
self._last_revision = from_history[-1]
223
# no history in the source branch
224
self._last_revision = None
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
class KnitRepoFetcher(RepoFetcher):
261
"""This is a knit format repository specific fetcher.
263
This differs from the GenericRepoFetcher by not doing a
264
reconciliation after copying, and using knit joining to
268
def _fetch_revision_texts(self, revs):
269
# may need to be a InterRevisionStore call here.
270
from_transaction = self.from_repository.get_transaction()
271
to_transaction = self.to_repository.get_transaction()
272
to_sf = self.to_repository._revision_store.get_signature_file(
274
from_sf = self.from_repository._revision_store.get_signature_file(
276
to_sf.join(from_sf, version_ids=revs, ignore_missing=True)
277
to_rf = self.to_repository._revision_store.get_revision_file(
279
from_rf = self.from_repository._revision_store.get_revision_file(
281
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)