[기술 딥다이브] Git은 내부적으로 어떻게 저장하나


한 줄 요약: Git은 변경 사항(diff)이 아니라 커밋마다 전체 스냅샷을 저장하고, blob·tree·commit 객체를 내용의 SHA로 키 지정해 보관하는 콘텐츠 주소 저장소다.

Git은 ’변경 사항(diff)’을 저장한다고 흔히 생각하지만, 실제로는 스냅샷 전체를 저장합니다. 그리고 그것을 SHA로 키를 지정한 ’객체’로 보관합니다. 내부 구조를 풀어 설명하겠습니다.

Git은 diff가 아니라 스냅샷을 저장한다

다른 VCS는 파일별 ’변경분(delta)’을 누적합니다. Git은 커밋마다 해당 시점의 프로젝트 전체 스냅샷을 저장합니다. 변경되지 않은 파일은 새로 저장하지 않고 이전 것을 가리키기만 하여(즉 같은 객체를 재사용하여) 효율성을 확보합니다.

네 가지 객체: blob, tree, commit, tag

Git의 모든 데이터는 .git/objects객체로 저장됩니다.

  • blob: 파일의 내용(데이터). 파일명은 없고 내용만 존재합니다.
  • tree: 디렉토리. 내부에 blob(파일)과 하위 tree(폴더)를 이름과 함께 가리킵니다.
  • commit: 스냅샷 하나. 최상위 tree를 가리키고, 부모 커밋, 작성자, 메시지를 포함합니다.
  • tag: (annotated tag) 특정 커밋을 가리키는 이름표.

즉, commit이 tree를 가리키고 tree가 blob을 가리키는 구조로 전체 스냅샷을 복원합니다.

키는 SHA: 내용이 같으면 해시도 같다

각 객체는 내용의 SHA 해시(SHA-1, 최근에는 SHA-256 옵션)를 키로 저장됩니다. 내용이 같으면 해시가 같고, 한 글자라도 변경되면 해시가 완전히 달라집니다. 그 이유는 다음과 같습니다:

  • 같은 파일 내용은 같은 blob(자동 중복 제거)
  • 무결성 보장: 해시만 확인해도 변조 여부를 판단할 수 있음
  • 분산 환경에서 충돌 없이 동일한 객체를 공유 가능

콘텐츠 주소 저장소(content-addressable storage)

‘객체를 내용(해시)으로 찾는’ 구조입니다. 위치로 찾는 일반 파일 시스템과 반대됩니다. 그래서 Git에서는 ’어디에 저장했는가’가 아니라 ’내용이 무엇인가’가 중요합니다.

저장은 압축하여: packfile

느슨한 객체(loose object)를 그대로 두면 작은 파일이 수천 개로 늘어납니다. 그래서 Git은 주기적으로 packfile로 묶고 zlib 압축을 수행합니다. 유사한 객체끼리 델타(delta) 압축까지 적용하여 용량을 크게 줄입니다.

어떻게 연결되는가

한 줄로 요약하면: Git은 커밋마다 전체 스냅샷을 blob, tree, commit 객체로 저장하고, 각 객체를 내용의 SHA로 키를 지정하여 .git/objects에 보관(콘텐츠 주소 저장)하며, packfile로 압축하는 시스템입니다. ‘diff를 누적하는’ 것이 아니라 ‘스냅샷을 해시로 보존하는’ 구조가 Git의 무결성, 분산성, 효율성을 동시에 만들어냅니다.

출처: Pro Git: Git Internals(Plumbing and Porcelain) · Pro Git: Git Objects · 위키백과: Git

이미지: Jason Long, Wikimedia Commons (CC-BY 3.0) — 원본