ใน Git ฉันจะเขียนแฮชคอมมิตปัจจุบันไปยังไฟล์ในคอมมิตเดียวกันได้อย่างไร


131

ฉันกำลังพยายามทำของแฟนซีที่นี่ด้วย Git hooks แต่ฉันไม่รู้ว่าจะทำอย่างไร (หรือถ้าเป็นไปได้)

สิ่งที่ฉันต้องทำคือ: ในการคอมมิตทุกครั้งฉันต้องการแฮชของมันแล้วอัปเดตไฟล์ในคอมมิตด้วยแฮชนี้

ความคิดใด ๆ ?


12
โดยพื้นฐานแล้วฉันมีเว็บแอปพลิเคชันและฉันต้องการเชื่อมโยงเวอร์ชันที่ติดตั้งของแอปพลิเคชันนั้นกับการคอมมิตที่เกี่ยวข้องกับเวอร์ชันนั้น ๆ ความคิดเริ่มต้นของฉันคือการอัปเดตประเภทของไฟล์ about.html ด้วยคอมมิตแฮช แต่หลังจากศึกษาโมเดลวัตถุของคอมไพล์ฉันก็รู้ว่ามันเป็นไปไม่ได้เลย = /
Felipe Kamakura

29
นี่เป็นปัญหาในทางปฏิบัติมาก ฉันเจอมันด้วย!
Li Dong

7
สำหรับฉันฉันต้องการให้โปรแกรมของฉันเขียนข้อความเช่นนี้ในบันทึก: "myprog เริ่มต้นขึ้น v.56c6bb2" ด้วยวิธีนี้หากมีคนส่งข้อบกพร่องและส่งไฟล์บันทึกมาให้ฉันฉันจะพบว่าโปรแกรมของฉันกำลังทำงานอยู่ในเวอร์ชันใด
Edward Falk

5
@Jefromi กรณีการใช้งานจริงเป็นเรื่องธรรมดามากและผู้เริ่มต้นใช้งานได้ง่ายมาก การมีเวอร์ชันจริง "ตราตรึง" ลงในไฟล์ที่มีฐานข้อมูลเป็นความต้องการขั้นพื้นฐานและยังไม่ชัดเจนว่าเหตุใดจึงเป็นความคิดที่ผิดเช่นเพราะนั่นเป็นทางเลือกเดียวของคุณที่มีแฮ็กควบคุมการแก้ไขด้วยตนเอง (โปรดจำไว้ว่าผู้เริ่มต้น) นอกจากนี้โครงการจำนวนมากไม่มีขั้นตอนการสร้าง / การติดตั้ง / การปรับใช้ใด ๆ เลยซึ่งสามารถจับและประทับเวอร์ชันลงในไฟล์ที่ใช้งานได้ ไม่ว่าจะเป็นการผูกมัดล่วงหน้า แต่เบ็ดหลังการชำระเงินสามารถช่วยได้แม้ในกรณีเหล่านั้น
Sz.

มันเป็นไปไม่ได้! หากคุณสามารถทำได้คุณจะทำลายอัลกอริทึมแฮช SHA-1 ... ericsink.com/vcbe/html/cryptographic_hashes.html
betontalpfa

คำตอบ:


82

ฉันขอแนะนำให้ทำสิ่งที่คล้ายกับสิ่งที่คุณคิดไว้: การวาง SHA1 ในไฟล์ที่ไม่มีการติดตามซึ่งสร้างขึ้นเป็นส่วนหนึ่งของกระบวนการสร้าง / การติดตั้ง / การปรับใช้ เห็นได้ชัดว่าทำได้ง่าย ( git rev-parse HEAD > filenameหรืออาจgit describe [--tags] > filename) และหลีกเลี่ยงการทำอะไรที่บ้าคลั่งเช่นจบลงด้วยไฟล์ที่แตกต่างจากการติดตามของคอมไพล์

จากนั้นรหัสของคุณสามารถอ้างอิงไฟล์นี้เมื่อต้องการหมายเลขเวอร์ชันหรือกระบวนการสร้างอาจรวมข้อมูลไว้ในผลิตภัณฑ์ขั้นสุดท้าย อันหลังนี้เป็นวิธีที่ git รับหมายเลขเวอร์ชันของมันเอง - กระบวนการสร้างจะดึงหมายเลขเวอร์ชันออกจาก repo จากนั้นสร้างเป็นไฟล์ปฏิบัติการ


3
ใครช่วยอธิบายเพิ่มเติมทีละขั้นตอนเกี่ยวกับวิธีการทำเช่นนี้ หรืออย่างน้อยก็เขยิบไปในทิศทางที่ถูกต้อง?
Joel Worsham

1
@ โจเอลทำไงดี? ฉันพูดถึงวิธีการใส่แฮชในไฟล์ ส่วนที่เหลือน่าจะเกี่ยวกับกระบวนการสร้างของคุณหรือไม่? อาจเป็นคำถามใหม่หากคุณกำลังพยายามถามเกี่ยวกับส่วนนั้น
Cascabel

1
ในกรณีของฉันฉันได้เพิ่มกฎลงใน Makefile ของฉันที่สร้างไฟล์ "gitversion.h" ในทุกบิลด์ ดูstackoverflow.com/a/38087913/338479
Edward Falk

1
คุณอาจสามารถทำให้การดำเนินการนี้เป็นไปโดยอัตโนมัติด้วยเบ็ด "git-checkout" ปัญหาคือจะต้องติดตั้งตะขอด้วยตนเอง
Edward Falk

14

เป็นไปไม่ได้ที่จะเขียนแฮชคอมมิตปัจจุบัน: หากคุณจัดการคำนวณแฮชคอมมิตในอนาคตล่วงหน้า - มันจะเปลี่ยนทันทีที่คุณแก้ไขไฟล์ใด ๆ

อย่างไรก็ตามมีสามตัวเลือก:

  1. ใช้สคริปต์เพื่อเพิ่ม 'กระทำ id' และรวมไว้ที่ใดที่หนึ่ง น่าเกลียด
  2. .gitignore ไฟล์ที่คุณจะจัดเก็บแฮช ไม่ค่อยสะดวก
  3. ในpre-commitจัดเก็บแฮชคอมมิตก่อนหน้านี้ :) คุณไม่ได้แก้ไข / แทรกคอมมิตในกรณี 99.99% ดังนั้นสิ่งนี้จะได้ผล ในกรณีที่แย่ที่สุดคุณยังสามารถระบุการแก้ไขแหล่งที่มาได้

ฉันกำลังทำงานกับสคริปต์เบ็ดจะโพสต์ไว้ที่นี่ 'เมื่อมันเสร็จสิ้น' แต่ก็ยัง - เร็วกว่าที่ Duke Nukem Forever จะออก :))

ปรับปรุง : รหัสสำหรับ.git/hooks/pre-commit:

#!/usr/bin/env bash
set -e

#=== 'prev-commit' solution by o_O Tync
#commit_hash=$(git rev-parse --verify HEAD)
commit=$(git log -1 --pretty="%H%n%ci") # hash \n date
commit_hash=$(echo "$commit" | head -1)
commit_date=$(echo "$commit" | head -2 | tail -1) # 2010-12-28 05:16:23 +0300

branch_name=$(git symbolic-ref -q HEAD) # http://stackoverflow.com/questions/1593051/#1593487
branch_name=${branch_name##refs/heads/}
branch_name=${branch_name:-HEAD} # 'HEAD' indicates detached HEAD situation

# Write it
echo -e "prev_commit='$commit_hash'\ndate='$commit_date'\nbranch='$branch'\n" > gitcommit.py

ตอนนี้สิ่งเดียวที่เราต้องการคือเครื่องมือที่แปลงprev_commit,branchคู่เป็นแฮชคอมมิตจริง :)

ฉันไม่รู้ว่าวิธีนี้สามารถบอกการรวมคอมมิตออกจากกัน จะตรวจสอบเร็ว ๆ นี้


13

มีคนชี้ให้ฉันไปที่ส่วน "man gitattributes" ใน ident ซึ่งมีสิ่งนี้:

ident

เมื่อแอตทริบิวต์ระบุถูกตั้งค่าสำหรับเส้นทาง git จะแทนที่ $ Id $ ในวัตถุ blob ด้วย $ Id: ตามด้วยชื่อวัตถุหยดเลขฐานสิบหก 40 อักขระตามด้วยเครื่องหมายดอลลาร์ $ เมื่อชำระเงิน ลำดับไบต์ใด ๆ ที่ขึ้นต้นด้วย $ Id: และลงท้ายด้วย $ ในไฟล์ worktree จะถูกแทนที่ด้วย $ Id $ เมื่อเช็คอิน

ถ้าคุณลองคิดดูนี่คือสิ่งที่ CVS การโค่นล้มและอื่น ๆ ทำได้เช่นกัน หากคุณดูที่เก็บคุณจะเห็นว่าไฟล์ในที่เก็บนั้นมีอยู่เสมอตัวอย่างเช่น $ Id $ มันไม่เคยมีส่วนขยายของสิ่งนั้น เฉพาะในขั้นตอนการชำระเงินที่ขยายข้อความ


8
identคือแฮชสำหรับไฟล์นั้นเองไม่ใช่แฮชของคอมมิต จากgit-scm.com/book/en/… : "อย่างไรก็ตามผลลัพธ์นั้นใช้งานได้ จำกัด หากคุณเคยใช้การทดแทนคีย์เวิร์ดใน CVS หรือ Subversion คุณสามารถใส่ datestamp - SHA ไม่ได้มีประโยชน์ทั้งหมด เพราะมันค่อนข้างสุ่มและคุณไม่สามารถบอกได้ว่า SHA อันหนึ่งเก่าหรือใหม่กว่าอีกอันหนึ่ง " filterใช้งานได้ แต่สามารถรับข้อมูลการคอมมิตเข้า (และออกจาก) ไฟล์ได้
Zach Young

11

นี้สามารถทำได้โดยใช้filterแอตทริบิวต์ในgitattributes คุณต้องระบุsmudgeคำสั่งที่แทรกรหัสคอมมิตและไฟล์cleanคำสั่งที่ลบมันออกไปเพื่อให้ไฟล์ที่ใส่เข้าไปจะไม่เปลี่ยนแปลงเพียงเพราะรหัสคอมมิต

ดังนั้นรหัสการคอมมิตจะไม่ถูกเก็บไว้ในหยดของไฟล์ เพียงแค่ขยายในสำเนาการทำงานของคุณ (จริงๆแล้วการใส่รหัสคอมมิตลงในหยดจะกลายเป็นงานวนซ้ำไม่สิ้นสุด☺) ใครก็ตามที่โคลนนิ่งต้นไม้นี้จะต้องตั้งค่าคุณสมบัติให้ตัวเอง


7
งานที่เป็นไปไม่ได้ไม่ใช่งานซ้ำ Commit hash ขึ้นอยู่กับ tree hash ซึ่งขึ้นอยู่กับ file hash ซึ่งขึ้นอยู่กับเนื้อหาของไฟล์ คุณต้องได้รับความสม่ำเสมอในตนเอง เว้นแต่คุณจะพบจุดคงที่ [ทั่วไป] ชนิดหนึ่งสำหรับแฮช SHA-1
Jakub Narębski

1
@Jakub มีกลเม็ดบางอย่างใน git ที่อนุญาตให้สร้างไฟล์ที่ติดตามซึ่งไม่ได้แก้ไขแฮชที่เป็นผลลัพธ์หรือไม่? วิธีบางอย่างในการลบล้างแฮชอาจจะ นั่นจะเป็นทางออก :)
kolypto

@o_O Tync: เป็นไปไม่ได้ ไฟล์ที่เปลี่ยนแปลงหมายถึงแฮชที่เปลี่ยนแปลง (ของไฟล์) ซึ่งเกิดจากการออกแบบและตามความหมายของฟังก์ชันแฮช
Jakub Narębski

2
นี่เป็นวิธีแก้ปัญหาที่ค่อนข้างดี แต่โปรดทราบว่าสิ่งนี้เกี่ยวข้องกับตะขอที่ต้องติดตั้งด้วยตนเองทุกครั้งที่คุณโคลนที่เก็บ
Edward Falk

7

คิดนอกกรอบการกระทำ!

เปิดสิ่งนี้ลงใน file hooks / post-checkout

#!/bin/sh
git describe --all --long > config/git-commit-version.txt

เวอร์ชันนี้จะพร้อมใช้งานทุกที่ที่คุณใช้งาน


3

ฉันไม่คิดว่าคุณต้องการทำเช่นนั้นจริง ๆ เพราะเมื่อไฟล์ในคอมมิตถูกเปลี่ยนแฮชของคอมมิตก็เปลี่ยนไปด้วย


1

ให้ฉันสำรวจว่าเหตุใดปัญหานี้จึงเป็นปัญหาที่ท้าทายโดยใช้ git internalals คุณสามารถรับ sha1 ของการกระทำปัจจุบันโดย

#!/bin/bash
commit=$(git cat-file commit HEAD) #
sha1=($((printf "commit %s\0" $(echo "$commit" | wc -c); echo "$commit") | sha1sum))
echo ${sha1[0]}

โดยเฉพาะอย่างยิ่งคุณเรียกใช้การตรวจสอบ sha1 git cat-file commit HEADข้อความที่ส่งกลับโดย มีสองสิ่งที่จะกลายเป็นปัญหาทันทีเมื่อคุณตรวจสอบข้อความนี้ หนึ่งคือต้นไม้ sha1 และที่สองคือเวลากระทำ

ตอนนี้เวลาในการคอมมิตนั้นได้รับการดูแลอย่างง่ายดายโดยการแก้ไขข้อความและคาดเดาว่าจะต้องใช้เวลาในการคอมมิตหรือกำหนดเวลาในการคอมมิตนานแค่ไหน ปัญหาที่แท้จริงคือ sha1 git ls-tree $(git write-tree) | git mktreeต้นไม้ที่คุณจะได้รับจาก โดยพื้นฐานแล้วคุณกำลังทำการตรวจสอบ sha1 บนข้อความจาก ls-tree ซึ่งเป็นรายการไฟล์ทั้งหมดและการตรวจสอบ sha1

ดังนั้นการตรวจสอบ sha1 ของคุณจึงขึ้นอยู่กับการตรวจสอบ sha1 ต้นไม้ของคุณซึ่งขึ้นอยู่กับไฟล์ sha1 checksum โดยตรงซึ่งทำให้วงกลมเสร็จสมบูรณ์และขึ้นอยู่กับการกระทำ sha1 ดังนั้นคุณจึงมีปัญหาแบบวงกลมกับเทคนิคที่มีให้กับตัวเอง

ด้วยการตรวจสอบที่มีความปลอดภัยน้อยแสดงให้เห็นว่าสามารถเขียนเช็คซัมของไฟล์ลงในไฟล์ได้โดยใช้กำลังดุร้าย อย่างไรก็ตามฉันไม่รู้ว่ามีงานใดที่ทำให้งานนั้นสำเร็จด้วย sha1 นี่ไม่ใช่เรื่องที่เป็นไปไม่ได้ แต่ถัดจากความเข้าใจในปัจจุบันของเราไปไม่ได้ (แต่ใครจะรู้ว่าในอีกไม่กี่ปีข้างหน้ามันจะเป็นเรื่องเล็กน้อย) อย่างไรก็ตามสิ่งนี้ยังยากกว่าที่จะ brute Force เนื่องจากคุณต้องเขียน (กระทำ) การตรวจสอบ (ต้นไม้) การตรวจสอบ (ต้นไม้) ของการตรวจสอบ (หยด) ลงในไฟล์


มีวิธีใดบ้างที่สามารถคอมมิตไฟล์จากนั้นทำการชำระเงินและวางแฮชคอมมิตล่าสุดไว้เป็นความคิดเห็นที่จุดเริ่มต้นของไฟล์ซอร์สโค้ดแต่ละไฟล์ แล้วสร้างและเรียกใช้จากที่?
John Wooten
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.