在Python脚本中获取当前的git hash


164

我想在Python脚本的输出中包括当前的git哈希值(作为生成该输出的代码的版本号)。

如何在Python脚本中访问当前的git哈希?


7
git rev-parse HEAD从命令行开始。输出语法应该很明显。
梅尔·尼科尔森

Answers:


96

git describe命令是创建代码的人为表示的“版本号”的好方法。从文档中的示例中:

使用git.git之类的当前树,我得到:

[torvalds@g5 git]$ git describe parent
v1.0.4-14-g2414721

即我的“父”分支的当前头基于v1.0.4,但是由于它的顶部还有一些提交,describe添加了额外的提交数量(“ 14”)和该提交的缩写对象名称本身(“ 2414721”)结尾。

在Python中,您可以执行以下操作:

import subprocess
label = subprocess.check_output(["git", "describe"]).strip()

3
这样做的缺点是,如果在没有git repo的情况下运行版本打印代码,则该代码将被破坏。例如,在生产中。:)
JosefAssad

5
@JosefAssad:如果您在生产中需要版本标识符,则您的部署过程应运行上面的代码,并且结果应“嵌入”到部署到生产中的代码中。
Greg Hewgill

14
请注意,如果没有标签,则git describe将失败:fatal: No names found, cannot describe anything.
kynan 2014年

40
git describe --always如果找不到标签,将回退到最后一次提交
Leonardo

5
@CharlieParker:git describe通常至少需要一个标签。如果没有任何标签,请使用该--always选项。有关更多信息,请参见git describe文档
Greg Hewgill '16

189

无需费力地git自己从命令中获取数据。GitPython是执行此操作以及许多其他工作的非常好方法git。它甚至对Windows具有“尽力而为”的支持。

之后pip install gitpython,你可以做

import git
repo = git.Repo(search_parent_directories=True)
sha = repo.head.object.hexsha

9
@crishoj不确定发生这种情况时如何将其称为可移植的:ImportError: No module named gitpython。您不能依赖已gitpython安装的最终用户,而要求他们在代码工作之前安装它就使其无法移植。除非您要包括自动安装协议,否则它不再是干净的解决方案。
user5359531

39
@ user5359531我希望与众不同。GitPython提供了一个纯Python实现,可以抽象出特定于平台的细节,并且可以在所有平台上使用标准打包工具(pip/ requirements.txt)进行安装。什么不是“干净”的?
crishoj

22
这是在Python中执行操作的正常方法。如果OP需要这些要求,那么他们会这么说。我们不是读书者,我们无法预测每个问题的所有可能。那就是疯狂。
OldTinfoil '18

14
@ user5359531,我不清楚为什么import numpy as np可以在整个stackoverflow中使用它,但是安装gitpython超出了“ clean”和“ portable”的范围。我认为这是迄今为止最好的解决方案,因为它不会重新发明轮子,不会隐藏丑陋的实现,也不会从子进程中破解git的答案。
Jblasco

7
@ user5359531虽然我通常都同意您不应在每个小问题上都添加一个闪亮的新库,但是您对“可移植性”的定义似乎忽略了现代场景,在该场景下,开发人员可以完全控制运行该应用程序的所有环境。在2018年,具有pip或易于安装的Docker容器,虚拟环境和机器映像(例如AMI)pip。在这些现代方案中,pip解决方案与“标准库”解决方案一样可移植。
瑞安

106

这篇文章包含命令,Greg的答案包含subprocess命令。

import subprocess

def get_git_revision_hash():
    return subprocess.check_output(['git', 'rev-parse', 'HEAD'])

def get_git_revision_short_hash():
    return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'])

32
在结果中添加strip()即可获得此效果而无需换行:)
Grasshopper

您将如何在特定路径上针对git repo运行此程序?
pkamb 2015年

2
@pkamb使用os.chdir cd到您感兴趣的git repo的路径
Zac

如果当前签出的修订版不是分支头,这不会给出错误的答案吗?
最大

7
添加一个.decode('ascii').strip()以解码二进制字符串(并删除换行符)。
pfm

13

numpy有一个好看的多平台程序在其setup.py

import os
import subprocess

# Return the git revision as a string
def git_version():
    def _minimal_ext_cmd(cmd):
        # construct minimal environment
        env = {}
        for k in ['SYSTEMROOT', 'PATH']:
            v = os.environ.get(k)
            if v is not None:
                env[k] = v
        # LANGUAGE is used on win32
        env['LANGUAGE'] = 'C'
        env['LANG'] = 'C'
        env['LC_ALL'] = 'C'
        out = subprocess.Popen(cmd, stdout = subprocess.PIPE, env=env).communicate()[0]
        return out

    try:
        out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD'])
        GIT_REVISION = out.strip().decode('ascii')
    except OSError:
        GIT_REVISION = "Unknown"

    return GIT_REVISION

2
我喜欢这个,非常干净,没有任何外部库
17aal

Yuji的答案仅在一行产生相同结果的代码中提供了类似的解决方案。您能解释一下为什么numpy认为有必要“构建一个最小的环境”吗?(假设他们有充分的理由)
MD004

我刚刚在他们的回购中注意到了这一点,并决定将其添加到此问题中,以供感兴趣的人们使用。我没有在Windows中进行开发,因此尚未进行测试,但是我假设设置envdict对于跨平台功能是必需的。Yuji的答案没有,但是也许在UNIX和Windows上都可以。
ryanjdillon

回顾git的责任,他们在11年前就将其作为SVN的错误修复程序:github.com/numpy/numpy/commit/…git可能不再需要该错误修复程序。
gparent

@ MD004 @ryanjdillon他们设置了区域设置以便可以.decode('ascii')工作-否则编码是未知的。
z0r

7

如果子进程不是可移植的,并且您不想安装软件包来执行此简单操作,则也可以执行此操作。

import pathlib

def get_git_revision(base_path):
    git_dir = pathlib.Path(base_path) / '.git'
    with (git_dir / 'HEAD').open('r') as head:
        ref = head.readline().split(' ')[-1].strip()

    with (git_dir / ref).open('r') as git_hash:
        return git_hash.readline().strip()

我只在我的存储库上测试过此功能,但它似乎工作得相当稳定。


有时找不到/ refs /,但可以在“ packed-refs”中找到当前的提交ID。
am9417

6

这是Greg答案的更完整版本:

import subprocess
print(subprocess.check_output(["git", "describe", "--always"]).strip().decode())

或者,如果脚本是从仓库外调用的:

import subprocess, os
os.chdir(os.path.dirname(__file__))
print(subprocess.check_output(["git", "describe", "--always"]).strip().decode())

1
而不是使用os.chdir时,cwd=ARG可以用于check_output临时改变工作目录执行前。
马克

0

如果由于某种原因没有可用的git,但是您有git repo(找到了.git文件夹),则可以从.git / fetch / heads / [branch]获取提交哈希

例如,我使用了在存储库根目录下运行的以下快速且肮脏的Python代码段来获取提交ID:

git_head = '.git\\HEAD'

# Open .git\HEAD file:
with open(git_head, 'r') as git_head_file:
    # Contains e.g. ref: ref/heads/master if on "master"
    git_head_data = str(git_head_file.read())

# Open the correct file in .git\ref\heads\[branch]
git_head_ref = '.git\\%s' % git_head_data.split(' ')[1].replace('/', '\\').strip()

# Get the commit hash ([:7] used to get "--short")
with open(git_head_ref, 'r') as git_head_ref_file:
    commit_id = git_head_ref_file.read().strip()[:7]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.