รับแฮช git ปัจจุบันในสคริปต์ Python


165

ฉันต้องการรวมแฮช git ปัจจุบันในผลลัพธ์ของสคริปต์ Python (เป็นหมายเลขเวอร์ชันของรหัสที่สร้างผลลัพธ์นั้น)

ฉันจะเข้าถึงแฮช git ปัจจุบันในสคริปต์ Python ของฉันได้อย่างไร


7
เริ่มต้นด้วยgit rev-parse HEADจากบรรทัดคำสั่ง ไวยากรณ์เอาต์พุตควรชัดเจน
Mel Nicholson

คำตอบ:


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
นี่เป็นข้อเสียเปรียบที่รหัสการพิมพ์เวอร์ชันจะใช้งานไม่ได้หากรหัสนั้นเคยถูกเรียกใช้โดยไม่มี repo git อยู่ ตัวอย่างเช่นในการผลิต :)
JosefAssad

5
@JosefAssad: หากคุณต้องการตัวระบุเวอร์ชันในการผลิตดังนั้นขั้นตอนการปรับใช้ของคุณควรเรียกใช้โค้ดด้านบนและผลลัพธ์ควรเป็น "baked in" กับโค้ดที่ปรับใช้กับการผลิต
Greg Hewgill

14
โปรดทราบว่าคอมไพล์อธิบายจะล้มเหลวถ้ามีไม่แท็กปัจจุบัน:fatal: No names found, cannot describe anything.
kynan

40
git describe --alwaysจะย้อนกลับไปสู่การคอมมิชชันล่าสุดหากไม่พบแท็ก
Leonardo

5
@CharlieParker: git describeโดยปกติจะต้องมีอย่างน้อยหนึ่งแท็ก หากคุณไม่มีแท็กให้ใช้--alwaysตัวเลือก ดูเอกสารอธิบาย gitสำหรับข้อมูลเพิ่มเติม
Greg Hewgill

190

ไม่ต้องแฮ็คเพื่อรับข้อมูลจาก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

39
@ user5359531 ฉันขอแตกต่างกัน GitPython ให้การใช้งาน Python อย่างแท้จริงโดยย่อรายละเอียดเฉพาะแพลตฟอร์มและสามารถติดตั้งได้โดยใช้เครื่องมือแพ็คเกจมาตรฐาน ( pip/ requirements.txt) บนทุกแพลตฟอร์ม ไม่ "สะอาด" คืออะไร
crishoj

22
นี่เป็นวิธีปกติในการทำสิ่งต่างๆใน Python หาก OP ต้องการข้อกำหนดเหล่านั้นพวกเขาก็จะพูดเช่นนั้น เราไม่ได้เป็นผู้อ่าน - ใจเราไม่สามารถคาดการณ์เหตุการณ์ในแต่ละคำถามได้ วิธีนั้นก็คือความบ้าคลั่ง
OldTinfoil

14
@ user5359531 ฉันไม่ชัดเจนว่าทำไมimport numpy as npสามารถสันนิษฐานได้ตลอดทั้ง stackoverflow แต่การติดตั้ง gitpython นั้นเกินกว่า 'สะอาด' และ 'พกพา' ฉันคิดว่านี่เป็นทางออกที่ดีที่สุดเพราะมันไม่ได้คิดค้นใหม่ล้อซ่อนการดำเนินการที่น่าเกลียดและไม่ไปแฮ็คคำตอบของ git จาก subprocess
Jblasco

7
@ user5359531 ในขณะที่ฉันเห็นด้วยโดยทั่วไปว่าคุณไม่ควรทิ้งไลบรารี่ใหม่ในทุกปัญหาเล็ก ๆ แต่นิยามของคำว่า "การพกพา" ของคุณดูเหมือนว่าจะมองข้ามสถานการณ์สมัยใหม่ที่นักพัฒนาสามารถควบคุมสภาพแวดล้อมทั้งหมด ตู้คอนเทนเนอร์หางสภาพแวดล้อมเสมือนจริงและภาพเครื่อง (เช่น AMIS) ด้วยหรือความสามารถในการติดตั้งได้อย่างง่ายดายpip pipในสถานการณ์ที่ทันสมัยเหล่านี้pipวิธีการแก้ปัญหาเป็นแบบพกพาเช่นเดียวกับโซลูชั่น "ห้องสมุดมาตรฐาน"
Ryan

106

โพสต์นี้มีคำสั่งคำตอบของ Gregประกอบด้วยคำสั่ง subprocess

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
เพิ่มแถบ () ลงในผลลัพธ์เพื่อให้ได้สิ่งนี้โดยไม่มีตัวแบ่งบรรทัด :)
ตั๊กแตน

คุณจะเรียกใช้สิ่งนี้สำหรับ repo คอมไพล์ที่เส้นทางเฉพาะได้อย่างไร
pkamb

2
@pkamb ใช้ os.chdir เพื่อ cd ไปยังเส้นทางของ repo git ที่คุณสนใจที่จะทำงานด้วย
Zac Crites

จะไม่ให้คำตอบที่ไม่ถูกต้องหากการตรวจสอบการตรวจสอบในปัจจุบันไม่ใช่หัวหน้าสาขา?
สูงสุด

7
เพิ่ม a .decode('ascii').strip()เพื่อถอดรหัสสตริงไบนารี (และลบตัวแบ่งบรรทัด)
pfm 9'18 น

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

ฉันเพิ่งสังเกตสิ่งนี้ใน repo ของพวกเขาและตัดสินใจที่จะเพิ่มลงในคำถามนี้สำหรับผู้ที่สนใจ ฉันไม่ได้พัฒนาใน Windows ดังนั้นฉันจึงไม่ได้ทดสอบสิ่งนี้ แต่ฉันคิดว่าการตั้งค่าenvdict นั้นจำเป็นสำหรับการทำงานข้ามแพลตฟอร์ม คำตอบของ Yuji ไม่ได้ แต่อาจใช้ได้กับทั้ง UNIX และ Windows
ryanjdillon

เมื่อมองไปที่ตำหนิ git พวกเขาทำสิ่งนี้เพื่อแก้ไขข้อบกพร่องของ SVN เมื่อ 11 ปีที่แล้ว: github.com/numpy/numpy/commit/ ......เป็นไปได้ว่าการแก้ไขข้อผิดพลาดนั้นไม่จำเป็นสำหรับ git อีกต่อไป
แจ่มใส

@ 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 / แต่การส่งมอบรหัสประจำตัวปัจจุบันพบได้ใน "packing-refs"
am9417

7

ต่อไปนี้เป็นคำตอบที่สมบูรณ์ของGregมากขึ้น:

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

หรือถ้าสคริปต์ถูกเรียกจากนอก repo:

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

1
แทนการใช้os.chdirการcwd=หาเรื่องสามารถนำมาใช้ในcheck_outputการเปลี่ยนแปลงชั่วคราวไดเรกทอรีการทำงานก่อนที่จะดำเนิน
Marc

0

หากคุณไม่มี git พร้อมใช้งานด้วยเหตุผลบางอย่าง แต่คุณมี git repo (พบโฟลเดอร์. git) คุณสามารถดึงแฮชคอมมิตจาก. git / fetch / heads / [branch]

ตัวอย่างเช่นฉันใช้ตัวอย่างข้อมูล 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.