git은 파일 해시를 어떻게 계산합니까?


124

(에 의해 반환되는 트리 객체에 저장된 SHA1 해시 git ls-tree()에 의해 반환되는 파일 내용의 SHA1 해시 일치하지 않습니다 sha1sum)

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

git은 파일 해시를 어떻게 계산합니까? 해시를 계산하기 전에 콘텐츠를 압축합니까?



1
자세한 내용은 progit.org/book/ch9-2.html
netvope

5
netvope의 링크는 이제 죽은 것 같습니다. :이 새 위치라고 생각 git-scm.com/book/en/Git-Internals-Git-Objects 에서 §9.2입니다 git-scm.com/book
Rhubbarb

답변:


122

Git은 객체 앞에 "blob", 길이 (사람이 읽을 수있는 정수), NUL 문자를 붙입니다.

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

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


2
또한 "\ r \ n"이 "\ n"으로 대체되지만 분리 된 "\ r"만 남겨진다는 점도 언급 할 가치가 있습니다.
user420667

8
^ 위 주석 수정 : 때때로 git은 eol / autocrlf 설정에 따라 위의 대체 작업을 수행합니다.
user420667

5
이것을의 출력과 비교할 수도 있습니다 echo 'Hello, World!' | git hash-object --stdin. 선택적으로 --no-filterscrlf 변환이 발생하지 않도록 지정하거나 --path=somethi.nggit이 gitattributes@ user420667을 통해 지정된 필터를 사용하도록 지정할 수 있습니다 . 그리고 -w사실에 BLOB를 제출 .git/objects(당신이 경우 입니다 힘내의 repo에서).
Tobias Kienzler

, 등가을 표현하는 것은 이해하기 : echo -e 'blob 16\0Hello, \r\nWorld!' | shasum == echo -e 'Hello, \r\nWorld!' | git hash-object --stdin --no-filters 그것은으로도 해당 될 것입니다 \n및 15
피터 크라우스

1
echo출력에 줄 바꿈을 추가하며 이는 git에도 전달됩니다. 그래서 14 자입니다. 줄 바꿈없이 에코를 사용하려면, 쓰기echo -n 'Hello, World!'
Bouke Versteegh

36

나는 대답을 확장하고 제공 @Leif Gruenwoldt하는 참조에 무엇이 있는지 자세히 설명 하고 있습니다.@Leif Gruenwoldt

스스로 해..

  • 1 단계. 저장소에 빈 텍스트 문서 (이름은 중요하지 않음)를 만듭니다.
  • 2 단계. 문서 준비 및 커밋
  • 3 단계. 다음을 실행하여 Blob의 해시를 식별합니다. git ls-tree HEAD
  • 4 단계. blob의 해시를 찾습니다. e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
  • 5 단계. 놀라움에서 벗어나 아래를 읽어보세요.

GIT는 커밋 해시를 어떻게 계산합니까?

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

텍스트 blob⎵는 상수 접두사이며 \0상수이며 NULL문자입니다. <size_of_file><contents_of_file>파일에 따라 달라집니다.

참조 : git commit 객체의 파일 형식은 무엇입니까?

그리고 그게 다야!

하지만 기다려! , <filename>해시 계산에 사용되는 매개 변수가 아님 을 눈치 챘습니까? 두 파일의 내용이 생성 된 날짜 및 시간과 이름에 관계없이 동일한 경우 잠재적으로 동일한 해시를 가질 수 있습니다. 이것이 Git이 다른 버전 제어 시스템보다 이동 및 이름 변경을 더 잘 처리하는 이유 중 하나입니다.

스스로해라 (Ext)

  • 6 단계. 다른 빈 파일을 만듭니다. filename 동일한 디렉토리에
  • 단계 7. 두 파일의 해시를 비교합니다.

노트 :

링크는 tree객체가 해시되는 방법을 언급하지 않습니다 . 나는 알고리즘과 매개 변수에 대해 확신하지 못하지만 내 관찰에서 아마도 포함 된 모든 blobstrees(아마도 해시)를 기반으로 해시를 계산합니다.


SHA1("blob" + <size_of_file>-Blob과 크기 사이에 추가 공백 문자가 있습니까? 크기는 십진수입니까? 접두사가 0입니까?
osgx

1
@osgx 있습니다. 참조와 내 테스트는 그렇게 확인합니다. 답을 정정했습니다. 크기는 접두사가없는 정수로 바이트 수인 것 같습니다.
Samuel Harmer

13

git hash-object

다음은 테스트 방법을 확인하는 빠른 방법입니다.

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

산출:

f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f
f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f  -

어디 sha1sumGNU Coreutils는 ?

그런 다음 각 개체 유형의 형식을 이해해야합니다. 우리는 이미 사소한 부분을 다루었 blob으며 다른 부분은 다음과 같습니다.


이전 답변에서 언급했듯이 길이는 $(printf "\0$s" | wc -c). 추가 된 빈 문자에 유의하십시오. 즉, 문자열이 'abc'이고 앞에 빈 문자가 추가 된 경우 길이는 3이 아닌 4가됩니다. 그러면 sha1sum을 사용한 결과가 git hash-object와 일치합니다.
Michael Ekoka

당신 말이 맞아요. 여기에 echo -e 대신 printf를 사용하면 약간의 유해한 부작용이있는 것 같습니다. 문자열 'abc'를 포함하는 파일에 git hash-object를 적용하면 8baef1b ... f903을 얻게됩니다. 이는 printf가 아닌 echo -e를 사용할 때 얻는 것입니다. echo -e가 문자열 끝에 줄 바꿈을 추가한다면 printf와 동작을 일치시키기 위해 똑같이 할 수 있습니다 (예 : s = "$ s \ n").
Michael Ekoka

3

Leif Gruenwoldt 답변을 기반으로 다음과 같은 셸 함수 대체가 있습니다 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/ .*$//'
}

테스트:

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

3

파이썬 3의 일부 단위 테스트를 위해 이것이 필요했기 때문에 여기에 남겨 두겠다고 생각했습니다.

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()

나는 \n모든 곳 에서 줄 끝을 고수 하지만 어떤 상황에서는 Git 이이 해시를 계산하기 전에 줄 끝을 변경할 수도 있으므로 .replace('\r\n', '\n')거기에도 필요할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.