在Python脚本中获取当前git散列

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

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

128681 次浏览

git describe命令是创建一个人类像样的“版本号”的好方法。代码的。参考文档中的例子:

和像少年犯一样的东西。git当前树,我得到:

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

也就是说,我“父母”的现任负责人;Branch基于v1.0.4,但由于它在此基础上有一些提交,所以describe在末尾添加了额外提交的数量(&;14&;)和提交本身的缩写对象名称(&;2414721&;)。

在Python内部,你可以做如下的事情:

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

这篇文章包含命令,格雷格的回答包含子进程命令。

import subprocess


def get_git_revision_hash() -> str:
return subprocess.check_output(['git', 'rev-parse', 'HEAD']).decode('ascii').strip()


def get_git_revision_short_hash() -> str:
return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']).decode('ascii').strip()

运行时

print(get_git_revision_hash())
print(get_git_revision_short_hash())

你得到输出:

fd1cd173fc834f62fa7db3034efc5b8e0f3b43fe
fd1cd17

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

不需要自己从git命令中获取数据。GitPython是一种很好的方法,还有很多其他git的东西。它甚至有“尽最大努力”;支持Windows。

pip install gitpython之后你可以这样做

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

使用这个库时需要考虑的一些问题。以下内容取自gitpython.readthedocs.io

系统资源泄漏

GitPython不适合长时间运行的进程(比如守护进程),因为它容易泄漏系统资源。它是在析构函数(在__del__方法中实现)仍然确定性地运行时编写的。

如果你仍然想在这样的上下文中使用它,你会想要搜索__del__实现的代码库,并在你认为合适的时候自己调用这些实现。

另一种确保正确清理资源的方法是将GitPython分解为一个可以定期删除的单独进程

如果subprocess是不可移植的,并且你不想安装一个包来做这么简单的事情,你也可以这样做。

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

我只测试了这个在我的回购,但它似乎工作相当稳定。

下面是更完整的格雷格的回答版本:

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

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

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

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

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

如果你因为某些原因没有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]

如果你想要比哈希值多一点的数据,你可以使用git-log:

import subprocess


def get_git_hash():
return subprocess.check_output(['git', 'log', '-n', '1', '--pretty=tformat:%H']).strip()


def get_git_short_hash():
return subprocess.check_output(['git', 'log', '-n', '1', '--pretty=tformat:%h']).strip()


def get_git_short_hash_and_commit_date():
return subprocess.check_output(['git', 'log', '-n', '1', '--pretty=tformat:%h-%ad', '--date=short']).strip()

有关格式化选项的完整列表-查看git log --help

如果你像我一样:

  • 多平台,所以子进程可能有一天崩溃
  • 使用Python 2.7,所以GitPython不可用
  • 不要仅仅为了这个而使用Numpy
  • 已经使用哨兵(旧贬值版本:乌鸦)

然后(这将不会在shell上工作,因为shell不检测当前文件路径,将BASE_DIR替换为当前文件路径):

import os
import raven


BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
print(raven.fetch_git_sha(BASE_DIR))

就是这样。

我正在寻找另一种解决方案,因为我想迁移到sentry_sdk并离开raven,但可能有些人想继续使用raven一段时间。

< a href = " https://forum.sentry。io/t/fetch-git-sha-equivalent- the- the-unified-python-sdk/5521/2" rel="nofollow noreferrer">这是让我陷入这个stackoverflow问题的讨论

所以使用raven的代码而不使用raven也是可能的(见讨论):

from __future__ import absolute_import


import os.path


__all__ = 'fetch_git_sha'




def fetch_git_sha(path, head=None):
"""
>>> fetch_git_sha(os.path.dirname(__file__))
"""
if not head:
head_path = os.path.join(path, '.git', 'HEAD')


with open(head_path, 'r') as fp:
head = fp.read().strip()


if head.startswith('ref: '):
head = head[5:]
revision_file = os.path.join(
path, '.git', *head.split('/')
)
else:
return head
else:
revision_file = os.path.join(path, '.git', 'refs', 'heads', head)


if not os.path.exists(revision_file):
# Check for Raven .git/packed-refs' file since a `git gc` may have run
# https://git-scm.com/book/en/v2/Git-Internals-Maintenance-and-Data-Recovery
packed_file = os.path.join(path, '.git', 'packed-refs')
if os.path.exists(packed_file):
with open(packed_file) as fh:
for line in fh:
line = line.rstrip()
if line and line[:1] not in ('#', '^'):
try:
revision, ref = line.split(' ', 1)
except ValueError:
continue
if ref == head:
return revision


with open(revision_file) as fh:
return fh.read().strip()

我把这个文件命名为version .py,然后导入“fetch_git_sha"我需要它把文件路径作为参数。

希望它能对你们中的一些人有所帮助;)

这是富田裕治答案的改进。

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())
我遇到了这个问题,并通过实现这个函数解决了它。 https://gist.github.com/NaelsonDouglas/9bc3bfa26deec7827cb87816cad88d59 < / p >
from pathlib import Path


def get_commit(repo_path):
git_folder = Path(repo_path,'.git')
head_name = Path(git_folder, 'HEAD').read_text().split('\n')[0].split(' ')[-1]
head_ref = Path(git_folder,head_name)
commit = head_ref.read_text().replace('\n','')
return commit




r = get_commit('PATH OF YOUR CLONED REPOSITORY')
print(r)

我遇到了类似于OP的问题,但在我的情况下,我将源代码作为zip文件交付给我的客户端,尽管我知道他们将安装python,但我不能假设他们将安装git。因为OP没有指定他的操作系统,如果他安装了git,我想我可以在这里贡献一下。

为了只获得提交的散列,Naelson Douglas的回答是完美的,但为了获得标记名,我使用了达利奇 python包。这是一个用python简化的git客户端。

在使用pip install dulwich --global-option="--pure"安装包之后,可以这样做:

from dulwich import porcelain


def get_git_revision(base_path):
return porcelain.describe(base_path)


r = get_git_revision("PATH OF YOUR REPOSITORY's ROOT FOLDER")
print(r)

我刚刚在这里的一个存储库中运行了这段代码,它显示了输出v0.1.2-1-gfb41223,类似于由git description返回,这意味着我在标记v0.1.2之后提交了1,并且提交的7位哈希是fb41223

它有一些限制:目前它没有显示存储库是否脏和始终显示7位哈希的选项,但没有必要安装git,所以可以选择权衡。

如果由于选项--pure导致命令pip install中出现错误(问题在在这里中解释),选择两个可能的解决方案之一:

    首先安装Dulwich包的依赖项: 李pip install urllib3 certifi && pip install dulwich --global-option="--pure" < / >
  1. 安装时不使用pure: pip install dulwich选项。这将在您的系统中安装一些平台相关的文件,但它将改进包的性能