Pythonスクリプトで現在のgitハッシュを取得する


164

現在のgitハッシュをPythonスクリプトの出力に含めたい(その出力を生成したコードのバージョン番号として)。

Pythonスクリプトで現在のgitハッシュにアクセスするにはどうすればよいですか?


7
git rev-parse HEADコマンドラインから始めます。出力構文は明白でなければなりません。
メルニコルソン

回答:


96

このgit describeコマンドは、人間が提示できるコードの「バージョン番号」を作成するための優れた方法です。ドキュメントの例から:

git.git current treeのようなもので、私は得ます:

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

つまり、私の「親」ブランチの現在のヘッドはv1.0.4に基づいていますが、その上にいくつかのコミットがあるため、describeは追加のコミット( "14")の数とそのコミットの省略されたオブジェクト名を追加しました最後にそれ自体(「2414721」)。

Python内から、次のようなことができます。

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

3
これには、gitリポジトリが存在しない状態でコードを実行すると、バージョン印刷コードが破損するという欠点があります。たとえば、生産で。:)
JosefAssad 2013

5
@JosefAssad:本番環境でバージョン識別子が必要な場合は、デプロイメントプロシージャで上記のコードを実行し、結果を本番環境にデプロイされたコードに「ベイクイン」する必要があります。
グレッグヒューギル

14
本タグがない場合Gitは説明することを注記は失敗しますfatal: No names found, cannot describe anything.
kynan

40
git describe --alwaysタグが見つからない場合、最後のコミットにフォールバックします
Leonardo

5
@CharlieParker:git describe通常、少なくとも1つのタグが必要です。タグがない場合は、--alwaysオプションを使用します。詳細については、git describeのドキュメントを参照してください。
グレッグヒューギル2016

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 2017年

39
@ user5359531違いますね。GitPythonは純粋なPython実装を提供し、プラットフォーム固有の詳細を抽象化します。すべてのプラットフォームで標準パッケージツール(pip/ requirements.txt)を使用してインストールできます。「クリーン」ではないものは何ですか?
crishoj 2017年

22
これはPythonで通常行う方法です。OPがそれらの要件を必要とする場合、彼らはそのように言っていただろう。私たちはマインドリーダーではなく、各質問のすべての結果を予測することはできません。その方法は狂気です。
OldTinfoil 2018年

14
@ user5359531、import numpy as npstackoverflow全体で想定できる理由は不明ですが、gitpythonのインストールは「クリーン」で「ポータブル」ではありません。ホイールを再発明せず、醜い実装を隠蔽し、サブプロセスからのgitの回答をハッキングしないので、これは断然最良のソリューションだと思います。
Jblasco

7
@ user5359531一般的に、小さな問題ごとに光沢のある新しいライブラリを投げるべきではないことに同意しますが、「移植性」の定義は、開発者がアプリケーションを実行するすべての環境を完全に制御できる現代のシナリオを無視しているようです。2018年には、 Dockerコンテナ、仮想環境、およびマシンイメージ(AMIなど)pipまたはを簡単にインストールできる機能pip。これらの最新のシナリオでは、pipソリューションは「標準ライブラリ」ソリューションと同じくらい移植可能です。
ライアン

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 2015

2
@pkamb os.chdirを使用して、使用するgitリポジトリのパスにcdします
Zac Crites

現在チェックアウトされているリビジョンがブランチヘッドでない場合、間違った答えを返さないでしょうか?
最大

7
.decode('ascii').strip()バイナリ文字列をデコードするためにa を追加します(そして改行を削除します)。
pfm 2018年

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の答えは、同じ結果を生成する1行のコードのみで同様のソリューションを提供します。numpy「最小限の環境を構築する」ことがなぜ必要だと考えられたのか説明していただけますか?(彼らがする正当な理由があると仮定して)
MD004

私は彼らのリポジトリでこれに気づき、興味のある人のためにこの質問に追加することにしました。私はWindowsで開発していないので、これをテストしていませんenvが、クロスプラットフォーム機能にはdictのセットアップが必要だと思っていました。祐二の答えはそうではありませんが、おそらくそれはUNIXとWindowsの両方で機能します。
ryanjdillon

gitのせいで、彼らはこれを11年前のSVNのバグ修正として行いました:github.com/numpy/numpy/commit/…gitのバグ修正が不要になった可能性があります。
1

@ 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 /が見つからないことがありますが、現在のコミットIDは "packed-refs"にあります。
am9417

7

これがグレッグの答えのより完全なバージョンです:

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=argを使用しcheck_outputて、実行前に作業ディレクトリを一時的に変更できます。
Marc

0

何らかの理由でgitを使用できないが、gitリポジトリ(.gitフォルダーが見つかった)がある場合は、.git / fetch / heads / [branch]からコミットハッシュをフェッチできます。

たとえば、コミットIDを取得するために、リポジトリルートで実行される次の汚い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]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.