hgext3rd/topic/__init__.py
changeset 2899 32306ee32806
parent 2898 3dfc88c06378
child 2900 1928e9c802dd
--- 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."""