3 Git의 안을 들여다보고 Git이 어떻게 작동하는지 알려드리겠습니다. 너무 디테일한 것들은 알아서 빼놓고 설명해드리겠습니다.
4 그래도 자세히 알고 싶은 분들은: http://schacon.github.com/git/user-manual.html[the user manual]로 가시길 바랍니다.
8 Git은 어떻게 눈에 띄지않게 강력한 툴일 수 있을까요? 습관적으로 하게되는 commit과 병합을 제외하고, VCS자체가 컴퓨터에 설치되지 않은 것 같아보일 때가 있습니다.
10 다른 VCS들은 사용할때 쓸때없이 많은 절차와 검열 등으로 고생할 수 있습니다. 파일의 보안상태가 '읽기전용'으로 세팅되어 작업을 하려고 할 때마다 중앙서버에 승인을 요청해야 할 경우도 빈번합니다.
11 그리고 VCS의 유저들이 많아질수록 업무처리 속도가 현저히 떨어질수도 있습니다. 그리고 중앙서버나 네트워크가 다운 될 경우에는 아무런 작업을 할 수 없죠.
13 반면에, Git은 당신의 로컬컴퓨터 디렉토리에 '.git'디렉토리를 형성하여 그곳에 작업기록을 하게됩니다. 그 기록은 온전히 당신만의 것이죠. 그렇기에 오프라인 상태에서도 작업을 끊기지 않고 진행할 수 있습니다.
14 그리고 작업중인 파일에 대해 모든 권한을 가지고 있게 해주죠.
18 대부분의 사람들은 크립토그래피를 어떤 정보를 비밀스럽게 숨기는 정도로 생각합니다. 그러나 크립토그래피의 진정한 목적은 정보를 보안하는 것이죠. 크립토그래피로 보호되는 hash는 데이터가 공격받거나 지워질 위험으로부터 보호해줍니다.
20 SHA1 hash는 고유의 160-비트 ID 번호로 생각하시면 됩니다. 그리고 이 번호는 당신이 평생, 또는 열번의 삶을 살 정도의 시간에, 쓸 byte에 부여되는 번호이기에 보안이 철저합니다.
22 SHA1 hash 자체 역시 byte로 구성되어 있기에 SHA1 hash의 hash를 만드는 것도 가능합니다. 이건 생각보다 유용한 기능입니다: 'hash chains'를 한번 살펴보세요. 우리는 나중에 Git이 이 기능을 어떻게 사용해서 효율적으로 데이터를 보호하는지 살펴보겠습니다.
24 짧게 말하자면, Git은 당신의 모든 데이터를 `.git/objects`섭디렉토리에 저장합니다. 그리고 보통의 파일이름대신 각 파일에 지정된 ID를 통해서 이 파일들을 찾을 수 있습니다. 그렇기에 Git은 보통의 파일시스템을 뭔가 굉장히 효율적인 데이터베이스로 변화시켜줍니다.
28 어떻게 Git이 당신이 파일의 이름을 변경할 때 Git에게 이름을 바꾼다 말한적도 없는데 알수 있을까요? *git mv*를 실행할수도 있겠지만 그것은 *git rm*을 사용하고 *git add*를 사용하는 것과 같습니다.
30 Git은 단순화된 지침으로 재설정된 파일명을 찾아내고 버전사이의 카피들을 만들어냅니다. Git은 코드들이 옮겨지고 카피되고 지워질 경우를 다 알아낼수 있죠. 모든 경우의 수를 다 알수는 없겠지만, Git은 대부분을 알아채고 있고 이 자동화된 기능은 날이 갈수록 발전하고 있습니다.
35 Git은 Git이 트랙킹하는 모든 파일들의 크기, 생성된 시간, 마지막 편집시간을 색인 (index)를 이용해서 기록하여 둡니다. 만약에 어떤 파일에 작업하여 변화가 생겼다면 Git은 현 파일상태와 인덱스에 저장되어있는 상태를 비교하여 파일의 변화를 감지합니다. 만약에 서로간의 차이가 없다면 Git은 그 파일이 가장 최신버전으로 업데이트되었다고 생각하고 더 이상 읽지 않습니다.
37 인덱스 정보를 읽는 작업은 파일을 읽는 것보다는 훨씬 빠르게 진행되니 당신이 한 작업들은 Git이 아주 빠르게 업데이트 해줄 것입니다.
39 인덱스는 마치 중간 대기 구역과 같다고 말씀드린 적이 있습니다. 왜 그렇게 얘기 했을까요? *Add* 명령어는 파일들을 순전히 업데이트 시켜 데이터베이스에 업데이트 하지만
40 *commit*은 (별도의 옵션을 사용하지 않는다는 전제하에) 자체적인 로컬데이터베이스에 있는 파일들에 대한 commit만 진행하지요.
44 http://lkml.org/lkml/2005/4/6/121[Linux Kernel Mailing List post] 에서 Git의 역사를 나열하여 설명해줍니다. Git의 역사학자들에게 정말 흥미있는 웹사이트지요.
48 데이터의 모든 버전은 '오브젝트 데이터베이스'에 보관되며, 이 데이터베이스는 `.git/objects`의 섭디렉토리에 상주하고 있습니다. `.git/`에 있는 다른 파일들은 더 적은 정보를
49 담고 있지요 (인덱스, branch 이름, 태그, 설정 정보, 로그, head commit의 위치 등). 오브젝트 데이터베이스는 Git의 기본이지만 우아하고 또 Git의 힘의 원천입니다.
51 `.git/objects'에 있는 파일들은 각각 오브젝트입니다. 그리고 크게 세가지 오브젝트로 나눌수 있습니다:
52 'blob' 오브젝트, 'tree' 오브젝트, and 'commit' 오브젝트.
56 첫째, 마술을 보여드리겠습니다. 우선 아무 파일 이름을 선택하십시오. 빈 디렉토리에서 :
58 $ echo sweet > YOUR_FILENAME
61 $ find .git/objects -type f
63 +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+ 을 보게 될 것입니다.
65 저는 파일이름을 알지도 못하는데 이걸 제가 어떻게 알까요? 왜냐하면
67 "blob" SP "6" NUL "sweet" LF
69 의 SHA1 hash 는 aa823728ea7d592acc69b36875a482cdf3fd5c8d 이고,
70 SP 는 공간이며, NUL 는 0 바이트이고, LF는 라인피드이기 때문입니다. 직접 확인해보시려면 다음 명령어를 입력하세요:
72 $ printf "blob 6\000sweet\n" | sha1sum
74 Git은 콘텐츠 주소를 지정하는 것이 가능합니다: 파일은 파일 이름에 따라 저장되지 않습니다.
75 대신에 hash로 저장이 되며 이런 정보는 blob 오브젝트라고 불리우는 곳에 저장되어 있지요.
76 우리는 hash를 파일 내용에 대한 고유 ID로 생각할 수 있습니다. 그래서
77 어떤 의미에서 우리는 파일의 내용에 따라 주소를 지정하는 것으로 생각이 가능합니다. 초기`blob 6`는
78 오브젝트 타입과 크기를 저장해 놓은 header나 다름없습니다; 단지 내부 부기를 단순화합니다.
80 따라서 저는 당신이 보게 될 것을 쉽게 예측할 수 있었습니다. 파일 이름은 관련이 없습니다.
81 내부 데이터만을 이용해 blob 오브젝트를 구성하는게 가능합니다.
83 그러면 당신은 동일한 파일이 어떻게되는지 궁금 할 수 있습니다. 우선 아무런 파일 이름을 사용해 복사본을 추가해보십시오.
84 + .git / objects +의 내용은 몇개의 복사본을 추가해도 동일합니다. Git은 데이터를 한 번만 저장합니다.
86 별개로, + .git / objects + 내의 파일은 zlib로 압축되어 있으므로
87 그들을 직접 보지 마십시오. http://www.zlib.net/zpipe.c[zpipe -d] 를 통해 필터링을 해서
88 보시던지, 아니면 다음 명령어를 실행해 보시면 됩니다.:
90 $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d
96 그러나 파일 이름은 어디에 간거죠? 그들은 어떤 단계에서 어딘가에 저장되어야합니다.
97 Git은 커밋 중에 파일 이름을 찾습니다.
99 $ git commit # Type some message.
100 $ find .git/objects -type f
102 이제 3 개의 개체를 보게 될 것입니다. 이번에는 당신이 선택한 파일 이름에 부분적으로 의존하기 때문에 두 개의 새 파일이 무엇인지 제가 말씀드릴 수 없습니다. ``rose''를 파일이름을 지정한 것으로 가정하여 진행하겠습니다. 그렇게 설정하시지 않은 경우 기록을 다시 작성하여 그렇게 지정한 것처럼 보이게 할 수 있습니다.
104 You should now see 3 objects. This time I cannot tell you what the 2 new files are, as it partly depends on the filename you picked. We'll proceed assuming you chose ``rose''. If you didn't, you can rewrite history to make it look like you did:
106 $ git filter-branch --tree-filter 'mv YOUR_FILENAME rose'
107 $ find .git/objects -type f
109 그럼 이제 +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+를 보실 수 있을겁니다. 왜냐하면 이것이
112 "tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d
115 다음을 입력하여 이 파일에 실제로 위 내용이 포함되어 있는지 확인하십시오.
117 $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch
119 zpipe으로 이 hash를 확인하는 것이 아마 쉬운 방법일 겁니다:
121 $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum
123 Hash 확인은 cat-file로는 좀 어려울 수도 있습니다. 왜냐하면 압축되지 않은 원래의 파일보다 더 많은 파일을
127 This file is a 'tree' object: a list of tuples consisting of a file
128 type, a filename, and a hash. In our example, the file type is 100644, which
129 means `rose` is a normal file, and the hash is the blob object that contains
130 the contents of `rose'. Other possible file types are executables, symlinks or
131 directories. In the last case, the hash points to a tree object.
133 If you ran filter-branch, you'll have old objects you no longer need. Although
134 they will be jettisoned automatically once the grace period expires, we'll
135 delete them now to make our toy example easier to follow:
137 $ rm -r .git/refs/original
138 $ git reflog expire --expire=now --all
141 실제 프로젝트의 경우 일반적으로 이와 같은 명령을 피해야합니다. 이 명령어들은 백업들을 지울 수 있기 때문이죠.
142 깨끗한 repository를 원한다면 일반적으로 새로운 클론을 새롭게 만드는 것이 좋습니다. 그리고 +.git+을 조작할때는
143 주의하십시오: 만약에 여러가지 커맨드들이 동시에 실행 중이거나 갑작스러운 정전이 발생하면 안되잖아요.
144 일반적으로 refs는 *git update-ref -d*로 삭제해야합니다. 그래도 +refs / original+를 수동으로 제거하는 것이 안전하긴 하지만요.
148 우리는 3 개의 오브젝트들 중 2 개를 설명했습니다. 세 번째는 'commit'오브젝트입니다. 이 오브젝트의
149 내용은 commit 메시지와 날짜 및 시간에 따라 다릅니다.
150 여기에있는 것과 일치 시키려면 약간의 조정이 필요합니다.
152 $ git commit --amend -m Shakespeare # Commit 메시지를 바꿉니다.
153 $ git filter-branch --env-filter 'export
154 GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800"
155 GIT_AUTHOR_NAME="Alice"
156 GIT_AUTHOR_EMAIL="alice@example.com"
157 GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800"
158 GIT_COMMITTER_NAME="Bob"
159 GIT_COMMITTER_EMAIL="bob@example.com"' # 타임스탬프와 저자를 바꿉니다.
160 $ find .git/objects -type f
162 그럼 이제 +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+ 를 보게될것 입니다.
165 "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF
166 "author Alice <alice@example.com> 1234567890 -0800" LF
167 "committer Bob <bob@example.com> 1234567890 -0800" LF
172 이전과 마찬가지로 zpipe 또는 cat-file을 실행하여 직접 확인할 수 있습니다.
173 이것은 첫 번째 commit이므로 부모 commit이 없지만 이제 나중에 commit을 할때마다
174 항상 부모 commit을 식별하는 한 줄이 포함될것 입니다.
176 === 마술과 분간이 안되는 프로그램 ===
178 Git의 비밀은 너무 단순 해 보입니다. 몇 시간 안에 몇 개의 셸 스크립트를 혼합하고 C 코드를 추가하여 몇 시간 만에 만들 수있는 것 같아 보입니다. 결국에 Git은 견고성을 위해 잠금 파일과 fsync로 장식 된 기본 파일 시스템 작업과 SHA1 해싱의 혼합으로 구성된 프로그램입니다. 실제로 이것은 Git의 초기 버전을 정확하게 설명합니다. 그럼에도 불구하고 공간을 절약하기위한 독창적인 패킹 트릭과 시간을 절약하기위한 독창적 인 인덱싱 트릭을 포함해 이제 우리는 Git이 버전 컨트롤을 위한 완벽한 데이터베이스로 거듭나게 되는지 알게되었습니다.
180 예를 들어, 오브젝트 데이터베이스 내의 파일이 디스크에 의해 손상된 경우
181 오류가 발생하면 hash가 더 이상 일치하지 않아 문제를 알려줍니다.
182 기존 hash에 다른 hash를 지정해 주면서, 우리는 모든 수준에서 무결성을 유지합니다. Commit은 부분적으로
183 파일을 관리하지않고 전체적으로 프로젝트를 관리하여줍니다.
184 Commit의 hash를 계산하고서 모든 tree, blob 과 부모 commit들을 저장한 후에 데이터베이스에 저장합니다.
185 오브젝트 데이터베이스는 정전과 같은 예상치 못한 방해에 영향을받지 않습니다.
187 우리는 Git으로 가장 사악한 적도 물리칠수 있습니다. 만약에 누군가가
188 아주 예전 버전의 프로젝트에서 파일 내용을 은밀하게 수정한다고 생각해봅시다.
189 오브젝트 데이터베이스를 정상 상태처럼 보이게 유지하려면 해당 데이터베이스의 해시도 변경해야 할겁니다.
190 이 말은 해당 파일을 참조하는 tree 오브젝트의 hash도 변경해야한다는 의미입니다.
191 그리고 차례로 그러한 tree를 포함하는 모든 commit 오브젝트의 hash도 변경해야 한다는 거지요.
192 이러면 해당 파일 이후의 commit모두 변경을 해야한다는 말이됩니다.
193 이것은 공식 헤드의 hash는 나쁜 repository의 hash와 달라질 수 밖에 없다는 것입니다. 그러니
194 일치하지 않는 hash의 흔적을 따라 나쁜의도로 변경된 파일을 정확히 찾아 낼 수 있습니다.
195 처음에 손상된 commit도 마찬가지로 찾을 수 있습니다.
197 짧게 말하자면, 마지막 commit을 나타내는 20 바이트가 안전하다면
198 Git repository를 임의로 아무에게도 들키지 않게 조작하는 것은 불가능합니다.
200 Git의 유명한 기능은 또 어떻습니까? branch 만들기? 병합하기? 태깅하기?
201 세부 사항. 현재 head는 + .git / HEAD + 파일에 보관됩니다.
202 commit 오브젝트의 hash를 포함합니다. Commit 중에 hash가 업데이트됩니다.
203 Branch는 거의 동일합니다: 그것들은 +.git/refs/heads+에 저장된 파일들 입니다. 태그도 +.git/refs/tags +에 있지만