使用 python 编写 Mercurial 脚本

发布于 2024-07-29 00:48:48 字数 919 浏览 6 评论 0原文

我正在尝试在 python 中以编程方式获取 Mercurial 修订号/id(它是哈希而不是数字)。

原因是我想将其添加到我们网站上的 css/js 文件中,如下所示:

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

这样,每当对样式表进行更改时,它将获得一个新的 url,并且不再使用旧的缓存版本。

或者如果您知道在哪里可以找到 Mercurial python 模块的良好文档,那也会很有帮助。 我似乎无法在任何地方找到它。

我的解决方案

我最终使用 subprocess 来运行获取 hg 节点的命令。 我选择这个解决方案是因为 api 不能保证保持不变,但 bash 接口可能会:

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()

示例使用:

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

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'

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

红ご颜醉 2024-08-05 00:48:48

确实没有官方 API,但您可以通过阅读其他扩展(尤其是与 hg 捆绑的扩展)来了解最佳实践。 对于这个特定的问题,我会做这样的事情:

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())

更新 在某些时候参数顺序发生了变化,现在是这样的:

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

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' )
淡莣 2024-08-05 00:48:48

您是指此文档吗?
请注意,如该页面所述,没有官方 API,因为他们仍然保留随时更改它的权利。 但你可以看到最近几个版本的更改列表,它不是很广泛。

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.

风筝在阴天搁浅。 2024-08-05 00:48:48

一个更新的、更干净的子进程版本(使用 .check_output(),在 Python 2.7/3.1 中添加),我在 Django 设置文件中使用它进行粗略的端到端部署检查(我将其转储到HTML 注释):

import subprocess

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

如果您不希望出现一些奇怪的问题来阻止启动,您可以将其包装在 try 中:

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 = "???"

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 = "???"
孤独难免 2024-08-05 00:48:48

如果您使用的是 Python 2,则需要使用 hglib.

抱歉,如果您使用的是 Python 3,我不知道该使用什么。 可能是hgapi

本答案的内容

  • Mercurial 的 API
  • 如何使用 hglib
  • 为什么 hglib 是 Python 2 用户的最佳选择
  • 如果您正在编写一个钩子,那么令人沮丧的内部接口非常方便

Mercurial 的 API

Mercurial 有两个官方 API。

  1. Mercurial 命令服务器。 您可以使用 hglib (wiki) 从 Python 2 与它进行交互PyPI) 包,由 Mercurial 团队维护。
  2. Mercurial 的命令行界面。 您可以通过 subprocess 与其对话,或 < a href="https://pypi.python.org/pypi/hgapi/1.7.2" rel="noreferrer">hgapi 等。

如何使用 hglib

安装:

pip install python-hglib

用法:

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

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

更多使用信息请参见 hglib wiki 页面

为什么 hglib 是 Python 2 用户的最佳选择

因为它是由 Mercurial 团队维护的,并且是 Mercurial 团队推荐用于与 Mercurial 交互的方式。

来自 Mercurial 的 wiki,有关与 Mercurial 交互的以下声明:

对于绝大多数第三方代码,最好的方法是使用 Mercurial 已发布、有文档记录且稳定的 API:命令行界面。 或者,使用 CommandServer 或基于它的库来获得快速、稳定的语言-中性界面。

从命令服务器页面:

[命令服务器允许]第三方应用程序和库通过管道与 Mercurial 进行通信,从而消除了每个命令的启动开销。 然后,库可以封装命令生成和解析,以便为这些命令提供适合语言的 API。

如前所述,Mercurial 命令服务器的 Python 接口是 hglib。

顺便说一句,命令行界面的每个命令的开销可不是开玩笑的。 我曾经构建了一个非常小的测试套件(只有大约 5 个测试),它通过 subprocess 使用 hg 来创建、逐次提交、一些具有例如合并情况的存储库。 在整个项目中,套件的运行时间保持在 5 到 30 秒之间,几乎所有时间都花在 hg 调用上。

如果你正在编写一个钩子,那么不鼓励的内部接口非常方便

Python钩子函数的签名如下:

# 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)

uirepo是前面提到的不鼓励的非官方的一部分内部 API。 事实上,它们就在函数参数中,这使得它们使用起来非常方便,例如在这个 preupdate 挂钩的示例中,它不允许在某些分支之间进行合并。

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.

如果您的钩子代码不是那么重要,并且您不发布它,您可能会选择使用不鼓励的非官方内部 API。 如果您的挂钩是您要发布的扩展的一部分,最好使用 hglib

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.

怪我太投入 2024-08-05 00:48:48

FWIW 为了避免在每个页面/视图渲染上获取该值,我只是将部署放入 settings.py 文件中。 然后我可以引用settings.REVISION,而无需访问 Mercurial 和/或其他进程的所有开销。 您是否曾经在不重新加载服务器的情况下更改过此值?

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?

只等公子 2024-08-05 00:48:48

我想做OP想做的同样的事情,从脚本中获取hg id -i(获取整个存储库的提示修订版,而不是该存储库中的单个文件),但我没有想要使用 popen,来自 brendan 的代码让我开始使用,但这不是我想要的。

所以我写了这个......欢迎评论/批评。 这会将十六进制的尖端转速作为字符串获取。

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")

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")
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文