Claude와 함께 MIT Missing Semester 강의를 실습하며 정리한 내용입니다.
들어가며
이번 강의는 Git이 어떻게 동작하는지 원리부터 다룬다.
핵심 개념
Git의 자료구조: Content-Addressable Storage
Git의 핵심은 해시맵(key-value store) 이다. 모든 객체를 SHA-1 해시로 저장하는데, 해시의 입력이 파일 내용 자체다.
echo "hello" | git hash-object --stdin
# ce013625030ba8dba906f756967f9e9ca394464a같은 내용이면 항상 같은 해시. 같은 파일이 10개 커밋에 있어도 한 번만 저장된다. 이게 Git이 빠르고 효율적인 이유.
실제로 .git/objects/에 해시 앞 2글자를 폴더명으로 사용해서 저장된다.
세 가지 객체 타입
Git에는 딱 3종류의 객체만 있다:
blob (Binary Large Object) - 파일 내용만 저장. 파일명은 저장하지 않는다.
tree - 디렉토리 구조. 파일명 + blob/tree 해시의 목록:
tree
├── 100644 blob a1b2c3 package.json
├── 100644 blob d4e5f6 tsconfig.json
└── 040000 tree g7h8i9 src/
├── 100644 blob j0k1l2 index.ts
└── 100644 blob m3n4o5 App.tsx100644는 일반 파일, 040000은 디렉토리 (Unix 파일 권한).
commit - 스냅샷 + 메타데이터:
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)는 그냥 텍스트 파일
브랜치는 커밋 해시를 담은 파일일 뿐이다:
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 전체 구조
.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 등록
git config --global alias.graph 'log --oneline --graph --all --decorate'git graph로 히스토리를 그래프로 시각화.
bisect 실습
git bisect start → git bisect bad HEAD → git bisect good HEAD~10으로 이진 탐색 체험. 실무에서 "이 버그 언제부터였지?" 할 때 유용.
글로벌 gitignore 설정
git config --global core.excludesfile ~/.gitignore_global.DS_Store, *.swp 같은 OS/에디터 파일을 전역으로 무시하도록 설정.