[TIL] Missing Semester #5: Version Control (Git)

2026. 04. 12.Yeji Kim
missing-semester

Claude와 함께 MIT Missing Semester 강의를 실습하며 정리한 내용입니다.

들어가며

이번 강의는 Git이 어떻게 동작하는지 원리부터 다룬다.

핵심 개념

Git의 자료구조: Content-Addressable Storage

Git의 핵심은 해시맵(key-value store) 이다. 모든 객체를 SHA-1 해시로 저장하는데, 해시의 입력이 파일 내용 자체다.

bash
echo "hello" | git hash-object --stdin
# ce013625030ba8dba906f756967f9e9ca394464a

같은 내용이면 항상 같은 해시. 같은 파일이 10개 커밋에 있어도 한 번만 저장된다. 이게 Git이 빠르고 효율적인 이유.

실제로 .git/objects/에 해시 앞 2글자를 폴더명으로 사용해서 저장된다.

세 가지 객체 타입

Git에는 딱 3종류의 객체만 있다:

blob (Binary Large Object) - 파일 내용만 저장. 파일명은 저장하지 않는다.

tree - 디렉토리 구조. 파일명 + blob/tree 해시의 목록:

text
tree
├── 100644 blob a1b2c3  package.json
├── 100644 blob d4e5f6  tsconfig.json
└── 040000 tree g7h8i9  src/
                         ├── 100644 blob j0k1l2  index.ts
                         └── 100644 blob m3n4o5  App.tsx

100644는 일반 파일, 040000은 디렉토리 (Unix 파일 권한).

commit - 스냅샷 + 메타데이터:

text
commit
├── tree: f7a3b2...     (루트 tree를 가리킴)
├── parent: e5c1d8...   (이전 커밋)
├── author: yeji <...>
└── message: "feat: add shell TIL"

git cat-file -p HEAD로 커밋 객체를, git cat-file -p HEAD^{tree}로 tree 구조를 직접 확인할 수 있다.

DAG (Directed Acyclic Graph)

커밋들은 방향 비순환 그래프를 형성한다.

  • Directed - 화살표 방향이 있음 (자식 → 부모)
  • Acyclic - 순환이 없음 (과거로만 향함)

merge commit은 부모가 2개인 것이 일반 commit과의 차이.

참조(Reference)는 그냥 텍스트 파일

브랜치는 커밋 해시를 담은 파일일 뿐이다:

bash
cat .git/refs/heads/main
# 0cd29548a3f...  (해시 한 줄)
 
cat .git/HEAD
# ref: refs/heads/main  (어떤 브랜치를 가리키는지)

브랜치 생성 = 파일 하나 만들기. 브랜치 이동 = 해시 한 줄 변경. 그래서 Git 브랜치가 가볍다.

스테이징 = Index 파일

.git/index라는 바이너리 파일에 "다음 커밋에 포함될 파일 목록"이 저장된다. git add는 이 index를 업데이트하는 것. git ls-files --stage로 확인 가능.

Packfile (최적화)

처음에는 객체마다 파일 하나씩 저장하지만, git gc 실행 시 delta compression으로 비슷한 파일끼리 diff만 저장해서 용량을 줄인다. git push/fetch 할 때도 packfile로 전송.

.git 전체 구조

text
.git/
├── objects/           ← 해시맵 (blob, tree, commit)
│   ├── ce/
│   ├── d4/
│   └── pack/          ← 압축된 packfile
├── refs/
│   ├── heads/
│   │   └── main       ← 브랜치 (커밋 해시 한 줄)
│   └── tags/
│       └── v1.0.0     ← 태그
├── HEAD               ← 현재 브랜치 포인터
└── index              ← 스테이징 영역

결국 Git은 해시맵 + DAG + 텍스트 파일 몇 개다.

브랜치와 머지

  • git branch feature - 브랜치 생성
  • git checkout feature - 브랜치 이동
  • git merge feature - main에서 feature를 합치기

merge - 히스토리를 보존 (merge commit 생성). rebase - 히스토리를 깔끔하게 일직선으로 정리. 팀 컨벤션에 따라 다름.

브랜치와 머지

  • git branch feature - 브랜치 생성
  • git checkout feature - 브랜치 이동
  • git merge feature - main에서 feature를 합치기

merge - 히스토리를 보존 (merge commit 생성). rebase - 히스토리를 깔끔하게 일직선으로 정리. 팀 컨벤션에 따라 다름.

되돌리기

  • git stash - 변경사항 임시 저장 (안전)
  • git revert <hash> - 해당 커밋을 취소하는 새 커밋 (안전)
  • git reset --soft - 커밋만 취소, 변경사항 유지 (보통)
  • git reset --hard - 커밋 + 변경사항 모두 삭제 (위험!)

고급 도구

  • git bisect - 이진 탐색으로 버그 도입 커밋 찾기. 자동으로 중간 커밋을 체크아웃하고, good/bad로 답하면 범위를 좁혀감
  • git blame - 각 줄을 마지막으로 수정한 사람/커밋 표시
  • git worktree - 하나의 repo에서 여러 브랜치를 동시에 체크아웃

실습 기록

Git 내부 들여다보기

git cat-file -p HEAD로 커밋 객체를, git cat-file -p HEAD^{tree}로 tree 구조를 직접 확인. blob/tree/commit 데이터 모델을 눈으로 봤다.

graph alias 등록

bash
git config --global alias.graph 'log --oneline --graph --all --decorate'

git graph로 히스토리를 그래프로 시각화.

bisect 실습

git bisect startgit bisect bad HEADgit bisect good HEAD~10으로 이진 탐색 체험. 실무에서 "이 버그 언제부터였지?" 할 때 유용.

글로벌 gitignore 설정

bash
git config --global core.excludesfile ~/.gitignore_global

.DS_Store, *.swp 같은 OS/에디터 파일을 전역으로 무시하도록 설정.


레퍼런스