Refs
💡 Git refs
시스템
Git은 객체 기반의 분산 버전 관리 시스템이며, 모든 객체는 불변의 해시값(SHA-1 혹은 SHA-256)으로 식별됩니다. 이때 refs
는 이러한 해시를 사람이 기억할 수 있고 관리할 수 있는 식별자(브랜치, 태그 등)로 연결해 주는 이름-값 매핑 시스템입니다.
📁 1. refs
의 위치와 구조
Git 저장소는 .git
디렉토리 안에 아래와 같은 구조를 가집니다.
.git/
├── HEAD
├── refs/
│ ├── heads/
│ │ ├── main
│ │ └── feature/login
│ ├── tags/
│ │ └── v1.0
│ └── remotes/
│ └── origin/
│ └── main
├── packed-refs
각 경로의 의미
디렉토리 | 설명 |
---|---|
refs/heads/ |
로컬 브랜치 이름들이 저장됨 (refs/heads/main ) |
refs/remotes/ |
원격 추적 브랜치들이 저장됨 (refs/remotes/origin/main ) |
refs/tags/ |
태그들이 저장됨 (refs/tags/v1.0 ) |
packed-refs |
다량의 refs를 하나의 파일로 저장한 최적화된 구조 |
🔗 2. Ref = 커밋 객체를 가리키는 포인터
Git에서 모든 ref
는 커밋 객체를 가리킵니다. Git의 커밋은 해시값(SHA-1 또는 SHA-256)으로 식별되며, refs
는 다음 중 하나의 역할을 합니다.
- 직접 참조 (Direct ref)→ 해당 브랜치는 이 커밋을 가리킴
$ cat .git/refs/heads/main e1a3e5fbe9c2d3ffcc29d24d0e826173ed8420c9
- 간접 참조 (Symbolic ref)→ HEAD는 실제 커밋을 직접 가리키지 않고,
refs/heads/main
이라는 ref를 가리킴 $ cat .git/HEAD ref: refs/heads/main
이것이 Git이 HEAD → 브랜치 → 커밋
구조를 가지는 이유입니다.
🧠 3. HEAD는 특별한 Symbolic Ref
HEAD
는 Git에서 작업 디렉토리의 기준점으로, 우리가 현재 어떤 브랜치 위에 있는지를 나타내며 symbolic ref입니다.
- HEAD가 브랜치를 가리킬 때:
ref: refs/heads/main
- HEAD가 detached 상태일 때 (직접 커밋을 가리킬 때):
e1a3e5fbe9c2d3ffcc29d24d0e826173ed8420c9
이 상태를 Detached HEAD 상태라고 하며, 브랜치가 아닌 특정 커밋을 체크아웃 했을 때 발생합니다.
🏷️ 4. 참조의 종류 정리
종류 | 경로 | 설명 |
---|---|---|
로컬 브랜치 | refs/heads/<name> |
개발자가 만든 브랜치 |
원격 브랜치 | refs/remotes/<remote>/<name> |
원격 저장소의 브랜치 추적 |
태그 | refs/tags/<tag> |
특정 커밋을 식별하기 위한 불변 레퍼런스 |
HEAD | HEAD |
현재 체크아웃된 브랜치(또는 커밋)를 가리킴 |
FETCH_HEAD | .git/FETCH_HEAD |
git fetch 결과를 임시로 저장 |
ORIG_HEAD | .git/ORIG_HEAD |
마지막 변경 전 HEAD 상태 |
MERGE_HEAD | .git/MERGE_HEAD |
병합 대상 커밋 저장 |
CHERRY_PICK_HEAD | .git/CHERRY_PICK_HEAD |
체리픽 중 대상 커밋 저장 |
📦 5. packed-refs
: 많은 ref를 위한 최적화
Git은 많은 수의 태그나 브랜치를 효율적으로 관리하기 위해 .git/packed-refs
라는 파일에 저장합니다. 이 파일은 ref를 개별 파일로 분산 저장하는 대신 하나로 압축합니다.
$ cat .git/packed-refs
# pack-refs with: peeled fully-peeled
e1a3e5fbe9c2d3ffcc29d24d0e826173ed8420c9 refs/tags/v1.0
git gc
,git pack-refs
등을 실행하면 이 구조로 전환됨
🧾 6. Reflog: ref
의 변경 이력 추적
Git은 각 ref의 변경 이력을 기록하기 위해 reflog를 사용합니다. 이는 .git/logs/
경로에 저장되며, HEAD뿐 아니라 브랜치의 이력도 추적합니다.
.git/logs/
├── HEAD
└── refs/
└── heads/
└── main
예시
$ git reflog
e1a3e5f HEAD@{0}: commit: Add new login feature
d5c3a67 HEAD@{1}: checkout: moving from main to feature/login
HEAD@{1}
→ 이전 HEAD 위치git reset --hard HEAD@{1}
으로 복원 가능
🧭 7. 브랜치 생성/이동 시 ref 동작
브랜치 생성
$ git branch dev
.git/refs/heads/dev
생성- 내용은 현재 HEAD가 가리키는 커밋의 SHA
브랜치 이동
$ git checkout dev
.git/HEAD
→ref: refs/heads/dev
로 갱신
🔂 8. Detached HEAD의 의미와 위험성
$ git checkout <commit-hash>
.git/HEAD
에 커밋 해시가 직접 저장됨- 새 커밋 생성 시 브랜치에 연결되지 않음 → 부주의 시 고아 커밋 발생
🔬 9. Git 명령어와 refs의 관계
명령어 | 내부 동작 |
---|---|
git commit |
현재 HEAD 가 가리키는 ref를 갱신 |
git branch |
refs/heads/<branch> 생성 |
git tag |
refs/tags/<tag> 생성 |
git push |
로컬 refs/heads/* → 원격 refs/heads/* 전송 |
git fetch |
원격의 refs/heads/* 를 refs/remotes/<remote>/* 에 복사 |
git reset |
HEAD 나 브랜치 ref를 다른 커밋으로 이동 |
🧷 10. 심볼릭 ref와 직접 ref 비교
유형 | 예시 | 설명 |
---|---|---|
심볼릭 ref | ref: refs/heads/main |
다른 ref를 가리키는 ref (간접 참조) |
직접 ref | e1a3e5fb... |
실제 커밋 해시를 저장 (직접 참조) |
🧩 11. Git 내부에서 ref는 어떻게 해석될까?
Git은 내부적으로 다음 순서로 ref를 탐색합니다:
refs/heads/<name>
refs/tags/<name>
refs/remotes/<name>
packed-refs
에서 검색
예: git show main
→ refs/heads/main
→ SHA → git cat-file -p SHA
🧰 12. refs
확인 명령 정리
git show-ref
# 모든 ref와 대응하는 커밋 해시 출력
git symbolic-ref HEAD
# HEAD가 가리키는 브랜치 ref 경로 확인
git rev-parse refs/heads/main
# 해당 ref가 가리키는 커밋 ID 출력
git update-ref refs/heads/main <new-commit-id>
# 수동으로 ref를 다른 커밋으로 갱신
🧾 결론: refs
는 Git의 심장이다
refs
는 Git에서 브랜치, 태그, 원격 브랜치 등 모든 명명된 참조의 핵심HEAD
,reflog
,symbolic-ref
,packed-refs
,detached HEAD
는 모두refs
와 연관refs
는 Git을 단순한 SHA-1 저장소가 아닌 사람이 관리 가능한 버전 관리 시스템으로 만들어주는 필수 시스템