Git없이 파일에 Git SHA1을 할당하는 방법은 무엇입니까?


138

Git이 파일에 SHA1 해시를 할당 할 때이 SHA1은 내용에 따라 파일에 고유합니다.

결과적으로 파일이 한 저장소에서 다른 저장소로 이동하면 파일의 SHA1은 내용이 변경되지 않은 것과 동일하게 유지됩니다.

Git은 SHA1 다이제스트를 어떻게 계산합니까? 압축되지 않은 전체 파일 내용에서 수행됩니까?

SHA1을 Git 외부에 할당하는 것을 모방하고 싶습니다.




답변:


255

다음은 Git이 파일 (또는 Git 용어로 "블롭")에 대한 SHA1을 계산하는 방법입니다.

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

따라서 Git을 설치하지 않고도 쉽게 직접 계산할 수 있습니다. "\ 0"은 2 바이트 문자열이 아닌 NULL 바이트입니다.

예를 들어, 빈 파일의 해시 :

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

$ touch empty
$ git hash-object empty
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391

다른 예시:

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

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

다음은 Python 구현입니다.

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

이 대답은 Python 2를 가정 한 것입니까? 파이썬 3에서 이것을 시도 TypeError: Unicode-objects must be encoded before hashing하면 첫 번째 s.update()줄 에서 예외 가 발생 합니다.
Mark Booth

3
파이썬 3에서는 데이터를 인코딩 s.update(("blob %u\0" % filesize).encode('utf-8'))해야합니다 TypeError.
Mark Booth

utf-8로 인코딩하면 작동하지만 처음에는 바이트 문자열에서 빌드하는 것이 좋습니다 (utf-8 인코딩은 유니 코드 문자가 ASCII가 아니기 때문에 작동합니다).
torek

git hash-object도 데이터 내용에서 "\ r \ n"을 "\ n"으로 바꾸는 것 같습니다. "\ r"을 완전히 제거 할 수도 있습니다. 나는 그것을 확인하지 않았습니다.
user420667

1
github.com/chris3torek/scripts/blob/master/githash.py (트리는 트리 트리를 읽습니다) 파일 트리 해시 생성기 의 Python 2 + 3 (둘 다) 구현을 여기에 습니다.
torek

17

작은 케이크 : 쉘

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

1
나는 echo -en "blob ${#CONTENTS}\0$CONTENTS" | sha1sum출력 과 비교 하여 git hash-object path-to-file다른 결과를 낳습니다. 그러나 echo -e ...후행이있는 경우를 제외하고 올바른 결과를 생성합니다 - ( 후행 문자가 git hash-object생성 되지 않음 ). 이것이 내가 걱정해야 할 것입니까?
FrustratedWithFormsDesigner

2
@FrustratedWithFormsDesigner : 후행 -sha1sum파일이 아닌 stdin에서 해시를 계산 한 경우 사용됩니다 . 걱정할 것이 없습니다. 에 대한 이상한 것은 -n에코가 일반적으로 추가하는 줄 바꿈을 억제해야합니다. 우연히 파일에 빈 마지막 줄이 있는데 CONTENTS변수 에 추가하는 것을 잊었 습니까?
knittl

네 맞습니다. 그리고 sha1sum의 출력은 해시 일뿐 이지만 sed 또는 무언가로 제거하는 것은 어렵지 않다고 생각했습니다.
FrustratedWithFormsDesigner

@FrustratedWithFormsDesigner : cat file | sha1sum대신 sha1sum file더 많은 프로세스와 파이프 를 사용 하는 경우 동일한 출력을 얻을 수 있습니다
knittl

8

git이 설치되어 있지 않은 경우 bash 쉘 함수를 사용하여 쉽게 계산할 수 있습니다.

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

1
조금 더 짧습니다 : (stat --printf="blob %s\0" "$1"; cat "$1") | sha1sum -b | cut -d" " -f1.
sschuberth

4

git-hash-object 의 매뉴얼 페이지를 살펴보십시오 . 특정 파일의 git 해시를 계산하는 데 사용할 수 있습니다. 내가 생각하는 그 자식 피드 더 해시 알고리즘으로 파일의 내용 만 이상, 그러나 나는 확실히 알고하지 않으며,이 여분의 데이터에 공급 않는 경우에, 나는 그것이 무엇인지 알지 못한다.


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

이것은 F #의 솔루션입니다.


여전히 움라우트에 문제가 있습니다 : calcGitSHA1 ( "ü"). git hash-object가 움라우트를 처리하는 방법에 대한 아이디어가 있습니까?
forki23

그것은 (그것은 단지 하나의 눈에 보이는 문자이기 때문에) 아마도 길이 2 (유니 코드)가 ü 수단, F♯의 길이 속성은 길이 1을 반환 할 것, 바이트 스트림으로 방울을 처리해야
knittl가

그러나 System.Text.Encoding.ASCII.GetBytes ( "ü")는 1 개의 요소가있는 바이트 배열을 반환합니다.
forki23

문자열 길이로 UTF8과 2를 사용하면 바이트 배열이 제공됩니다. [98; 108; 111; 98; 32; 50; 0; 195; 188] 및 이에 대한 99fe40df261f7d4afd1391fe2739b2c7466fe968의 SHA1. git SHA1도 아닙니다.
forki23

1
문자열에 다이제스트를 적용해서는 안됩니다. 대신 명시 적 인코딩을 사용하여 문자열을 바이트로 변환하여 얻을 수있는 바이트 문자열 (바이트 배열)에이를 적용해야합니다.
고인돌

2

완전한 Python3 구현 :

import os
from hashlib import sha1

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

    s = sha1()
    s.update(b"blob %u\0" % filesize_bytes)

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

    return s.hexdigest() 

2
실제로 원하는 것은 ASCII 인코딩입니다. 이 ASCII와 호환되며 "BLOB는 \ 0 X"단지 코드 문자가 포함되어 있기 때문에 UTF8은 여기서 일하는 <= 127
페르디난트 바이어

1

펄에서 :

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

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

쉘 명령으로 :

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

1

그리고 Perl에서 ( http://search.cpan.org/dist/Git-PurePerl/의 Git :: PurePerl 참조 )

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

my @input = &lt;&gt;;

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

Ruby를 사용하면 다음과 같이 할 수 있습니다.

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

다음과 동일한 출력을 생성해야하는 작은 Bash 스크립트 git hash-object:

#!/bin/sh
( 
    echo -en 'blob '"$(stat -c%s "$1")"'\0';
    cat "$1" 
) | sha1sum | cut -d\  -f 1

0

자바 스크립트에서

const crypto = require('crypto')
const bytes = require('utf8-bytes')

function sha1(data) {
    const shasum = crypto.createHash('sha1')
    shasum.update(data)
    return shasum.digest('hex')
}

function shaGit(data) {
    const total_bytes = bytes(data).length
    return sha1(`blob ${total_bytes}\0${data}`)
}

-4

분명히 Git은 해시되기 전에 데이터 끝에 줄 바꿈 문자를 추가한다는 점에 주목하는 것이 흥미 롭습니다. "Hello World!"이외의 것을 포함하는 파일 980a0d5의 블롭 해시를 얻습니다.

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

4
해당 개행 문자는 님이 아닌 텍스트 편집기에서 추가하고 있습니다 git hash-object. 일을하는 것이 있습니다 echo "Hello World!" | git hash-object --stdin주는 980a0d5...사용이 동안 echo -n의 해시주는 c57eff5...대신합니다.
bdesham
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.