Holen Sie sich den aktuellen Git-Hash in einem Python-Skript

164

Ich möchte den aktuellen Git-Hash in die Ausgabe eines Python-Skripts aufnehmen (als Versionsnummer des Codes, der diese Ausgabe generiert hat).

Wie kann ich in meinem Python-Skript auf den aktuellen Git-Hash zugreifen?

Sieger
quelle
7
Beginnen Sie mit git rev-parse HEADüber die Befehlszeile. Die Ausgabesyntax sollte offensichtlich sein.
Mel Nicholson

Antworten:

95

Der git describeBefehl ist eine gute Möglichkeit, eine vom Menschen präsentierbare "Versionsnummer" des Codes zu erstellen. Aus den Beispielen in der Dokumentation:

Mit so etwas wie git.git aktueller Baum bekomme ich:

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

Das heißt, der aktuelle Kopf meines "übergeordneten" Zweigs basiert auf Version 1.0.4. Da er jedoch einige Commits enthält, hat description die Anzahl der zusätzlichen Commits ("14") und einen abgekürzten Objektnamen für das Commit hinzugefügt selbst ("2414721") am Ende.

In Python können Sie Folgendes tun:

import subprocess
label = subprocess.check_output(["git", "describe"]).strip()
Greg Hewgill
quelle
3
Dies hat den Nachteil, dass der Versionsdruckcode beschädigt wird, wenn der Code jemals ohne das vorhandene Git-Repo ausgeführt wird. Zum Beispiel in der Produktion. :)
JosefAssad
5
@JosefAssad: Wenn Sie in der Produktion eine Versionskennung benötigen, sollte Ihre Bereitstellungsprozedur den obigen Code ausführen und das Ergebnis in den für die Produktion bereitgestellten Code "eingebrannt" werden.
Greg Hewgill
14
Beachten Sie, dass Git Beschreibung fehlschlägt, wenn keine Tags vorhanden sind:fatal: No names found, cannot describe anything.
Kynan
39
git describe --alwayswird auf das letzte Commit zurückgreifen, wenn keine Tags gefunden werden
Leonardo
5
@CharlieParker: git describeNormalerweise ist mindestens ein Tag erforderlich. Wenn Sie keine Tags haben, verwenden Sie die --alwaysOption. Weitere Informationen finden Sie in der Dokumentation zur Git-Beschreibung .
Greg Hewgill
189

Sie müssen nicht herumhacken, um selbst Daten vom gitBefehl abzurufen. GitPython ist eine sehr schöne Möglichkeit, dies und viele andere Dinge zu tun git. Es bietet sogar "Best Effort" -Unterstützung für Windows.

Nachdem pip install gitpythonSie tun können

import git
repo = git.Repo(search_parent_directories=True)
sha = repo.head.object.hexsha
kqw
quelle
9
@crishoj Ich bin mir nicht sicher, wie ich es in diesem Fall als portabel bezeichnen kann : ImportError: No module named gitpython. Sie können sich nicht darauf verlassen, dass der Endbenutzer ihn gitpythoninstalliert hat. Wenn er ihn installieren muss, bevor Ihr Code funktioniert, ist er nicht portierbar. Sofern Sie keine automatischen Installationsprotokolle einschließen, ist dies keine saubere Lösung mehr.
user5359531
39
@ user5359531 Ich bitte um Unterschied. GitPython bietet eine reine Python-Implementierung, die plattformspezifische Details abstrahiert, und kann mit Standardpaket-Tools ( pip/ requirements.txt) auf allen Plattformen installiert werden . Was ist nicht "sauber"?
Crishoj
22
Dies ist die normale Vorgehensweise in Python. Wenn das OP diese Anforderungen benötigt, hätten sie dies gesagt. Wir sind keine Gedankenleser, wir können nicht jede Eventualität in jeder Frage vorhersagen. Auf diese Weise liegt der Wahnsinn.
OldTinfoil
14
@ user5359531, ich bin mir nicht sicher, warum import numpy as npwährend des gesamten Stackoverflow davon ausgegangen werden kann, aber die Installation von Gitpython geht über "sauber" und "portabel" hinaus. Ich denke, dies ist bei weitem die beste Lösung, da es das Rad nicht neu erfindet, die hässliche Implementierung verbirgt und nicht darum herumgeht, die Antwort von git aus dem Teilprozess zu hacken.
Jblasco
7
@ user5359531 Obwohl ich im Allgemeinen der Meinung bin, dass Sie nicht bei jedem kleinen Problem eine glänzende neue Bibliothek erstellen sollten, scheint Ihre Definition von "Portabilität" moderne Szenarien zu vernachlässigen, in denen Entwickler die volle Kontrolle über alle Umgebungen haben, in denen Anwendungen ausgeführt werden. 2018 haben wir Docker-Container, virtuelle Umgebungen und Maschinenabbilder (z. B. AMIs) mit pipoder die Möglichkeit zur einfachen Installation pip. In diesen modernen Szenarien ist eine pipLösung genauso portabel wie eine "Standardbibliothek".
Ryan
105

Dieser Beitrag enthält den Befehl, Gregs Antwort enthält den Unterprozessbefehl.

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
quelle
32
Fügen Sie einen Streifen () zum Ergebnis hinzu, um dies ohne Zeilenumbrüche zu erhalten :)
Grashüpfer
Wie würden Sie dies für ein Git-Repo auf einem bestimmten Pfad ausführen?
Pkamb
2
@pkamb Verwenden Sie os.chdir, um auf den Pfad des Git-Repo zu gelangen, mit dem Sie arbeiten
möchten
Würde das nicht die falsche Antwort geben, wenn die aktuell ausgecheckte Revision nicht der Zweigstellenleiter ist?
Max
7
Fügen Sie a hinzu .decode('ascii').strip(), um die Binärzeichenfolge zu dekodieren (und entfernen Sie den Zeilenumbruch).
Pfm
13

numpyhat eine gut aussehende Multi-Plattform-Routine in seiner 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
quelle
2
Ich mag das, ziemlich sauber und keine externen Bibliotheken
13aal
Yujis Antwort bietet eine ähnliche Lösung in nur einer Codezeile, die das gleiche Ergebnis liefert. Können Sie erklären, warum numpyes notwendig war, "eine minimale Umgebung zu konstruieren"? (vorausgesetzt, sie hatten guten Grund dazu)
MD004
Ich habe dies gerade in ihrem Repo bemerkt und beschlossen, es dieser Frage für interessierte Leute hinzuzufügen. Ich entwickle nicht unter Windows, daher habe ich dies nicht getestet, aber ich war davon ausgegangen, dass das Einrichten des envDiktats für die plattformübergreifende Funktionalität erforderlich ist. Yujis Antwort nicht, aber vielleicht funktioniert das sowohl unter UNIX als auch unter Windows.
Ryanjdillon
Mit Blick auf die Git-Schuld haben sie dies vor 11 Jahren als Fehlerbehebung für SVN getan: github.com/numpy/numpy/commit/… Möglicherweise ist die Fehlerbehebung für Git nicht mehr erforderlich.
Elternteil
@ MD004 @ryanjdillon Sie stellen das Gebietsschema so ein, dass es .decode('ascii')funktioniert - ansonsten ist die Codierung unbekannt.
26.
7

Wenn der Unterprozess nicht portierbar ist und Sie kein Paket installieren möchten, um etwas so Einfaches zu tun, können Sie dies auch tun.

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

Ich habe dies nur auf meinen Repos getestet, aber es scheint ziemlich konstant zu funktionieren.

Kagronick
quelle
Manchmal wird das / refs / nicht gefunden, aber die aktuelle Commit-ID befindet sich in "gepackten Refs".
am9417
6

Hier ist eine vollständigere Version von Gregs Antwort :

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

Oder wenn das Skript von außerhalb des Repos aufgerufen wird:

import subprocess, os
os.chdir(os.path.dirname(__file__))
print(subprocess.check_output(["git", "describe", "--always"]).strip().decode())
AndyP
quelle
1
Anstatt zu verwenden os.chdir, kann das cwd=Argument verwendet werden, check_outputum das Arbeitsverzeichnis vor der Ausführung vorübergehend zu ändern.
Marc
0

Wenn Sie aus irgendeinem Grund kein Git zur Verfügung haben, aber das Git-Repo haben (der Ordner .git wurde gefunden), können Sie den Commit-Hash aus .git / fetch / Heads / [branch] abrufen.

Zum Beispiel habe ich ein folgendes schnelles und schmutziges Python-Snippet verwendet, das im Repository-Stammverzeichnis ausgeführt wird, um die Commit-ID abzurufen:

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
quelle