-
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 명령어
7.1 Git 도구 - 리비전 조회하기
지금까지 일상적으로 자주 사용하는 명령들과 몇 가지 워크플로를 배웠다. 파일을 추적하고 커밋하는 등의 기본적인 명령뿐만 아니라 Staging Area가 왜 좋은지도 배웠고 가볍게 토픽 브랜치를 만들고 Merge 하는 방법도 다뤘다. 이제는 Git 저장소로 충분히 소스코드를 관리할 수 있을 것이다.
이 장에서는 일상적으로 사용하지는 않지만 위급한 상황에서 반드시 필요한 Git 도구를 살펴본다.
리비전 조회하기
Git은 커밋 하나를 가리키거나 범위를 사용하여 여러 커밋을 가리키는 다양한 방법을 갖고 있다. 그 많은 방법을 아는 것이 필요하진 않지만 알아두면 좋다.
리비전 하나 가리키기
40글자나 되는 긴긴 SHA-1 해시 값으로도 커밋을 외울 수 있지만 사람이 사용하기 좋은 방법이 있다. 이 절에서는 커밋을 가리키거나 표현하는 방법을 몇 가지 설명한다.
SHA-1 줄여 쓰기
Git은 해시 값의 앞 몇 글자만으로도 어떤 커밋인지 충분히 식별할 수 있다. 저장소 안에서 해시 값이 중복되지 않으면 해시 값의 앞 4자만으로도 나타낼 수 있다. 즉 짧은 SHA-1 값이라고 해도 유일해야 한다.
먼저 git log
명령으로 어떤 커밋이 있는지 조회하는 예제를 보자.
$ git log
commit 734713bc047d87bf7eac9674765ae793478c50d3
Author: Scott Chacon <schacon@gmail.com>
Date: Fri Jan 2 18:32:33 2009 -0800
fixed refs handling, added gc auto, updated tests
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Merge: 1c002dd... 35cfb2b...
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 15:08:43 2008 -0800
Merge commit 'phedders/rdocs'
commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 14:58:32 2008 -
added some blame and merge stuff
위의 내용은 1c002dd…
로 시작하는 커밋에 대해 로그를 살펴보겠다는 말이다.
git show
명령을 사용하는 다음 결과는 모두 같다(단 짧은 해시 값이 다른 커밋과 중복되지 않다고 가정).
$ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b
$ git show 1c002dd4b536e7479f
$ git show 1c002d
git log
명령에 --abbrev-commit
이라는 옵션을 추가하면 짧고 중복되지 않는 해시 값을 보여준다. 기본으로 7자를 보여주고 해시 값이 중복되는 경우 더 긴 해시 값을 보여준다.
$ git log --abbrev-commit --pretty=oneline
ca82a6d changed the version number
085bb3b removed unnecessary test code
a11bef0 first commit
보통은 8자에서 10자 내외로도 충분히 유일하게 커밋을 나타낼 수 있다. 2018년 6월 현재 꽤 큰 프로젝트인 Linux 커널은 79만 개 이상의 커밋, 650만 개 이상의 오브젝트가 있다. Linux 커널 프로젝트는 해시 값 11개만 사용해도 충돌이 없다.
노트
|
SHA-1 해시 값에 대한 단상
Git을 쓰는 사람들은 가능성이 작긴 하지만 언젠가 SHA-1 값이 중복될까 봐 걱정한다. 정말 그렇게 되면 어떤 일이 벌어질까? 이미 있는 SHA-1 값이 Git 데이터베이스에 커밋되면 새로운 개체라고 해도 이미 커밋된 것으로 생각하고 이전의 커밋을 재사용한다. 그래서 해당 SHA-1 값의 커밋을 Checkout 하면 항상 처음 저장한 커밋만 Checkout 된다. 그러나 해시 값이 중복되는 일은 일어나기 어렵다.
SHA-1 값의 크기는 20 바이트(160비트)이다.
해시 값이 중복될 확률이 50%가 되는 데 필요한 개체의 수는 280이다.
이 수는 1자 2,000해
('자’는 '경’의 '억’배 - 1024, 충돌 확률을 구하는 공식은 아직도 SHA-1 해시 값이 중복될까 봐 걱정하는 사람들을 위해 좀 더 덧붙이겠다. 지구에서 약 6억 5천만 명의 인구가 개발하고 각자 매초 Linux 커널 히스토리 전체와(650만 개) 맞먹는 개체를 쏟아 내고 바로 Push 한다고 가정하자. 이런 상황에서 해시 값의 충돌 날 확률이 50%가 되기까지는 약 2년이 걸린다. 그냥 어느 날 동료가 한 순간에 모두 늑대에게 물려 죽을 확률이 훨씬 더 높다. |
브랜치로 가리키기
커밋을 가리키는 방법 중에 가장 많이 사용하는 방법이 있다. 어떤 커밋이 브랜치의 가장 최신 커밋이라면 간단히 브랜치 이름으로 커밋을 가리킬 수 있다. 브랜치 이름을 Git 명령에 전달하면 브랜치가 가리키는 커밋을 가리키게 된다.
만약 topic1
브랜치의 최근 커밋을 보고 싶으면 아래와 같이 실행한다. topic1
브랜치가 ca82a6d…
를 가리키고 있기 때문에 두 명령의 결과는 같다.
$ git show ca82a6dff817ec66f44342007202690a93763949
$ git show topic1
브랜치가 가리키는 개체의 SHA-1 값에 대한 궁금증은 rev-parse
이라는 Plumbing 도구가 해결해 준다.
Git의 내부에서 이 뚫어뻥에 대해 시원하게 설명한다. 기본적으로 rev-parse
은 저수준 명령이기 때문에 평소에는 전혀 필요하지 않다.
그래도 한번 사용해보고 어떤 결과가 나오는지 알아 두자.
$ git rev-parse topic1
ca82a6dff817ec66f44342007202690a93763949
RefLog로 가리키기
Git은 자동으로 브랜치와 HEAD가 지난 몇 달 동안에 가리켰었던 커밋을 모두 기록하는데 이 로그를 “Reflog” 라고 부른다.
git reflog
를 실행하면 Reflog를 볼 수 있다.
$ git reflog
734713b HEAD@{0}: commit: fixed refs handling, added gc auto, updated
d921970 HEAD@{1}: merge phedders/rdocs: Merge made by the 'recursive' strategy.
1c002dd HEAD@{2}: commit: added some blame and merge stuff
1c36188 HEAD@{3}: rebase -i (squash): updating HEAD
95df984 HEAD@{4}: commit: # This is a combination of two commits.
1c36188 HEAD@{5}: rebase -i (squash): updating HEAD
7e05da5 HEAD@{6}: rebase -i (pick): updating HEAD
Git은 브랜치가 가리키는 것이 달라질 때마다 그 정보를 임시 영역에 저장한다.
그래서 예전에 가리키던 것이 무엇인지 확인해 볼 수 있다.
@{n}
규칙을 사용하면 아래와 같이 HEAD가 5번 전에 가리켰던 것을 알 수 있다.
$ git show HEAD@{5}
순서뿐 아니라 시간도 사용할 수 있다. 어제 날짜의 master
브랜치를 보고 싶으면 아래와 같이 한다.
$ git show master@{yesterday}
이 명령은 어제 master
브랜치가 가리키고 있던 것이 무엇인지 보여준다.
Reflog에 남아있을 때만 조회할 수 있기 때문에 너무 오래된 커밋은 조회할 수 없다.
git log -g
명령을 사용하면 git reflog
결과를 git log
명령과 같은 형태로 볼 수 있다.
$ git log -g master
commit 734713bc047d87bf7eac9674765ae793478c50d3
Reflog: master@{0} (Scott Chacon <schacon@gmail.com>)
Reflog message: commit: fixed refs handling, added gc auto, updated
Author: Scott Chacon <schacon@gmail.com>
Date: Fri Jan 2 18:32:33 2009 -0800
fixed refs handling, added gc auto, updated tests
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Reflog: master@{1} (Scott Chacon <schacon@gmail.com>)
Reflog message: merge phedders/rdocs: Merge made by recursive.
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 15:08:43 2008 -0800
Merge commit 'phedders/rdocs'
Reflog의 일은 모두 로컬의 일이기 때문에 내 Reflog가 동료의 저장소에는 있을 수 없다.
이제 막 Clone 한 저장소는 아무것도 한 것이 없어서 Reflog가 하나도 없다.
git show HEAD@{2.months.ago}
같은 명령은 적어도 두 달 전에 Clone 한 저장소에서나 사용할 수 있다. 그러니까 이 명령을 5분 전에 Clone 한 저장소에 사용하면 아무 결과도 나오지 않는다.
힌트
|
Reflog를 Git의 Shell의 명령 히스토리 기능 버전으로 생각하기 |
Unix나 Linux 사용 경험이 있는 경우 reflog를 Git의 Shell의 명령 히스토리 기능 버전으로 생각해볼 수 있다. 여기서 중요한 점은 오직 나의 “세션” 에서만 확인할 수 있는 내용이라는 점으로 같은 시스템에 있더라도 다른이와 공유되지 않는 정보라는 점이다.
계통 관계로 가리키기
계통 관계로도 커밋을 표현할 수 있다. 이름 끝에 ^
(캐럿) 기호를 붙이면 Git은 해당 커밋의 부모를 찾는다. 프로젝트 히스토리가 아래와 같을 때는 아래처럼 한다.
$ git log --pretty=format:'%h %s' --graph
* 734713b fixed refs handling, added gc auto, updated tests
* d921970 Merge commit 'phedders/rdocs'
|\
| * 35cfb2b Some rdoc changes
* | 1c002dd added some blame and merge stuff
|/
* 1c36188 ignore *.gem
* 9b29157 add open3_detach to gemspec file list
HEAD^
는 바로 “HEAD의 부모” 를 의미하므로 바로 이전 커밋을 보여준다.
$ git show HEAD^
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Merge: 1c002dd... 35cfb2b...
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 15:08:43 2008 -0800
Merge commit 'phedders/rdocs'
노트
|
Windows에서 캐럿 기호 사용하기 |
Windows에서 실행한 cmd.exe
에서는 ^
기호가 이미 다른 의미로 사용되고 있어서 Git 에서 사용하려면 좀 다른 방식을 사용해야 한다. 기호 두 개를 연속으로 사용하거나 쌍따옴표 안에서 사용해야 한다.
$ git show HEAD^ # will NOT work on Windows
$ git show HEAD^^ # OK
$ git show "HEAD^" # OK
^
뒤에 숫자도 사용할 수 있다. 예를 들어 d921970^2
는 “d921970의 두 번째 부모” 를 의미한다.
그래서 두 번째 부모가 있는 Merge 커밋에만 사용할 수 있다.
첫 번째 부모는 Merge 할 때 Checkout 했던 브랜치를 말하고 두 번째 부모는 Merge 한 대상 브랜치를 의미한다.
$ git show d921970^
commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 14:58:32 2008 -0800
added some blame and merge stuff
$ git show d921970^2
commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548
Author: Paul Hedderly <paul+git@mjr.org>
Date: Wed Dec 10 22:22:03 2008 +0000
Some rdoc changes
계통을 표현하는 방법으로 ~
라는 것도 있다.
HEAD~
와 HEAD^
는 똑같이 첫 번째 부모를 가리킨다.
하지만, 그 뒤에 숫자를 사용하면 달라진다.
HEAD~2
는 명령을 실행할 시점의 “첫 번째 부모의 첫 번째 부모” , 즉 “조부모” 를 가리킨다. 위의 예제에서 HEAD~3
은 아래와 같다.
$ git show HEAD~3
commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Author: Tom Preston-Werner <tom@mojombo.com>
Date: Fri Nov 7 13:47:59 2008 -0500
ignore *.gem
이것은 HEAD^
와 같은 표현이다. 부모의 부모의 부모 즉 증조 부모쯤 되겠다.
$ git show HEAD^^^
commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Author: Tom Preston-Werner <tom@mojombo.com>
Date: Fri Nov 7 13:47:59 2008 -0500
ignore *.gem
이 두 표현을 같이 사용할 수도 있다. 위의 예제에서 HEAD~3^2
를 사용하면 증조부모의 Merge 커밋의 부모의 부모를 조회한다.
범위로 커밋 가리키기
커밋을 하나씩 조회할 수도 있지만, 범위를 주고 여러 커밋을 한꺼번에 조회할 수도 있다. 범위를 사용하여 조회할 수 있으면 브랜치를 관리할 때 유용하다. 상당히 많은 브랜치를 가지고 있고 “왜 이 브랜치들은 아직도 주 브랜치에 Merge도 안 되고 뭐임?” 라는 의문이 들면 범위를 주고 어떤 브랜치인지 쉽게 찾을 수 있다.
Double Dot
범위를 표현하는 문법으로 Double Dot(..)을 많이 쓴다. Double Dot은 어떤 커밋들이 한쪽에는 관련됐고 다른 쪽에는 관련되지 않았는지 Git에게 물어보는 것이다. 예들 들어 범위를 설명하는 데 사용할 예제과 같은 커밋 히스토리가 있다고 가정하자.
experiment
브랜치의 커밋들 중에서 아직 master 브랜치에 Merge 하지 않은 것들만 보고 싶으면
master..experiment
라고 사용한다. 이 표현은 “master에는 없지만, experiment에는 있는 커밋” 을 의미한다.
여기에서는 설명을 쉽게 하려고 실제 조회 결과가 아니라 그림의 커밋을 의미하는 문자를 사용한다.
$ git log master..experiment
D
C
반대로 experiment
에는 없고 master
에만 있는 커밋이 궁금하면 브랜치 순서를 거꾸로 사용한다.
experiment..master
는 experiment
에는 없고 master
에만 있는 것을 알려준다.
$ git log experiment..master
F
E
experiment
브랜치를 Merge 할 때마다 Merge 하기 전에 무엇이 변경됐는지 확인해보고 싶을 것이다.
그리고 리모트 저장소에 Push 할 때도 마찬가지로 차이점을 확인해보고 싶을 것이다. 이럴 때 굉장히 유용하다.
$ git log origin/master..HEAD
이 명령은 origin
저장소의 master
브랜치에는 없고 현재 Checkout 중인 브랜치에만 있는 커밋을 보여준다.
Checkout 한 브랜치가 origin/master
라면 git log origin/master..HEAD
가 보여주는 커밋이 Push 하면 서버에 전송될 커밋들이다.
그리고 한쪽의 Refs를 생략하면 Git은 HEAD
라고 가정하기 때문에
git log origin/master..
는 git log origin/master..HEAD
와 같다.
세 개 이상의 Refs
Double Dot은 간단하고 유용하지만 두 개 이상의 브랜치에는 사용할 수 없다. 그러니까 현재 작업 중인 브랜치에는 있지만 다른 여러 브랜치에는 없는 커밋을 보고 싶으면 ..
으로는 확인할 수 없다.
Git은 ^
이나 --not
옵션 뒤에 브랜치 이름을 넣으면 그 브랜치에 없는 커밋을 찾아준다.
아래의 명령 세 가지는 모두 같은 명령이다.
$ git log refA..refB
$ git log ^refA refB
$ git log refB --not refA
이 옵션들은 Double Dot으로는 할 수 없는, 세 개 이상의 Refs에 사용할 수 있는 장점이 있다.
예를 들어 refA
나 refB
에는 있지만 refC
에는 없는 커밋을 보려면 아래 중 한 명령을 사용한다.
$ git log refA refB ^refC
$ git log refA refB --not refC
이 조건을 잘 응용하면 작업 중인 브랜치와 다른 브랜치을 매우 상세하게 비교해볼 수 있다.
Triple Dot
Triple Dot은 양쪽에 있는 두 Refs 사이에서 공통으로 가지는 것을 제외하고 서로 다른 커밋만 보여준다.
범위를 설명하는 데 사용할 예제의 커밋 히스토리를 다시 보자.
만약 master
와 experiment
의 공통부분은 빼고 다른 커밋만 보고 싶으면 아래와 같이 하면 된다.
$ git log master...experiment
F
E
D
C
우리가 아는 log
명령의 결과를 최근 날짜순으로 보여준다. 이 예제에서는 커밋을 네 개 보여준다.
그리고 log
명령에 --left-right
옵션을 추가하면 각 커밋이 어느 브랜치에 속하는지도 보여주기 때문에 좀 더 이해하기 쉽다.
$ git log --left-right master...experiment
< F
< E
> D
> C
위와 같은 명령을 사용하면 원하는 커밋을 좀 더 꼼꼼하게 살펴볼 수 있다.