Отримайте поточний хеш git у сценарії Python


165

Я хотів би включити поточний git хеш у висновок сценарію Python (як номер версії коду, який генерував цей вихід).

Як я можу отримати доступ до поточного git-хешу в моєму сценарії Python?


7
Почніть з git rev-parse HEADкомандного рядка. Синтаксис виводу повинен бути очевидним.
Мел Нікольсон

Відповіді:


96

git describeКоманда є хорошим способом створення людини-презентабельно «номер версії» коду. З прикладів у документації:

З чимось на зразок поточного дерева git.git, я отримую:

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

тобто поточний керівник моєї "батьківської" гілки базується на v1.0.4, але оскільки у неї є кілька комірок поверх цього, опис додав кількість додаткових комітів ("14") та скорочене ім'я об'єкта для комітів себе ("2414721") наприкінці.

Зсередини Python ви можете зробити щось на кшталт наступного:

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

3
Це має недолік, що код друку версії буде порушений, якщо код коли-небудь запускається без присутності git repo. Наприклад, у виробництві. :)
JosefAssad

5
@JosefAssad: Якщо вам потрібен ідентифікатор версії у виробництві, тоді ваша процедура розгортання повинна запускати вищевказаний код, і результат повинен бути "запечений" в коді, розгорнутому для виробництва.
Грег Х'югілл

14
Зауважте, що опис git не вдасться, якщо немає тегів:fatal: No names found, cannot describe anything.
kynan,

40
git describe --alwaysповернеться до останнього комітету, якщо не знайдено тегів
Леонардо

5
@CharlieParker: git describeзазвичай потрібен принаймні один тег. Якщо у вас немає тегів, скористайтеся --alwaysопцією. Додаткову інформацію див. У документації з опису git .
Грег Х'югілл

190

Не потрібно зловживати отриманням даних з 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і вимагати від нього встановити його до того, як ваш код зробить його не портативним. Якщо ви не збираєтеся включати протоколи автоматичної установки, тоді вже це не є чистим рішенням.
користувач5359531

39
@ user5359531 Я прошу відрізнятися. GitPython забезпечує чисту реалізацію Python, абстрагуючи деталі, що залежать від платформи, і її можна встановити за допомогою стандартних інструментів пакету ( pip/ requirements.txt) на всіх платформах. Що не "чисто"?
crishoj

22
Це звичайний спосіб робити речі в Python. Якщо ОП потребують цих вимог, вони б так сказали. Ми не читачі розуму, ми не можемо передбачити подій у кожному питанні. Так лежить божевілля.
OldTinfoil

14
@ user5359531, мені незрозуміло, чому import numpy as npце можна вважати протягом усього потокового потоку, але встановлення gitpython виходить за рамки "чистого" та "портативного". Я думаю, що це, безумовно, найкраще рішення, тому що воно не винаходить колесо, приховує негарну реалізацію і не обходить хакерську відповідь git з підпроцесу.
Jblasco

7
@ user5359531 Хоча я взагалі погоджуюся, що вам не слід кидати блискучу нову бібліотеку при кожній невеликій проблемі, але ваше визначення "переносимості", здається, нехтує сучасними сценаріями, коли розробники мають повний контроль над усіма середовищами, про які говорять, що програми працюють. У 2018 році у нас є Докер-контейнери, віртуальне середовище та зображення машин (наприклад, AMI) з pipможливістю легкої установки pip. У цих сучасних сценаріях pipрішення настільки ж портативне, як і рішення «стандартної бібліотеки».
Райан

106

Цей пост містить команду, відповідь Грега містить команду підпроцесу.

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
Додайте до результату смужку (), щоб отримати це без розривів рядків :)
коник

Як би ти запустив це для git repo на певному шляху?
пкемб

2
@pkamb Використовуйте os.chdir, щоб перейти на шлях git repo, з яким вам цікаво працювати
Зак Критс

Хіба це не дасть неправильну відповідь, якщо поточна перевірена редакція не є керівником філії?
макс

7
Додайте a .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
Мені подобається це, досить чиста та без зовнішніх бібліотек
13aal,

Юдзі в відповідь забезпечує аналогічне рішення лише в одному рядку коду , який виробляє один і той же результат. Чи можете ви пояснити, чому numpyвважали за необхідне "побудувати мінімальне середовище"? (якщо припустити, що вони мали вагомі підстави)
MD004

Я щойно помітив це у їхньому репо, і вирішив додати це питання до зацікавлених людей. Я не розвиваюсь в Windows, тому не перевіряв цього, але вважав, що налаштування envдиктату необхідна для функціонування між платформ. Відповідь Юджі не відповідає, але, можливо, це працює і в UNIX, і в Windows.
ryanjdillon

Дивлячись на провину git, вони зробили це як виправлення помилок для SVN 11 років тому: github.com/numpy/numpy/commit/… Можливо, виправлення помилок більше не потрібно для git.
геть

@ 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 / не знайдено, але поточний ідентифікатор фіксації знайдений у "пакується-refs".
am9417

7

Ось більш повна версія відповіді Грега :

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

Або, якщо скрипт викликається з-за меж repo:

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

1
Замість того , щоб використовувати os.chdir, то cwd=аргумент може бути використаний в check_outputтимчасові зміни в робочому каталозі перед виконанням.
Марк

0

Якщо у вас з якихось причин немає git, але у вас є git repo (папка .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]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.