Verkrijg de huidige Git Hash in een Python-script

Ik wil graag de huidige Git Hash opnemen in de uitvoer van een Python-script (als een -versienummer van de code die die uitvoer heeft gegenereerd).

Hoe kan ik toegang krijgen tot de huidige Git Hash in My Python Script?


Antwoord 1, Autoriteit 100%

Niet nodig om te hacken rond het krijgen van gegevens van de gitcommando zelf. Gitpython is een erg leuke manier om dit en veel andere gitDingen. Het heeft zelfs “beste inspanning” -ondersteuning voor Windows.

Na pip install gitpythonU kunt

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

iets om te overwegen bij het gebruik van deze bibliotheek. Het volgende is afkomstig van gitpython.readthedocs.io

Lekkage van systeembronnen

Gitpython is niet geschikt voor langlopende processen (zoals daemons) omdat het de neiging heeft om systeembronnen te lekken. Het is geschreven in een tijd waarin de vernietigers (zoals geïmplementeerd in de __del__-methode) nog steeds determistisch liepen.

Als u het nog steeds in een dergelijke context wilt gebruiken, wilt u de CENTOBEBASE zoeken voor __del__implementaties en noem deze zelf wanneer u fit ziet.

Nog een manier die een goede opruimen van middelen verzekeren is om Gitpython te factoreren in een afzonderlijk proces dat periodiek kan worden gevallen


Antwoord 2, Autoriteit 53%

Dit bericht bevat de opdracht, GREG’s antwoord bevat de subprocess-opdracht.

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

Bij het uitvoeren

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

U krijgt uitvoer:

fd1cd173fc834f62fa7db3034efc5b8e0f3b43fe
fd1cd17

Antwoord 3, Autoriteit 44%

De git describecommando is een goede manier om een ​​mens-presentabel “versienummer” van de code te maken. Van de voorbeelden in de documentatie:

met iets als git.git huidige boom, ik krijg:

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

d.w.z. De huidige kop van mijn “ouder” -tak is gebaseerd op v1.0.4, maar aangezien het een paar commits heeft bovendien, heeft beschrijf het aantal extra commits toegevoegd (“14”) en een verkorte objectnaam voor de commit zelf (“2414721”) aan het einde.

Vanuit Binnen Python kun je iets doen als het volgende:

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

Antwoord 4, Autoriteit 6%

numpyheeft een leuke uitziend Multi-platform routine in zijn 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

Antwoord 5, autoriteit 6%

Hier is een completere versie van Gregs antwoord:

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

Of, als het script van buiten de repo wordt aangeroepen:

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

Of, als het script van buiten de repo wordt aangeroepen en je houdt van pathlib:

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

Antwoord 6, autoriteit 4%

Als het subproces niet overdraagbaar is en u geen pakket wilt installeren om zoiets eenvoudigs te doen, kunt u dit ook doen.

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

Ik heb dit alleen getest op mijn repo’s, maar het lijkt redelijk consistent te werken.


Antwoord 7, autoriteit 2%

Dit is een verbetering van het antwoord van Yuji ‘Tomita’ Tomita.

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

Antwoord 8, autoriteit 2%

als je wat meer gegevens wilt dan de hash, kun je git-loggebruiken:

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

voor een volledige lijst met opmaakopties – bekijk git log --help


Antwoord 9

Als je git om de een of andere reden niet beschikbaar hebt, maar je hebt de git repo (.git map is gevonden), kun je de commit-hash ophalen van .git/fetch/heads/[branch]

Ik heb bijvoorbeeld een volgend snel-en-vuil Python-fragment gebruikt dat in de root van de repository is uitgevoerd om de commit-ID te krijgen:

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]

Antwoord 10

Als je op mij lijkt:

  • Multiplatform, dus subproces kan op een dag crashen
  • Python 2.7 gebruiken, dus GitPython niet beschikbaar
  • Wil Numpy daar niet alleen voor gebruiken
  • Sentry al in gebruik (oude afgeschreven versie: raven)

Vervolgens (dit werkt niet op shell omdat shell het huidige bestandspad niet detecteert, vervang BASE_DIR door uw huidige bestandspad):

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

Dat is het.

Ik was op zoek naar een andere oplossing omdat ik naar sentry_sdk wilde migreren en raven wilde verlaten, maar misschien willen sommigen van jullie raven nog een tijdje blijven gebruiken.

Hier was de discussie die me bij dit stackoverflow-probleem bracht

Dus het gebruik van de code van raaf zonder raaf is ook mogelijk (zie discussie):

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

Ik heb dit bestand versioning.py genoemd en ik importeer “fetch_git_sha” waar ik het bestandspad als argument moet doorgeven.

Hopelijk zal het sommigen van jullie helpen 😉


Antwoord 11

Ik kwam dit probleem tegen en loste het op door deze functie te implementeren.
https://gist.github.com/NaelsonDouglas/9bc3bfa26deec7827cb87816cad88d59

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)

Antwoord 12

Ik had een probleem vergelijkbaar met de OP, maar in mijn geval lever ik de broncode aan mijn klant als een zipbestand en hoewel ik weet dat python is geïnstalleerd, kan ik niet aannemen dat ze git zullen hebben. Aangezien de OP zijn besturingssysteem niet heeft gespecificeerd en als hij git heeft geïnstalleerd, denk ik dat ik hier kan bijdragen.

Om alleen de hash van de commit te krijgen, Naelson Douglas’s antwoordwas perfect, maar om de tagnaam te hebben, gebruik het dulwichpython-pakket. Het is een vereenvoudigde git-client in python.

Na het installeren van het pakket met pip install dulwich --global-option="--pure"kan men het volgende doen:

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)

Ik heb deze code zojuist in één repository hier uitgevoerd en het toonde de uitvoer v0.1.2-1-gfb41223, vergelijkbaar met wat is geretourneerd door git description, wat betekent dat ik 1commit na de tag v0.1.2en de 7-cijferige hash van de commit is fb41223.

Het heeft enkele beperkingen: momenteel heeft het geen optie om te laten zien of een repository vies is en het geeft altijd een 7-cijferige hash weer, maar het is niet nodig om git te hebben geïnstalleerd, dus men kan de afweging maken.

Bewerken:in geval van fouten in het commando pip installvanwege de optie --pure(het probleem wordt uitgelegd hier), kies een van de twee mogelijke oplossingen:

  1. Installeer eerst de afhankelijkheden van het Dulwich-pakket:
    pip install urllib3 certifi && pip install dulwich --global-option="--pure"
  2. Installeer zonder de optie pure: pip install dulwich. Dit zal een aantal platformafhankelijke bestanden op uw systeem installeren, maar het zal de prestaties van het pakket verbeteren.

Other episodes