Git 스터디 노트 (1) 개념: 스냅샷, 세 영역, 브랜치
Git을 명령어가 아니라 모델부터 이해하기 위한 개념 정리입니다. 커밋이 diff가 아니라 스냅샷이라는 것, 분산 버전 관리의 의미, working/staging/repository 세 영역, 그리고 브랜치가 포인터일 뿐이라는 것까지 다룹니다.
Git을 명령어 암기가 아니라 밑에 깔린 모델로 이해하기 위한 시리즈입니다. 1편은 개념, 2편은 기본 명령어, 3편은 Pull Request, 4편은 CI/CD를 다룹니다.
왜 개념부터 잡는가
Git을 명령어 암기로 시작하면 금방 벽에 부딪힙니다. 명령어는 수십 개인데 밑에 깔린 모델을 모르면 각각이 무슨 일을 하는지 연결이 안 되기 때문입니다. reset과 revert가 왜 다른지, pull을 했는데 왜 충돌이 나는지 같은 질문은 명령어 설명서를 아무리 읽어도 풀리지 않습니다. 밑에 깔린 두 개의 그림, 스냅샷과 세 영역을 잡으면 처음 보는 명령도 유추할 수 있습니다.
이 글에서는 명령어를 거의 쓰지 않고 모델만 다룹니다. 실제 명령어는 2편에서 이 모델 위에 얹습니다.
Git은 무엇을 저장하는가: diff가 아니라 스냅샷
흔한 오해와 달리 Git은 “바뀐 부분(diff)”을 저장하지 않습니다. 커밋 하나는 그 시점 프로젝트 전체의 스냅샷입니다. git log에서 보는 커밋 목록은 “변경 내역의 목록”이 아니라 “시점별 전체 사진첩”이고, diff는 두 사진을 비교해서 그때그때 계산해 보여주는 것입니다.
이 모델에서 Git의 성격이 대부분 나옵니다.
- 어떤 커밋으로든 완전한 상태로 즉시 이동할 수 있습니다. 사진 한 장을 꺼내면 그 시점 전체가 복원됩니다.
- 커밋은 부모 커밋을 가리키는 연결 리스트를 이룹니다. 각 커밋은 자기 부모가 누구인지 기억합니다.
- 한 번 커밋된 내용은 사실상 사라지지 않습니다. 되돌리기가 무섭지 않은 이유이고, 자주 커밋하는 습관이 곧 백업 습관인 이유입니다.
분산 버전 관리: 이력이 통째로 내 컴퓨터에
Git은 분산(distributed) 버전 관리입니다. clone을 받는 순간 전체 이력이 내 컴퓨터에 통째로 복사됩니다. 그래서 커밋, 브랜치 생성, 이력 조회는 전부 네트워크 없이 로컬에서 일어납니다. 비행기 안에서도 커밋할 수 있고, 그 속도가 즉각적인 것도 이 때문입니다.
서버(GitHub)와 통신하는 것은 딱 세 계열뿐입니다. 내 커밋을 올리는 push, 원격의 변경을 받아만 오는 fetch, 받아서 합치는 pull입니다. 나머지는 전부 내 컴퓨터 안에서 끝납니다. “GitHub이 곧 Git”이라고 생각하면 헷갈리는데, GitHub은 여러 사람의 저장소가 만나는 중앙 약속 장소일 뿐이고 Git 자체는 각자의 로컬에서 완결됩니다.
세 영역 모델: Git 이해의 절반
Git의 거의 모든 명령은 세 영역 사이에서 파일을 옮기는 일입니다. 이 그림 하나가 Git 이해의 절반입니다.
1
2
working directory --add--> staging area --commit--> repository
(작업 폴더) (커밋 대기석) (이력 보관소)
- working directory: 내가 실제로 편집하는 파일들
- staging area: “다음 커밋에 넣을 것”을 골라두는 대기석 (index라고도 부릅니다)
- repository: 커밋(스냅샷)이 영구 보관되는 곳 (
.git폴더)
staging area가 왜 있는지가 처음엔 의아합니다. 답은 커밋을 편집 단위가 아니라 의미 단위로 만들기 위해서입니다. 세 파일을 고쳤어도 “버그 수정”에 해당하는 두 파일만 골라 커밋하고, “오타 수정”인 나머지 하나는 따로 커밋할 수 있습니다. 커밋 하나 = 의미 있는 변경 하나라는 원칙이 가능해지는 장치가 staging area입니다.
이 모델을 잡아두면 나중에 되돌리기 명령이 세 개인 이유도 자연스럽습니다. “어느 영역까지 되감을 것인가”의 차이일 뿐이기 때문입니다. 그 부분은 2편에서 다룹니다.
브랜치: 커밋을 가리키는 이름표 하나
브랜치는 커밋 사슬을 통째로 복사하는 게 아니라 특정 커밋을 가리키는 이름표 하나입니다. 파일 하나에 커밋 해시를 적어둔 것에 가깝습니다. 그래서 브랜치 생성은 즉각적이고 저장 공간도 거의 들지 않습니다. 새 실험, 새 기능마다 부담 없이 만들라고 있는 것입니다.
여기서 두 가지 개념이 따라옵니다.
- HEAD: “지금 내가 어느 브랜치에 있는가”를 가리키는 포인터입니다. 브랜치를 옮긴다는 것은 HEAD를 다른 이름표로 옮기고, working directory를 그 시점 스냅샷으로 맞추는 일입니다.
- merge와 충돌: 두 브랜치를 합칠 때 Git은 각자의 변경을 한데 모읍니다. 서로 다른 파일이나 다른 부분을 고쳤다면 자동으로 합쳐집니다. 두 브랜치가 같은 파일의 같은 부분을 다르게 고쳤을 때만 충돌(conflict)이 납니다.
충돌은 에러가 아니라 “둘 다 맞을 수 있으니 사람이 골라라”는 정상적인 질문입니다. 이 질문에 답하는 절차는 2편에서 실제로 다룹니다. 지금은 “브랜치는 포인터, 충돌은 같은 곳을 다르게 고쳤을 때만”이라는 두 문장만 기억하면 됩니다.
좋은 커밋이란 무엇인가
명령어보다 오래가는 것은 커밋을 만드는 기준입니다. 이건 도구가 아니라 습관이라 개념 편에서 짚어둡니다.
- 하나의 커밋 = 하나의 의미. “모델 수정 + 리팩토링 + 오타”를 한 커밋에 넣으면 나중에 그중 하나만 되돌릴 수 없습니다. 스냅샷 모델에서 되돌리기의 최소 단위는 커밋이기 때문입니다.
- 메시지는 “무엇을”이 아니라 “왜”. 무엇이 바뀌었는지는 diff가 보여줍니다. 메시지에는 diff가 말해주지 않는 의도를 남깁니다. “lr 0.001로 변경”보다 “발산 방지를 위해 lr 인하”가 좋은 메시지입니다.
- 동작하는 상태에서 커밋. 커밋은 “언제든 돌아갈 수 있는 지점”인데, 깨진 상태의 스냅샷은 돌아갈 가치가 없습니다.
이 기준은 혼자 작업할 때는 사소해 보이지만, 3편에서 다룰 협업과 코드 리뷰로 가면 그대로 팀의 생산성이 됩니다. 리뷰어가 읽어야 하는 것이 결국 커밋이기 때문입니다.
정리
| 개념 | 한 줄 요약 |
|---|---|
| 스냅샷 모델 | 커밋은 diff가 아니라 그 시점 전체 사진. diff는 두 사진의 비교 |
| 분산 | clone하면 전체 이력이 로컬에. 서버 통신은 push/fetch/pull뿐 |
| 세 영역 | working, staging, repository. staging은 커밋을 의미 단위로 만드는 장치 |
| 브랜치 | 특정 커밋을 가리키는 이름표. 생성이 즉각적이고 공짜에 가깝다 |
| 충돌 | 같은 파일의 같은 부분을 다르게 고쳤을 때만. 에러가 아니라 질문 |
| 좋은 커밋 | 의미 단위 하나씩, 메시지엔 “왜”, 동작하는 상태에서 |
명령어보다 오래가는 것은 모델입니다. 스냅샷과 세 영역, 이 두 그림만 잡혀 있으면 처음 보는 명령도 “어느 영역에서 어느 영역으로 무엇을 옮기는가”로 유추할 수 있습니다.
다음 글
개념이 잡혔으니 2편에서는 이 모델 위에 실제 명령어를 얹습니다. 기본 사이클(status, add, commit), 브랜치와 충돌 해결, 원격, 그리고 상황별 되돌리기까지 다룹니다.