comparison mercurial/phases.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents ab893a99b645
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
121 util, 121 util,
122 ) 122 )
123 123
124 _fphasesentry = struct.Struct('>i20s') 124 _fphasesentry = struct.Struct('>i20s')
125 125
126 INTERNAL_FLAG = 64 # Phases for mercurial internal usage only 126 INTERNAL_FLAG = 64 # Phases for mercurial internal usage only
127 HIDEABLE_FLAG = 32 # Phases that are hideable 127 HIDEABLE_FLAG = 32 # Phases that are hideable
128 128
129 # record phase index 129 # record phase index
130 public, draft, secret = range(3) 130 public, draft, secret = range(3)
131 internal = INTERNAL_FLAG | HIDEABLE_FLAG 131 internal = INTERNAL_FLAG | HIDEABLE_FLAG
132 archived = HIDEABLE_FLAG 132 archived = HIDEABLE_FLAG
133 allphases = range(internal + 1) 133 allphases = range(internal + 1)
134 trackedphases = allphases[1:] 134 trackedphases = allphases[1:]
135 # record phase names 135 # record phase names
136 cmdphasenames = ['public', 'draft', 'secret'] # known to `hg phase` command 136 cmdphasenames = ['public', 'draft', 'secret'] # known to `hg phase` command
137 phasenames = [None] * len(allphases) 137 phasenames = [None] * len(allphases)
138 phasenames[:len(cmdphasenames)] = cmdphasenames 138 phasenames[: len(cmdphasenames)] = cmdphasenames
139 phasenames[archived] = 'archived' 139 phasenames[archived] = 'archived'
140 phasenames[internal] = 'internal' 140 phasenames[internal] = 'internal'
141 # record phase property 141 # record phase property
142 mutablephases = tuple(allphases[1:]) 142 mutablephases = tuple(allphases[1:])
143 remotehiddenphases = tuple(allphases[2:]) 143 remotehiddenphases = tuple(allphases[2:])
144 localhiddenphases = tuple(p for p in allphases if p & HIDEABLE_FLAG) 144 localhiddenphases = tuple(p for p in allphases if p & HIDEABLE_FLAG)
145 145
146
146 def supportinternal(repo): 147 def supportinternal(repo):
147 """True if the internal phase can be used on a repository""" 148 """True if the internal phase can be used on a repository"""
148 return 'internal-phase' in repo.requirements 149 return 'internal-phase' in repo.requirements
150
149 151
150 def _readroots(repo, phasedefaults=None): 152 def _readroots(repo, phasedefaults=None):
151 """Read phase roots from disk 153 """Read phase roots from disk
152 154
153 phasedefaults is a list of fn(repo, roots) callable, which are 155 phasedefaults is a list of fn(repo, roots) callable, which are
176 for f in phasedefaults: 178 for f in phasedefaults:
177 roots = f(repo, roots) 179 roots = f(repo, roots)
178 dirty = True 180 dirty = True
179 return roots, dirty 181 return roots, dirty
180 182
183
181 def binaryencode(phasemapping): 184 def binaryencode(phasemapping):
182 """encode a 'phase -> nodes' mapping into a binary stream 185 """encode a 'phase -> nodes' mapping into a binary stream
183 186
184 Since phases are integer the mapping is actually a python list: 187 Since phases are integer the mapping is actually a python list:
185 [[PUBLIC_HEADS], [DRAFTS_HEADS], [SECRET_HEADS]] 188 [[PUBLIC_HEADS], [DRAFTS_HEADS], [SECRET_HEADS]]
187 binarydata = [] 190 binarydata = []
188 for phase, nodes in enumerate(phasemapping): 191 for phase, nodes in enumerate(phasemapping):
189 for head in nodes: 192 for head in nodes:
190 binarydata.append(_fphasesentry.pack(phase, head)) 193 binarydata.append(_fphasesentry.pack(phase, head))
191 return ''.join(binarydata) 194 return ''.join(binarydata)
195
192 196
193 def binarydecode(stream): 197 def binarydecode(stream):
194 """decode a binary stream into a 'phase -> nodes' mapping 198 """decode a binary stream into a 'phase -> nodes' mapping
195 199
196 Since phases are integer the mapping is actually a python list.""" 200 Since phases are integer the mapping is actually a python list."""
204 break 208 break
205 phase, node = _fphasesentry.unpack(entry) 209 phase, node = _fphasesentry.unpack(entry)
206 headsbyphase[phase].append(node) 210 headsbyphase[phase].append(node)
207 return headsbyphase 211 return headsbyphase
208 212
213
209 def _trackphasechange(data, rev, old, new): 214 def _trackphasechange(data, rev, old, new):
210 """add a phase move the <data> dictionnary 215 """add a phase move the <data> dictionnary
211 216
212 If data is None, nothing happens. 217 If data is None, nothing happens.
213 """ 218 """
215 return 220 return
216 existing = data.get(rev) 221 existing = data.get(rev)
217 if existing is not None: 222 if existing is not None:
218 old = existing[0] 223 old = existing[0]
219 data[rev] = (old, new) 224 data[rev] = (old, new)
225
220 226
221 class phasecache(object): 227 class phasecache(object):
222 def __init__(self, repo, phasedefaults, _load=True): 228 def __init__(self, repo, phasedefaults, _load=True):
223 if _load: 229 if _load:
224 # Cheap trick to allow shallow-copy without copy module 230 # Cheap trick to allow shallow-copy without copy module
228 self.filterunknown(repo) 234 self.filterunknown(repo)
229 self.opener = repo.svfs 235 self.opener = repo.svfs
230 236
231 def getrevset(self, repo, phases, subset=None): 237 def getrevset(self, repo, phases, subset=None):
232 """return a smartset for the given phases""" 238 """return a smartset for the given phases"""
233 self.loadphaserevs(repo) # ensure phase's sets are loaded 239 self.loadphaserevs(repo) # ensure phase's sets are loaded
234 phases = set(phases) 240 phases = set(phases)
235 if public not in phases: 241 if public not in phases:
236 # fast path: _phasesets contains the interesting sets, 242 # fast path: _phasesets contains the interesting sets,
237 # might only need a union and post-filtering. 243 # might only need a union and post-filtering.
238 if len(phases) == 1: 244 if len(phases) == 1:
272 ph._phasesets = self._phasesets 278 ph._phasesets = self._phasesets
273 return ph 279 return ph
274 280
275 def replace(self, phcache): 281 def replace(self, phcache):
276 """replace all values in 'self' with content of phcache""" 282 """replace all values in 'self' with content of phcache"""
277 for a in ('phaseroots', 'dirty', 'opener', '_loadedrevslen', 283 for a in (
278 '_phasesets'): 284 'phaseroots',
285 'dirty',
286 'opener',
287 '_loadedrevslen',
288 '_phasesets',
289 ):
279 setattr(self, a, getattr(phcache, a)) 290 setattr(self, a, getattr(phcache, a))
280 291
281 def _getphaserevsnative(self, repo): 292 def _getphaserevsnative(self, repo):
282 repo = repo.unfiltered() 293 repo = repo.unfiltered()
283 nativeroots = [] 294 nativeroots = []
284 for phase in trackedphases: 295 for phase in trackedphases:
285 nativeroots.append(pycompat.maplist(repo.changelog.rev, 296 nativeroots.append(
286 self.phaseroots[phase])) 297 pycompat.maplist(repo.changelog.rev, self.phaseroots[phase])
298 )
287 return repo.changelog.computephases(nativeroots) 299 return repo.changelog.computephases(nativeroots)
288 300
289 def _computephaserevspure(self, repo): 301 def _computephaserevspure(self, repo):
290 repo = repo.unfiltered() 302 repo = repo.unfiltered()
291 cl = repo.changelog 303 cl = repo.changelog
385 else: 397 else:
386 phasetracking = tr.changes.get('phases') 398 phasetracking = tr.changes.get('phases')
387 399
388 repo = repo.unfiltered() 400 repo = repo.unfiltered()
389 401
390 changes = set() # set of revisions to be changed 402 changes = set() # set of revisions to be changed
391 delroots = [] # set of root deleted by this path 403 delroots = [] # set of root deleted by this path
392 for phase in pycompat.xrange(targetphase + 1, len(allphases)): 404 for phase in pycompat.xrange(targetphase + 1, len(allphases)):
393 # filter nodes that are not in a compatible phase already 405 # filter nodes that are not in a compatible phase already
394 nodes = [n for n in nodes 406 nodes = [
395 if self.phase(repo, repo[n].rev()) >= phase] 407 n for n in nodes if self.phase(repo, repo[n].rev()) >= phase
408 ]
396 if not nodes: 409 if not nodes:
397 break # no roots to move anymore 410 break # no roots to move anymore
398 411
399 olds = self.phaseroots[phase] 412 olds = self.phaseroots[phase]
400 413
401 affected = repo.revs('%ln::%ln', olds, nodes) 414 affected = repo.revs('%ln::%ln', olds, nodes)
402 changes.update(affected) 415 changes.update(affected)
403 if dryrun: 416 if dryrun:
404 continue 417 continue
405 for r in affected: 418 for r in affected:
406 _trackphasechange(phasetracking, r, self.phase(repo, r), 419 _trackphasechange(
407 targetphase) 420 phasetracking, r, self.phase(repo, r), targetphase
408 421 )
409 roots = set(ctx.node() for ctx in repo.set( 422
410 'roots((%ln::) - %ld)', olds, affected)) 423 roots = set(
424 ctx.node()
425 for ctx in repo.set('roots((%ln::) - %ld)', olds, affected)
426 )
411 if olds != roots: 427 if olds != roots:
412 self._updateroots(phase, roots, tr) 428 self._updateroots(phase, roots, tr)
413 # some roots may need to be declared for lower phases 429 # some roots may need to be declared for lower phases
414 delroots.extend(olds - roots) 430 delroots.extend(olds - roots)
415 if not dryrun: 431 if not dryrun:
418 self._retractboundary(repo, tr, targetphase, delroots) 434 self._retractboundary(repo, tr, targetphase, delroots)
419 repo.invalidatevolatilesets() 435 repo.invalidatevolatilesets()
420 return changes 436 return changes
421 437
422 def retractboundary(self, repo, tr, targetphase, nodes): 438 def retractboundary(self, repo, tr, targetphase, nodes):
423 oldroots = self.phaseroots[:targetphase + 1] 439 oldroots = self.phaseroots[: targetphase + 1]
424 if tr is None: 440 if tr is None:
425 phasetracking = None 441 phasetracking = None
426 else: 442 else:
427 phasetracking = tr.changes.get('phases') 443 phasetracking = tr.changes.get('phases')
428 repo = repo.unfiltered() 444 repo = repo.unfiltered()
429 if (self._retractboundary(repo, tr, targetphase, nodes) 445 if (
430 and phasetracking is not None): 446 self._retractboundary(repo, tr, targetphase, nodes)
447 and phasetracking is not None
448 ):
431 449
432 # find the affected revisions 450 # find the affected revisions
433 new = self.phaseroots[targetphase] 451 new = self.phaseroots[targetphase]
434 old = oldroots[targetphase] 452 old = oldroots[targetphase]
435 affected = set(repo.revs('(%ln::) - (%ln::)', new, old)) 453 affected = set(repo.revs('(%ln::) - (%ln::)', new, old))
438 for phase in pycompat.xrange(targetphase, -1, -1): 456 for phase in pycompat.xrange(targetphase, -1, -1):
439 if phase: 457 if phase:
440 roots = oldroots[phase] 458 roots = oldroots[phase]
441 revs = set(repo.revs('%ln::%ld', roots, affected)) 459 revs = set(repo.revs('%ln::%ld', roots, affected))
442 affected -= revs 460 affected -= revs
443 else: # public phase 461 else: # public phase
444 revs = affected 462 revs = affected
445 for r in revs: 463 for r in revs:
446 _trackphasechange(phasetracking, r, phase, targetphase) 464 _trackphasechange(phasetracking, r, phase, targetphase)
447 repo.invalidatevolatilesets() 465 repo.invalidatevolatilesets()
448 466
455 raise error.ProgrammingError(msg) 473 raise error.ProgrammingError(msg)
456 474
457 repo = repo.unfiltered() 475 repo = repo.unfiltered()
458 currentroots = self.phaseroots[targetphase] 476 currentroots = self.phaseroots[targetphase]
459 finalroots = oldroots = set(currentroots) 477 finalroots = oldroots = set(currentroots)
460 newroots = [n for n in nodes 478 newroots = [
461 if self.phase(repo, repo[n].rev()) < targetphase] 479 n for n in nodes if self.phase(repo, repo[n].rev()) < targetphase
480 ]
462 if newroots: 481 if newroots:
463 482
464 if nullid in newroots: 483 if nullid in newroots:
465 raise error.Abort(_('cannot change null revision phase')) 484 raise error.Abort(_('cannot change null revision phase'))
466 currentroots = currentroots.copy() 485 currentroots = currentroots.copy()
467 currentroots.update(newroots) 486 currentroots.update(newroots)
468 487
469 # Only compute new roots for revs above the roots that are being 488 # Only compute new roots for revs above the roots that are being
470 # retracted. 489 # retracted.
471 minnewroot = min(repo[n].rev() for n in newroots) 490 minnewroot = min(repo[n].rev() for n in newroots)
472 aboveroots = [n for n in currentroots 491 aboveroots = [
473 if repo[n].rev() >= minnewroot] 492 n for n in currentroots if repo[n].rev() >= minnewroot
493 ]
474 updatedroots = repo.set('roots(%ln::)', aboveroots) 494 updatedroots = repo.set('roots(%ln::)', aboveroots)
475 495
476 finalroots = set(n for n in currentroots if repo[n].rev() < 496 finalroots = set(
477 minnewroot) 497 n for n in currentroots if repo[n].rev() < minnewroot
498 )
478 finalroots.update(ctx.node() for ctx in updatedroots) 499 finalroots.update(ctx.node() for ctx in updatedroots)
479 if finalroots != oldroots: 500 if finalroots != oldroots:
480 self._updateroots(targetphase, finalroots, tr) 501 self._updateroots(targetphase, finalroots, tr)
481 return True 502 return True
482 return False 503 return False
485 """remove unknown nodes from the phase boundary 506 """remove unknown nodes from the phase boundary
486 507
487 Nothing is lost as unknown nodes only hold data for their descendants. 508 Nothing is lost as unknown nodes only hold data for their descendants.
488 """ 509 """
489 filtered = False 510 filtered = False
490 nodemap = repo.changelog.nodemap # to filter unknown nodes 511 nodemap = repo.changelog.nodemap # to filter unknown nodes
491 for phase, nodes in enumerate(self.phaseroots): 512 for phase, nodes in enumerate(self.phaseroots):
492 missing = sorted(node for node in nodes if node not in nodemap) 513 missing = sorted(node for node in nodes if node not in nodemap)
493 if missing: 514 if missing:
494 for mnode in missing: 515 for mnode in missing:
495 repo.ui.debug( 516 repo.ui.debug(
496 'removing unknown node %s from %i-phase boundary\n' 517 'removing unknown node %s from %i-phase boundary\n'
497 % (short(mnode), phase)) 518 % (short(mnode), phase)
519 )
498 nodes.symmetric_difference_update(missing) 520 nodes.symmetric_difference_update(missing)
499 filtered = True 521 filtered = True
500 if filtered: 522 if filtered:
501 self.dirty = True 523 self.dirty = True
502 # filterunknown is called by repo.destroyed, we may have no changes in 524 # filterunknown is called by repo.destroyed, we may have no changes in
507 # anyway. If this change we should consider adding a dedicated 529 # anyway. If this change we should consider adding a dedicated
508 # "destroyed" function to phasecache or a proper cache key mechanism 530 # "destroyed" function to phasecache or a proper cache key mechanism
509 # (see branchmap one) 531 # (see branchmap one)
510 self.invalidate() 532 self.invalidate()
511 533
534
512 def advanceboundary(repo, tr, targetphase, nodes, dryrun=None): 535 def advanceboundary(repo, tr, targetphase, nodes, dryrun=None):
513 """Add nodes to a phase changing other nodes phases if necessary. 536 """Add nodes to a phase changing other nodes phases if necessary.
514 537
515 This function move boundary *forward* this means that all nodes 538 This function move boundary *forward* this means that all nodes
516 are set in the target phase or kept in a *lower* phase. 539 are set in the target phase or kept in a *lower* phase.
520 If dryrun is True, no actions will be performed 543 If dryrun is True, no actions will be performed
521 544
522 Returns a set of revs whose phase is changed or should be changed 545 Returns a set of revs whose phase is changed or should be changed
523 """ 546 """
524 phcache = repo._phasecache.copy() 547 phcache = repo._phasecache.copy()
525 changes = phcache.advanceboundary(repo, tr, targetphase, nodes, 548 changes = phcache.advanceboundary(
526 dryrun=dryrun) 549 repo, tr, targetphase, nodes, dryrun=dryrun
550 )
527 if not dryrun: 551 if not dryrun:
528 repo._phasecache.replace(phcache) 552 repo._phasecache.replace(phcache)
529 return changes 553 return changes
554
530 555
531 def retractboundary(repo, tr, targetphase, nodes): 556 def retractboundary(repo, tr, targetphase, nodes):
532 """Set nodes back to a phase changing other nodes phases if 557 """Set nodes back to a phase changing other nodes phases if
533 necessary. 558 necessary.
534 559
538 Simplify boundary to contains phase roots only.""" 563 Simplify boundary to contains phase roots only."""
539 phcache = repo._phasecache.copy() 564 phcache = repo._phasecache.copy()
540 phcache.retractboundary(repo, tr, targetphase, nodes) 565 phcache.retractboundary(repo, tr, targetphase, nodes)
541 repo._phasecache.replace(phcache) 566 repo._phasecache.replace(phcache)
542 567
568
543 def registernew(repo, tr, targetphase, nodes): 569 def registernew(repo, tr, targetphase, nodes):
544 """register a new revision and its phase 570 """register a new revision and its phase
545 571
546 Code adding revisions to the repository should use this function to 572 Code adding revisions to the repository should use this function to
547 set new changeset in their target phase (or higher). 573 set new changeset in their target phase (or higher).
548 """ 574 """
549 phcache = repo._phasecache.copy() 575 phcache = repo._phasecache.copy()
550 phcache.registernew(repo, tr, targetphase, nodes) 576 phcache.registernew(repo, tr, targetphase, nodes)
551 repo._phasecache.replace(phcache) 577 repo._phasecache.replace(phcache)
578
552 579
553 def listphases(repo): 580 def listphases(repo):
554 """List phases root for serialization over pushkey""" 581 """List phases root for serialization over pushkey"""
555 # Use ordered dictionary so behavior is deterministic. 582 # Use ordered dictionary so behavior is deterministic.
556 keys = util.sortdict() 583 keys = util.sortdict()
578 # The server can't handle it on it's own as it has no idea of 605 # The server can't handle it on it's own as it has no idea of
579 # client phase data. 606 # client phase data.
580 keys['publishing'] = 'True' 607 keys['publishing'] = 'True'
581 return keys 608 return keys
582 609
610
583 def pushphase(repo, nhex, oldphasestr, newphasestr): 611 def pushphase(repo, nhex, oldphasestr, newphasestr):
584 """List phases root for serialization over pushkey""" 612 """List phases root for serialization over pushkey"""
585 repo = repo.unfiltered() 613 repo = repo.unfiltered()
586 with repo.lock(): 614 with repo.lock():
587 currentphase = repo[nhex].phase() 615 currentphase = repo[nhex].phase()
588 newphase = abs(int(newphasestr)) # let's avoid negative index surprise 616 newphase = abs(int(newphasestr)) # let's avoid negative index surprise
589 oldphase = abs(int(oldphasestr)) # let's avoid negative index surprise 617 oldphase = abs(int(oldphasestr)) # let's avoid negative index surprise
590 if currentphase == oldphase and newphase < oldphase: 618 if currentphase == oldphase and newphase < oldphase:
591 with repo.transaction('pushkey-phase') as tr: 619 with repo.transaction('pushkey-phase') as tr:
592 advanceboundary(repo, tr, newphase, [bin(nhex)]) 620 advanceboundary(repo, tr, newphase, [bin(nhex)])
593 return True 621 return True
594 elif currentphase == newphase: 622 elif currentphase == newphase:
595 # raced, but got correct result 623 # raced, but got correct result
596 return True 624 return True
597 else: 625 else:
598 return False 626 return False
599 627
628
600 def subsetphaseheads(repo, subset): 629 def subsetphaseheads(repo, subset):
601 """Finds the phase heads for a subset of a history 630 """Finds the phase heads for a subset of a history
602 631
603 Returns a list indexed by phase number where each item is a list of phase 632 Returns a list indexed by phase number where each item is a list of phase
604 head nodes. 633 head nodes.
611 for phase in allphases[:secret]: 640 for phase in allphases[:secret]:
612 revset = "heads(%%ln & %s())" % phasenames[phase] 641 revset = "heads(%%ln & %s())" % phasenames[phase]
613 headsbyphase[phase] = [cl.node(r) for r in repo.revs(revset, subset)] 642 headsbyphase[phase] = [cl.node(r) for r in repo.revs(revset, subset)]
614 return headsbyphase 643 return headsbyphase
615 644
645
616 def updatephases(repo, trgetter, headsbyphase): 646 def updatephases(repo, trgetter, headsbyphase):
617 """Updates the repo with the given phase heads""" 647 """Updates the repo with the given phase heads"""
618 # Now advance phase boundaries of all but secret phase 648 # Now advance phase boundaries of all but secret phase
619 # 649 #
620 # run the update (and fetch transaction) only if there are actually things 650 # run the update (and fetch transaction) only if there are actually things
624 revset = '%ln - _phase(%s)' 654 revset = '%ln - _phase(%s)'
625 heads = [c.node() for c in repo.set(revset, headsbyphase[phase], phase)] 655 heads = [c.node() for c in repo.set(revset, headsbyphase[phase], phase)]
626 if heads: 656 if heads:
627 advanceboundary(repo, trgetter(), phase, heads) 657 advanceboundary(repo, trgetter(), phase, heads)
628 658
659
629 def analyzeremotephases(repo, subset, roots): 660 def analyzeremotephases(repo, subset, roots):
630 """Compute phases heads and root in a subset of node from root dict 661 """Compute phases heads and root in a subset of node from root dict
631 662
632 * subset is heads of the subset 663 * subset is heads of the subset
633 * roots is {<nodeid> => phase} mapping. key and value are string. 664 * roots is {<nodeid> => phase} mapping. key and value are string.
635 Accept unknown element input 666 Accept unknown element input
636 """ 667 """
637 repo = repo.unfiltered() 668 repo = repo.unfiltered()
638 # build list from dictionary 669 # build list from dictionary
639 draftroots = [] 670 draftroots = []
640 nodemap = repo.changelog.nodemap # to filter unknown nodes 671 nodemap = repo.changelog.nodemap # to filter unknown nodes
641 for nhex, phase in roots.iteritems(): 672 for nhex, phase in roots.iteritems():
642 if nhex == 'publishing': # ignore data related to publish option 673 if nhex == 'publishing': # ignore data related to publish option
643 continue 674 continue
644 node = bin(nhex) 675 node = bin(nhex)
645 phase = int(phase) 676 phase = int(phase)
646 if phase == public: 677 if phase == public:
647 if node != nullid: 678 if node != nullid:
648 repo.ui.warn(_('ignoring inconsistent public root' 679 repo.ui.warn(
649 ' from remote: %s\n') % nhex) 680 _('ignoring inconsistent public root' ' from remote: %s\n')
681 % nhex
682 )
650 elif phase == draft: 683 elif phase == draft:
651 if node in nodemap: 684 if node in nodemap:
652 draftroots.append(node) 685 draftroots.append(node)
653 else: 686 else:
654 repo.ui.warn(_('ignoring unexpected root from remote: %i %s\n') 687 repo.ui.warn(
655 % (phase, nhex)) 688 _('ignoring unexpected root from remote: %i %s\n')
689 % (phase, nhex)
690 )
656 # compute heads 691 # compute heads
657 publicheads = newheads(repo, subset, draftroots) 692 publicheads = newheads(repo, subset, draftroots)
658 return publicheads, draftroots 693 return publicheads, draftroots
694
659 695
660 class remotephasessummary(object): 696 class remotephasessummary(object):
661 """summarize phase information on the remote side 697 """summarize phase information on the remote side
662 698
663 :publishing: True is the remote is publishing 699 :publishing: True is the remote is publishing
675 ana = analyzeremotephases(repo, remotesubset, remoteroots) 711 ana = analyzeremotephases(repo, remotesubset, remoteroots)
676 self.publicheads, self.draftroots = ana 712 self.publicheads, self.draftroots = ana
677 # Get the list of all "heads" revs draft on remote 713 # Get the list of all "heads" revs draft on remote
678 dheads = unfi.set('heads(%ln::%ln)', self.draftroots, remotesubset) 714 dheads = unfi.set('heads(%ln::%ln)', self.draftroots, remotesubset)
679 self.draftheads = [c.node() for c in dheads] 715 self.draftheads = [c.node() for c in dheads]
716
680 717
681 def newheads(repo, heads, roots): 718 def newheads(repo, heads, roots):
682 """compute new head of a subset minus another 719 """compute new head of a subset minus another
683 720
684 * `heads`: define the first subset 721 * `heads`: define the first subset
701 affected_zone = repo.revs("(%ld::%ld)", roots, new_heads) 738 affected_zone = repo.revs("(%ld::%ld)", roots, new_heads)
702 # heads in the area are no longer heads 739 # heads in the area are no longer heads
703 new_heads.difference_update(affected_zone) 740 new_heads.difference_update(affected_zone)
704 # revisions in the area have children outside of it, 741 # revisions in the area have children outside of it,
705 # They might be new heads 742 # They might be new heads
706 candidates = repo.revs("parents(%ld + (%ld and merge())) and not null", 743 candidates = repo.revs(
707 roots, affected_zone) 744 "parents(%ld + (%ld and merge())) and not null", roots, affected_zone
745 )
708 candidates -= affected_zone 746 candidates -= affected_zone
709 if new_heads or candidates: 747 if new_heads or candidates:
710 # remove candidate that are ancestors of other heads 748 # remove candidate that are ancestors of other heads
711 new_heads.update(candidates) 749 new_heads.update(candidates)
712 prunestart = repo.revs("parents(%ld) and not null", new_heads) 750 prunestart = repo.revs("parents(%ld) and not null", new_heads)
713 pruned = dagop.reachableroots(repo, candidates, prunestart) 751 pruned = dagop.reachableroots(repo, candidates, prunestart)
714 new_heads.difference_update(pruned) 752 new_heads.difference_update(pruned)
715 753
716 return pycompat.maplist(cl.node, sorted(new_heads)) 754 return pycompat.maplist(cl.node, sorted(new_heads))
755
717 756
718 def newcommitphase(ui): 757 def newcommitphase(ui):
719 """helper to get the target phase of new commit 758 """helper to get the target phase of new commit
720 759
721 Handle all possible values for the phases.new-commit options. 760 Handle all possible values for the phases.new-commit options.
729 return int(v) 768 return int(v)
730 except ValueError: 769 except ValueError:
731 msg = _("phases.new-commit: not a valid phase name ('%s')") 770 msg = _("phases.new-commit: not a valid phase name ('%s')")
732 raise error.ConfigError(msg % v) 771 raise error.ConfigError(msg % v)
733 772
773
734 def hassecret(repo): 774 def hassecret(repo):
735 """utility function that check if a repo have any secret changeset.""" 775 """utility function that check if a repo have any secret changeset."""
736 return bool(repo._phasecache.phaseroots[2]) 776 return bool(repo._phasecache.phaseroots[2])
777
737 778
738 def preparehookargs(node, old, new): 779 def preparehookargs(node, old, new):
739 if old is None: 780 if old is None:
740 old = '' 781 old = ''
741 else: 782 else:
742 old = phasenames[old] 783 old = phasenames[old]
743 return {'node': node, 784 return {'node': node, 'oldphase': old, 'phase': phasenames[new]}
744 'oldphase': old,
745 'phase': phasenames[new]}