# HG changeset patch # User Jun Wu # Date 1498508031 25200 # Node ID 4bae3c117b57cfd11f70ac54517af07e8022988e # Parent ba43e5ee9c6da81f2e3eea6b2411d102626a7346 scmutil: make cleanupnodes delete divergent bookmarks cleanupnodes takes care of bookmark movement, and bookmark movement could cause bookmark divergent resolution as a side effect. This patch adds such bookmark divergent resolution logic so future rebase migration will be easier. The revset is carefully written to be equivalent to what rebase does today. Although I think it might make sense to remove divergent bookmarks more aggressively, for example: F book@1 | E book@2 | | D book | | | C |/ B book@3 | A When rebase -s C -d E, "book@1" will be removed, "book@3" will be kept, and the end result is: D book | C | F | E book@2 (?) | B book@3 | A The question is should we keep book@2? The current logic keeps it. If we choose not to (makes some sense to me), the "deleterevs" revset could be simplified to "newnode % oldnode". For now, I just make it compatible with the existing behavior. If we want to make the "deleterevs" revset simpler, we can always do it in the future. diff -r ba43e5ee9c6d -r 4bae3c117b57 mercurial/scmutil.py --- a/mercurial/scmutil.py Mon Jun 26 15:08:37 2017 -0700 +++ b/mercurial/scmutil.py Mon Jun 26 13:13:51 2017 -0700 @@ -567,6 +567,16 @@ return fullorigpath + ".orig" +class _containsnode(object): + """proxy __contains__(node) to container.__contains__ which accepts revs""" + + def __init__(self, repo, revcontainer): + self._torev = repo.changelog.rev + self._revcontains = revcontainer.__contains__ + + def __contains__(self, node): + return self._revcontains(self._torev(node)) + def cleanupnodes(repo, mapping, operation): """do common cleanups when old nodes are replaced by new nodes @@ -583,17 +593,16 @@ # Move bookmarks bmarks = repo._bookmarks bmarkchanged = False + allnewnodes = [n for ns in mapping.values() for n in ns] for oldnode, newnodes in mapping.items(): oldbmarks = repo.nodebookmarks(oldnode) if not oldbmarks: continue + from . import bookmarks # avoid import cycle bmarkchanged = True if len(newnodes) > 1: - heads = list(repo.set('heads(%ln)', newnodes)) - if len(heads) != 1: - raise error.ProgrammingError( - 'cannot figure out bookmark movement') - newnode = heads[0].node() + # usually a split, take the one with biggest rev number + newnode = next(repo.set('max(%ln)', newnodes)).node() elif len(newnodes) == 0: # move bookmark backwards roots = list(repo.set('max((::%n) - %ln)', oldnode, @@ -606,8 +615,13 @@ newnode = newnodes[0] repo.ui.debug('moving bookmarks %r from %s to %s\n' % (oldbmarks, hex(oldnode), hex(newnode))) + # Delete divergent bookmarks being parents of related newnodes + deleterevs = repo.revs('parents(roots(%ln & (::%n))) - parents(%n)', + allnewnodes, newnode, oldnode) + deletenodes = _containsnode(repo, deleterevs) for name in oldbmarks: bmarks[name] = newnode + bookmarks.deletedivergent(repo, deletenodes, name) if bmarkchanged: bmarks.recordchange(tr) diff -r ba43e5ee9c6d -r 4bae3c117b57 tests/test-strip.t --- a/tests/test-strip.t Mon Jun 26 15:08:37 2017 -0700 +++ b/tests/test-strip.t Mon Jun 26 13:13:51 2017 -0700 @@ -1002,6 +1002,9 @@ $ for i in B C D F G I Z; do > hg bookmark -i -r $i b-$i > done + $ hg bookmark -i -r E 'b-F@divergent1' + $ hg bookmark -i -r H 'b-F@divergent2' + $ hg bookmark -i -r G 'b-F@divergent3' $ cp -R . ../scmutilcleanup.obsstore $ cat > $TESTTMP/scmutilcleanup.py <