파이썬 스크립트에서 현재 자식 해시를 얻으십시오


164

파이썬 스크립트의 출력에 현재 git hash를 포함시키고 싶습니다 ( 해당 출력을 생성 한 코드 의 버전 번호 ).

파이썬 스크립트에서 현재 git hash에 어떻게 액세스 할 수 있습니까?


7
git rev-parse HEAD명령 행에서 시작하십시오 . 출력 구문은 분명해야합니다.
Mel Nicholson 2013

답변:


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 2019

5
@JosefAssad : 프로덕션에서 버전 식별자가 필요한 경우 배포 절차에서 위 코드를 실행해야하며 결과는 프로덕션에 배포 된 코드에 "구워 져야"합니다.
Greg Hewgill

14
git describe는 태그가 없으면 실패합니다 :fatal: No names found, cannot describe anything.
kynan

40
git describe --always태그를 찾지 못하면 마지막 커밋으로 대체됩니다.
Leonardo

5
@CharlieParker : git describe일반적으로 하나 이상의 태그가 필요합니다. 태그가 없으면 --always옵션을 사용하십시오 . 자세한 내용은 git describe 설명서 를 참조하십시오.
Greg Hewgill

189

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설치 한 최종 사용자에게 의존 할 수 없으며 코드가 작동하기 전에 설치하도록 요구하면 이식성이 없습니다. 자동 설치 프로토콜을 포함시키지 않으면 더 이상 깨끗한 솔루션이 아닙니다.
user5359531 2016 년

39
@ user5359531 나는달라고 간청한다. GitPython은 플랫폼 별 세부 정보를 추상화하여 순수한 Python 구현을 제공하며 모든 플랫폼에서 표준 패키지 도구 ( pip/ requirements.txt)를 사용하여 설치할 수 있습니다. "깨끗하지 않은"무엇입니까?
crishoj

22
이것은 파이썬에서 일을하는 일반적인 방법입니다. OP가 이러한 요구 사항을 필요로한다면 그렇게했을 것입니다. 우리는 마음을 읽는 사람이 아니며 각 질문에서 모든 사건을 예측할 수는 없습니다. 그 방법은 광기입니다.
OldTinfoil

14
@ user5359531, 왜 import numpy as np전체 스택 오버 플로우에서 가정 할 수 있는지 확실하지 않지만 gitpython 설치는 '깨끗한'및 '휴대용'을 넘어서는 것입니다. 휠을 재발 명하지 않고 추악한 구현을 숨기고 하위 프로세스에서 git의 대답을 해킹하지 않기 때문에 이것이 가장 좋은 해결책이라고 생각합니다.
Jblasco

7
@ user5359531 모든 작은 문제에서 빛나는 새 라이브러리를 포기해서는 안된다는 데 동의하지만, "휴대 성"에 대한 정의는 개발자가 애플리케이션이 실행되는 모든 환경을 완전히 제어 할 수있는 최신 시나리오를 무시하는 것으로 보입니다. Docker 컨테이너, 가상 환경 및 머신 이미지 (예 : AMI) pip또는 쉽게 설치할 수있는 기능 pip. 이러한 현대 시나리오에서 pip솔루션은 "표준 라이브러리"솔루션만큼 이식성이 뛰어납니다.
Ryan

106

이 게시물 에는 명령 포함되어 있으며 Greg의 답변 에는 하위 프로세스 명령이 포함되어 있습니다.

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
줄 바꿈없이 이것을 얻으려면 결과에 strip ()을 추가하십시오 :)
메뚜기

특정 경로에서 git repo에 대해 이것을 어떻게 실행 하시겠습니까?
pkamb 2019

2
@pkamb os.chdir을 사용하여 작업하고 싶은 git repo의 경로로 CD를 보내십시오
Zac Crites

현재 체크 아웃 된 개정판이 지점장이 아닌 경우 잘못된 대답을하지 않습니까?
최대

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

Yuji의 대답 은 동일한 결과를 생성하는 한 줄의 코드에서만 유사한 솔루션을 제공합니다. numpy"최소 환경 구축"이 필요한 이유를 설명 할 수 있습니까 ? (그들이 합당한 이유가 있다고 가정)
MD004

방금 레포에서 이것을 발견하고 관심있는 사람들을 위해이 질문에 추가하기로 결정했습니다. Windows에서 개발하지 않았으므로 이것을 테스트하지는 않았지만 env크로스 플랫폼 기능에 dict 설정 이 필요 하다고 가정했습니다 . Yuji의 대답은 그렇지 않지만 아마도 UNIX와 Windows 모두에서 작동합니다.
ryanjdillon

git blame을 살펴보면, 11 년 전 SVN의 버그 수정으로이 작업을 수행했습니다. github.com/numpy/numpy/commit/… git에 버그 수정이 더 이상 필요하지 않을 수 있습니다.
gparent

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

나는 이것을 repos에서만 테스트했지만 꽤 일관되게 작동하는 것 같습니다.


때때로 / refs /를 찾을 수 없지만 현재 커밋 ID는 "packed-refs"에 있습니다.
am9417

6

다음은 Greg의 답변에 대한 완전한 버전입니다 .

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

1
대신 사용하는 os.chdircwd=인수를 사용할 수 있습니다 check_output실행하기 전에 일시적 변화에 작업 디렉토리.
Marc

0

어떤 이유로 git을 사용할 수 없지만 git repo (.git 폴더가 있음)가있는 경우 .git / fetch / heads / [branch]에서 커밋 해시를 가져올 수 있습니다

예를 들어, 저장소 루트에서 다음과 같은 빠르고 더러운 Python 스 니펫을 사용하여 커밋 ID를 얻었습니다.

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]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.