Mercurial > public > mercurial-scm > hg
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: |