변경사항을 압축해 저장하는 objects 폴더
objects 폴더 내부를 보면 두 글자 이름을 가진 폴더들이 있다.
objects 폴더는 git의 모든 변경사항을 압축해서 파일들을 각 폴더 내부에 저장한다. 이 내부에는 commit, tree, blob, annotated tag들이 저장되며, git을 통해 히스토리를 확인할 수 있는 것은 바로 이 폴더 내부에 모든 스냅샷들이 저장되기 때문이다. 저장되는 방식은 hash에 해당하는 압축된 변경사항으로 저장된다.
예를 들어 위 폴더 중 02에는 7f7aa 로 시작하는 파일명인 파일이 있다. 이 파일은 Git의 저장의 기본 단위인 blob로, blob 파일 내부에는 027f7aa 해시값에 대한 변경사항이 압축되어 있다. 깃에서는 blob 파일의 압축을 풀어 스냅샷을 읽어들인다. 물론 blob만 저장되는 것은 아니다. 이 object 폴더 내부의 특정한 파일은 commit을 가리키고, tree를 가리키기도 한다. 하지만, 이번 글에서는 가장 기본 단위인 blob에 대해서만 다룬다.
$ ls
02 17 1f 36 45 4c 6d 78 8f a2 c9 d8 e9 info
05 1b 28 40 46 4d 6f 7f 99 bd ca da f0 pack
13 1d 2f 44 48 62 72 81 9e c2 cc e0 f8
$ vim 02/7f7aa0b6a2c917543b8643f6238c83ff9169f2
x^A<9d><8e>K
Â@^P^E]Ï)z/È|2?^P^Q<<82>'èét'^A<93><91>8Yx{^C¹<81>ËWPÅ£:ÏS^CëÌ©Ì@<94>H<8a>^M%æbÑI^_:^Q<8f>â9é^\KN<88>A÷^ZÕ^[W^^Zô<89>^K¥<98>^\gk¼&ã3^Rû<80>É<91><8e>V¨Ëb%^F<85>[^[ë
OþÖm^Yà1V¸~<8e>Acµ÷aÆéu¡:ßÀ<84>θì<8c> pÖYkµÓýdã?u%<93>´^Q<8e><8a>ú^AeSJ<89>
이 파일들은 압축되고 암호화 되어 있어 git 명령어로만 조작이 가능하다. 아래에서 어떻게 해시와 파일을 만들고, 변경사항을 압축하고, 압축을 푸는지 알아보자.
데이터 저장소로써의 Git : object 폴더에 hash 생성하기
즉 git은 모든 변경사항들을 key-value 형태로 저장하는 저장소이다. Git 특정한 변경 사항을 commit hash라 불리는 key값에 압축된 파일(blob)을 대응시켜서 저장한다. 예를 들어 위의 예제에서는 02/7f7aa0... 위치에 027f7aa0...해시값에 대응되어 압축된 파일이 대응되어 저장된다.
*해시값 앞 두글자가 폴더명이다
자 이번에는 Key에 해당하는 Hash를 어떻게 만드는지 알아보고, 위 object 폴더에 Hash에 대응하는 폴더와 파일을 만들어보자.
Git 의 Hashing Function
git에서 사용하는 Hashing Function은 SHA-1 이다. SHA-1은 어떤 값을 넣던지 40자리 16진수 숫자로 만들어주는 암호화 알고리즘이다.
따라서 변경사항이 몇개이든 이 Hashing Function을 사용하면 하나의 Hash 값으로 만들어진다. Git에서는 이러한 방식을 기초로 커밋 해시 값을 만들어낸다. git에서는 파일 뿐만 아니라 디렉토리, 커밋들을 저장하기 위해 hashing function을 사용한다.
*git folder 내부에서 해시 중복이 일어날 확률은 0에 수렴한다.
Git의 blob가 만들어지는 과정
git object hash 만들기
Git의 blob는 우리가 변경한 파일에 대해 해당 파일을 hash-object 하라는 요청을 했을 때 git에서 생성한다. git hash-object [파일명] 명령어를 통해 요청이 가능하다.
git hash-object [파일 명]
이 글에서는 git의 hashing 을 간단히 확인해보기 위해서 다음의 명령어를 사용한다. 문자열 입력받아 해시값으로 만드는 명령어이다.
echo [문자열] | git hash-object --stdin
예를 들어 "what's up Kotlin World" 라는 문자열에 git 해싱을 적용해보자. 다음과 같이 "echo -n "what's up Kotlin World" | git hash-object --stdin" 를 통해 해싱이 가능하다.
$ echo -n "what's up Kotlin World" | git hash-object --stdin
3b223da1bfc54e2c54e85a8cd57279eb312cc26b
$ echo -n "what's up Kotlin World" | git hash-object --stdin
3b223da1bfc54e2c54e85a8cd57279eb312cc26b
오류가 나서 두 번 한거 같아 보이지만, 같은 값에 대해 해시값이 나오는 것을 보여주기 위해 같은 명령어를 2번 수행했다. 이 말은 즉 모든 변경 사항이 같은 파일에 대해 해시를 생성하면 같은 해시값이 생성됨을 뜻한다. 하지만, 파일을 대상으로 하는 해시는 그렇게 될 확률이 0에 수렴한다. 인위적으로 만들지 않는 이상 평생 쓰더라도 같은 해시값을 찾기 힘들 것이다.
git object hashing 후 저장하기
위에서는 특정 object에 대한 해싱을 진행했다. 하지만, 위의 방식만으로는 해싱된 값이 objects 폴더에 저장되지 않는다. objects 폴더에 저장하기 위해서는 추가적으로 -w(write) 옵션을 사용해야 한다.
git hash-object -w [파일 명]
이 글에서는 git의 hashing 한 후 저장을 간단히 확인해보기 위해서 다음의 명령어를 사용한다. 문자열 입력받아 변경사항을 해시값에 대응시켜 저장하는 명령어이다.
echo [문자열] | git hash-object --stdin -w
이를 테스트 하기 위해 새로운 git repo를 만들어 .git의 object 폴더 내부를 보자
$ ls .git/objects/
info pack
이 옵션을 사용해 해싱을 진행하면 git에 작성된 hash값이 리턴된다.
➜ $ echo -n "what's up Kotlin World" | git hash-object --stdin -w
3b223da1bfc54e2c54e85a8cd57279eb312cc26b
그러면 .git 폴더의 objects 내부에 다음 값이 생성되는 것을 확인할 수 있다. 여기서 3b는 3b223da로 시작하는 위 해시값의 앞 두 글자이다.
$ ls .git/objects
3b info pack
이 폴더 내부를 보면 3b를 제외한 223da1bf.. 에 대한 파일이 생성되어 있음을 확인할 수 있다.
$ ls .git/objects/3b
223da1bfc54e2c54e85a8cd57279eb312cc26b
즉, 위에서 우리가 생성한 해시값에 대한 파일이 앞 2글자를 폴더 이름으로 나머지를 이름으로 해서 저장한다. 우리는 이 파일을 blob라고 부르며, 파일(223da1..)은 변경사항을 저장한 값이다.
git object hash값으로 압축 풀기
blob 파일을 vim으로 보면 압축된 파일이기 때문에 모든 내용이 알아볼 수 없는 형태로 나온다. 압축된 파일을 확인하기 위해서는 git에서 파일의 압축을 풀어 출력하는 git cat file -p [파일 명] 명령어를 사용해야 한다.
git cat file -p [해시값]
예를 들어 위에서 만든 3b223da.. 에 해당하는 해시값의 변경 사항을 알기 위해서는 다음과 같은 명령어를 사용하면 된다.
$ git cat-file -p 3b223da1bfc54e2c54e85a8cd57279eb312cc26b
what's up Kotlin World
여기서 주의할 점은 해시값으로 확인해야 한다는 것이다.
정리
Git 내부에서는 해시값을 Key로 사용하고 변경사항을 압축한 파일을 Value로 사용해 해시 값에 대한 변경 사항을 저장한다. Value 에 해당하는 파일의 압축을 풀면 원본 파일이 나오므로 저장된 모든 파일의 복구가 가능해지는 것이다. git에서는 이를 바탕으로 모든 파일들을 관리한다.
git object 관련 명령어
git cat-file -t [hash] : 타입 확인
git cat -file-p [hash] : 해시 파일에 압축된 내용 print