Làm thế nào để git tính toán các băm tệp?


124

Các băm SHA1 được lưu trữ trong các đối tượng cây (được trả về bởi git ls-tree) không khớp với các băm SHA1 của nội dung tệp (được trả về bởi sha1sum)

$ git cat-file blob 4716ca912495c805b94a88ef6dc3fb4aff46bf3c | sha1sum
de20247992af0f949ae8df4fa9a37e4a03d7063e  -

Làm thế nào để git tính toán các băm tệp? Nó có nén nội dung trước khi tính toán băm không?



1
Để biết thêm chi tiết, hãy xem progit.org/book/ch9-2.html
netvope,

5
liên kết của netvope dường như đã chết. Tôi nghĩ rằng đây là địa điểm mới: git-scm.com/book/en/Git-Internals-Git-Objects đó là §9.2 từ git-scm.com/book
Rhubbarb

Câu trả lời:


122

Git đặt tiền tố đối tượng bằng "blob", theo sau là độ dài (dưới dạng số nguyên có thể đọc được của con người), theo sau là ký tự NUL

$ echo -e 'blob 14\0Hello, World!' | shasum 8ab686eafeb1f44702738c8b0f24f2567c36da6d

Nguồn: http://alblue.bandlem.com/2011/08/git-tip-of-week-objects.html


2
Cũng đáng nói là nó thay thế "\ r \ n" bằng "\ n", nhưng chỉ để lại "\ r" riêng biệt.
user420667

8
^ sửa cho nhận xét trên: đôi khi git thực hiện thay thế ở trên, tùy thuộc vào cài đặt eol / autocrlf của một người.
user420667

5
Bạn cũng có thể so sánh điều này với đầu ra của echo 'Hello, World!' | git hash-object --stdin. Theo tùy chọn, bạn có thể chỉ định --no-filtersđể đảm bảo không có chuyển đổi crlf nào xảy ra hoặc chỉ định --path=somethi.ngcho phép git sử dụng bộ lọc được chỉ định qua gitattributes(also @ user420667). Và -wthực sự gửi blob tới .git/objects(nếu bạn đang ở trong git repo).
Tobias Kienzler

Diễn đạt sự tương đương, để có ý nghĩa: echo -e 'blob 16\0Hello, \r\nWorld!' | shasum == echo -e 'Hello, \r\nWorld!' | git hash-object --stdin --no-filters và nó cũng sẽ tương đương với \nvà 15.
Peter Krauss

1
echothêm một dòng mới vào đầu ra, dòng này cũng được chuyển vào git. Đó là lý do tại sao 14 ký tự của nó. Để sử dụng tiếng vang mà không một dòng mới, ghiecho -n 'Hello, World!'
Bouke Versteegh

36

Tôi chỉ mở rộng câu trả lời bằng cách @Leif Gruenwoldtnêu chi tiết những gì có trong tài liệu tham khảo được cung cấp bởi@Leif Gruenwoldt

Tự làm ..

  • Bước 1. Tạo một tài liệu văn bản trống (tên không quan trọng) trong kho lưu trữ của bạn
  • Bước 2. Giai đoạn và cam kết tài liệu
  • Bước 3. Xác định băm của đốm màu bằng cách thực thi git ls-tree HEAD
  • Bước 4. Tìm băm của đốm màu e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
  • Bước 5. Ngạc nhiên của bạn và đọc bên dưới

GIT tính toán các băm cam kết của nó như thế nào

    Commit Hash (SHA1) = SHA1("blob " + <size_of_file> + "\0" + <contents_of_file>)

Văn bản blob⎵là một tiền tố không đổi và \0cũng không đổi và là NULLký tự. Các <size_of_file><contents_of_file>thay đổi tùy theo tập tin.

Hãy xem: Định dạng tệp của một đối tượng git commit là gì?

Và đó là tất cả mọi người!

Nhưng đợi đã! , bạn có nhận thấy rằng <filename>tham số không phải là một tham số được sử dụng cho tính toán băm? Hai tệp có thể có cùng một hàm băm nếu nội dung của chúng giống nhau, không quan tâm đến ngày giờ chúng được tạo và tên của chúng. Đây là một trong những lý do Git xử lý việc di chuyển và đổi tên tốt hơn các hệ thống điều khiển phiên bản khác.

Tự làm (Ext)

  • Bước 6. Tạo một tệp trống khác với một tệp khác filenametrong cùng một thư mục
  • Bước 7. So sánh hàm băm của cả hai tệp của bạn.

Ghi chú:

Liên kết không đề cập đến cách treeđối tượng được băm. Tôi không chắc chắn về thuật toán và các tham số tuy nhiên từ quan sát của tôi, nó có thể tính toán một hàm băm dựa trên tất cả blobstrees( có thể là các hàm băm của chúng) mà nó chứa


SHA1("blob" + <size_of_file>- có ký tự khoảng trắng bổ sung giữa đốm màu và kích thước không? Kích thước có phải là số thập phân không? Nó có tiền tố 0 không?
osgx

1
@osgx Có. Tài liệu tham khảo và thử nghiệm của tôi xác nhận như vậy. Tôi đã sửa câu trả lời. Kích thước dường như là số byte dưới dạng số nguyên không có tiền tố.
Samuel Harmer

13

git hash-object

Đây là một cách nhanh chóng để xác minh phương pháp thử nghiệm của bạn:

s='abc'
printf "$s" | git hash-object --stdin
printf "blob $(printf "$s" | wc -c)\0$s" | sha1sum

Đầu ra:

f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f
f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f  -

ở đâu sha1sumtrong GNU Coreutils.

Sau đó, nó đi xuống để hiểu định dạng của từng loại đối tượng. Chúng tôi đã đề cập đến những điều tầm thường blob, đây là những điều khác:


Như đã đề cập trong câu trả lời trước, độ dài nên được tính bằng $(printf "\0$s" | wc -c). Lưu ý ký tự trống được thêm vào. Nghĩa là, nếu chuỗi là 'abc' với ký tự trống được thêm vào phía trước độ dài sẽ mang lại 4, không phải 3. Sau đó, kết quả với sha1sum khớp với git hash-object.
Michael Ekoka

Bạn nói đúng, họ phù hợp. Có vẻ như có một chút tác dụng phụ nguy hiểm khi sử dụng printf thay vì echo -e ở đây. Khi bạn áp dụng git hash-object vào một tệp chứa chuỗi 'abc', bạn nhận được 8baef1b ... f903 là những gì bạn nhận được khi sử dụng echo -e thay vì printf. Với điều kiện là echo -e thêm một dòng mới vào cuối chuỗi, có vẻ như để khớp hành vi với printf, bạn có thể làm tương tự (tức là s = ​​"$ s \ n").
Michael Ekoka

3

Dựa trên câu trả lời của Leif Gruenwoldt , đây là một hàm shell thay thế cho git hash-object:

git-hash-object () { # substitute when the `git` command is not available
    local type=blob
    [ "$1" = "-t" ] && shift && type=$1 && shift
    # depending on eol/autocrlf settings, you may want to substitute CRLFs by LFs
    # by using `perl -pe 's/\r$//g'` instead of `cat` in the next 2 commands
    local size=$(cat $1 | wc -c | sed 's/ .*$//')
    ( echo -en "$type $size\0"; cat "$1" ) | sha1sum | sed 's/ .*$//'
}

Kiểm tra:

$ echo 'Hello, World!' > test.txt
$ git hash-object test.txt
8ab686eafeb1f44702738c8b0f24f2567c36da6d
$ git-hash-object test.txt
8ab686eafeb1f44702738c8b0f24f2567c36da6d

3

Tôi cần cái này cho một số bài kiểm tra đơn vị trong Python 3 vì vậy tôi nghĩ tôi sẽ để nó ở đây.

def git_blob_hash(data):
    if isinstance(data, str):
        data = data.encode()
    data = b'blob ' + str(len(data)).encode() + b'\0' + data
    h = hashlib.sha1()
    h.update(data)
    return h.hexdigest()

Tôi chú ý đến \nphần cuối dòng ở mọi nơi nhưng trong một số trường hợp, Git cũng có thể thay đổi phần cuối dòng của bạn trước khi tính toán hàm băm này, vì vậy bạn cũng có thể cần một .replace('\r\n', '\n')trong đó.

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.