diff hgext3rd/topic/__init__.py @ 2899:32306ee32806

topics: add a new debugconvertbookmark command to convert bookmarks to topics This new command, aliased to debugcb converts bookmarks to topics. This command either accept name of a bookmark using flag '-b' or '--all'. If a bookmark is passed using '-b', the whole stack defined by that bookmark is converted to topics. If '--all' is passed, the above thing is repeated for every bookmark in the repository. If the revset which we are using unable to decides revisions of the stack and return an expty set, in that cases we are not deleting the bookmark as we are not writing any topics of the same name as that of bookmark. Also currently if a changeset has two bookmarks, than there is buggy behaviour. So these are known lackings in the current implementation which will be fixed in upcoming patches. This also adds tests for the same.
author Pulkit Goyal <7895pulkit@gmail.com>
date Fri, 01 Sep 2017 18:02:50 +0200
parents 3dfc88c06378
children 1928e9c802dd
line wrap: on
line diff
--- a/hgext3rd/topic/__init__.py	Fri Sep 01 17:53:14 2017 +0200
+++ b/hgext3rd/topic/__init__.py	Fri Sep 01 18:02:50 2017 +0200
@@ -57,6 +57,7 @@
 
 from mercurial.i18n import _
 from mercurial import (
+    bookmarks,
     cmdutil,
     commands,
     context,
@@ -439,6 +440,82 @@
         branch = repo[None].branch()
     return stack.showstack(ui, repo, branch=branch, topic=topic, opts=opts)
 
+@command('debugcb|debugconvertbookmark', [
+        ('b', 'bookmark', '', _('bookmark to convert to topic')),
+        ('', 'all', None, _('convert all bookmarks to topics')),
+    ],
+    _('[-b BOOKMARK] [--all]'))
+def debugconvertbookmark(ui, repo, **opts):
+    """Converts a bookmark to a topic with the same name.
+    """
+
+    bookmark = opts.get('bookmark')
+    convertall = opts.get('all')
+
+    if convertall and bookmark:
+        raise error.Abort(_("cannot use '--all' and '-b' together"))
+    if not (convertall or bookmark):
+        raise error.Abort(_("you must specify either '--all' or '-b'"))
+
+    bmstore = repo._bookmarks
+    lock = wlock = tr = None
+
+    if bookmark:
+        try:
+            node = bmstore[bookmark]
+        except KeyError:
+            raise error.Abort(_("no such bookmark exists: '%s'") % bookmark)
+
+        revnum = repo[node].rev()
+        try:
+            wlock = repo.wlock()
+            lock = repo.lock()
+            tr = repo.transaction('debugconvertbookmark')
+            _convertbmarktopic(ui, repo, revnum, bookmark, tr)
+            tr.close()
+        finally:
+            lockmod.release(tr, lock, wlock)
+
+    elif convertall:
+        # deletion of bookmark will result in change in size of bmstore during
+        # iteration, so let's make a copy to iterate
+        storecopy = bmstore.copy()
+        try:
+            wlock = repo.wlock()
+            lock = repo.lock()
+            tr = repo.transaction('debugconvertbookmark')
+            for bmark, revnode in sorted(storecopy.iteritems()):
+                if bmark == '@':
+                    continue
+                _convertbmarktopic(ui, repo, repo[revnode].rev(), bmark, tr)
+            tr.close()
+        finally:
+            lockmod.release(tr, lock, wlock)
+
+def _convertbmarktopic(ui, repo, rev, bmark, tr):
+    """Sets a topic as same as bname to all the changesets under the bookmark
+    and delete the bookmark, if topic is set to any changeset
+
+    rev is the revision on which bookmark bmark is and tr is transaction object.
+    """
+
+    # copied from mercurial.repair.stripbmrevset
+    bookrevset = ("ancestors(bookmark(%s)) - ancestors(head() and not "
+                  "bookmark(%s)) - ancestors(bookmark() and not "
+                  "bookmark(%s))")
+    touchedrevs = repo.revs(bookrevset, bmark, bmark, bmark)
+    rewrote = _changetopics(ui, repo, touchedrevs, bmark)
+    # We didn't changed topic to any changesets because the revset
+    # returned an empty set of revisions, so let's skip deleting the
+    # bookmark corresponding to which we didn't put a topic on any
+    # changeset
+    if rewrote == 0:
+        return
+    ui.status(_('changed topic to "%s" on %d revisions\n') % (bmark,
+              rewrote))
+    ui.debug('removing bookmark "%s" from "%d"' % (bmark, rev))
+    bookmarks.delete(repo, tr, [bmark])
+
 def _changecurrenttopic(repo, newtopic):
     """changes the current topic."""