안녕하세요!
이번 글에서는 Git의 동작 방식을 이해하는 데 꼭 필요한 네 가지 객체(Blob, Tree, Commit, Tag)에 대해 알아보도록하겠습니다!
왜 Git 내부 구조를 알아야 해?
Git을 사용하다 보면 git add, git commit, git push 같은 명령어는 너무나 익숙하죠.
하지만 우리가 Git을 사용할 때, 이게 내부적으로 어떤 구조로 데이터를 저장하고 관리하는지 고민해 본 적 있나요? 🤔
Git은 단순한 파일 저장 도구가 아니라, 객체 데이터베이스(Object Database)라는 시스템을 사용해 데이터를 관리합니다.
이 구조를 이해하면 버전 관리 실수를 줄이고, 협업 과정에서 Git을 더 효과적으로 활용할 수 있습니다.
Git의 데이터 저장 방식
Git의 객체 데이터베이스(Object Database)는 데이터를 Blob, Tree, Commit, Tag 네 가지 객체로 저장합니다.
각 객체는 서로 유기적으로 연결되어 있으며, Git의 강력한 버전 관리 기능을 제공합니다.
객체 | 역할 | 특징 |
Blob | 파일의 원본 데이터를 저장 | 내용만 저장, 이름 없음 |
Tree | 디렉터리 구조(폴더) 구조 저장 | 파일과 디렉터리 정보 포함 |
Commit | 특정 시점의 저장소 상태 기록 | 부모 Commit 정보 포함 |
Tag | 특정 Commit을 가리키는 참조 | 주로 버전 관리에 사용 |
Git은 모든 객체를 SHA-1 해시 값으로 변환하여 저장하며, 이 객체들은 .git/objects 디렉터리에서 효율적으로 관리됩니다.
Blob: 파일의 내용을 저장하는 객체
Blob(Binary Large Object)은 Git에서 파일의 내용을 저장하는 기본 단위입니다.
파일이 Git에 추가되면, 이름이나 권한 정보 없이 오직 내용만 저장되며, 같은 내용의 파일은 동일한 Blob 객체를 재사용합니다.
Blob의 역할
파일의 내용을 SHA-1 해시로 저장
- Git은 파일 내용을 압축한 후 SHA-1 해시 값을 생성하여 Blob 객체로 저장함
- 같은 내용의 파일이라면 항상 같은 SHA-1 해시 값을 가지므로, 중복 저장을 방지할 수 있음
변경된 내용만 저장
- 동일한 파일이 변경되지 않았다면, Git은 기존 Blob 객체를 그대로 사용하고 새로운 Blob을 만들지 않음
- 같은 파일이 여러 번 저장소에서 참조되더라도, 실제로는 같은 Blob 객체를 가리키는 것과 같음
파일의 메타데이터(파일명, 퍼미션 등)는 저장되지 않음
- Git은 파일의 이름이나 형식과 무관하게, 오직 파일의 내용을 Blob에 저장함
- 파일 이름이나 권한 정보는 Tree 객체에서 따로 관리함
다양한 형식의 파일 저장 가능
- Blob 객체는 텍스트, 이미지, 음악, 단순 이진 파일 등 다양한 형식의 파일을 저장할 수 있음
Blob 요약
역할 | 설명 |
파일 내용 저장 | Git에서 파일의 내용을 저장하는 기본 객체 |
중복 저장 방지 | 같은 내용의 파일은 동일한 Blob 객체를 재사용 |
변경 사항 추적 | 변경된 파일만 새로운 Blob으로 저장 |
Tree 객체와 연결 | Tree 객체에서 Blob을 참조하여 파일 구조 관리 |
Tree: 디렉터리 구조를 저장하는 객체
Tree 객체는 디렉터리 구조를 저장하는 역할을 하며, 파일명과 권한 정보를 포함합니다.
각 파일은 Blob 객체를 참조하고, 하위 디렉터리는 다른 Tree 객체를 가리켜 계층적 구조를 형성합니다.
Tree의 역할
디렉터리 구조 표현
- Tree 객체는 Git이 디렉터리와 파일을 계층적으로 관리할 수 있도록 돕습니다
- 저장소의 모든 파일과 디렉터리 구조는 Tree 객체로 표현됩니다
효율적 데이터 저장
- 디렉터리 구조를 관리하며, 동일한 파일이 변경되지 않으면 기존 객체를 재사용하여 중복 저장을 방지
빠른 히스토리 탐색
- 특정 커밋 시점의 전체 파일 구조를 빠르게 확인할 수 있음
Tree와 Blob의 관계 (예제)
예를 들어, 아래와 같은 프로젝트 구조를 가진 Git 저장소가 있다고 가정해볼게요.
project/
├── README.md → Blob
├── src/ → Tree
│ ├── main.swift → Blob
│ └── utils.swift → Blob
└── .gitignore → Blob
이 경우, Git은 다음과 같은 방식으로 Tree 객체와 Blob 객체를 구성합니다:
- 각 파일(예: README.md, main.swift, utils.swift, .gitignore)은 Blob 객체로 저장됩니다.
- src/ 디렉터리는 하나의 Tree 객체로 저장되며, 그 안에서 main.swift와 utils.swift Blob 객체를 참조합니다.
- 최상위 디렉터리(project/)도 하나의 Tree 객체로 저장되며, 하위 Tree(src/)와 Blob(README.md, .gitignore)을 참조합니다.
Tree 요약
역할 | 설명 |
디렉터리 구조 저장 | 파일과 폴더의 계층 관계 유지 |
파일 및 디렉터리 정보 관리 | 파일명, 권한, 참조 대상 (Blob/Tree) 저장 |
변경되지 않은 객체 재사용 | 동일한 디렉터리나 파일이 변경되지 않았다면 기존 객체 유지 |
빠른 버전 탐색 지원 | 특정 커밋 시점에서 전체 파일 구조 확인 가능 |
Commit: 특정 시점의 스냅샷을 저장하는 객체
Commit 객체는 Git이 특정 시점의 저장소 상태를 기록하는 핵심 요소입니다.
각 Commit은 Tree 객체(디렉터리 구조)를 가리키며, 이전 Commit(부모 Commit)과 연결됩니다.
Commit의 역할
Tree 객체를 참조하여 저장소 상태를 기록
- Commit은 Tree 객체를 통해 해당 시점의 디렉터리 및 파일 상태를 나타냄
- Tree 객체는 다시 Blob(파일 내용)과 하위 Tree(디렉터리)를 참조하는 구조
변경 이력(버전 관리) 유지
- Commit 객체는 부모 Commit과의 연결을 통해 버전 히스토리를 구성
- Git은 이러한 부모-자식 관계를 활용하여 브랜치와 병합(Merge)을 관리
변경 원인과 작성자 기록
- Commit 메시지를 통해 변경 이유를 기록하고, 작성자 정보를 통해 누가 변경했는지 추적할 수 있음
Git의 히스토리 탐색 기반
- git log 명령어를 사용하여 Commit 히스토리를 탐색할 수 있음
- 특정 Commit의 SHA-1 해시를 활용해 저장소를 과거 상태로 되돌리거나 특정 시점의 파일을 확인할 수 있음
Commit, Tree, Blob의 관계
Commit 객체는 Tree 객체를 참조하고, Tree 객체는 Blob(파일 내용)과 다른 Tree 객체(하위 디렉터리)를 참조하는 구조를 가집니다.
Commit
└── Tree (디렉터리 구조)
├── Blob (파일 내용)
└── Tree (하위 디렉터리)
├── Blob (파일 내용)
└── ...
Commit 객체는 저장소 전체의 상태를 스냅샷으로 저장하며,
Tree와 Blob은 이를 구성하는 계층적 요소입니다.
Commit 요약
역할 | 설명 |
저장소 상태 기록 | 특정 시점의 저장소 상태를 기록하고 스냅샷으로 관리 |
Tree 객체 참조 | 해당 시점의 디렉터리 및 파일 구조를 저장 |
버전 이력 관리 | 이전 Commit과 연결하여 변경 사항 추적 |
브랜치 및 병합 지원 | git log를 통해 변경 이력 탐색 가능 |
Tag: 특정 커밋을 가리키는 객체
Tag 객체는 특정 Commit을 고정된 이름으로 참조하는 역할을 합니다.
주로 릴리스 버전 관리(v1.0.0 등) 또는 중요 커밋 식별에 사용됩니다.
브랜치와 달리 Tag는 커밋 이후 변경되지 않는 고정 참조(Fixed Reference) 입니다.
Git에서 Tag는 두 가지 유형으로 구분됩니다:
- Lightweight Tag: 단순히 커밋을 가리키는 포인터 역할만 하며, 메타데이터(작성자, 메시지 등)를 포함하지 않음
- Annotated Tag: 작성자, 날짜, 설명을 포함하며, Git 내부에 별도 객체로 저장됨
TAG의 역할
특정 커밋을 변경되지 않는 레이블로 고정
릴리스 버전 관리 또는 중요 커밋을 쉽게 식별하는 용도로 사용
브랜치와 다르게 이동하지 않으며, 특정 커밋을 영구적으로 가리킴
Tag 요약
역할 | 설명 |
커밋 참조 | 특정 커밋을 변경되지 않는 이름으로 참조 |
버전 관리 | 릴리스 버전(v1.0.0 등) 식별 |
브랜치와 차이점 | 브랜치처럼 이동하지 않고 고정됨 |
Lightweight & Annotated | 경량 태그와 주석 태그로 구분됨 |
Git 객체 간의 연결 구조
Git의 핵심 객체인 Blob, Tree, Commit, Tag는 서로 연결되어 Git의 버전 관리가 이루어집니다.
직접 확인해보기
📁 실제 구조 예시
.git/objects/
├── 12/
│ └── abcd5678... (Blob, Tree 등 저장된 오브젝트)
├── 34/
│ └── efgh1234...
위 구조처럼, 해시 값의 앞 두 자리는 디렉터리 이름이 되고, 나머지는 해당 디렉터리 안에 저장된 파일 이름이 됩니다.
이 방식 덕분에 Git은 객체를 빠르게 찾고, 효율적으로 관리할 수 있습니다
CLI에서 Git 객체 확인하기
저장된 Git 객체 목록 확인
find .git/objects -type f
Blob, Tree, Commit 중 어떤 객체인지 확인
git cat-file -t <SHA-1>
Blob, Tree, Commit 등의 실제 데이터 확인
git cat-file -p <SHA-1>
간단한 커밋 로그 확인
git log --oneline
특정 커밋의 상세 정보 확인
git show <커밋 해시>
어?? show랑 cat-file -p랑 출력 내용이 같네요?? 뭐가 다른 건가요???🤔
Git을 일반적으로 사용할 때 → git show
Git 내부 구조를 깊이 이해하고 싶을 때 → git cat-file -p
객체 타입을 확인하고 싶을 때 → git cat-file -t
즉, git show는 사람이 읽기 편한 형태로 가공된 정보를 제공하고,
git cat-file -p는 Git 내부 데이터를 그대로 출력하는 저수준 명령어!!
Tag 생성
git tag v1.0.0
Tag 목록 확인
git tag
Tag가 가리키는 커밋 확인
git show v1.0.0
파일 시스템에서 객체 목록 확인하는 법
Command + Shift + .(점) 을 누르면 숨겨진 .git 파일이 나옵니다.
.git > objects > 35 > 0cf71f2b-
결론
이번 글에서는 Git이 단순한 파일 저장소가 아닌 객체 기반 데이터베이스라는 점을 살펴보았습니다.
Git의 내부 구조를 이해하면 단순히 명령어를 외우는 것이 아니라, 더 효과적으로 버전 관리를 할 수 있습니다.
특히, Git이 네 가지 객체(Blob, Tree, Commit, Tag)를 활용해 데이터를 저장하고 관리하는 방식을 알게 되면 다음과 같은 장점이 있습니다.
✅ 같은 파일이 중복 저장되지 않아, 저장 공간을 효율적으로 활용할 수 있음
✅ 특정 시점으로 되돌릴 때 내부적으로 어떤 과정이 수행되는지 이해할 수 있음
✅ 태그(Tag)와 커밋 로그를 활용해 프로젝트의 이력을 체계적으로 관리할 수 있음
이제 Git의 핵심 원리를 이해했으니, 다음 글에서는 Reset과 Revert의 차이점과 활용법을 알아보겠습니다.
차근차근 가보자구요~~!
참고 문서
'GitHub' 카테고리의 다른 글
[GitHub] repo에 .gitignore 파일 추가하기 (0) | 2023.08.22 |
---|---|
[GitHub] 특정 branch만clone하는 방법 (0) | 2023.01.27 |
[GitHub] Git commit 메세지 변경하기 (0) | 2023.01.27 |
Linux 명령어 모음 (1) | 2023.01.27 |
[GitHub] Xcode에서 branch 가져오기 (0) | 2023.01.27 |