Git 명령어 몇 줄 치면 끝나긴 하는데, 이게 내부적으로는 어떤 객체가 만들어지고 어떤 포인터가 움직이는 걸까요? 이번 포스팅에서는 Git이 데이터를 어떻게 저장하는지, 커밋은 어떤 구조로 이루어져 있는지, 그리고 효율적인 협업을 위한 브랜치 전략까지 Git 활용법을 다룹니다.
🧱 Git은 데이터를 어떻게 저장할까?
Git은 단순히 파일 스냅샷을 저장하는 도구가 아닙니다. 내부적으로는 SHA-1 해시 기반의 객체 저장소를 구성하며, 모든 커밋, 파일, 폴더는 특정한 객체 타입으로 관리됩니다.
[Git의 세 가지 객체]
- Blob: 실제 파일의 내용
- Tree: 디렉토리 구조 및 파일 목록 (즉, 스냅샷)
- Commit: 하나의 버전 스냅샷을 나타내는 객체로, 부모 커밋과 트리 객체, 메타 데이터를 포함함
$ git cat-file -p <commit-hash>
커밋 객체를 열어보면, 다음과 같은 정보가 포함되어 있습니다.
tree 37e0c822...
parent 2b5b63a3...
author charsyam <...>
committer charsyam <...>
add 2.txt
📁 .git/objects/ 디렉토리 하위에 실제 객체 파일들이 zlib으로 압축된 형태로 저장됩니다.
🧠 Git의 상태 흐름 이해하기
Git은 파일의 상태를 다음과 같이 추적합니다.
| 상태 | 설명 |
| Untracked | Git이 추적하지 않는 새 파일 |
| Modified | 수정되었지만 staging 되지 않음 |
| Staged | git add로 스테이징 완료 |
| Committed | git commit으로 로컬 저장소에 저장됨 |
🌿 Git의 브랜치는 실제로 어떻게 동작할까?
Git에서 브랜치는 단순히 커밋 해시를 가리키는 포인터 파일입니다. 예를 들어, git branch featrue/test_1 명령어는 .git/refs/heads/feature/test_1 에 현재 커밋 해시를 저장합니다.
따라서 브랜치는 매우 가볍고, 자유롭게 만들고 삭제해도 부담이 없습니다.
🚀 대표적인 브랜치 전략 3가지
[Git Flow]
- main: 안정화된 배포 버전
- develop: 통합 개발 브랜치
- feature/*: 기능 개발
- release/*: 릴리즈 준비
- hotfix/*: 긴급 수정
[GitHub Flow]
- main: 항상 배포 가능한 상태
- feature/*: 작업 후 PR -> 리뷰 후 main에 병합
[GitLab Flow]
- main: 기본 배포용 브랜치
- staging, production: 환경 별 배포 브랜치
🔁 Merge vs Rebase: 커밋 이력 다루기
[Merge]
- 브랜치를 병합하면서 공통 조상과 두 부모를 가지는 커밋을 생성- 이력은 그대로 유지되지만, 히스토리가 복잡해질 수 있음
[Rebase]
- 커밋을 재작성해서 선형 커밋 히스토리를 유지- 커밋 순서가 깔끔해지고, 리뷰가 수월해짐
🧬 Git 명령어의 내부 동작 흐름 따라가기
다음은 흔히 사용하는 Git 명령어들이 내부적으로 .git 디렉토리에 어떤 변화를 일으키는지 하나씩 살펴보는 시간입니다. 예제 디렉토리에서 아래 명령어들을 순서대로 실행해봅시다.
[git init]

- 현재 디렉토리에 .git/ 폴더가 생성됩니다.
- 내부에는 다음과 같은 파일 및 디렉토리가 초기화됩니다.
.git/
├── HEAD (현재 브랜치 정보, 기본값은 ref: refs/heads/main)
├── config
├── description
├── objects/ (Git 객체 저장소)
└── refs/
└── heads/ (브랜치 정보 저장)
[git add]

- 추가한 파일의 내용이 Blob 객체로 .git/objects/에 저장됩니다.
- 이 객체의 SHA-1 해시가 .git/index (staging area)에 기록됩니다.
- git add는 단순히 스테이징만 한다고 생각하기 쉽지만, 사실은 Blob 객체를 만들고 Index에 반영하는 작업입니다.
[git commit]

- 현재 .git/index의 상태를 기반으로 Tree 객체가 생성됩니다.
- Tree 객체와 메타 정보(author, date 등)를 기반으로 Commit 객체가 생성됩니다.
- Commit 객체의 SHA-1 해시가 .git/refs/heads/main에 기록됩니다.
- Git은 디렉토리 상태(snapshot)의 해시 트리를 저장합니다.
.git/
├── objects/
│ ├── blob
│ ├── tree (snapshot)
│ └── commit (metadata + tree)
└── refs/heads/main → commit 해시
[git checkout -b feature/*]

- .git/refs/heads/featuer/* 파일이 생성되고, 현재 커밋 해시가 기록됩니다.
- .git/HEAD는 이제 ref: refs/heads/feature/* 로 변경되어 새로운 브랜치를 가리킵니다.
.git/
├── refs/heads/
│ ├── main
│ └── feature/test1 ← 현재 브랜치
├── HEAD → refs/heads/feature/test1
[git merge]

- 현재 main과 feature/*의 커밋 차이를 계산합니다.
- 병합이 충돌 없이 성공하면 새 Tree 객체를 생성하고, main과 feature/* 을 부모로 갖는 새 Commit 객체가 생성되며, .git/refs/heads/main이 새로운 커밋 해시로 업데이트됩니다.
| 명령어 | 주요 변경점 |
| git init | .git/ 디렉토리 생성 |
| git add | Blob 객체 생성 + index 반영 |
| git commit | Tree/Commit 객체 생성 + 브랜치 포인터 업데이트 |
| git checkout -b | 새 브랜치 파일 생성 + HEAD 이동 |
| git merge | 새로운 병합 커밋 생성 (2-parent commit) |
'DevOps' 카테고리의 다른 글
| ⚖️ CAP 정리 — 분산 시스템의 세 가지 불가능한 균형 (0) | 2025.10.29 |
|---|---|
| 🔍 DB 인덱스 - 빠른 검색의 비밀 (0) | 2025.10.28 |
| 🧩 DB 커넥션 풀, 실무에서 반드시 알아야 하는 이유 (0) | 2025.10.27 |
| 협업 할 때 쓰이는 여러가지 Git 명령어 (0) | 2022.07.21 |
| DBMS 선택 가이드 (0) | 2022.02.01 |