165 """an object representing ongoing discovery |
165 """an object representing ongoing discovery |
166 |
166 |
167 Feed with data from the remote repository, this object keep track of the |
167 Feed with data from the remote repository, this object keep track of the |
168 current set of changeset in various states: |
168 current set of changeset in various states: |
169 |
169 |
170 - common: own nodes I know we both know |
170 - common: own nodes I know we both know |
|
171 - undecided: own nodes where I don't know if remote knows them |
171 """ |
172 """ |
172 |
173 |
173 def __init__(self, repo): |
174 def __init__(self, repo, targetheads): |
174 self._repo = repo |
175 self._repo = repo |
|
176 self._targetheads = targetheads |
175 self._common = repo.changelog.incrementalmissingrevs() |
177 self._common = repo.changelog.incrementalmissingrevs() |
|
178 self._undecided = None |
176 |
179 |
177 def addcommons(self, commons): |
180 def addcommons(self, commons): |
178 """registrer nodes known as common""" |
181 """registrer nodes known as common""" |
179 self._common.addbases(commons) |
182 self._common.addbases(commons) |
180 |
183 |
181 def hasinfo(self): |
184 def hasinfo(self): |
182 """return True is we have any clue about the remote state""" |
185 """return True is we have any clue about the remote state""" |
183 return self._common.hasbases() |
186 return self._common.hasbases() |
|
187 |
|
188 @property |
|
189 def undecided(self): |
|
190 if self._undecided is not None: |
|
191 return self._undecided |
|
192 self._undecided = set(self._common.missingancestors(self._targetheads)) |
|
193 return self._undecided |
184 |
194 |
185 def commonheads(self): |
195 def commonheads(self): |
186 """the heads of the known common set""" |
196 """the heads of the known common set""" |
187 # heads(common) == heads(common.bases) since common represents |
197 # heads(common) == heads(common.bases) since common represents |
188 # common.bases and all its ancestors |
198 # common.bases and all its ancestors |
254 ownheadhashes = [clnode(r) for r in ownheads] |
264 ownheadhashes = [clnode(r) for r in ownheads] |
255 return ownheadhashes, True, srvheadhashes |
265 return ownheadhashes, True, srvheadhashes |
256 |
266 |
257 # full blown discovery |
267 # full blown discovery |
258 |
268 |
259 disco = partialdiscovery(local) |
269 disco = partialdiscovery(local, ownheads) |
260 # treat remote heads (and maybe own heads) as a first implicit sample |
270 # treat remote heads (and maybe own heads) as a first implicit sample |
261 # response |
271 # response |
262 disco.addcommons(srvheads) |
272 disco.addcommons(srvheads) |
263 commoninsample = set(n for i, n in enumerate(sample) if yesno[i]) |
273 commoninsample = set(n for i, n in enumerate(sample) if yesno[i]) |
264 disco.addcommons(commoninsample) |
274 disco.addcommons(commoninsample) |
265 # own nodes where I don't know if remote knows them |
|
266 undecided = set(disco._common.missingancestors(ownheads)) |
|
267 # own nodes I know remote lacks |
275 # own nodes I know remote lacks |
268 missing = set() |
276 missing = set() |
269 |
277 |
270 full = False |
278 full = False |
271 progress = ui.makeprogress(_('searching'), unit=_('queries')) |
279 progress = ui.makeprogress(_('searching'), unit=_('queries')) |
272 while undecided: |
280 while disco.undecided: |
273 |
281 |
274 if sample: |
282 if sample: |
275 missinginsample = [n for i, n in enumerate(sample) if not yesno[i]] |
283 missinginsample = [n for i, n in enumerate(sample) if not yesno[i]] |
276 |
284 |
277 if missing: |
285 if missing: |
278 missing.update(local.revs('descendants(%ld) - descendants(%ld)', |
286 missing.update(local.revs('descendants(%ld) - descendants(%ld)', |
279 missinginsample, missing)) |
287 missinginsample, missing)) |
280 else: |
288 else: |
281 missing.update(local.revs('descendants(%ld)', missinginsample)) |
289 missing.update(local.revs('descendants(%ld)', missinginsample)) |
282 |
290 |
283 undecided.difference_update(missing) |
291 disco.undecided.difference_update(missing) |
284 |
292 |
285 if not undecided: |
293 if not disco.undecided: |
286 break |
294 break |
287 |
295 |
288 if full or disco.hasinfo(): |
296 if full or disco.hasinfo(): |
289 if full: |
297 if full: |
290 ui.note(_("sampling from both directions\n")) |
298 ui.note(_("sampling from both directions\n")) |
295 else: |
303 else: |
296 # use even cheaper initial sample |
304 # use even cheaper initial sample |
297 ui.debug("taking quick initial sample\n") |
305 ui.debug("taking quick initial sample\n") |
298 samplefunc = _takequicksample |
306 samplefunc = _takequicksample |
299 targetsize = initialsamplesize |
307 targetsize = initialsamplesize |
300 sample = samplefunc(local, ownheads, undecided, targetsize) |
308 sample = samplefunc(local, ownheads, disco.undecided, targetsize) |
301 |
309 |
302 roundtrips += 1 |
310 roundtrips += 1 |
303 progress.update(roundtrips) |
311 progress.update(roundtrips) |
304 ui.debug("query %i; still undecided: %i, sample size is: %i\n" |
312 ui.debug("query %i; still undecided: %i, sample size is: %i\n" |
305 % (roundtrips, len(undecided), len(sample))) |
313 % (roundtrips, len(disco.undecided), len(sample))) |
306 # indices between sample and externalized version must match |
314 # indices between sample and externalized version must match |
307 sample = list(sample) |
315 sample = list(sample) |
308 |
316 |
309 with remote.commandexecutor() as e: |
317 with remote.commandexecutor() as e: |
310 yesno = e.callcommand('known', { |
318 yesno = e.callcommand('known', { |
314 full = True |
322 full = True |
315 |
323 |
316 if sample: |
324 if sample: |
317 commoninsample = set(n for i, n in enumerate(sample) if yesno[i]) |
325 commoninsample = set(n for i, n in enumerate(sample) if yesno[i]) |
318 disco.addcommons(commoninsample) |
326 disco.addcommons(commoninsample) |
319 disco._common.removeancestorsfrom(undecided) |
327 disco._common.removeancestorsfrom(disco.undecided) |
320 |
328 |
321 result = disco.commonheads() |
329 result = disco.commonheads() |
322 elapsed = util.timer() - start |
330 elapsed = util.timer() - start |
323 progress.complete() |
331 progress.complete() |
324 ui.debug("%d total queries in %.4fs\n" % (roundtrips, elapsed)) |
332 ui.debug("%d total queries in %.4fs\n" % (roundtrips, elapsed)) |