-
1. 시작하기
-
2. Git의 기초
- 2.1 Git 저장소 만들기
- 2.2 수정하고 저장소에 저장하기
- 2.3 커밋 히스토리 조회하기
- 2.4 되돌리기
- 2.5 리모트 저장소
- 2.6 태그
- 2.7 Git Alias
- 2.8 요약
-
3. Git 브랜치
-
4. Git 서버
- 4.1 프로토콜
- 4.2 서버에 Git 설치하기
- 4.3 SSH 공개키 만들기
- 4.4 서버 설정하기
- 4.5 Git 데몬
- 4.6 스마트 HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 또 다른 선택지, 호스팅
- 4.10 요약
-
5. 분산 환경에서의 Git
- 5.1 분산 환경에서의 워크플로
- 5.2 프로젝트에 기여하기
- 5.3 프로젝트 관리하기
- 5.4 요약
-
6. GitHub
- 6.1 계정 만들고 설정하기
- 6.2 GitHub 프로젝트에 기여하기
- 6.3 GitHub 프로젝트 관리하기
- 6.4 Organization 관리하기
- 6.5 GitHub 스크립팅
- 6.6 요약
-
7. Git 도구
- 7.1 리비전 조회하기
- 7.2 대화형 명령
- 7.3 Stashing과 Cleaning
- 7.4 내 작업에 서명하기
- 7.5 검색
- 7.6 히스토리 단장하기
- 7.7 Reset 명확히 알고 가기
- 7.8 고급 Merge
- 7.9 Rerere
- 7.10 Git으로 버그 찾기
- 7.11 서브모듈
- 7.12 Bundle
- 7.13 Replace
- 7.14 Credential 저장소
- 7.15 요약
-
8. Git맞춤
- 8.1 Git 설정하기
- 8.2 Git Attributes
- 8.3 Git Hooks
- 8.4 정책 구현하기
- 8.5 요약
-
9. Git과 여타 버전 관리 시스템
- 9.1 Git: 범용 Client
- 9.2 Git으로 옮기기
- 9.3 요약
-
10. Git의 내부
- 10.1 Plumbing 명령과 Porcelain 명령
- 10.2 Git 개체
- 10.3 Git Refs
- 10.4 Packfile
- 10.5 Refspec
- 10.6 데이터 전송 프로토콜
- 10.7 운영 및 데이터 복구
- 10.8 환경변수
- 10.9 요약
-
A1. 부록 A: 다양한 환경에서 Git 사용하기
- A1.1 GUI
- A1.2 Visual Studio
- A1.3 Eclipse
- A1.4 Bash
- A1.5 Zsh
- A1.6 Git in Powershell
- A1.7 요약
-
A2. 부록 B: 애플리케이션에 Git 넣기
-
A3. 부록 C: Git 명령어
- A3.1 설치와 설정
- A3.2 프로젝트 가져오기와 생성하기
- A3.3 스냅샷 다루기
- A3.4 Branch와 Merge
- A3.5 공유하고 업데이트하기
- A3.6 보기와 비교
- A3.7 Debugging
- A3.8 Patch 하기
- A3.9 Email
- A3.10 다른 버전 관리 시스템
- A3.11 관리
- A3.12 Plumbing 명령어
10.3 Git의 내부 - Git Refs
Git Refs
어떤 커밋 1a410e
이전의 모든 히스토리를 보려면 git log 1a410e
라고 실행하면 히스토리를 볼 수 있지만, 여전히 1a410e
를 기억해야 한다. 이 커밋은 마지막 커밋이기 때문에 히스토리를 따라 모든 개체를 조회할 수 있다.
SHA-1 값을 날로 사용하기보다 쉬운 이름으로 된 포인터가 있으면 그걸 사용하는 게 더 좋다. 외우기 쉬운 이름으로 된 파일에 SHA-1 값을 저장한다.
Git에서는 이런 것을 “References” 또는 “Refs” 라고 부른다. 이 SHA-1 값을 저장하는 파일은 .git/refs
디렉토리에 있다.
예제의 프로젝트에는 아직 .git/refs
디렉토리 안에 파일은 없고 디렉토리만 몇 개 있다.
$ find .git/refs
.git/refs
.git/refs/heads
.git/refs/tags
$ find .git/refs -type f
Refs가 있으면 커밋을 찾기 쉬워진다. 사실 내부는 아래처럼 단순하다.
$ echo 1a410efbd13591db07496601ebc7a059dd55cfe9 > .git/refs/heads/master
SHA-1 값 대신에 지금 만든 Refs를 사용할 수 있다.
$ git log --pretty=oneline master
1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
Refs 파일을 직접 고치는 것이 좀 못마땅하다. Git에는 좀 더 안전하게 바꿀 수 있는 git update-ref
명령이 있다.
$ git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9
Git 브랜치의 역할이 바로 이거다. 브랜치는 어떤 작업 중 마지막 작업을 가리키는 포인터 또는 Refs이다. 간단히 두 번째 커밋을 가리키는 브랜치를 만들어 보자.
$ git update-ref refs/heads/test cac0ca
브랜치는 직접 가리키는 커밋과 그 커밋으로 따라갈 수 있는 모든 커밋을 포함한다.
$ git log --pretty=oneline test
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
이제 Git 데이터베이스는 아래 그림처럼 보인다.
![브랜치 Refs가 추가된 Git 데이터베이스.](png/data-model-4.png)
git branch <branch>
명령을 실행하면 Git은 내부적으로 update-ref
명령을 실행한다. 입력받은 브랜치 이름과 현 브랜치의 마지막 커밋의 SHA-1 값을 가져다 update-ref
명령을 실행한다.
HEAD
git branch <branch>
명령을 실행할 때 Git은 어떻게 마지막 커밋의 SHA-1 값을 아는 걸까?
HEAD 파일은 현 브랜치를 가리키는 간접(symbolic) Refs다.
간접 Refs라서 다른 것과 다르다. 이 Refs는 다른 Refs를 가리키는 것이라서 SHA-1 값이 없다. 파일을 열어 보면 아래와 같이 생겼다.
$ cat .git/HEAD
ref: refs/heads/master
git checkout test
를 실행하면 Git은 HEAD 파일을 아래와 같이 바꾼다.
$ cat .git/HEAD
ref: refs/heads/test
git commit
을 실행하면 커밋 개체가 만들어지는데, 지금 HEAD가 가리키고 있던 커밋의 SHA-1 값이 그 커밋 개체의 부모로 사용된다.
이 파일도 손으로 직접 편집할 수 있지만 git symbolic-ref
라는 명령어가 있어서 좀 더 안전하게 사용할 수 있다.
이 명령으로 HEAD의 값을 읽을 수 있다.
$ git symbolic-ref HEAD
refs/heads/master
HEAD의 값을 변경할 수도 있다.
$ git symbolic-ref HEAD refs/heads/test
$ cat .git/HEAD
ref: refs/heads/test
refs 형식에 맞지 않으면 수정할 수 없다.
$ git symbolic-ref HEAD test
fatal: Refusing to point HEAD outside of refs/
태그
중요한 개체 세 가지는 모두 살펴봤고(Blob, Tree, 커밋) 살펴볼 개체가 하나가 남았다. 태그 개체는 커밋 개체랑 매우 비슷하다. 커밋 개체처럼 누가, 언제 태그를 달았는지 태그 메시지는 무엇이고 어떤 커밋을 가리키는지에 대한 정보가 포함된다. 태그 개체는 Tree 개체가 아니라 커밋 개체를 가리키는 것이 그 둘의 차이다. 브랜치처럼 커밋 개체를 가리키지만 옮길 수는 없다. 태그 개체는 늘 그 이름이 뜻하는 커밋만 가리킨다.
Git의 기초 에서 배웠듯 태그는 Annotated 태그와 Lightweight 태그 두 종류로 나뉜다. 먼저 아래와 같이 Lightweight 태그를 만들어 보자.
$ git update-ref refs/tags/v1.0 cac0cab538b970a37ea1e769cbbde608743bc96d
Lightwieght 태그는 만들기 쉽다. 브랜치랑 비슷하지만 브랜치처럼 옮길 수는 없다.
이에 비해 Annotated 태그는 좀 더 복잡하다. Annotated 태그를 만들면 Git은 태그 개체를 만들고 거기에 커밋을 가리키는 Refs를 저장한다.
Annotated 태그는 커밋을 직접 가리키지 않고 태그 개체를 가리킨다.
-a
옵션을 주고 Annotated 태그를 만들어 확인해보자.
$ git tag -a v1.1 1a410efbd13591db07496601ebc7a059dd55cfe9 -m 'test tag'
태그 개체의 SHA-1 값을 확인한다.
$ cat .git/refs/tags/v1.1
9585191f37f7b0fb9444f35a9bf50de191beadc2
git cat-file -p
명령으로 해당 SHA-1 값의 내용을 조회한다.
$ git cat-file -p 9585191f37f7b0fb9444f35a9bf50de191beadc2
object 1a410efbd13591db07496601ebc7a059dd55cfe9
type commit
tag v1.1
tagger Scott Chacon <schacon@gmail.com> Sat May 23 16:48:58 2009 -0700
test tag
object
부분에 있는 SHA-1 값이 실제로 태그가 가리키는 커밋이다.
커밋 개체뿐만 아니라 모든 Git 개체에 태그를 달 수 있다.
커밋 개체에 태그를 다는 것이 아니라 Git 개체에 태그를 다는 것이다.
Git을 개발하는 프로젝트에서는 관리자가 자신의 GPG 공개키를 Blob 개체로 추가하고 그 파일에 태그를 달았다.
아래 명령으로 그 공개키를 확인할 수 있다.
$ git cat-file blob junio-gpg-pub
Linux Kernel 저장소에도 커밋이 아닌 다른 개체를 가리키는 태그 개체가 있다. 그 태그는 저장소에 처음으로 소스 코드를 임포트했을 때 그 첫 Tree 개체를 가리킨다.
리모트
리모트 Refs라는 것도 있다.
리모트를 추가하고 Push 하면 Git은 각 브랜치마다 Push 한 마지막 커밋이 무엇인지 refs/remotes
디렉토리에 저장한다.
예를 들어, origin
이라는 리모트를 추가하고 master
브랜치를 Push 한다.
$ git remote add origin git@github.com:schacon/simplegit-progit.git
$ git push origin master
Counting objects: 11, done.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (7/7), 716 bytes, done.
Total 7 (delta 2), reused 4 (delta 1)
To git@github.com:schacon/simplegit-progit.git
a11bef0..ca82a6d master -> master
origin
의 master
브랜치에서 서버와 마지막으로 교환한 커밋이 어떤 것인지 refs/remotes/origin/master
파일에서 확인할 수 있다.
$ cat .git/refs/remotes/origin/master
ca82a6dff817ec66f44342007202690a93763949
refs/heads
에 있는 Refs인 브랜치와 달리 리모트 Refs는 Checkout 할 수 없고 읽기 용도로만 쓸 수 있는 브랜치인 것이다.
이 리모트 Refs는 서버의 브랜치가 가리키는 커밋이 무엇인지 적어둔 일종의 북마크이다.