Writing Your First Mercurial Extension

I wanted to write an extension for mercurial, and after extensive googling, decided that there was no suitable starting point for an absolute beginner like me.

By absolute beginner I mean any person who's never messed with mercurial configuration before and never written any python before. It's strange that such people are not catered for, because most people on the planet have never messed with mercurial configuration before and have never written any python before. Even Shakespeare and Elvis have this particular attribute in common.

(The often cited 'Writing Extensions' only begins to be comprehensible once you've mastered all the concepts involved.)

So here's my absolute beginner's 3 minute guide, pitched at me and possibly you.

To tell mercurial about an extension, you edit your mercurial.ini file (or on non-windows machines, your '.hgrc' file.)

Your mercurial.ini file is located here:

%userprofile%\mercurial.ini

(If you don't have a file at '%userprofile%\mercurial.ini' then create one now.)

(In powershell, you would edit '$env:userprofile\mercurial.ini')

Now look for the extensions section, i.e. the section headed "[extensions]". (And add one if you need to)

And tell it about your extension. Like so:

[extensions]
helloworld =

It looks like we're setting helloworld to nothing. But we're not.

What we've done there is we've said "Hey mercurial, old buddy, if you can find an extension called 'helloworld', then I want you to enable it."

Naturally this isn't going to work, because mercurial won't be able to find just such an extension. But that technique is good enough for all of the built-in extensions (like 'color' and 'fetch').

At this point if we run the command:

hg help extensions

We'll get the result:

*** failed to import extension helloworld: No module named helloworld

(And a lot of other information as well.)

Very well then, let's give mercurial a little more info, so it can find our new extension.

[extensions]
helloworld = C:\Program Files\TortoiseHg\hgext\helloworld.py

That's neat... but there's no such file. Running 'hg help extensions' at this point will, quite predictably result in:

*** failed to import extension helloworld from C:\Program Files\TortoiseHg\hgext\helloworld.py: [Errno 2] No such file or
directory

...and we'll go right ahead and create the file 'helloworld.py'. Let's just create an empty text file in the right place. (You may need admin rights to create a file in that location.)

Now when we type hg help extensions we'll see the details of all the extensions, and in the section titled 'enabled extensions' it should say:

   helloworld   (no help text available)

Fine. So the next step is to add some help text to our extension.

Go to the empty file and add a 'doc string'. You do this by enclosing a statement in triple-quotes, like this """

"""hello world is a very simple extension
"""

(Python is white-space sensitive, so make sure there's no whitespace before any of those triple quotes)

Now we have our simple extension that does nothing, it just exists and broadcasts its existence.

When we type 'hg help extensions', we'll see the following in the 'enabled extensions' section:

  helloworld    helloworld is a very simple extension

And, even better, if you type:

hg help helloworld

you'll see...

C:\>hg help helloworld
helloworld extension - helloworld is a very simple extension

no commands defined

Ah ha! No commands defined? So our new mission is to create a "command" within our extension!

Here's a very simple command called 'hello'. And it introduces a few other concepts I'll describe in a moment.

"""helloworld is a very simple extension
"""
from mercurial import commands, extensions, util

def hello(ui):
    """say a big hello.
    """
    ui.write('well hello there world.\n\n')

cmdtable = {'hello':(hello,[],'hg hello says hello')}

commands.norepo += ' hello'

In order to tell mercurial about our command, I had to create a list called 'cmdtable' and put my command, 'hello' in it, plus a few details about ways to call it.

I also needed to tell mercurial that this particular command should work just fine even if it is called when there is no repository present. (Thus, commands.norepo += ' hello')

So what have we achieved so far?

now i can type:

hg help extensions

-- and see that my extension exists. and i can type

hg help helloworld

-- and see help for my extension, including details of its commands.

And i can type

hg help hello

-- and get help on its one command.

Finally -- and most importantly, I can type:

hg hello

and have it run my new command.

Now there was a little hint given when I typed hg help hello. The message mercurial gave me was:

list of commands:

 hello    say a big hello.

use "hg -v help helloworld" to show builtin aliases and global options

And that just about wraps it up for a complete beginner. We've reached the hello world stage.

Notice the enticing message mentions builtin aliases and global options. That's what you can look into next, if you really want to achieve some Next Level Mastery of Mercurial Extenions. But for now we're done.

Further reading

 

I'm currently writing a book about how to build your first product. If you want to build your first product, please sign up to be notified when the book is available.

(By the way, I read every comment and often respond.)

Your comment, please?

Your Name
Your Url (optional)
Note: I may edit, reuse or delete your comment. Don't be mean.