Mercurial scripting with python

会有一股神秘感。 提交于 2019-12-17 22:32:24

问题


I am trying to get the mercurial revision number/id (it's a hash not a number) programmatically in python.

The reason is that I want to add it to the css/js files on our website like so:

<link rel="stylesheet" href="example.css?{% mercurial_revision "example.css" %}" />

So that whenever a change is made to the stylesheet, it will get a new url and no longer use the old cached version.

OR if you know where to find good documentation for the mercurial python module, that would also be helpful. I can't seem to find it anywhere.

My Solution

I ended up using subprocess to just run a command that gets the hg node. I chose this solution because the api is not guaranteed to stay the same, but the bash interface probably will:

import subprocess

def get_hg_rev(file_path):
    pipe = subprocess.Popen(
        ["hg", "log", "-l", "1", "--template", "{node}", file_path],
        stdout=subprocess.PIPE
        )
    return pipe.stdout.read()

example use:

> path_to_file = "/home/jim/workspace/lgr/pinax/projects/lgr/site_media/base.css"
> get_hg_rev(path_to_file)
'0ed525cf38a7b7f4f1321763d964a39327db97c4'

回答1:


It's true there's no official API, but you can get an idea about best practices by reading other extensions, particularly those bundled with hg. For this particular problem, I would do something like this:

from mercurial import ui, hg
from mercurial.node import hex

repo = hg.repository('/path/to/repo/root', ui.ui())
fctx = repo.filectx('/path/to/file', 'tip')
hexnode = hex(fctx.node())

Update At some point the parameter order changed, now it's like this:

   repo = hg.repository(ui.ui(), '/path/to/repo/root' )



回答2:


Do you mean this documentation?
Note that, as stated in that page, there is no official API, because they still reserve the right to change it at any time. But you can see the list of changes in the last few versions, it is not very extensive.




回答3:


An updated, cleaner subprocess version (uses .check_output(), added in Python 2.7/3.1) that I use in my Django settings file for a crude end-to-end deployment check (I dump it into an HTML comment):

import subprocess

HG_REV = subprocess.check_output(['hg', 'id', '--id']).strip()

You could wrap it in a try if you don't want some strange hiccup to prevent startup:

try:
    HG_REV = subprocess.check_output(['hg', 'id', '--id']).strip()
except OSError:
    HG_REV = "? (Couldn't find hg)"
except subprocess.CalledProcessError as e:
    HG_REV = "? (Error {})".format(e.returncode)
except Exception:  # don't use bare 'except', mis-catches Ctrl-C and more
    # should never have to deal with a hangup 
    HG_REV = "???"



回答4:


give a try to the keyword extension




回答5:


If you are using Python 2, you want to use hglib.

I don't know what to use if you're using Python 3, sorry. Probably hgapi.

Contents of this answer

  • Mercurial's APIs
  • How to use hglib
  • Why hglib is the best choice for Python 2 users
  • If you're writing a hook, that discouraged internal interface is awfully convenient

Mercurial's APIs

Mercurial has two official APIs.

  1. The Mercurial command server. You can talk to it from Python 2 using the hglib (wiki, PyPI) package, which is maintained by the Mercurial team.
  2. Mercurial's command-line interface. You can talk to it via subprocess, or hgapi, or somesuch.

How to use hglib

Installation:

pip install python-hglib

Usage:

import hglib
client = hglib.open("/path/to/repo")

commit = client.log("tip")
print commit.author

More usage information on the hglib wiki page.

Why hglib is the best choice for Python 2 users

Because it is maintained by the Mercurial team, and it is what the Mercurial team recommend for interfacing with Mercurial.

From Mercurial's wiki, the following statement on interfacing with Mercurial:

For the vast majority of third party code, the best approach is to use Mercurial's published, documented, and stable API: the command line interface. Alternately, use the CommandServer or the libraries which are based on it to get a fast, stable, language-neutral interface.

From the command server page:

[The command server allows] third-party applications and libraries to communicate with Mercurial over a pipe that eliminates the per-command start-up overhead. Libraries can then encapsulate the command generation and parsing to present a language-appropriate API to these commands.

The Python interface to the Mercurial command-server, as said, is hglib.

The per-command overhead of the command line interface is no joke, by the way. I once built a very small test suite (only about 5 tests) that used hg via subprocess to create, commit by commit, a handful of repos with e.g. merge situations. Throughout the project, the runtime of suite stayed between 5 to 30 seconds, with nearly all time spent in the hg calls.

If you're writing a hook, that discouraged internal interface is awfully convenient

The signature of a Python hook function is like so:

# In the hgrc:
# [hooks]
# preupdate.my_hook = python:/path/to/file.py:my_hook

def my_hook(
    ui, repo, hooktype, 
    ... hook-specific args, find them in `hg help config` ..., 
    **kwargs)

ui and repo are part of the aforementioned discouraged unofficial internal API. The fact that they are right there in your function args makes them terribly convenient to use, such as in this example of a preupdate hook that disallows merges between certain branches.

def check_if_merge_is_allowed(ui, repo, hooktype, parent1, parent2, **kwargs):
    from_ = repo[parent2].branch()
    to_ = repo[parent1].branch()
    ...
    # return True if the hook fails and the merge should not proceed.

If your hook code is not so important, and you're not publishing it, you might choose to use the discouraged unofficial internal API. If your hook is part of an extension that you're publishing, better use hglib.




回答6:


FWIW to avoid fetching that value on every page/view render, I just have my deploy put it into the settings.py file. Then I can reference settings.REVISION without all the overhead of accessing mercurial and/or another process. Do you ever have this value change w/o reloading your server?




回答7:


I wanted to do the same thing the OP wanted to do, get hg id -i from a script (get tip revision of the whole REPOSITORY, not of a single FILE in that repo) but I did not want to use popen, and the code from brendan got me started, but wasn't what I wanted.

So I wrote this... Comments/criticism welcome. This gets the tip rev in hex as a string.

from mercurial import ui, hg, revlog
# from mercurial.node import hex  # should I have used this?

def getrepohex(reporoot):
    repo = hg.repository(ui.ui(), reporoot)
    revs = repo.revs('tip')
    if len(revs)==1:
      return str(repo.changectx(revs[0]))
    else:
      raise Exception("Internal failure in getrepohex")


来源:https://stackoverflow.com/questions/1153469/mercurial-scripting-with-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!