Trong Git, làm cách nào tôi có thể viết hàm băm hiện tại vào một tệp trong cùng một xác nhận


131

Tôi đang cố gắng làm một thứ lạ mắt ở đây với móc Git, nhưng tôi thực sự không biết làm thế nào (hoặc nếu có thể).

Điều tôi cần làm là: trong mọi cam kết tôi muốn lấy hàm băm của nó và sau đó cập nhật một tệp trong cam kết với hàm băm này.

Có ý kiến ​​gì không?


12
Về cơ bản tôi có một ứng dụng web và tôi muốn liên kết một phiên bản đã cài đặt của ứng dụng đó với cam kết chính xác mà phiên bản đó được liên kết. Ideia ban đầu của tôi là cập nhật một loại tệp about.html với hàm băm cam kết. Nhưng sau khi nghiên cứu mô hình đối tượng của git, tôi nhận ra rằng điều này là không thể = /
Felipe Kamakura

29
Đây là một vấn đề rất thực tế. Tôi cũng chạy vào đó!
Li Dong

7
Đối với tôi, tôi muốn chương trình của mình viết một thông điệp như thế này vào nhật ký: "myprog start up, v.56c6bb2". Bằng cách đó, nếu ai đó báo lỗi và gửi cho tôi tệp nhật ký, tôi có thể tìm ra chính xác phiên bản chương trình của mình đang chạy.
Edward Falk

5
@Jefromi, trường hợp sử dụng thực tế là rất phổ biến, và người mới bắt đầu rất dễ dàng. Có phiên bản thực bằng cách nào đó "in dấu" vào các tệp cơ sở là một nhu cầu cơ bản và không rõ ràng tại sao nó lại là một ý tưởng sai, ví dụ như vì đó là lựa chọn duy nhất của bạn với các bản hack kiểm soát sửa đổi thủ công. (Hãy nhớ người mới bắt đầu.) Thêm vào đó là nhiều dự án đơn giản là không có bất kỳ bước xây dựng / cài đặt / triển khai nào có thể lấy và đóng dấu phiên bản vào các tệp trực tiếp. Bất kể, thay vì cam kết trước, móc sau thanh toán có thể giúp ngay cả trong những trường hợp đó.
Sz.

Điều này là không thể! Nếu bạn có thể làm điều này, bạn đã phá vỡ thuật toán băm SHA-1 ... ericsink.com/vcbe/html/cryptographic_hashes.html
betontalpfa

Câu trả lời:


82

Tôi khuyên bạn nên làm một cái gì đó tương tự như những gì bạn có trong đầu: đặt SHA1 vào một tệp không bị theo dõi , được tạo như một phần của quá trình xây dựng / cài đặt / triển khai. Rõ ràng là dễ dàng để làm ( git rev-parse HEAD > filenamehoặc có lẽ git describe [--tags] > filename), và nó tránh làm bất cứ điều gì điên rồ như kết thúc với một tệp khác với những gì theo dõi của git.

Mã của bạn sau đó có thể tham chiếu tệp này khi nó cần số phiên bản hoặc quá trình xây dựng có thể kết hợp thông tin vào sản phẩm cuối cùng. Cái sau thực sự là cách git tự lấy số phiên bản của nó - quá trình xây dựng lấy số phiên bản ra khỏi repo, sau đó xây dựng nó thành tệp thực thi.


3
Ai đó có thể tiếp tục với một bước từng bước về cách làm điều này? Hoặc ít nhất là một cú huých đúng hướng?
Joel Worsham

1
@Joel Làm thế nào để làm gì? Tôi đã đề cập đến cách đặt băm trong một tập tin; phần còn lại có lẽ là một cái gì đó về quá trình xây dựng của bạn? Có thể một câu hỏi mới nếu bạn đang cố gắng hỏi về phần đó.
Cascabel

1
Trong trường hợp của tôi, tôi đã thêm một quy tắc vào Makefile của mình để tạo tệp "gitversion.h" trên mỗi bản dựng. Xem stackoverflow.com/a/38087913/338479
Edward Falk

1
Bạn có thể tự động hóa việc này bằng móc "git-checkout". Vấn đề là các móc sẽ phải được cài đặt bằng tay.
Edward Falk

14

Không thể viết hàm băm cam kết hiện tại: nếu bạn quản lý để tính toán trước hàm băm cam kết trong tương lai - nó sẽ thay đổi ngay khi bạn sửa đổi bất kỳ tệp nào.

Tuy nhiên, có ba lựa chọn:

  1. Sử dụng tập lệnh để tăng 'id id' và đưa nó vào đâu đó. Xấu xí
  2. .gitignore tệp bạn sẽ lưu trữ băm vào. Không tiện dụng
  3. Trong pre-commit , lưu trữ băm xác nhận trước đó :) Bạn không sửa đổi / chèn các cam kết trong 99,99% trường hợp, vì vậy, điều này SILL hoạt động. Trong trường hợp xấu nhất, bạn vẫn có thể xác định sửa đổi nguồn.

Tôi đang làm việc với một kịch bản hook, sẽ đăng nó ở đây 'khi nó hoàn thành', nhưng vẫn còn - sớm hơn Duke Nukem Forever được phát hành :))

Cập nhật : mã cho .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

Bây giờ điều duy nhất chúng ta cần là một công cụ chuyển đổi prev_commit,branchcặp thành hàm băm xác thực :)

Tôi không biết liệu phương pháp này có thể phân biệt các cam kết hợp nhất hay không. Sẽ kiểm tra sớm


13

Một người nào đó đã chỉ cho tôi phần "man gitattribut" trên danh tính, có phần này:

xác định

Khi nhận dạng thuộc tính được đặt cho một đường dẫn, git sẽ thay thế $ Id $ trong đối tượng blob bằng $ Id:, theo sau là tên đối tượng blob thập lục phân 40 ký tự, theo sau là ký hiệu đô la $ khi thanh toán. Bất kỳ chuỗi byte nào bắt đầu bằng $ Id: và kết thúc bằng $ trong tệp worktree được thay thế bằng $ Id $ khi đăng ký.

Nếu bạn nghĩ về nó, đây là những gì CVS, Subversion, vv cũng làm. Nếu bạn nhìn vào kho lưu trữ, bạn sẽ thấy rằng tệp trong kho luôn chứa, ví dụ: $ Id $. Nó không bao giờ chứa sự mở rộng của điều đó. Chỉ khi thanh toán rằng văn bản được mở rộng.


8
identlà hàm băm cho chính tệp đó, không phải là dấu vết của cam kết. Từ git-scm.com/book/en/ trên : "Tuy nhiên, kết quả đó chỉ được sử dụng hạn chế. Nếu bạn đã sử dụng thay thế từ khóa trong CVS hoặc Subversion, bạn có thể bao gồm một dấu dữ liệu - SHA không hữu ích lắm, bởi vì nó khá ngẫu nhiên và bạn không thể biết một SHA cũ hơn hay mới hơn một SHA khác. " filtermất công, nhưng nó có thể lấy thông tin cam kết vào (và ra) một tệp.
Zach Young

11

Điều này có thể đạt được bằng cách sử dụng filterthuộc tính trong gitattribut . Bạn cần cung cấp một smudgelệnh chèn id xác nhận và một cleanlệnh loại bỏ nó, sao cho tệp được chèn vào sẽ không thay đổi chỉ vì id xác nhận.

Do đó, id xác nhận không bao giờ được lưu trữ trong blob của tệp; nó chỉ được mở rộng trong bản sao làm việc của bạn. (Trên thực tế, việc chèn id xác thực vào blob sẽ trở thành một nhiệm vụ đệ quy vô hạn. ☺) Bất cứ ai nhân bản cây này sẽ cần phải thiết lập các thuộc tính cho chính mình.


7
Nhiệm vụ bất khả thi , không phải nhiệm vụ đệ quy. Cam kết băm phụ thuộc vào băm cây phụ thuộc vào băm tệp, phụ thuộc vào nội dung tệp. Bạn phải có được sự tự nhất quán. Trừ khi bạn sẽ tìm thấy một loại điểm cố định [tổng quát] cho hàm băm SHA-1.
Jakub Narębski

1
@Jakub, có một số loại mẹo trong git sẽ cho phép tạo các tệp được theo dõi mà không sửa đổi băm kết quả? Một số cách để ghi đè băm của nó, có thể. Đó sẽ là một giải pháp :)
kolypto

@o_O Tync: Không thể. Thay đổi tệp có nghĩa là thay đổi hàm băm (của một tệp) - đây là theo thiết kế và theo định nghĩa của hàm băm.
Jakub Narębski

2
Đây là một giải pháp khá tốt, nhưng hãy nhớ rằng điều này liên quan đến các hook phải được cài đặt thủ công bất cứ khi nào bạn sao chép một kho lưu trữ.
Edward Falk

7

Hãy suy nghĩ bên ngoài hộp cam kết!

bật cái này vào tập tin hook / post-checkout

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

Phiên bản sẽ có sẵn ở mọi nơi bạn sử dụng nó.


3

Tôi không nghĩ rằng bạn thực sự muốn làm điều đó, bởi vì khi một tệp trong cam kết bị thay đổi, hàm băm của cam kết cũng bị thay đổi.


1

Hãy để tôi khám phá lý do tại sao đây là một vấn đề đầy thách thức khi sử dụng nội bộ git. Bạn có thể nhận được sha1 của cam kết hiện tại bằng cách

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

Về cơ bản, bạn chạy tổng kiểm tra sha1 trên tin nhắn được trả về git cat-file commit HEAD. Hai điều ngay lập tức nhảy ra như một vấn đề khi bạn kiểm tra tin nhắn này. Một là cây sha1 và thứ hai là thời gian cam kết.

Bây giờ thời gian cam kết dễ dàng được quan tâm bằng cách thay đổi tin nhắn và đoán mất bao lâu để thực hiện một cam kết hoặc lên lịch để cam kết tại một thời điểm cụ thể. Vấn đề thực sự là cây sha1, mà bạn có thể nhận được từ git ls-tree $(git write-tree) | git mktree. Về cơ bản, bạn đang thực hiện tổng kiểm tra sha1 trên tin nhắn từ ls-tree, đây là danh sách tất cả các tệp và tổng kiểm tra sha1 của chúng.

Do đó, tổng kiểm tra sha1 của bạn phụ thuộc vào tổng kiểm tra cây sha1 của bạn, điều này phụ thuộc trực tiếp vào các tập tin kiểm tra sha1, hoàn thành vòng tròn và phụ thuộc vào cam kết sha1. Vì vậy, bạn có một vấn đề tròn với các kỹ thuật có sẵn cho bản thân mình.

Với tổng kiểm tra kém an toàn hơn , người ta đã chứng minh có thể ghi tổng kiểm tra của tệp vào chính tệp thông qua lực lượng vũ phu; tuy nhiên, tôi không biết bất kỳ công việc nào đã hoàn thành nhiệm vụ đó với sha1. Điều này không phải là không thể, nhưng bên cạnh không thể với sự hiểu biết hiện tại của chúng tôi (nhưng ai biết có thể trong một vài năm nữa, nó sẽ là tầm thường). Tuy nhiên, điều này thậm chí còn khó hơn đối với vũ lực vì bạn phải viết tổng kiểm tra (cam kết) của tổng kiểm tra (cây) của tổng kiểm tra (blob) vào tệp.


Có cách nào để người ta có thể cam kết các tệp, sau đó thực hiện kiểm tra và có mã băm cam kết mới nhất được đặt làm nhận xét ở đầu mỗi tệp mã nguồn không? Sau đó xây dựng và chạy từ đó?
John Wooten
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.