Pobierz aktualny skrót git w skrypcie Pythona

165

Chciałbym dołączyć bieżący skrót git do danych wyjściowych skryptu w języku Python (jako numer wersji kodu, który wygenerował dane wyjściowe).

Jak mogę uzyskać dostęp do aktualnego skrótu git w moim skrypcie Python?

Zwycięzca
źródło
7
Zacznij git rev-parse HEADod z wiersza poleceń. Składnia wyjściowa powinna być oczywista.
Mel Nicholson,

Odpowiedzi:

96

git describePolecenia jest dobrym sposobem na stworzenie „numeru wersji” człowieka-reprezentacyjny kodu. Z przykładów w dokumentacji:

W przypadku aktualnego drzewa git.git otrzymuję:

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

tzn. obecna nagłówek mojej gałęzi "macierzystej" jest oparta na wersji 1.0.4, ale ponieważ ma kilka zatwierdzeń, opis dodał liczbę dodatkowych zatwierdzeń ("14") i skróconą nazwę obiektu dla zatwierdzenia („2414721”) na końcu.

Z poziomu Pythona możesz wykonać następujące czynności:

import subprocess
label = subprocess.check_output(["git", "describe"]).strip()
Greg Hewgill
źródło
3
Ma to tę wadę, że kod drukowania wersji zostanie uszkodzony, jeśli kod zostanie kiedykolwiek uruchomiony bez obecnego repozytorium git. Na przykład w produkcji. :)
JosefAssad
5
@JosefAssad: Jeśli potrzebujesz identyfikatora wersji w środowisku produkcyjnym, procedura wdrażania powinna uruchomić powyższy kod, a wynik powinien zostać „zapieczętowany” w kodzie wdrożonym do produkcji.
Greg Hewgill
14
Zauważ, że opisywanie przez git zakończy się niepowodzeniem, jeśli nie ma tagów:fatal: No names found, cannot describe anything.
kynan
40
git describe --alwayspowróci do ostatniego zatwierdzenia, jeśli nie zostaną znalezione żadne tagi
Leonardo
5
@CharlieParker: git describezwykle wymaga co najmniej jednego tagu. Jeśli nie masz żadnych tagów, użyj --alwaysopcji. Zobacz dokumentację opisującą git, aby uzyskać więcej informacji.
Greg Hewgill,
190

Nie ma potrzeby samodzielnego pobierania danych z gitpolecenia. GitPython to bardzo fajny sposób na zrobienie tego i wielu innych gitrzeczy. Obsługuje nawet system Windows „do najlepszych starań”.

Po tym, jak pip install gitpythonmożesz to zrobić

import git
repo = git.Repo(search_parent_directories=True)
sha = repo.head.object.hexsha
kqw
źródło
9
@crishoj Nie wiem, jak można nazwać to przenośny, kiedy to nastąpi: ImportError: No module named gitpython. Nie możesz polegać na gitpythonzainstalowaniu przez użytkownika końcowego i wymaganie od niego zainstalowania go, zanim kod zacznie działać, sprawia, że ​​nie jest on przenośny. Chyba że zamierzasz dołączyć protokoły automatycznej instalacji, w którym to momencie nie jest to już czyste rozwiązanie.
user5359531
39
@ user5359531 Błagam się różnić. GitPython zapewnia czystą implementację Pythona, odrywając szczegóły specyficzne dla platformy i można go zainstalować za pomocą standardowych narzędzi pakietowych ( pip/ requirements.txt) na wszystkich platformach. Co nie jest „czyste”?
crishoj
22
To jest normalny sposób robienia rzeczy w Pythonie. Jeśli PO potrzebuje tych wymagań, to by tak powiedzieli. Nie czytamy w myślach, nie możemy przewidzieć każdej ewentualności w każdym pytaniu. W ten sposób leży szaleństwo.
OldTinfoil
14
@ user5359531, nie jestem pewien, dlaczego import numpy as npmożna założyć, że w całym stosie przepełnienia stosu, ale instalacja gitpythona wykracza poza „czysty” i „przenośny”. Myślę, że jest to zdecydowanie najlepsze rozwiązanie, ponieważ nie wymyśla koła na nowo, ukrywa brzydką implementację i nie hakuje odpowiedzi gita z podprocesu.
Jblasco,
7
@ user5359531 Chociaż ogólnie zgadzam się, że nie powinieneś rzucać nowej, błyszczącej biblioteki przy każdym małym problemie, twoja definicja „przenośności” wydaje się pomijać współczesne scenariusze, w których programiści mają pełną kontrolę nad wszystkimi środowiskami, w których działają aplikacje. W 2018 roku mamy Kontenery Docker, środowiska wirtualne i obrazy maszyn (np. AMI) z pipmożliwością łatwej instalacji pip. W tych nowoczesnych scenariuszach piprozwiązanie jest tak samo przenośne, jak rozwiązanie „biblioteki standardowej”.
Ryan
106

Ten post zawiera polecenie, odpowiedź Grega zawiera polecenie podprocesu.

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'])
Yuji „Tomita” Tomita
źródło
32
Dodaj pasek () do wyniku, aby uzyskać ten wynik bez łamania linii :)
konik polny
Jak można to uruchomić dla repozytorium git na określonej ścieżce?
pkamb
2
@pkamb Użyj os.chdir, aby cd do ścieżki repozytorium git, z którym chcesz pracować
Zac Crites
Czy nie dałoby to złej odpowiedzi, gdyby aktualnie wyewidencjonowana wersja nie jest nagłówkiem gałęzi?
maksymalnie
7
Dodaj a, .decode('ascii').strip()aby zdekodować ciąg binarny (i usuń podział wiersza).
pfm
13

numpyma ładnie wyglądającą wieloplatformową procedurę w swoim 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
ryanjdillon
źródło
2
Podoba mi się to, całkiem czyste i żadnych zewnętrznych bibliotek
sierpnia
Odpowiedź Yuji dostarcza podobnego rozwiązania tylko w jednej linii kodu, która daje ten sam wynik. Czy możesz wyjaśnić, dlaczego numpyuznano za konieczne „skonstruowanie minimalnego środowiska”? (zakładając, że mieli ku temu dobry powód)
MD004
Właśnie zauważyłem to w ich repozytorium i postanowiłem dodać to do tego pytania dla zainteresowanych osób. Nie rozwijam się w systemie Windows, więc nie testowałem tego, ale założyłem, że skonfigurowanie envdyktu jest konieczne dla funkcjonalności międzyplatformowej. Odpowiedź Yuji nie działa, ale być może to działa zarówno w systemie UNIX, jak i Windows.
ryanjdillon,
Patrząc na winę gita, zrobili to jako naprawę błędu dla SVN 11 lat temu: github.com/numpy/numpy/commit/… Możliwe, że naprawa błędu nie jest już konieczna dla git.
gparent
@ MD004 @ryanjdillon Ustawiają ustawienia regionalne tak, aby działały .decode('ascii')- w przeciwnym razie kodowanie jest nieznane.
z0r
7

Jeśli podproces nie jest przenośny i nie chcesz instalować pakietu, aby zrobić coś tak prostego, możesz również to zrobić.

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

Testowałem to tylko na moich repozytoriach, ale wydaje się, że działa dość konsekwentnie.

kagronick
źródło
Czasami / refs / nie jest znaleziony, ale bieżący identyfikator zatwierdzenia znajduje się w „spakowanych referencjach”.
am9417
7

Oto pełniejsza wersja odpowiedzi Grega :

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

Lub, jeśli skrypt jest wywoływany spoza repozytorium:

import subprocess, os
os.chdir(os.path.dirname(__file__))
print(subprocess.check_output(["git", "describe", "--always"]).strip().decode())
AndyP
źródło
1
Zamiast używać os.chdirThe cwd=Arg może być stosowany w check_outputzmian tymczasowych katalogu roboczego przed wykonaniem.
Marc
0

Jeśli z jakiegoś powodu nie masz dostępnego gita, ale masz repozytorium git (znaleziono folder .git), możesz pobrać skrót zatwierdzenia z .git / fetch / heads / [branch]

Na przykład użyłem następującego szybkiego i nieczytelnego fragmentu kodu Pythona uruchomionego w katalogu głównym repozytorium, aby uzyskać identyfikator zatwierdzenia:

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]
am9417
źródło