343 """future remote heads if the changeset push fails""" |
343 """future remote heads if the changeset push fails""" |
344 if self.revs is None: |
344 if self.revs is None: |
345 # not target to push, all common are relevant |
345 # not target to push, all common are relevant |
346 return self.outgoing.commonheads |
346 return self.outgoing.commonheads |
347 unfi = self.repo.unfiltered() |
347 unfi = self.repo.unfiltered() |
348 # I want cheads = heads(::ancestorsof and ::commonheads) |
348 # I want cheads = heads(::push_heads and ::commonheads) |
349 # (ancestorsof is revs with secret changeset filtered out) |
|
350 # |
349 # |
351 # This can be expressed as: |
350 # To push, we already computed |
352 # cheads = ( (ancestorsof and ::commonheads) |
351 # common = (::commonheads) |
353 # + (commonheads and ::ancestorsof))" |
352 # missing = ((commonheads::push_heads) - commonheads) |
354 # ) |
|
355 # |
353 # |
356 # while trying to push we already computed the following: |
354 # So we basically search |
357 # common = (::commonheads) |
|
358 # missing = ((commonheads::ancestorsof) - commonheads) |
|
359 # |
355 # |
360 # We can pick: |
356 # almost_heads = heads((parents(missing) + push_heads) & common) |
361 # * ancestorsof part of common (::commonheads) |
357 # |
|
358 # We use "almost" here as this can return revision that are ancestors |
|
359 # of other in the set and we need to explicitly turn it into an |
|
360 # antichain later. We can do so using: |
|
361 # |
|
362 # cheads = heads(almost_heads::almost_heads) |
|
363 # |
|
364 # In pratice the code is a bit more convulted to avoid some extra |
|
365 # computation. It aims at doing the same computation as highlighted |
|
366 # above however. |
362 common = self.outgoing.common |
367 common = self.outgoing.common |
363 rev = self.repo.changelog.index.rev |
368 unfi = self.repo.unfiltered() |
364 cheads = [node for node in self.revs if rev(node) in common] |
369 cl = unfi.changelog |
365 # and |
370 to_rev = cl.index.rev |
366 # * commonheads parents on missing |
371 to_node = cl.node |
367 revset = unfi.set( |
372 parent_revs = cl.parentrevs |
368 b'%ln and parents(roots(%ln))', |
373 unselected = [] |
369 self.outgoing.commonheads, |
374 cheads = set() |
370 self.outgoing.missing, |
375 # XXX-perf: `self.revs` and `outgoing.missing` could hold revs directly |
371 ) |
376 for n in self.revs: |
372 cheads.extend(c.node() for c in revset) |
377 r = to_rev(n) |
373 return cheads |
378 if r in common: |
|
379 cheads.add(r) |
|
380 else: |
|
381 unselected.append(r) |
|
382 known_non_heads = cl.ancestors(cheads, inclusive=True) |
|
383 if unselected: |
|
384 missing_revs = {to_rev(n) for n in self.outgoing.missing} |
|
385 missing_revs.add(nullrev) |
|
386 root_points = set() |
|
387 for r in missing_revs: |
|
388 p1, p2 = parent_revs(r) |
|
389 if p1 not in missing_revs and p1 not in known_non_heads: |
|
390 root_points.add(p1) |
|
391 if p2 not in missing_revs and p2 not in known_non_heads: |
|
392 root_points.add(p2) |
|
393 if root_points: |
|
394 heads = unfi.revs('heads(%ld::%ld)', root_points, root_points) |
|
395 cheads.update(heads) |
|
396 # XXX-perf: could this be a set of revision? |
|
397 return [to_node(r) for r in sorted(cheads)] |
374 |
398 |
375 @property |
399 @property |
376 def commonheads(self): |
400 def commonheads(self): |
377 """set of all common heads after changeset bundle push""" |
401 """set of all common heads after changeset bundle push""" |
378 if self.cgresult: |
402 if self.cgresult: |