view @ 16:b3efe54b242d default tip

Also tested on 5.0.2 (OpenBSD).
author Marc Simpson <>
date Sun, 09 Feb 2020 14:06:37 -0800
parents 83f760d33add
line wrap: on
line source

'''easily set ui.username on a per-repository basis

This extension provides an easy way to set ui.username on a
repository by repository basis.

At its simplest, ::

    hg persona -n "Firstname Lastname <>"

You can configure individual personas in your ~/.hgrc as follows::

    home = Firstname Lastname <firstname@home.domain>
    work = Firstname Lastname <firstname.lastname@work.domain>

which allows for quick switching::

    hg persona -n home
    hg persona -n work

or even quicker switching with aliases::

    home = persona -n home
    work = persona -n work

To list known personas, ::

    hg persona

You can also specify a persona on clone::

    hg clone --persona work src dest

Note that this extension modifies a repository's .hgrc file, removing
comments and potentially re-ordering sections.  If this is unacceptable,
don't use persona.

The following hook is handy for displaying ui.username info on
each commit (allowing you to catch repository-username mismatches early)::

    # ...
    pre-commit = echo "(as '`hg showconfig ui.username`')"

import os
import shutil

from mercurial import commands, config, error, extensions, hg, registrar
from mercurial.i18n import _

cmdtable = {}
command = registrar.command(cmdtable)

testedwith = b'4.5.3 5.0.2 5.3'

         [(b"n", b"name", b"", _(b"repository local username"), _(b"PERSONA"))],
         _(b"hg persona [-n PERSONA]"))
def persona(ui, repo, *args, **opts):
    "Specify or list repository-local persona(s)."
    if args:
        raise error.Abort(_("use the -n switch to specify a new persona."))
    if opts["name"]:
        set_persona(ui, repo, opts["name"])

def show_personas(ui):
    "Display the set of defined personas."
    personas = ui.configitems(b"persona")
    ui.write(_(b"Known personas:\n"))
    for entry in personas:
        name, username = entry[0].ljust(12), entry[1]
        ui.write(b"  %s %s\n" % (name, username))
    ui.write(_(b"Current username:\n"))
    ui.write(b"  %s\n" % ui.config(b"ui", b"username"))

def set_persona(ui, repo, name):
    "Select a persona for use in this repository."
    hgrc = repo.path + os.sep.encode() + b"hgrc"
    conf = read_repo_config(hgrc)
    username = lookup_persona(ui, name)
    ui.write(_(b"~ Setting username to '%s'\n" % username))
    conf.set(b"ui", b"username", username)
    write_repo_config(conf, hgrc)

def lookup_persona(ui, name):
    "Find a persona by name; unknown personas are considered usernames."
    username = ui.config(b"persona", name)
    if not username:
        ui.write(_(b"Persona not found; treating argument as a username.\n"))
        username = name
    return username

def read_repo_config(hgrc):
    "Attempt to parse a repository's .hgrc file."
    conf = config.config(None)
    except IOError:  # TODO: more granular error checking
    return conf

# XXX will re-order sections and remove comments from a repository's hgrc
def write_repo_config(conf, hgrc):
    "Serialise a config to the supplied path, first attempting a backup."
        shutil.copy(hgrc, hgrc + b"_bak")
    except IOError:  # TODO: more granular error checking
    with open(hgrc, "w") as hgrc_file:
        first = True
        for section in conf.sections():
            if not first:
            first = False
            hgrc_file.write("[%s]\n" % section.decode())
            for item in conf.items(section):
                name, value = (b.decode() for b in (item[0], item[1]))
                hgrc_file.write("%s = %s\n" % (name, value))

def clone_wrapper(orig, ui, repo, *args, **opts):
    orig(ui, repo, *args, **opts)
    if args and opts["persona"]:
        target = hg.repository(ui, args[0])
        persona(ui, target, name=opts["persona"])

def uisetup(_ui):
    entry = extensions.wrapcommand(commands.table, b"clone", clone_wrapper)
    entry[1].append((b"", b"persona", b"", _(b"Clone with persona"), _(b"PERSONA")))