Differences between revisions 9 and 52 (spanning 43 versions)
Revision 9 as of 2005-09-22 16:47:00
Size: 3914
Comment:
Revision 52 as of 2010-03-02 03:06:20
Size: 7634
Editor: JulianCowley
Comment: correct option list usage, fix small typos
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
Mercurial features an extension mechanism for adding new commands. It allows you to create new features and use them directly from the main hg command line. ## page was renamed from ExtensionHowto
= Writing Mercurial extensions =
Line 3: Line 4:
== Using the hgk extension == Mercurial features an extension mechanism for adding new commands.
Line 5: Line 6:
The contrib directory includes an extension to add support for a port of gitk under Mercurial. This is named hgk.py, and will be used as an example here.

To load an extension, you add it to your .hgrc file. You can specify an absolute path: {{{
[extensions]
hgk=/usr/local/lib/hgk.py
}}}

Mercurial can also scan the default python library path for a file named 'hgit': {{{
[extensions]
hgk=
}}}

<!> In version 0.7, this extension is named "hgit" (without the .py) and does not provide the view command. You must invoke hgk directly.

hg help will now show the new commands provided by the hgk extension:

{{{
$ hg help status show changed files in the working directory
...
 tag add a tag for the current tip or a given revision
 tags list repository tags
 tip show the tip revision
 unbundle apply a changegroup file
 undo undo the last commit or pull
 update update or merge working directory
 verify verify the integrity of the repository
 version output version and copyright information
 view start interactive history viewer
$ hg view
}}}
Extensions allow the creation of new features and using them directly from the main hg command line as if they were built-in commands. The extensions have full access to the MercurialApi.
Line 38: Line 10:
To write your own extension, your python module can provide an optional dict named `cmdtable` with entries describing each command, and an optional callback named `reposetup`.
The `reposetup` callback is called after the main Mercurial repository initialization, and can be used to setup any local state the extension might need. Below is an example extension to help demonstrate how things work:
=== File Layout ===

Extensions are usually written as simple python modules. Larger ones are better split into multiple modules of a single package (see ConvertExtension). The package root module gives its name to the extension and implements the `cmdtable` and optional callbacks described below.

=== Command table ===
To write your own extension, your python module can provide an optional dict named `cmdtable` with entries describing each command.

==== The cmdtable dictionary ====
The `cmdtable` dictionary uses as key the new command names, and, as value, a tuple containing:

 1. the function name to be called when the command is used.
 1. a list of options the command can take.
 1. a help string for the command.

==== List of options ====
All the command flag options are documented in the [[http://selenic.com/repo/hg/file/tip/mercurial/fancyopts.py|mercurial/fancyopts.py]] sources.

The options list is a list of tuples containing:
 1. the short option letter, or {{{''}}} if no short option is available (for example, {{{o}}} for a {{{-o}}} option).
 1. the long option name (for example, {{{option}}} for a {{{--option}}} option).
 1. a default value for the option.
 1. a help string for the option (it's possible to ommit the "hg newcommand" part and only the options and parameter subsstring is needed).

==== Example cmdtable ====
{{{
cmdtable = {
    # "command-name": (function-call, options-list, help-string)
    "print-parents": (printparents,
                     [('s', 'short', None, 'print short form'),
                      ('l', 'long', None, 'print long form')],
                     "hg print-parents [options] node")
}
}}}

=== Command function signatures ===
Functions that implement new commands always receive a {{{ui}}} and usually a {{{repo}}} parameter. Please see the MercurialApi for information on how to use these. The rest of parameters are taken from the command line items that don't start with a dash and are passed in the same order they were written. If no default value is given in the parameter list they are required.

If there is no repo to be associated with the command and consequently no {{{repo}}} passed then {{{commands}}} should be imported from {{{mercurial}}} and the extension name should be added to {{{commands.norepo}}} like this:
{{{
from mercurial import commands
...
commands.norepo += " mycommand"
}}}
For examples of {{{norepo}}} see the source code to the RcpathExtension (direct link to [[attachment:RcpathExtension/rcpath.py]] extension source) or the ConvertExtension (direct link to [[http://hg.intevation.org/mercurial/crew/file/f5f6b7dcd217/hgext/convert/__init__.py#l186|convert]] extension source).

=== Communicating with the user ===

Besides the {{{ui}}} methods listed in MercurialApi, like {{{ui.write(*msg)}}} or {{{ui.prompt(msg, default="y")}}}, an extension can add help text for each of its commands and the extension itself.

The module docstring will be used as help string when {{{hg help extensionname}}} is used and, similarly, the help string for a command and the docstring belonging to the function that's wrapped by the command will be shown when {{{hg help command}}} is invoked.

=== Setup Callbacks ===

Extensions are loaded in phases, all extensions are processed in a given phase before the next phase begins. In the first phase all extension modules are loaded and registered with Mercurial. this means that you can find all enabled extensions with `extensions.find` in the following phases.

==== ui setup ====
Extensions can implement an optional callback named `uisetup`. `uisetup` is called when the extension is first loaded and receives a ui object:

{{{
def uisetup(ui):
    # ...
}}}

==== Extension setup ====
Extensions can implement an optional callback named `extsetup`. It is called after all the extension are loaded, and can be useful in case one extension optionally depends on another extension. Signature:

{{{
def extsetup():
    # ...
}}}

Mercurial version 8e6019b16a7d and later (that is post-1.3.1) will pass a `ui` argument to `extsetup`.

==== Command table setup ====

After `extsetup`, the `cmdtable` is copied into the global command table in Mercurial.

==== Repository setup ====

Extensions can implement an optional callback named `reposetup`. It is called after the main Mercurial repository initialization, and can be used to setup any local state the extension might need.

As other command functions it receives an `ui` object and a `repo` object (no additional parameters for this, though):

{{{
def reposetup(ui, repo):
    #do initialization here.
}}}


== Example extension ==
Line 44: Line 104:

'''printparents

Prints the parents of a given revision.
'''
Line 53: Line 118:
def print_parents(ui, repo, node, **opts): #
# For experimenting with Mercurial in the python interpreter:
# Getting the repository of the current dir:
# >>> from mercurial import hg, ui
# >>> repo = hg.repository(ui.ui(), path = ".")

def printparents(ui, repo, node, **opts):
Line 57: Line 128:
    # repo.lookup can lookup based on tags, an sha1, or a revision number
    node = repo.lookup(node)
    parents = repo.changelog.parents(node)
    # repo can be indexed based on tags, an sha1, or a revision number
    ctx = repo[node]
    parents = ctx.parents()
Line 62: Line 133:
 # hg.short will return a smaller portion of the sha1
 print "short %s %s" % (hg.short(parents[0]), hg.short(parents[1]))
 # the string representation of a context returns a smaller portion of the sha1
 ui.write("short %s %s\n" % (parents[0], parents[1]))
Line 65: Line 136:
 # hg.hex will return the full sha1 
 print "long %s %s" % (hg.hex(parents[0]), hg.hex(parents[1]))
 # the hex representation of a context returns the full sha1
 ui.write("long %s %s\n" % (parents[0].hex(), parents[1].hex()))
Line 68: Line 139:
 print "default %s %s" % (hg.short(parents[0]), hg.short(parents[1]))  ui.write("default %s %s\n" % (parents[0], parents[1]))
Line 72: Line 143:
    "print-parents": (print_parents,      "print-parents": (printparents,
Line 77: Line 148:
                     "hg print-parents [options] node")                      "[options] REV")
Line 80: Line 151:
def reposetup(ui, repo):
    pass
    # extension specific setup can go here
Line 92: Line 160:

----
See CategoryExtension for related pages and [[UsingExtensions]] for a list of readily avaliable extensions bundled with Mercurial or provided by third parties.
----
CategoryExtension CategoryHowTo CategoryInternals

Writing Mercurial extensions

Mercurial features an extension mechanism for adding new commands.

Extensions allow the creation of new features and using them directly from the main hg command line as if they were built-in commands. The extensions have full access to the MercurialApi.

1. Writing your own extension

1.1. File Layout

Extensions are usually written as simple python modules. Larger ones are better split into multiple modules of a single package (see ConvertExtension). The package root module gives its name to the extension and implements the cmdtable and optional callbacks described below.

1.2. Command table

To write your own extension, your python module can provide an optional dict named cmdtable with entries describing each command.

1.2.1. The cmdtable dictionary

The cmdtable dictionary uses as key the new command names, and, as value, a tuple containing:

  1. the function name to be called when the command is used.
  2. a list of options the command can take.
  3. a help string for the command.

1.2.2. List of options

All the command flag options are documented in the mercurial/fancyopts.py sources.

The options list is a list of tuples containing:

  1. the short option letter, or '' if no short option is available (for example, o for a -o option).

  2. the long option name (for example, option for a --option option).

  3. a default value for the option.
  4. a help string for the option (it's possible to ommit the "hg newcommand" part and only the options and parameter subsstring is needed).

1.2.3. Example cmdtable

cmdtable = {
    # "command-name": (function-call, options-list, help-string)
    "print-parents": (printparents,
                     [('s', 'short', None, 'print short form'),
                      ('l', 'long', None, 'print long form')],
                     "hg print-parents [options] node")
}

1.3. Command function signatures

Functions that implement new commands always receive a ui and usually a repo parameter. Please see the MercurialApi for information on how to use these. The rest of parameters are taken from the command line items that don't start with a dash and are passed in the same order they were written. If no default value is given in the parameter list they are required.

If there is no repo to be associated with the command and consequently no repo passed then commands should be imported from mercurial and the extension name should be added to commands.norepo like this:

from mercurial import commands
...
commands.norepo += " mycommand" 

For examples of norepo see the source code to the RcpathExtension (direct link to RcpathExtension/rcpath.py extension source) or the ConvertExtension (direct link to convert extension source).

1.4. Communicating with the user

Besides the ui methods listed in MercurialApi, like ui.write(*msg) or ui.prompt(msg, default="y"), an extension can add help text for each of its commands and the extension itself.

The module docstring will be used as help string when hg help extensionname is used and, similarly, the help string for a command and the docstring belonging to the function that's wrapped by the command will be shown when hg help command is invoked.

1.5. Setup Callbacks

Extensions are loaded in phases, all extensions are processed in a given phase before the next phase begins. In the first phase all extension modules are loaded and registered with Mercurial. this means that you can find all enabled extensions with extensions.find in the following phases.

1.5.1. ui setup

Extensions can implement an optional callback named uisetup. uisetup is called when the extension is first loaded and receives a ui object:

def uisetup(ui):
    # ...

1.5.2. Extension setup

Extensions can implement an optional callback named extsetup. It is called after all the extension are loaded, and can be useful in case one extension optionally depends on another extension. Signature:

def extsetup():
    # ...

Mercurial version 8e6019b16a7d and later (that is post-1.3.1) will pass a ui argument to extsetup.

1.5.3. Command table setup

After extsetup, the cmdtable is copied into the global command table in Mercurial.

1.5.4. Repository setup

Extensions can implement an optional callback named reposetup. It is called after the main Mercurial repository initialization, and can be used to setup any local state the extension might need.

As other command functions it receives an ui object and a repo object (no additional parameters for this, though):

def reposetup(ui, repo):
    #do initialization here.

2. Example extension

   1 #!/usr/bin/env python
   2 
   3 '''printparents
   4 
   5 Prints the parents of a given revision.
   6 '''
   7 
   8 from mercurial import hg
   9 
  10 # every command must take a ui and and repo as arguments.
  11 # opts is a dict where you can find other command line flags
  12 #
  13 # Other parameters are taken in order from items on the command line that
  14 # don't start with a dash.  If no default value is given in the parameter list,
  15 # they are required.
  16 # 
  17 # For experimenting with Mercurial in the python interpreter: 
  18 # Getting the repository of the current dir: 
  19 #    >>> from mercurial import hg, ui
  20 #    >>> repo = hg.repository(ui.ui(), path = ".")
  21 
  22 def printparents(ui, repo, node, **opts):
  23     # The doc string below will show up in hg help
  24     """Print parent information"""
  25 
  26     # repo can be indexed based on tags, an sha1, or a revision number
  27     ctx = repo[node]
  28     parents = ctx.parents()
  29 
  30     if opts['short']:
  31         # the string representation of a context returns a smaller portion of the sha1
  32         ui.write("short %s %s\n" % (parents[0], parents[1]))
  33     elif opts['long']:
  34         # the hex representation of a context returns the full sha1
  35         ui.write("long %s %s\n" % (parents[0].hex(), parents[1].hex()))
  36     else:
  37         ui.write("default %s %s\n" % (parents[0], parents[1]))
  38 
  39 cmdtable = {
  40     # cmd name        function call
  41     "print-parents": (printparents,
  42                      # see mercurial/fancyopts.py for all of the command
  43                      # flag options.
  44                      [('s', 'short', None, 'print short form'),
  45                       ('l', 'long', None, 'print long form')],
  46                      "[options] REV")
  47 }

If cmdtable or reposetup is not present, your extension will still work. This means that an extension can work "silently", without making new functionality directly visible through the command line interface.

3. Where to put extensions in the source tree

As of a change shortly after the 0.7 release, the recommended location for installing extensions in the source tree is the hgext directory. If you put a file in there called foo.py, you will need to refer to it in the hgrc file as a qualified package name, hgext.foo.

The contents of the hgext directory will be installed by the top-level setup.py script along with the rest of Mercurial.


See CategoryExtension for related pages and UsingExtensions for a list of readily avaliable extensions bundled with Mercurial or provided by third parties.


CategoryExtension CategoryHowTo CategoryInternals

WritingExtensions (last edited 2020-07-29 10:00:07 by aayjaychan)