Я хотел бы включить текущий хэш git в вывод скрипта Python (как номер версии кода, который сгенерировал этот вывод).
Как я могу получить доступ к текущему хешу git в моем скрипте Python?
Я хотел бы включить текущий хэш git в вывод скрипта Python (как номер версии кода, который сгенерировал этот вывод).
Как я могу получить доступ к текущему хешу git в моем скрипте Python?
Команда git describe
- хороший способ создания презентабельный номер версии кода. Из примеров в документации:
С чем-то вроде текущего дерева git.git я получаю:
[torvalds@g5 git]$ git describe parent v1.0.4-14-g2414721
т.е. текущий заголовок моей родительской ветки основан на версии 1.0.4, но, поскольку в ней есть несколько коммитов, в описании добавлено количество дополнительных коммитов (14) и сокращенное имя объекта для самого коммита (2414721) в конце.
Изнутри Python вы можете сделать что-то вроде следующего:
import subprocess
label = subprocess.check_output(["git", "describe"]).strip()
fatal: No names found, cannot describe anything.
- person kynan; 26.09.2014
git describe --always
вернется к последней фиксации, если теги не найдены
- person Leonardo; 06.03.2015
<last tag>-<num commits after tag>-<hash>
Мне пришлось использовать git describe --long --tags
- person djangonaut; 18.02.2016
>>> label = subprocess.check_output(["git", "describe"]) fatal: No names found, cannot describe anything. Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 573, in check_output raise CalledProcessError(retcode, cmd, output=output) subprocess.CalledProcessError: Command '['git', 'describe']' returned non-zero exit status 128
- person Charlie Parker; 01.07.2016
git describe
обычно требует хотя бы одного тега. Если у вас нет тегов, используйте параметр --always
. Дополнительную информацию см. В документации git description.
- person Greg Hewgill; 01.07.2016
parent
является примером, приведенным в документации. Вы бы использовали там свое собственное имя ветки.
- person Greg Hewgill; 09.10.2018
<branch_name>
или выделено в тексте
- person Dims; 20.12.2018
git describe --always
бесполезен, потому что он возвращает аннотированный тег, если он существует.
- person RobinL; 14.10.2020
Не нужно пытаться получить данные с помощью команды 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 в отдельный процесс, который можно периодически отбрасывать.
ImportError: No module named gitpython
. Вы не можете полагаться на то, что конечный пользователь установил gitpython
, и требование установить его до того, как ваш код заработает, сделает его непереносимым. Если вы не собираетесь включать протоколы автоматической установки, в этот момент это уже не чистое решение.
- person user5359531; 26.05.2017
pip
/ requirements.txt
) на всех платформах. Что не чистое?
- person crishoj; 27.05.2017
pip
доступен не во всех системах. В этом отношении pip
не нуждается и во внешнем доступе в Интернет для установки указанных пакетов.
- person user5359531; 28.05.2017
import numpy as np
можно предполагать на протяжении всего stackoverflow, но установка gitpython выходит за рамки «чистого» и «переносимого». Я думаю, что это, безусловно, лучшее решение, потому что оно не изобретает велосипед, скрывает уродливую реализацию и не пытается взломать ответ git из подпроцесса.
- person Jblasco; 10.04.2018
subprocess
- это стандартный метод взаимодействия с программами CLI из Python. Установка сторонних библиотек в качестве ключевого момента для решения каждой простой проблемы в Python не является хорошей практикой и вызывает проблемы в тот момент, когда вам нужно запустить свой код в любой другой системе. Если вы хотите скрыть «уродливую реализацию», используйте функцию. Если код никогда не будет запускаться кем-либо или где-либо еще, тогда, конечно, используйте любое решение, которое вам нравится.
- person user5359531; 10.04.2018
pip
или возможностью простой установки pip
. В этих современных сценариях решение pip
так же переносимо, как и стандартное библиотечное решение.
- person Ryan; 02.08.2018
Этот пост содержит команду, Ответ Грега содержит команду подпроцесса.
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'])
subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'])
для названия ветки
- person Ryan Allen; 23.05.2018
.decode('ascii').strip()
для декодирования двоичной строки (и удалите разрыв строки).
- person pfm; 09.11.2018
universal_newlines=True
, чтобы получить строку.
- person z0r; 26.04.2020
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
numpy
счел необходимым создать минимальную среду? (при условии, что у них были на то веские причины)
- person MD004; 18.07.2018
env
dict необходима для кросс-платформенной функциональности. Ответ Юдзи - нет, но, возможно, это работает как в UNIX, так и в Windows.
- person ryanjdillon; 05.08.2018
.decode('ascii')
работал - иначе кодировка неизвестна.
- person z0r; 26.04.2020
from numpy.setup import git_version
, и это не сработало
- person jlansey; 02.10.2020
setup.py
, она не является частью пакета numpy
, поэтому ее невозможно импортировать из numpy
. Чтобы использовать его, вам нужно будет где-нибудь добавить этот метод в свой собственный код.
- person ryanjdillon; 04.10.2020
Вот более полная версия ответа Грега:
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())
os.chdir
в check_output
можно использовать cwd=
arg для временного изменения рабочего каталога перед выполнением.
- person Marc; 19.09.2019
Если подпроцесс не переносится и вы не хотите устанавливать пакет, чтобы делать что-то настолько простое, вы также можете сделать это.
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
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())
если вам нужно немного больше данных, чем хэш, вы можете использовать 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
Если по какой-то причине у вас нет доступного git, но у вас есть репозиторий git (найдена папка .git), вы можете получить хеш фиксации из .git / fetch / Heads / [ветка]
Например, я использовал следующий быстрый и грязный фрагмент кода Python, запущенный в корне репозитория, чтобы получить идентификатор фиксации:
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]
Если вы похожи на меня:
Затем (это не будет работать в оболочке, потому что оболочка не определяет текущий путь к файлу, замените 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 какое-то время.
Здесь было обсуждение которые приводят меня к этой проблеме с переполнением стека
Так что использование кода 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()
Я назвал этот файл versioning.py и импортирую fetch_git_sha, где мне нужно, чтобы он передавал путь к файлу в качестве аргумента.
Надеюсь, это поможет некоторым из вас;)
Я столкнулся с этой проблемой и решил ее, реализовав эту функцию. https://gist.github.com/NaelsonDouglas/9bc3bfa26deec167827cb59d89
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)
git rev-parse HEAD
в командной строке. Синтаксис вывода должен быть очевиден. - person Mel Nicholson   schedule 21.02.2013