54
def _all_possible_ids(self):
55
"""Return all the possible revisions that we could find."""
56
return self.get_inventory_weave().names()
54
59
def all_revision_ids(self):
55
"""Returns a list of all the revision ids in the repository.
57
It would be nice to have this topologically sorted, but its not yet.
59
possible_ids = self.get_inventory_weave().names()
60
"""Returns a list of all the revision ids in the repository.
62
These are in as much topological order as the underlying store can
63
present: for weaves ghosts may lead to a lack of correctness until
64
the reweave updates the parents list.
66
result = self._all_possible_ids()
67
return self._eliminate_revisions_not_present(result)
70
def _eliminate_revisions_not_present(self, revision_ids):
71
"""Check every revision id in revision_ids to see if we have it.
73
Returns a set of the present revisions.
61
for id in possible_ids:
76
for id in revision_ids:
62
77
if self.has_revision(id):
161
176
def lock_read(self):
162
177
self.control_files.lock_read()
180
def missing_revision_ids(self, other, revision_id=None):
181
"""Return the revision ids that other has that this does not.
183
These are returned in topological order.
185
revision_id: only return revision ids included by revision_id.
187
if self._compatible_formats(other):
188
# fast path for weave-inventory based stores.
189
# we want all revisions to satisft revision_id in other.
190
# but we dont want to stat every file here and there.
191
# we want then, all revisions other needs to satisfy revision_id
192
# checked, but not those that we have locally.
193
# so the first thing is to get a subset of the revisions to
194
# satisfy revision_id in other, and then eliminate those that
195
# we do already have.
196
# this is slow on high latency connection to self, but as as this
197
# disk format scales terribly for push anyway due to rewriting
198
# inventory.weave, this is considered acceptable.
200
if revision_id is not None:
201
other_ids = other.get_ancestry(revision_id)
202
assert other_ids.pop(0) == None
204
other_ids = other._all_possible_ids()
205
other_ids_set = set(other_ids)
206
# other ids is the worst case to pull now.
207
# now we want to filter other_ids against what we actually
208
# have, but dont try to stat what we know we dont.
209
my_ids = set(self._all_possible_ids())
210
possibly_present_revisions = my_ids.intersection(other_ids_set)
211
actually_present_revisions = set(self._eliminate_revisions_not_present(possibly_present_revisions))
212
required_revisions = other_ids_set.difference(actually_present_revisions)
213
required_topo_revisions = [rev_id for rev_id in other_ids if rev_id in required_revisions]
214
if revision_id is not None:
215
# we used get_ancestry to determine other_ids then we are assured all
216
# revisions referenced are present as they are installed in topological order.
217
return required_topo_revisions
219
# we only have an estimate of whats available
220
return other._eliminate_revisions_not_present(required_topo_revisions)
222
my_ids = set(self.all_revision_ids())
223
if revision_id is not None:
224
other_ids = other.get_ancestry(revision_id)
225
assert other_ids.pop(0) == None
227
other_ids = other.all_revision_ids()
228
result_set = set(other_ids).difference(my_ids)
229
return [rev_id for rev_id in other_ids if rev_id in result_set]
166
233
"""Open the repository rooted at base.
171
238
control = bzrdir.BzrDir.open(base)
172
239
return control.open_repository()
174
def push_stores(self, to, revision=NULL_REVISION):
175
"""FIXME: document and find a consistent name with other classes."""
176
if (not isinstance(self._format, RepositoryFormat4) or
177
self._format != to._format):
178
from bzrlib.fetch import RepoFetcher
179
mutter("Using fetch logic to push between %s(%s) and %s(%s)",
180
self, self._format, to, to._format)
181
RepoFetcher(to_repository=to, from_repository=self,
182
last_revision=revision)
241
def _compatible_formats(self, other):
242
"""Return True if the stores in self and other are 'compatible'
244
'compatible' means that they are both the same underlying type
245
i.e. both weave stores, or both knits and thus support fast-path
247
return (isinstance(self._format, (RepositoryFormat5,
249
RepositoryFormat7)) and
250
isinstance(other._format, (RepositoryFormat5,
185
# format 4 to format 4 logic only.
186
store_pairs = ((self.text_store, to.text_store),
187
(self.inventory_store, to.inventory_store),
188
(self.revision_store, to.revision_store))
255
def copy_content_into(self, destination, revision_id=None, basis=None):
256
"""Make a complete copy of the content in self into destination."""
257
destination.lock_write()
190
for from_store, to_store in store_pairs:
191
copy_all(from_store, to_store)
192
except UnlistableStore:
193
raise UnlistableBranch(from_store)
261
if self._compatible_formats(destination):
262
if basis is not None:
263
# copy the basis in, then fetch remaining data.
264
basis.copy_content_into(destination, revision_id)
265
destination.fetch(self, revision_id=revision_id)
268
if self.control_files._transport.listable():
269
destination.control_weaves.copy_multi(self.control_weaves,
271
copy_all(self.weave_store, destination.weave_store)
272
copy_all(self.revision_store, destination.revision_store)
274
destination.fetch(self, revision_id=revision_id)
275
# compatible v4 stores
276
elif isinstance(self._format, RepositoryFormat4):
277
if not isinstance(destination._format, RepositoryFormat4):
278
raise BzrError('cannot copy v4 branches to anything other than v4 branches.')
279
store_pairs = ((self.text_store, destination.text_store),
280
(self.inventory_store, destination.inventory_store),
281
(self.revision_store, destination.revision_store))
283
for from_store, to_store in store_pairs:
284
copy_all(from_store, to_store)
285
except UnlistableStore:
286
raise UnlistableBranch(from_store)
289
destination.fetch(self, revision_id=revision_id)
294
def fetch(self, source, revision_id=None):
295
"""Fetch the content required to construct revision_id from source.
297
If revision_id is None all content is copied.
299
from bzrlib.fetch import RepoFetcher
300
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
301
source, source._format, self, self._format)
302
RepoFetcher(to_repository=self, from_repository=source, last_revision=revision_id)
195
304
def unlock(self):
196
305
self.control_files.unlock()
199
def clone(self, a_bzrdir):
308
def clone(self, a_bzrdir, revision_id=None, basis=None):
200
309
"""Clone this repository into a_bzrdir using the current format.
202
311
Currently no check is made that the format of this repository and
203
312
the bzrdir format are compatible. FIXME RBC 20060201.
205
result = self._format.initialize(a_bzrdir)
314
if not isinstance(a_bzrdir._format, self.bzrdir._format.__class__):
315
# use target default format.
316
result = a_bzrdir.create_repository()
317
# FIXME RBC 20060209 split out the repository type to avoid this check ?
318
elif isinstance(a_bzrdir._format,
319
(bzrdir.BzrDirFormat4,
320
bzrdir.BzrDirFormat5,
321
bzrdir.BzrDirFormat6)):
322
result = a_bzrdir.open_repository()
324
result = self._format.initialize(a_bzrdir)
325
self.copy_content_into(result, revision_id, basis)
210
def copy(self, destination):
211
destination.lock_write()
213
destination.control_weaves.copy_multi(self.control_weaves,
215
copy_all(self.weave_store, destination.weave_store)
216
copy_all(self.revision_store, destination.revision_store)
220
328
def has_revision(self, revision_id):
221
329
"""True if this branch has a copy of the revision.