# __init__.py - topic extension
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
"""support for topic branches
Topic branches are lightweight branches which
disappear when changes are finalized.
This is sort of similar to a bookmark, but it applies to a whole
series instead of a single revision.
"""
import functools
from mercurial.i18n import _
from mercurial import cmdutil
from mercurial import commands
from mercurial import context
from mercurial import extensions
from mercurial import namespaces
from mercurial import obsolete
from mercurial import phases
from mercurial import util
from . import constants
from . import revset as topicrevset
cmdtable = {}
command = cmdutil.command(cmdtable)
def _namemap(repo, name):
return [ctx.node() for ctx in
repo.set('not public() and extra(topic, %s)', name)]
def _nodemap(repo, node):
ctx = repo[node]
t = ctx.extra().get(constants.extrakey, '')
if t and ctx.phase() > phases.public:
return [t]
return []
def reposetup(ui, repo):
orig = repo.__class__
class topicrepo(repo.__class__):
def commitctx(self, ctx, error=None):
current = self.currenttopic
if current:
ctx.extra()[constants.extrakey] = current
return orig.commitctx(self, ctx, error=error)
@property
def topics(self):
topics = set(['', self.currenttopic])
for c in self.set('not public()'):
topics.add(c.extra().get(constants.extrakey, ''))
topics.remove('')
return topics
@property
def currenttopic(self):
return self.vfs.tryread('topic')
if util.safehasattr(repo, 'names'):
repo.names.addnamespace(namespaces.namespace(
'topics', 'topic', namemap=_namemap, nodemap=_nodemap))
repo.__class__ = topicrepo
@command('topics [TOPIC]', [
('', 'clear', False, 'clear active topic if any'),
('', 'change', '', 'revset of existing revisions to change topic'),
])
def topics(ui, repo, topic=None, clear=False, change=None):
"""View current topic, set current topic, or see all topics."""
if not obsolete.isenabled(repo, obsolete.createmarkersopt):
raise util.Abort('current reality means you should only '
'use topic with obsolete enabled')
if change:
if topic is None and not clear:
raise util.Abort('changing topic requires a topic name or --clear')
if any(not c.mutable() for c in repo.set('%r and public()', change)):
raise util.Abort("can't change topic of a public change")
rewrote = 0
needevolve = False
for c in repo.set('%r', change):
def filectxfn(repo, ctx, path):
try:
return c[path]
except error.ManifestLookupError:
return None
fixedextra = dict(c.extra())
newtopic = None if clear else topic
if fixedextra.get(constants.extrakey, None) == topic:
continue
if clear and constants.extrakey in fixedextra:
del fixedextra[constants.extrakey]
else:
fixedextra[constants.extrakey] = topic
c.parents()
mc = context.memctx(
repo, (c.p1().node(), c.p2().node()), c.description(),
c.files(), filectxfn,
user=c.user(), date=c.date(), extra=fixedextra)
newnode = repo.commitctx(mc)
needevolve = needevolve or (len(c.children()) > 0)
obsolete.createmarkers(repo, [(c, (repo[newnode],))])
rewrote += 1
ui.status('changed topic on %d changes\n' % rewrote)
if needevolve:
evolvetarget = 'topic(%s)' % topic if topic else 'not topic()'
ui.status('please run hg evolve --rev "%s" now\n' % evolvetarget)
if clear:
if repo.vfs.exists('topic'):
repo.vfs.unlink('topic')
return
if topic is not None:
with repo.vfs.open('topic', 'w') as f:
f.write(topic)
return
current = repo.currenttopic
for t in sorted(repo.topics):
marker = '*' if t == current else ' '
ui.write(' %s %s\n' % (marker, t))
def summaryhook(ui, repo):
t = repo.currenttopic
if not t:
return
# i18n: column positioning for "hg summary"
ui.write(_("topic: %s\n") % t)
def commitwrap(orig, ui, repo, *args, **opts):
if opts.get('topic'):
t = opts['topic']
with repo.vfs.open('topic', 'w') as f:
f.write(t)
return orig(ui, repo, *args, **opts)
def updatewrap(orig, ui, repo, *args, **kwargs):
ret = orig(ui, repo, *args, **kwargs)
pctx = repo['.']
ot = repo.currenttopic
if pctx.phase() == phases.public and repo.vfs.exists('topic'):
repo.vfs.unlink('topic')
else:
# inherit the topic of the parent revision
t = pctx.extra().get(constants.extrakey, '')
if t and pctx.phase() > phases.public:
with repo.vfs.open('topic', 'w') as f:
f.write(t)
if t != ot:
ui.status(_("switching to topic %s\n") % t)
return ret
entry = extensions.wrapcommand(commands.table, 'commit', commitwrap)
entry[1].append(('t', 'topic', '',
_("use specified topic"), _('TOPIC')))
extensions.wrapcommand(commands.table, 'update', updatewrap)
topicrevset.modsetup()
cmdutil.summaryhooks.add('topic', summaryhook)