260 heads[p] = plinkrev |
260 heads[p] = plinkrev |
261 if plinkrev >= minlinkrev: |
261 if plinkrev >= minlinkrev: |
262 futurelargelinkrevs.add(plinkrev) |
262 futurelargelinkrevs.add(plinkrev) |
263 |
263 |
264 return strippoint, brokenrevs |
264 return strippoint, brokenrevs |
|
265 |
|
266 def emitrevisions(store, revs, resultcls, deltaparentfn, candeltafn, |
|
267 rawsizefn, revdifffn, flagsfn, sendfulltext=False, |
|
268 revisiondata=False, assumehaveparentrevisions=False, |
|
269 deltaprevious=False): |
|
270 """Generic implementation of ifiledata.emitrevisions(). |
|
271 |
|
272 Emitting revision data is subtly complex. This function attempts to |
|
273 encapsulate all the logic for doing so in a backend-agnostic way. |
|
274 |
|
275 ``store`` |
|
276 Object conforming to ``ifilestorage`` interface. |
|
277 |
|
278 ``revs`` |
|
279 List of integer revision numbers whose data to emit. |
|
280 |
|
281 ``resultcls`` |
|
282 A type implementing the ``irevisiondelta`` interface that will be |
|
283 constructed and returned. |
|
284 |
|
285 ``deltaparentfn`` |
|
286 Callable receiving a revision number and returning the revision number |
|
287 of a revision that the internal delta is stored against. This delta |
|
288 will be preferred over computing a new arbitrary delta. |
|
289 |
|
290 ``candeltafn`` |
|
291 Callable receiving a pair of revision numbers that returns a bool |
|
292 indicating whether a delta between them can be produced. |
|
293 |
|
294 ``rawsizefn`` |
|
295 Callable receiving a revision number and returning the length of the |
|
296 ``store.revision(rev, raw=True)``. |
|
297 |
|
298 ``revdifffn`` |
|
299 Callable receiving a pair of revision numbers that returns a delta |
|
300 between them. |
|
301 |
|
302 ``flagsfn`` |
|
303 Callable receiving a revision number and returns the integer flags |
|
304 value for it. |
|
305 |
|
306 ``sendfulltext`` |
|
307 Whether to send fulltext revisions instead of deltas, if allowed. |
|
308 |
|
309 ``revisiondata`` |
|
310 ``assumehaveparentrevisions`` |
|
311 ``deltaprevious`` |
|
312 See ``ifiledata.emitrevisions()`` interface documentation. |
|
313 """ |
|
314 |
|
315 fnode = store.node |
|
316 |
|
317 prevrev = None |
|
318 |
|
319 if deltaprevious or assumehaveparentrevisions: |
|
320 prevrev = store.parentrevs(revs[0])[0] |
|
321 |
|
322 # Set of revs available to delta against. |
|
323 available = set() |
|
324 |
|
325 for rev in revs: |
|
326 if rev == nullrev: |
|
327 continue |
|
328 |
|
329 node = fnode(rev) |
|
330 deltaparentrev = deltaparentfn(rev) |
|
331 p1rev, p2rev = store.parentrevs(rev) |
|
332 |
|
333 # Forced delta against previous mode. |
|
334 if deltaprevious: |
|
335 baserev = prevrev |
|
336 |
|
337 # We're instructed to send fulltext. Honor that. |
|
338 elif sendfulltext: |
|
339 baserev = nullrev |
|
340 |
|
341 # There is a delta in storage. We try to use that because it |
|
342 # amounts to effectively copying data from storage and is |
|
343 # therefore the fastest. |
|
344 elif deltaparentrev != nullrev: |
|
345 # Base revision was already emitted in this group. We can |
|
346 # always safely use the delta. |
|
347 if deltaparentrev in available: |
|
348 baserev = deltaparentrev |
|
349 |
|
350 # Base revision is a parent that hasn't been emitted already. |
|
351 # Use it if we can assume the receiver has the parent revision. |
|
352 elif (assumehaveparentrevisions |
|
353 and deltaparentrev in (p1rev, p2rev)): |
|
354 baserev = deltaparentrev |
|
355 |
|
356 # No guarantee the receiver has the delta parent. Send delta |
|
357 # against last revision (if possible), which in the common case |
|
358 # should be similar enough to this revision that the delta is |
|
359 # reasonable. |
|
360 elif prevrev is not None: |
|
361 baserev = prevrev |
|
362 else: |
|
363 baserev = nullrev |
|
364 |
|
365 # Storage has a fulltext revision. |
|
366 |
|
367 # Let's use the previous revision, which is as good a guess as any. |
|
368 # There is definitely room to improve this logic. |
|
369 elif prevrev is not None: |
|
370 baserev = prevrev |
|
371 else: |
|
372 baserev = nullrev |
|
373 |
|
374 # But we can't actually use our chosen delta base for whatever |
|
375 # reason. Reset to fulltext. |
|
376 if baserev != nullrev and not candeltafn(baserev, rev): |
|
377 baserev = nullrev |
|
378 |
|
379 revision = None |
|
380 delta = None |
|
381 baserevisionsize = None |
|
382 |
|
383 if revisiondata: |
|
384 if store.iscensored(baserev) or store.iscensored(rev): |
|
385 try: |
|
386 revision = store.revision(node, raw=True) |
|
387 except error.CensoredNodeError as e: |
|
388 revision = e.tombstone |
|
389 |
|
390 if baserev != nullrev: |
|
391 baserevisionsize = rawsizefn(baserev) |
|
392 |
|
393 elif baserev == nullrev and not deltaprevious: |
|
394 revision = store.revision(node, raw=True) |
|
395 available.add(rev) |
|
396 else: |
|
397 delta = revdifffn(baserev, rev) |
|
398 available.add(rev) |
|
399 |
|
400 yield resultcls( |
|
401 node=node, |
|
402 p1node=fnode(p1rev), |
|
403 p2node=fnode(p2rev), |
|
404 basenode=fnode(baserev), |
|
405 flags=flagsfn(rev), |
|
406 baserevisionsize=baserevisionsize, |
|
407 revision=revision, |
|
408 delta=delta) |
|
409 |
|
410 prevrev = rev |