在Python脚本中获取当前的git哈希

Vic*_*tor 139 python git git-hash

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

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

fif*_*nce 143

无需git亲自从命令中获取数据.GitPython是一个非常好的方法来做这个和许多其他的git东西.它甚至为Windows提供"尽力而为"的支持.

之后pip install gitpython,你可以做

import git
repo = git.Repo(search_parent_directories=True)
sha = repo.head.object.hexsha
Run Code Online (Sandbox Code Playgroud)

  • @ user5359531我不同意.GitPython提供纯Python实现,抽象出特定于平台的细节,并且可以在所有平台上使用标准包工具(`pip` /`requirements.txt`)进行安装.什么不"干净"? (30认同)
  • 这是在Python中执行操作的常用方法.如果OP需要这些要求,那么他们就会这么说.我们不是读者,我们无法预测每个问题中的每一个可能性.那种方式就是疯狂. (20认同)
  • 到目前为止最干净,最便携的解决方案. (11认同)
  • @ user5359531,我不清楚为什么在整个stackoverflow中可以假设`import numpy as np`,但安装gitpython超出'clean'和'portable'.我认为这是迄今为止最好的解决方案,因为它不会重新发明轮子,隐藏丑陋的实现,也不会破坏从子进程中攻击git的答案. (11认同)
  • @crishoj当发生这种情况时,不确定如何将其称为可移植:`ImportError:没有名为gitpython的模块.您不能依赖安装了`gitpython`的最终用户,并要求他们在您的代码工作之前安装它使其不可移植.除非您要包含自动安装协议,否则它不再是一个干净的解决方案. (7认同)
  • @ user5359531虽然我一般都认为你不应该在每个小问题上抛出一个闪亮的新库,但你对"可移植性"的定义似乎忽略了现代场景,开发人员可以完全控制应用程序运行的所有环境.在2018年,我们有Docker容器,虚拟环境和机器映像(例如AMI)具有`pip`或能够轻松安装`pip`.在这些现代场景中,"pip"解决方案与"标准库"解决方案一样便携. (7认同)
  • `pip`并非在所有系统上都可用.就此而言,`pip`不需要外部互联网访问来安装所述包. (6认同)
  • > _如果它不在标准库中,则它不是“便携式的”。_很抱歉,但这根本没有任何意义。使用一种将程序员从所使用的平台中抽象出来的语言实现(程序包),比调用依赖于底层平台以及存在此类确切命令的子过程要轻便得多,这在Mac中可能会有所不同,Linux,Windows和BSD。根据定义,使用抽象接口是可移植性的真正含义,而从程序中调用子命令则绝对不是。 (2认同)
  • 无法导入库是一种病态情况,而不是常见情况。绝大多数编程中的常见情况是使用库 - 特别是为了避免与外部程序的笨拙交互。如果你不能,你应该使用子流程,但如果失败或其他令人信服的原因,这是最佳实践解决方案:使用经过验证的库来处理相关用例。 (2认同)

Yuj*_*ita 91

这篇文章包含命令,Greg的答案包含子进程命令.

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'])
Run Code Online (Sandbox Code Playgroud)

  • 在结果中添加一个strip()以获得没有换行符:) (29认同)
  • 添加`.decode('ascii').strip()`来解码二进制字符串(并删除换行符). (5认同)
  • 如果您的代码是从另一个目录运行的,您可能需要添加 `cwd=os.path.dirname(os.path.realpath(__file__))` 作为 `check_output` 的参数 (3认同)
  • @pkamb使用os.chdir cd到你有兴趣使用的git repo的路径 (2认同)

Gre*_*ill 84

git describe命令是创建代码的人类可呈现"版本号"的好方法.从文档中的示例:

有了类似git.git当前树的东西,我得到:

[torvalds@g5 git]$ git describe parent
v1.0.4-14-g2414721
Run Code Online (Sandbox Code Playgroud)

即我的"父"分支的当前头部基于v1.0.4,但由于它上面有一些提交,因此describe添加了额外提交的数量("14")和提交的缩写对象名称本身("2414721")在最后.

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

import subprocess
label = subprocess.check_output(["git", "describe"]).strip()
Run Code Online (Sandbox Code Playgroud)

  • 如果没有找到标签,`git describe --always`将回退到最后一次提交 (33认同)
  • 请注意,如果没有标签,git describe将失败:`fatal:找不到名字,不能描述任何内容 (10认同)
  • @CharlieParker:`git describe`通常需要至少一个标签.如果您没有任何标签,请使用`--always`选项.有关详细信息,请参阅[git describe documentation](https://git-scm.com/docs/git-describe). (5认同)
  • @JosefAssad:如果您需要生产中的版本标识符,那么您的部署过程应该运行上面的代码,结果应该"融入"到部署到生产的代码中. (4认同)
  • 这样做的缺点是,如果在没有git repo的情况下运行版本打印代码,则该代码将被破坏。例如,在生产中。:) (2认同)

And*_*dyP 12

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

import subprocess
print(subprocess.check_output(["git", "describe", "--always"]).strip().decode())
Run Code Online (Sandbox Code Playgroud)

或者,如果脚本是从 repo 外部调用的:

import subprocess, os
print(subprocess.check_output(["git", "describe", "--always"], cwd=os.path.dirname(os.path.abspath(__file__))).strip().decode())
Run Code Online (Sandbox Code Playgroud)

或者,如果脚本是从 repo 外部调用的,并且您喜欢pathlib

import subprocess
from pathlib import Path
print(subprocess.check_output(["git", "describe", "--always"], cwd=Path(__file__).resolve().parent).strip().decode())
Run Code Online (Sandbox Code Playgroud)

  • 除了使用 os.chdir 之外,还可以在 check_output 中使用 cwd= 参数来在执行之前临时更改工作目录。 (5认同)

kag*_*ick 10

如果子进程不可移植并且您不想安装包来做这么简单的事情,您也可以这样做。

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()
Run Code Online (Sandbox Code Playgroud)

我只在我的 repos 上测试过这个,但它似乎工作得非常一致。


rya*_*lon 9

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
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢这个,非常干净,没有外部库 (2认同)

Way*_*man 7

这是Yuji 'Tomita' Tomita答案的改进。

import subprocess

def get_git_revision_hash():
    full_hash = subprocess.check_output(['git', 'rev-parse', 'HEAD'])
    full_hash = str(full_hash, "utf-8").strip()
    return full_hash

def get_git_revision_short_hash():
    short_hash = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'])
    short_hash = str(short_hash, "utf-8").strip()
    return short_hash

print(get_git_revision_hash())
print(get_git_revision_short_hash())
Run Code Online (Sandbox Code Playgroud)