Di Git, bagaimana saya bisa menulis hash komit saat ini ke file di komit yang sama

131

Saya mencoba melakukan hal-hal mewah di sini dengan kait Git, tapi saya tidak benar-benar tahu bagaimana melakukannya (atau jika mungkin).

Yang perlu saya lakukan adalah: di setiap commit saya ingin mengambil hash-nya dan kemudian memperbarui file di commit dengan hash ini.

Ada ide?

Felipe Kamakura
sumber
12
Pada dasarnya saya memiliki aplikasi web dan saya ingin mengaitkan versi aplikasi yang diinstal dengan komit yang terkait dengan versi tersebut. Ide awal saya adalah memperbarui semacam file about.html dengan hash komit. Tapi setelah mempelajari model objek git, saya menyadari bahwa ini agak mustahil = /
Felipe Kamakura
29
Ini adalah masalah yang sangat praktis. Aku juga menabraknya!
Li Dong
7
Sedangkan bagi saya, saya ingin program saya menulis pesan seperti ini ke log: "myprog start up, v.56c6bb2". Dengan begitu, jika seseorang file bug dan mengirim saya file log, saya bisa mencari tahu persis apa versi program saya berjalan.
Edward Falk
5
@ Jeffromi, use case yang sebenarnya sebenarnya sangat umum, dan hits pemula dengan sangat mudah. Memiliki versi nyata entah bagaimana "dicetak" ke dalam file-file yang sudah ditentukan sebelumnya adalah kebutuhan dasar, dan itu jauh dari jelas mengapa itu akan menjadi ide yang salah, misalnya karena itu adalah satu-satunya pilihan Anda dengan hack kontrol kontrol manual. (Ingat pemula.) Tambahkan ke bahwa banyak proyek sama sekali tidak memiliki langkah membangun / instalasi / penyebaran apa pun yang dapat mengambil dan mencap versi ke file live. Bagaimanapun juga, alih-alih komitmen awal, kait pasca-checkout dapat membantu bahkan dalam kasus-kasus tersebut.
Sz.
Ini tidak mungkin! Jika Anda dapat melakukan ini, Anda memecahkan algoritma hash SHA-1 ... ericsink.com/vcbe/html/cryptographic_hashes.html
betontalpfa

Jawaban:

82

Saya akan merekomendasikan melakukan sesuatu yang mirip dengan apa yang ada dalam pikiran Anda: menempatkan SHA1 dalam file yang tidak terlacak , yang dihasilkan sebagai bagian dari proses pembangunan / instalasi / penyebaran. Ini jelas mudah dilakukan ( git rev-parse HEAD > filenameatau mungkin git describe [--tags] > filename), dan ia menghindari melakukan sesuatu yang gila seperti berakhir dengan file yang berbeda dari apa yang dilacak git.

Kode Anda kemudian dapat mereferensikan file ini ketika membutuhkan nomor versi, atau proses build dapat memasukkan informasi ke dalam produk akhir. Yang terakhir sebenarnya adalah bagaimana git sendiri mendapatkan nomor versinya - proses build mengambil nomor versi dari repo, lalu membuatnya menjadi executable.

Cascabel
sumber
3
Bisakah seseorang menjelaskan lebih lanjut dengan langkah demi langkah tentang bagaimana melakukan ini? Atau setidaknya dorongan ke arah yang benar?
Joel Worsham
1
@ Joel Bagaimana melakukan apa? Saya menyebutkan cara menempatkan hash dalam file; sisanya mungkin sesuatu tentang proses pembuatan Anda? Mungkin pertanyaan baru jika Anda mencoba bertanya tentang bagian itu.
Cascabel
1
Dalam kasus saya, saya menambahkan aturan ke Makefile saya yang menghasilkan file "gitversion.h" di setiap build. Lihat stackoverflow.com/a/38087913/338479
Edward Falk
1
Anda mungkin dapat mengotomatisasi ini dengan hook "git-checkout". Masalahnya adalah bahwa kait harus dipasang secara manual.
Edward Falk
14

Tidak mungkin untuk menulis hash komit saat ini: jika Anda berhasil pra-menghitung hash komit di masa depan - itu akan berubah segera setelah Anda memodifikasi file apa pun.

Namun, ada tiga opsi:

  1. Gunakan skrip untuk meningkatkan 'komit id' dan sertakan di suatu tempat. Jelek
  2. .gitignore file yang akan Anda gunakan untuk menyimpan hash. Tidak terlalu berguna
  3. Di pre-commit, simpan hash komit sebelumnya :) Anda tidak memodifikasi / menyisipkan komit dalam kasus 99,99%, jadi, ini AKAN bekerja. Dalam kasus terburuk Anda masih dapat mengidentifikasi revisi sumber.

Saya sedang mengerjakan skrip kait, akan mempostingnya di sini 'ketika sudah selesai', tetapi masih - lebih awal dari Duke Nukem Forever dirilis :))

Perbarui : kode untuk .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

Sekarang satu-satunya yang kita butuhkan adalah alat yang mengubah prev_commit,branchpasangan menjadi hash komit :)

Saya tidak tahu apakah pendekatan ini dapat membedakan penggabungan komitmen. Akan segera memeriksanya

kolypto
sumber
13

Seseorang menunjuk saya ke bagian "man gitattributes" pada ident, yang memiliki ini:

ident

Ketika atribut ident disetel untuk sebuah path, git mengganti $ Id $ dalam objek blob dengan $ Id :, diikuti oleh nama objek gumpalan heksadesimal 40 karakter, diikuti dengan tanda dolar $ pada saat checkout. Urutan byte apa pun yang dimulai dengan $ Id: dan diakhiri dengan $ pada file worktree diganti dengan $ Id $ pada saat check-in.

Jika Anda memikirkannya, ini juga yang dilakukan CVS, Subversion, dll. Jika Anda melihat repositori, Anda akan melihat bahwa file di repositori selalu berisi, misalnya, $ Id $. Tidak pernah mengandung perluasan itu. Hanya pada checkout bahwa teks diperluas.

Baron Schwartz
sumber
8
identadalah hash untuk file itu sendiri, bukan terburu-buru dari komit. Dari git-scm.com/book/en/… : "Namun, hasil itu penggunaannya terbatas. Jika Anda telah menggunakan substitusi kata kunci dalam CVS atau Subversion, Anda dapat memasukkan datestamp - SHA tidak terlalu membantu, karena itu cukup acak dan Anda tidak dapat mengetahui apakah satu SHA lebih tua atau lebih baru dari yang lain. " filtermembutuhkan kerja, tetapi itu bisa membuat info komit menjadi (dan keluar dari) file.
Zach Young
11

Ini dapat dicapai dengan menggunakan filteratribut di gitattributes . Anda harus memberikan smudgeperintah yang memasukkan id komit, dan cleanperintah yang menghapusnya, sehingga file yang dimasukkan tidak akan berubah hanya karena id komit.

Dengan demikian, id komit tidak pernah disimpan dalam gumpalan file; itu hanya diperluas di copy pekerjaan Anda. (Sebenarnya memasukkan id komit ke dalam gumpalan akan menjadi tugas rekursif tanpa batas. ☺) Siapa pun yang mengkloning pohon ini perlu mengatur atribut untuk dirinya sendiri.

legoscia
sumber
7
Tugas tidak mungkin , bukan tugas rekursif. Hash komit tergantung pada hash pohon yang tergantung pada hash file, yang tergantung pada konten file. Anda harus mendapatkan konsistensi diri. Kecuali Anda akan menemukan semacam titik tetap [digeneralisasi] untuk hash SHA-1.
Jakub Narębski
1
@ Yakub, apakah ada semacam trik di git yang akan memungkinkan untuk membuat file yang dilacak yang tidak mengubah hash yang dihasilkan? Beberapa cara untuk mengganti hash-nya, mungkin. Itu akan menjadi solusi :)
kolypto
@o_O Tync: Tidak mungkin. File yang diubah berarti hash yang diubah (dari file) - ini berdasarkan desain, dan menurut definisi fungsi hash.
Jakub Narębski
2
Ini adalah solusi yang cukup bagus, tetapi perlu diingat bahwa ini melibatkan kait yang harus dipasang secara manual setiap kali Anda mengkloning repositori.
Edward Falk
7

Pikirkan di luar kotak komit!

masukkan ini ke file hooks / post-checkout

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

Versi ini akan tersedia di mana saja Anda menggunakannya.

Keith Patrick
sumber
3

Saya tidak berpikir Anda benar-benar ingin melakukan itu, karena ketika file di komit diubah, hash komit juga berubah.

midtiby
sumber
1

Biarkan saya mengeksplorasi mengapa ini adalah masalah yang menantang menggunakan internal git. Anda bisa mendapatkan sha1 dari commit saat ini dengan

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

Pada dasarnya Anda menjalankan checksum sha1 pada pesan yang dikembalikan oleh git cat-file commit HEAD . Dua hal segera melompat keluar sebagai masalah ketika Anda memeriksa pesan ini. Satu adalah pohon sha1 dan yang kedua adalah waktu komit.

Sekarang waktu komit mudah dijaga dengan mengubah pesan dan menebak berapa lama waktu untuk membuat komit atau penjadwalan untuk komit pada waktu tertentu. Masalah sebenarnya adalah pohon sha1, yang bisa Anda dapatkangit ls-tree $(git write-tree) | git mktree . Pada dasarnya Anda melakukan checksum sha1 pada pesan dari ls-tree, yang merupakan daftar semua file dan checksum sha1 mereka.

Oleh karena itu komit Anda sha1 checksum tergantung pada pohon Anda sha1 checksum, yang secara langsung tergantung pada file sha1 checksum, yang melengkapi lingkaran dan tergantung pada komit sha1. Dengan demikian Anda memiliki masalah melingkar dengan teknik yang tersedia untuk saya sendiri.

Dengan checksum yang kurang aman , telah terbukti memungkinkan untuk menulis checksum file ke dalam file itu sendiri melalui brute force; Namun, saya tidak tahu ada pekerjaan yang menyelesaikan tugas itu dengan sha1. Ini bukan tidak mungkin, tetapi hampir tidak mungkin dengan pemahaman kita saat ini (tetapi siapa tahu mungkin dalam beberapa tahun ini akan menjadi sepele). Namun, masih ini bahkan lebih sulit untuk memaksa karena Anda harus menulis (melakukan) checksum dari (pohon) checksum dari (gumpalan) checksum ke dalam file.

Novice C
sumber
Apakah ada cara orang dapat melakukan file, kemudian melakukan checkout dan memiliki hash komit terbaru ditempatkan sebagai komentar di awal setiap file kode sumber? Lalu membangun dan lari dari itu?
John Wooten