Git Internals 정리 :: Git은 어떻게 동작할까?
Git의 내부 동작 방식
Git은 복잡하고 처음에는 매뉴얼만으로 이해하긴 어려웠다...
그리고 가끔씩 무슨 소리인지 모르겠는 경고문을 보여주곤 한다...
나도 처음에 막 건들다가 아래와 같은 경고문을 보게 되면 정말 당황한 적이 있다.
Detached Head?? Linked List의 헤드인가...? 왜 Head가 분리됐지...?
Git의 문서를 보면 아직 이해가 안 되거나 아주 중요한 내용인지 모르고 그냥 읽고 넘어가버릴 때가 많다... 매뉴얼은 이해하기 어렵고 Git의 주의점까지 다 외워야 할까? ㅠㅠ
나는 Pro Git의 마지막 장인 Git Internals(Git 내부 동작)를 읽으며 모든 막막함이 자연스럽게 해결되었다.
[참고] Pro Git이란?
Git의 교과서이다.
정리가 상세하게 되어있어서 웬만한 건 여기서 힌트를 얻을 수 있었다.
나는 제일 마지막 장(Git Internals)을 읽고 Git을 사용하는 데에 큰 도움을 얻었다.
번역도 괜찮고 https://github.com/progit/progit2에서 무료로 보거나 다운로드할 수 있다.
+) "Git Internals", "Git 내부 동작 방식"은 의외로 어렵지 않았습니다! 처음에 제목만 보고 겁먹었는데 오히려 어떻게 만들었는지 따라가다 보면 재미있었고 유익했습니다.
Git의 내부 동작 방식을 이해하면 이 명령어가 대충 어떻게 작동할지 예상이 간다. 오히려 Git의 내부 동작 방식을 이해하면 문서가 훨씬 더 읽기 쉬워진다! 해서는 안될 실수도 알 수 있다.
그러니 Git이 어렵다면 또는 Git 문서가 난해하게 느껴진다면 Git Internals를 한번 공부해 보자. 이 글은 내가 공부하면서 기억하고 싶었던 내용을 정리하는 글이다.
현재 추적 중인 파일들이 저장된 자료구조 - index
.git/index
에 위치한다.index
는 현재 브랜치가 추적 중인 모든 파일을 기록/관리한다.
흔히git add [파일명]
또는 Stage Area에 올리는 것을index
에 기록한다고 표현한다.index
는 그냥 출력해서 읽은 순 없기 때문에 명령어로 읽어야 한다.
- index 파일 출력:
git ls-files --stage
또는git ls-files -s
git add [파일명]
하면 index 파일의 내용이 바뀐다.- 다음 commit이 준비되는 장소이다.
- 내용
- 파일명, object SHA-1, mode bit, stage number
Git이 파일 관리하기 위해 만드는 파일 - Object(tree, blob, commit, tag)
.git/objects
폴더에 저장되어 있다. 이 것을 loose하게 저장된 파일이라고 말한다.
- Git의 모든 파일은 오브젝트로 관리한다. 내 파일의 텍스트 내용도 commit내용도 다 오브젝트로 만들어서 관리가 된다.
- Git은 commit마다 새로운 오브젝트를 만들어 낸다.
- 내가 아주 작은 부분만 저장해도 바뀐 부분만 저장하는 게 아니라 전체의 텍스트 내용을 저장하는 오브젝트를 만든다.
- 이것을 특정 시점의 사진을 찍는 것과 비슷하다고 해서
Snapshot
저장 방식이라고 부른다.
Git이 매번 파일의 Snapshot을 찍어서 저장하면 공간 낭비가 심합니다.
내가 소스코드에 스페이스바 하나만 쳐도 그 파일 전체를 저장하는 오브젝트를 파일을 만들어서 그래요. ㅠㅜ
만약에 매번 Snapshot만 찍는다면 1년만 지나도 어마 어마한 공간 낭비됩니다.
이것을 해결하기 위해 Git은 주기적으로 오브젝트 파일들을 하나로 압축(Pack up)합니다.
Object의 압축 - Packfiles
- 오브젝트 파일들이 너무 많아지면 Git 압축해
.git/pack
폴더 아래에 오브젝트들을 압축한바이너리 파일(*.pack)
과 그 파일에 무엇이 압축되었는지 기록한index 파일
이 만들어진다. 이 것을 압축해서 저장한packfile
이라고 한다. - 원래는 특정 시점(Ex. Push할 때)에 Git이 판단해서
packfile
로 오브젝트 파일들을 압축하지만 우리가 명령어(git gc
)를 쳐서 수동으로 압축할 수도 있다.
Object
- 오브젝트를 담고 있는
폴더명+오브젝트의 파일명=SHA-1값
이다.
한번 실제로 확인해 보자.
Test.txt파일을 만들어서 "Hello World"를 저장하고 commit했다.
.git/objects 폴더 안에는 어떤 변화가 생겼을까?
Git은 모두 오브젝트로 파일들을 관리한다 했다!
위에서 하나는 Hello World
라는 텍스트 내용을 저장하는 오브젝트, 또 다른 하나는 commit 내용을 저장하는 오브젝트이다.
commit을 오브젝트로 만든다고 하면 git은 commit의 SHA-1값의앞부분 두 글자를 폴더명
,나머지를 파일명
으로 하고 그 파일 안에 commit내용을 저장합니다! 이 사실을 확인해 보는 예제를 진행하는 중입니다.
git log
를 통해 방금 만든 commit의 SHA-1 값을 확인해 보자.
94ae61a1cc407d40c07660277efa4d8df53dd577
이다.
이것을 통해 우리는 내부적으로 .git/objects/94 라는 폴더가 생기고 94라는 폴더 아래에 ae61a1cc407d40c07660277efa4d8df53dd577라는 commit 오브젝트 파일이 생성됐다는 사실을 알 수 있다.
어떤 정보가 있을지 commit 오브젝트 파일을 출력해서 확인해 보자. 그런데 오브젝트 파일들 역시 loose하게 git이 압축을 해놓는다. 그래서 그냥 바로 열어서 보지는 못한다. 오브젝트 파일을 우리 문자로 출력해 주는 git cat-file
명령어를 사용해야 한다.
git cat-file -t [SHA-1값]
로 오브젝트 파일의 타입을 확인해 보자.
오브젝트 파일의 type을 출력하는데 쓰는 명령어입니다.git cat-file -t [오브젝트 SHA-1값]
git cat-file -p [SHA-1값]
으로 오브젝트 파일 안에 어떤 내용이 있는지 확인해 보자.
오브젝트 파일의 내용을 pretty하게 출력하는데 쓰는 명령어입니다. (p
가 pretty의 약자...)git cat-file -p [오브젝트 SHA-1값]
살펴보니 tree
라는 다른 오브젝트를 가리키는 SHA-1값parent
라는 다른 오브젝트를 가리키는 SHA-1값author
와 누가 커밋
했는지 그리고 커밋 메시지
가 뭔지 나온다.
내 commit의 내용을 잘 담고 있는 것을 확인할 수 있었다.
하지만 내 파일명과 안의 내용(snapshot)은 어디에 있는 걸까?
Test.txt에 "Hello World"
을 저장해서 commit했다. git은 commit할 때의 파일 내용을 또 다른 blob이라는 타입의 오브젝트
에 저장한다.
우리가 commit할 때를 생각해 보면 우리는 한 가지 파일만 수정하는지 않는다. 그래서 한 commit에는 여러 가지 파일들의 변경 사항이 저장된다. 그리고 파일의 각각의 결로도 다르다. Git을 그 내용을 commit에 다 때려 박기보다는 변경된 파일의 내용을 저장하고 있는 blob 오브젝트(snapshot)
을 모아놓기 위한 tree 오브젝트
를 만들어서 사용한다. 그래서 현재 commit에는 저장된 파일에 대한 직접적인 정보가 없고 tree 오브젝트에 대한 링크만 있다.
그러므로 내 commit 오브젝트
가 가리키는 tree 오브젝트
를 열어보면 commit할 때의 내 파일의 내용이 저장된 blob 오브젝트
를 찾을 수 있다.
tree 오브젝트
의 내용을 열어보자.
그럼 마지막으로 저 blob 오브젝트
를 열어보자!
Test.txt파일 안의 내용인 "Hello World"
가 저장되어 있다!
여기서 알 수 있는 중요한 사실이 있다. blob 오브젝트
는 파일의 내용만 저장하지 파일명까진 저장하지 않는다는 것이다!! 파일명은 무조건 tree
나 index
같은 다른 오브젝트에 의해서 링크되어야 한다.
Object의 종류와 그 역할 정리
blob
: 텍스트 내용 저장, 파일명에 대한 정보는 저장하지 않음tree
: 파일명 + 해당하는 blob 오브젝트의 SHA-1 값, 다른 디렉토리 가리키는 tree 오브젝트 SHA-1 값 저장commit
: tree SHA-1 값, parent 오브젝트 SHA-1 값, author, committer, commit 메시지 저장tag
: tag
추가로 보면 좋은 글
썸네일 출처
-
Icons made by Flat Icons from www.flaticon.com
댓글
이 글 공유하기
다른 글
-
Fork한 저장소에 upstream의 최신 commit 가져오기 :: GitHub Compare / Bitbucket / add remote
Fork한 저장소에 upstream의 최신 commit 가져오기 :: GitHub Compare / Bitbucket / add remote
2020.04.09 -
Git의 파일 상태 구분법
Git의 파일 상태 구분법
2020.04.09 -
.gitignore가 동작 안할 때 상황별로 해결하기
.gitignore가 동작 안할 때 상황별로 해결하기
2020.02.29 -
GitHub README.md 이미지가 갱신/업데이트 안 되는 경우 해결법
GitHub README.md 이미지가 갱신/업데이트 안 되는 경우 해결법
2020.02.20