comparison mercurial/scmutil.py @ 33088:65cadeea6c22

scmutil: add a cleanupnodes method for developers It's now common that an old node gets replaced by zero or more new nodes, that could happen with amend, rebase, histedit, etc. And it's a common requirement to do bookmark movements, strip or obsolete nodes and even moving working copy parent. Previously, amend, rebase, history have their own logic doing the above. This patch is an attempt to unify them and future code. This enables new developers to be able to do "replace X with Y" thing correctly, without any knowledge about bookmarks, strip or obsstore. The next step will be migrating rebase to the new API, so it works inside a transaction, and its code could be simplified.
author Jun Wu <quark@fb.com>
date Sun, 25 Jun 2017 13:31:56 -0700
parents 7b17f9de6d3e
children 784f2bd96d43
comparison
equal deleted inserted replaced
33087:fcd1c483f5ea 33088:65cadeea6c22
14 import re 14 import re
15 import socket 15 import socket
16 16
17 from .i18n import _ 17 from .i18n import _
18 from .node import ( 18 from .node import (
19 hex,
20 nullid,
19 wdirid, 21 wdirid,
20 wdirrev, 22 wdirrev,
21 ) 23 )
22 24
23 from . import ( 25 from . import (
24 encoding, 26 encoding,
25 error, 27 error,
26 match as matchmod, 28 match as matchmod,
29 obsolete,
27 pathutil, 30 pathutil,
28 phases, 31 phases,
29 pycompat, 32 pycompat,
30 revsetlang, 33 revsetlang,
31 similar, 34 similar,
559 ui.note(_('creating directory: %s\n') % origbackupdir) 562 ui.note(_('creating directory: %s\n') % origbackupdir)
560 util.makedirs(origbackupdir) 563 util.makedirs(origbackupdir)
561 564
562 return fullorigpath + ".orig" 565 return fullorigpath + ".orig"
563 566
567 def cleanupnodes(repo, mapping, operation):
568 """do common cleanups when old nodes are replaced by new nodes
569
570 That includes writing obsmarkers or stripping nodes, and moving bookmarks.
571 (we might also want to move working directory parent in the future)
572
573 mapping is {oldnode: [newnode]} or a iterable of nodes if they do not have
574 replacements. operation is a string, like "rebase".
575 """
576 if not util.safehasattr(mapping, 'items'):
577 mapping = {n: () for n in mapping}
578
579 with repo.transaction('cleanup') as tr:
580 # Move bookmarks
581 bmarks = repo._bookmarks
582 bmarkchanged = False
583 for oldnode, newnodes in mapping.items():
584 oldbmarks = repo.nodebookmarks(oldnode)
585 if not oldbmarks:
586 continue
587 bmarkchanged = True
588 if len(newnodes) > 1:
589 heads = list(repo.set('heads(%ln)', newnodes))
590 if len(heads) != 1:
591 raise error.ProgrammingError(
592 'cannot figure out bookmark movement')
593 newnode = heads[0].node()
594 elif len(newnodes) == 0:
595 # move bookmark backwards
596 roots = list(repo.set('max((::%n) - %ln)', oldnode,
597 list(mapping)))
598 if roots:
599 newnode = roots[0].node()
600 else:
601 newnode = nullid
602 else:
603 newnode = newnodes[0]
604 repo.ui.debug('moving bookmarks %r from %s to %s\n' %
605 (oldbmarks, hex(oldnode), hex(newnode)))
606 for name in oldbmarks:
607 bmarks[name] = newnode
608 if bmarkchanged:
609 bmarks.recordchange(tr)
610
611 # Obsolete or strip nodes
612 if obsolete.isenabled(repo, obsolete.createmarkersopt):
613 # If a node is already obsoleted, and we want to obsolete it
614 # without a successor, skip that obssolete request since it's
615 # unnecessary. That's the "if s or not isobs(n)" check below.
616 # Also sort the node in topology order, that might be useful for
617 # some obsstore logic.
618 # NOTE: the filtering and sorting might belong to createmarkers.
619 isobs = repo.obsstore.successors.__contains__
620 sortfunc = lambda ns: repo.changelog.rev(ns[0])
621 rels = [(repo[n], (repo[m] for m in s))
622 for n, s in sorted(mapping.items(), key=sortfunc)
623 if s or not isobs(n)]
624 obsolete.createmarkers(repo, rels, operation=operation)
625 else:
626 from . import repair # avoid import cycle
627 repair.delayedstrip(repo.ui, repo, list(mapping), operation)
628
564 def addremove(repo, matcher, prefix, opts=None, dry_run=None, similarity=None): 629 def addremove(repo, matcher, prefix, opts=None, dry_run=None, similarity=None):
565 if opts is None: 630 if opts is None:
566 opts = {} 631 opts = {}
567 m = matcher 632 m = matcher
568 if dry_run is None: 633 if dry_run is None: