2009-02-16 21 views
134

Khi tôi hiểu nó khi Git gán một hàm băm SHA1 cho một tệp, SHA1 là duy nhất cho tệp dựa trên nội dung của nó.Làm cách nào để gán Git SHA1 cho một tệp không có Git?

Kết quả là nếu tệp chuyển từ kho này sang kho lưu trữ khác, thì tệp SHA1 cho tệp vẫn giữ nguyên như nội dung của tệp không thay đổi.

Git tính toán thông báo SHA1 như thế nào? Nó có làm nó trên nội dung tập tin không nén đầy đủ không?

Tôi muốn mô phỏng việc gán SHA1 ở bên ngoài Git.

+0

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

Trả lời

240

Đây là cách Git tính toán SHA1 cho một tập tin (hoặc, về Git, một "blob"):

sha1("blob " + filesize + "\0" + data) 

Vì vậy, bạn có thể dễ dàng tính toán đó cho mình mà không cần phải cài đặt Git. Lưu ý rằng "\ 0" là NULL-byte, không phải là chuỗi gồm hai ký tự.

Ví dụ, các hash của một tập tin rỗng:

sha1("blob 0\0") = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391" 

$ touch empty 
$ git hash-object empty 
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 

Một ví dụ khác:

sha1("blob 7\0foobar\n") = "323fae03f4606ea9991df8befbb2fca795e648fa" 

$ echo "foobar" > foo.txt 
$ git hash-object foo.txt 
323fae03f4606ea9991df8befbb2fca795e648fa 

Đây là một thực hiện Python:

from hashlib import sha1 
def githash(data): 
    s = sha1() 
    s.update("blob %u\0" % len(data)) 
    s.update(data) 
    return s.hexdigest() 
+12

Rất hữu ích. cảm ơn –

+4

Điều này thật tuyệt vời, nếu tôi có thể là 10 người! – hasen

+0

Câu trả lời này có giả sử Python 2 không? Khi tôi thử điều này trên Python 3, tôi nhận được một 'TypeError: Unicode-objects phải được mã hóa trước khi hashing' exception trên dòng' s.update() 'đầu tiên. –

4

Hãy xem trang hướng dẫn cho git-hash-object. Bạn có thể sử dụng nó để tính toán git băm của bất kỳ tập tin cụ thể nào. Tôi nghĩ rằng mà git không chỉ cung cấp nội dung của tệp vào thuật toán băm, nhưng tôi không biết chắc chắn, và nếu nó nạp dữ liệu bổ sung, tôi không biết nó là gì.

8

Bạn có thể tạo ra một vỏ bash chức năng để tính toán nó khá dễ dàng nếu bạn không có git cài đặt.

git_id() { printf 'blob %s\0' "$(ls -l "$1" | awk '{print $5;}')" | cat - "$1" | sha1sum | awk '{print $1}'; } 
+1

Ngắn hơn một chút: '(stat --printf =" blob% s \ 0 "" $ 1 "; cat" $ 1 ") | sha1sum -b | cut -d "" -f1'. – sschuberth

2
/// Calculates the SHA1 for a given string 
let calcSHA1 (text:string) = 
    text 
     |> System.Text.Encoding.ASCII.GetBytes 
     |> (new System.Security.Cryptography.SHA1CryptoServiceProvider()).ComputeHash 
     |> Array.fold (fun acc e -> 
      let t = System.Convert.ToString(e, 16) 
      if t.Length = 1 then acc + "0" + t else acc + t) 
      "" 
/// Calculates the SHA1 like git 
let calcGitSHA1 (text:string) = 
    let s = text.Replace("\r\n","\n") 
    sprintf "blob %d%c%s" (s.Length) (char 0) s 
     |> calcSHA1 

Đây là một giải pháp trong F #.

+0

tôi vẫn còn có vấn đề với umlauts: calcGitSHA1 ("ü") ShouldBeEqualTo ("0f0f3e3b1ff2bc6722afc3e3812e6b782683896f") Nhưng chức năng của tôi cho 0d758c9c7bc06c1e307f05d92d896aaf0a8a6d2c.. Bất kỳ ý tưởng nào về cách đối tượng băm git xử lý các âm sắc? – forki23

+0

nó sẽ xử lý các đốm màu như là một bytestream, có nghĩa là ü có lẽ chiều dài 2 (unicode), F Length của chiều dài tài sản sẽ trở lại chiều dài 1 (vì nó chỉ có một nhân vật có thể nhìn thấy) – knittl

+0

Nhưng System.Text.Encoding.ASCII.GetBytes ("ü") trả về một mảng byte với 1 phần tử. – forki23

17

Một goodie nhỏ: trong vỏ

echo -en "blob ${#CONTENTS}\0$CONTENTS" | sha1sum 
+0

Tôi đang so sánh chữ "echo -en" $ {# CONTENTS} \ 0 $ CONTENTS "| sha1sum' đến đầu ra của 'git hash-object path-to-file' và chúng tạo ra các kết quả khác nhau. Tuy nhiên, 'echo -e ...' tạo ra kết quả chính xác, ngoại trừ có một dấu '-' (' git hash-object' tạo ra * no * trailing characters). Đây có phải là điều tôi nên lo lắng không? – FrustratedWithFormsDesigner

+1

@FrustratedWithFormsDesigner: Dấu sau '-' được sử dụng bởi' sha1sum' nếu nó tính toán băm từ stdin và không phải từ tệp. Không có gì phải lo lắng về. Điều kỳ lạ mặc dù về '-n', mà nên ngăn chặn các dòng mới thường được nối thêm bởi echo. Tệp của bạn có cơ hội có dòng cuối cùng trống không, mà bạn quên thêm vào biến 'CONTENTS' của bạn? – knittl

+0

Có, bạn đã đúng. Và tôi nghĩ rằng đầu ra của sha1sum nên * chỉ * là băm, nhưng nó không khó để loại bỏ nó với sed hoặc một cái gì đó. – FrustratedWithFormsDesigner

1

Và trong Perl (xem thêm Git :: PurePerl tại http://search.cpan.org/dist/Git-PurePerl/ )

use strict; 
use warnings; 
use Digest::SHA1; 

my @input = <>; 

my $content = join("", @input); 

my $git_blob = 'blob' . ' ' . length($content) . "\0" . $content; 

my $sha1 = Digest::SHA1->new(); 

$sha1->add($git_blob); 

print $sha1->hexdigest(); 
1

Trong Perl:

#!/usr/bin/env perl 
use Digest::SHA1; 

my $content = do { local $/ = undef; <> }; 
print Digest::SHA1->new->add('blob '.length($content)."\0".$content)->hexdigest(), "\n"; 

Là một lệnh shell:

perl -MDigest::SHA1 -E '$/=undef;$_=<>;say Digest::SHA1->new->add("blob ".length()."\0".$_)->hexdigest' < file 
2

Full Python3 thực hiện:

import os 
from hashlib import sha1 

def hashfile(filepath): 
    filesize_bytes = os.path.getsize(filepath) 

    s = sha1() 
    s.update(("blob %u\0" % filesize_bytes).encode('utf-8')) 

    with open(filepath, 'rb') as f: 
     s.update(f.read()) 

    return s.hexdigest() 
+2

Điều bạn thực sự muốn là mã hóa ASCII. UTF8 chỉ hoạt động ở đây vì nó tương thích với ASCII và "blob x \ 0" chỉ chứa các ký tự có mã <= 127. –

-4

Thật thú vị khi lưu ý rằng rõ ràng là Git thêm một ký tự xuống dòng đến hết dữ liệu trước khi nó sẽ được băm. Một tệp không chứa gì hơn "Hello World!" được một băm blob của 980a0d5 ..., Mà giống như thế này:

$ php -r 'echo sha1("blob 13" . chr(0) . "Hello World!\n") , PHP_EOL;' 
+4

Dòng mới đó đang được trình soạn thảo của bạn thêm vào, chứ không phải bởi 'git hash-object'. Lưu ý rằng làm 'echo 'Hello World!" | git hash-object --stdin' cho '980a0d5 ...', trong khi sử dụng 'echo -n' cho một băm của' c57eff5 ... 'thay thế. – bdesham

+0

-1 Câu trả lời này chỉ đơn giản là sai. –

1

Sử dụng Ruby, bạn có thể làm một cái gì đó như thế này:

require 'digest/sha1' 

def git_hash(file) 
    data = File.read(file) 
    size = data.bytesize.to_s 
    Digest::SHA1.hexdigest('blob ' + size + "\0" + data) 
end 
1

Một Bash script nhỏ mà nên sản xuất ra giống với git hash-object:

#!/bin/sh 
( 
    echo -en 'blob '"$(stat -c%s "$1")"'\0'; 
    cat "$1" 
) | sha1sum | cut -d\ -f 1 
Các vấn đề liên quan