Nhận băm git hiện tại trong tập lệnh Python


164

Tôi muốn bao gồm hàm git hiện tại trong đầu ra của tập lệnh Python (dưới dạng số phiên bản của mã đã tạo đầu ra đó).

Làm cách nào tôi có thể truy cập hàm git hiện tại trong tập lệnh Python của mình?


7
Bắt đầu với git rev-parse HEADtừ dòng lệnh. Cú pháp đầu ra phải rõ ràng.
Mel Nicholson

Câu trả lời:


96

Các git describelệnh là một cách tốt để tạo ra một "số phiên bản" con người-đoan của mã này. Từ các ví dụ trong tài liệu:

Với một cái gì đó như git.git cây hiện tại, tôi nhận được:

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

tức là người đứng đầu hiện tại của nhánh "cha mẹ" của tôi dựa trên v1.0.4, nhưng vì nó có một vài cam kết trên đó, mô tả đã thêm số lần xác nhận bổ sung ("14") và tên đối tượng viết tắt cho cam kết chính nó ("2414721") ở cuối.

Từ bên trong Python, bạn có thể làm một cái gì đó như sau:

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

3
Điều này có nhược điểm là mã in phiên bản sẽ bị hỏng nếu mã được chạy mà không có git repo. Ví dụ, trong sản xuất. :)
JosefAssad

5
@JosefAssad: Nếu bạn cần một mã định danh phiên bản trong sản xuất, thì quy trình triển khai của bạn sẽ chạy mã ở trên và kết quả sẽ được "nướng" vào mã được triển khai để sản xuất.
Greg Hewgill

14
Lưu ý rằng mô tả git sẽ thất bại nếu không có thẻ:fatal: No names found, cannot describe anything.
kynan

40
git describe --alwayssẽ chuyển sang cam kết cuối cùng nếu không tìm thấy thẻ nào
Leonardo

5
@CharlieParker: git describethường yêu cầu ít nhất một thẻ. Nếu bạn không có bất kỳ thẻ nào, hãy sử dụng --alwaystùy chọn. Xem tài liệu mô tả git để biết thêm thông tin.
Greg Hewgill

189

Không cần phải hack xung quanh để lấy dữ liệu từ gitlệnh. GitPython là một cách rất hay để làm điều này và rất nhiều gitthứ khác . Nó thậm chí còn có hỗ trợ "nỗ lực tốt nhất" cho Windows.

Sau khi pip install gitpythonbạn có thể làm

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

9
@crishoj Không chắc chắn làm thế nào bạn có thể gọi nó là xách tay khi điều này xảy ra : ImportError: No module named gitpython. Bạn không thể dựa vào người dùng cuối đã gitpythoncài đặt và yêu cầu họ cài đặt nó trước khi mã của bạn hoạt động khiến nó không thể di động được. Trừ khi bạn sẽ bao gồm các giao thức cài đặt tự động, tại thời điểm đó, nó không còn là một giải pháp sạch.
dùng5359531

39
@ user5359531 Tôi xin khác. GitPython cung cấp một triển khai Python thuần túy, trừu tượng hóa các chi tiết dành riêng cho nền tảng và có thể cài đặt bằng các công cụ gói tiêu chuẩn ( pip/ requirements.txt) trên tất cả các nền tảng. Cái gì không "sạch"?
crishoj

22
Đây là cách bình thường để làm mọi thứ trong Python. Nếu OP cần những yêu cầu đó, thì họ đã nói như vậy. Chúng tôi không phải là những người đọc ý nghĩ, chúng tôi không thể dự đoán mọi tình huống trong mỗi câu hỏi. Đó là cách nói dối điên rồ.
OldTinfoil

14
@ user5359531, tôi không rõ tại sao import numpy as npcó thể được giả sử trong toàn bộ stackoverflow nhưng cài đặt gitpython vượt quá 'sạch' và 'di động'. Tôi nghĩ rằng đây là giải pháp tốt nhất, bởi vì nó không phát minh lại bánh xe, che giấu việc thực hiện xấu xí và không đi vòng quanh việc hack câu trả lời của git từ quy trình con.
Jblasco

7
@ user5359531 Mặc dù tôi đồng ý rằng bạn không nên ném một thư viện mới sáng bóng vào mọi vấn đề nhỏ, định nghĩa về "tính di động" của bạn dường như bỏ qua các kịch bản hiện đại, nơi các nhà phát triển có toàn quyền kiểm soát tất cả các môi trường mà các ứng dụng chạy trong. Năm 2018 chúng tôi có Docker container, môi trường ảo và hình ảnh máy (ví dụ AMI) với piphoặc khả năng dễ dàng cài đặt pip. Trong các kịch bản hiện đại này, một pipgiải pháp cũng dễ mang theo như một giải pháp "thư viện chuẩn".
Ryan

106

Bài đăng này chứa lệnh, câu trả lời của Greg chứa lệnh quy trình con.

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
Thêm một dải () vào kết quả để có được điều này mà không ngắt dòng :)
châu chấu

Làm thế nào bạn sẽ chạy này cho một repo git tại một đường dẫn cụ thể?
pkamb

2
@pkamb Sử dụng os.chdir để cd vào đường dẫn của git repo bạn đang quan tâm đến việc hợp tác với
Zac Crites

Điều đó sẽ không đưa ra câu trả lời sai nếu bản sửa đổi hiện đang được kiểm tra không phải là người đứng đầu chi nhánh?
tối đa

7
Thêm một .decode('ascii').strip()để giải mã chuỗi nhị phân (và loại bỏ ngắt dòng).
pfm

13

numpycó một thói quen đa nền tảng đẹp mắt trong 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
Tôi thích cái này, khá sạch sẽ và không có thư viện bên ngoài
13aal

Câu trả lời của Yuji cung cấp một giải pháp tương tự chỉ trong một dòng mã tạo ra kết quả tương tự. Bạn có thể giải thích tại sao numpythấy cần thiết phải "xây dựng một môi trường tối thiểu" không? (giả sử họ có lý do chính đáng)
MD004

Tôi chỉ nhận thấy điều này trong repo của họ, và quyết định thêm nó vào câu hỏi này cho những người quan tâm. Tôi không phát triển trong Windows, vì vậy tôi đã không thử nghiệm điều này, nhưng tôi đã cho rằng việc thiết lập chính tả envlà cần thiết cho chức năng đa nền tảng. Câu trả lời của Yuji thì không, nhưng có lẽ nó hoạt động trên cả UNIX và Windows.
ryanjdillon

Nhìn vào sự đổ lỗi của git, họ đã làm điều này như là một sửa lỗi cho SVN 11 năm trước: github.com/numpy/numpy/commit/ trộm Có thể sửa lỗi không còn cần thiết cho git.
gparent

@ MD004 @ryanjdillon Họ đặt miền địa phương để nó .decode('ascii')hoạt động - nếu không thì mã hóa không xác định.
z0r

7

Nếu quy trình con không thể di động và bạn không muốn cài đặt một gói để làm điều gì đó đơn giản, bạn cũng có thể làm điều này.

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

Tôi chỉ thử nghiệm điều này trên repos của mình nhưng có vẻ như nó hoạt động khá ổn định.


Đôi khi / refs / không được tìm thấy, nhưng id xác nhận hiện tại được tìm thấy trong "pack-refs".
am9417

7

Đây là phiên bản hoàn chỉnh hơn của câu trả lời của Greg :

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

Hoặc, nếu tập lệnh đang được gọi từ bên ngoài repo:

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

1
Thay vì sử dụng os.chdir, cwd=arg có thể được sử dụng check_outputđể thay đổi tạm thời thư mục làm việc trước khi thực thi.
Marc

0

Nếu bạn không có sẵn git vì một số lý do, nhưng bạn có git repo (thư mục .git được tìm thấy), bạn có thể tìm nạp hàm băm từ .git / fetch / Heads / [Branch]

Ví dụ: tôi đã sử dụng đoạn mã Python nhanh và bẩn sau đây chạy ở thư mục gốc của kho lưu trữ để lấy id xác nhận:

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]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.