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.
--- 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."""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-topic-debugcb.t Fri Sep 01 18:02:50 2017 +0200
@@ -0,0 +1,132 @@
+Test for `hg debugconvertbookmark` added by topics
+==================================================
+
+ $ . "$TESTDIR/testlib/topic_setup.sh"
+
+ $ cat << EOF >> $HGRCPATH
+ > [ui]
+ > logtemplate = [{rev}:{node|short}] {desc|firstline}\n\
+ > {if(bookmarks, " bookmark: {join(bookmarks,"\n bookmark:")}\n")}\
+ > {if(topics, " topic: {topics}\n")}
+ > EOF
+
+ $ hg init repo
+ $ cd repo
+ $ echo "Hello" > a
+ $ hg commit -Aqm "First commit"
+ $ echo "Hello" > b
+ $ hg commit -Aqm "Second commit"
+ $ hg bookmark "hellos"
+ $ hg up 0 -q
+ $ echo "Fix 1" > l
+ $ hg commit -Aqm "Fixing first"
+ $ echo "Fix 2" > m
+ $ hg commit -Aqm "Fixing second"
+ $ hg bookmark "secondfix"
+
+ $ hg log -G
+ @ [3:b985e4fea4a4] Fixing second
+ | bookmark: secondfix
+ o [2:8e79b09248c2] Fixing first
+ |
+ | o [1:50634233706a] Second commit
+ |/ bookmark: hellos
+ o [0:3caf92e45cfb] First commit
+
+Help for the command
+====================
+
+ $ hg help debugconvertbookmark
+ hg debugcb [-b BOOKMARK] [--all]
+
+ aliases: debugconvertbookmark
+
+ Converts a bookmark to a topic with the same name.
+
+ options:
+
+ -b --bookmark VALUE bookmark to convert to topic
+ --all convert all bookmarks to topics
+
+ (some details hidden, use --verbose to show complete help)
+
+Running without any argument
+============================
+
+ $ hg debugconvertbookmark
+ abort: you must specify either '--all' or '-b'
+ [255]
+
+Changing a particular bookmark to topic
+=======================================
+
+ $ hg debugconvertbookmark -b hellos
+ changed topic to "hellos" on 1 revisions
+ $ hg log -G
+ o [4:ca8825a7eb18] Second commit
+ | topic: hellos
+ | @ [3:b985e4fea4a4] Fixing second
+ | | bookmark: secondfix
+ | o [2:8e79b09248c2] Fixing first
+ |/
+ o [0:3caf92e45cfb] First commit
+
+Changing all bookmarks to topic
+===============================
+
+ $ hg debugconvertbookmark --all
+ switching to topic secondfix
+ changed topic to "secondfix" on 2 revisions
+ $ hg log -G
+ @ [6:6efc0524f97a] Fixing second
+ | topic: secondfix
+ o [5:0a4244c62a16] Fixing first
+ | topic: secondfix
+ | o [4:ca8825a7eb18] Second commit
+ |/ topic: hellos
+ o [0:3caf92e45cfb] First commit
+
+Trying with multiple bookmarks on a single changeset
+====================================================
+
+ $ echo "multiple bookmarks" >> m
+ $ hg commit -Aqm "Trying multiple bookmarks"
+ $ hg bookmark book1
+ $ hg bookmark book2
+ $ hg log -G
+ @ [7:7c46b4bbdda3] Trying multiple bookmarks
+ | bookmark: book1
+ | bookmark:book2
+ | topic: secondfix
+ o [6:6efc0524f97a] Fixing second
+ | topic: secondfix
+ o [5:0a4244c62a16] Fixing first
+ | topic: secondfix
+ | o [4:ca8825a7eb18] Second commit
+ |/ topic: hellos
+ o [0:3caf92e45cfb] First commit
+
+XXX: When we have multiple bookmarks on the same changeset, we should skip that
+and do nothing. This should be fixed.
+
+ $ hg debugconvertbookmark --all
+ switching to topic book1
+ changed topic to "book1" on 3 revisions
+ changed topic to "book2" on 3 revisions
+ $ hg log -G
+ o [13:f979f772bd7d] Trying multiple bookmarks
+ | topic: book2
+ o [12:2397fdab7b79] Fixing second
+ | topic: book2
+ o [11:0dd194861ea1] Fixing first
+ | topic: book2
+ | @ [10:e738ed1df4b2] Trying multiple bookmarks
+ | | topic: book1
+ | o [9:4198fce21412] Fixing second
+ | | topic: book1
+ | o [8:221a9ddef504] Fixing first
+ |/ topic: book1
+ | o [4:ca8825a7eb18] Second commit
+ |/ topic: hellos
+ o [0:3caf92e45cfb] First commit
+