리비전 조회하기
리비전 조회하기
리비전 하나를 조회할 수도 있고 범위를 주고 여러 개를 조회할 수도 있다. 잘 쓰진 않지만 알아두는게 좋다.
리비전 하나 가리키기
사람은 커밋을 나타내는 SHA-1 해시 값을 쉽게 기억할 수 없다. 이 절에서는 커밋을 표현하는 방법을 몇 가지 설명한다. 좀 더 사람이 기억하기 쉬운 방법들이다.
짧은 SHA-1
해시 값의 앞 몇 글자만으로도 어떤 커밋인지 충분히 식별할 수 있다. 중복되지 않으면 해시 값의 앞 4자만 사용해도 된다. 유일하기만 하면 짧은 SHA-1 값이라도 괜찮다.
먼저
명령으로 어떤 커밋이 있는지 조회한다:1
git log
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | $ 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 -0800 added some blame and merge stuff |
명령으로 1
git show
로 시작하는 커밋을 조회한다면 아래와 같이 조회 할 수 있다. 다음 명령어는 모두 같다(단 짧은 해시 값이 다른 커밋과 중복되지 않다고 가정):1
1c002dd....
1 2 3 | $ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b $ git show 1c002dd4b536e7479f $ git show 1c002d |
명령어에 1
git log
옵션을 추가하면 짧은 해시 값을 보여준다. 기본으로 7자를 보여주고 해시 값이 중복되면 더 긴 해시 값을 보여준다:1
--abbrev-commit
1 2 3 4 | $ git log --abbrev-commit --pretty=oneline ca82a6d changed the version number 085bb3b removed unnecessary test code a11bef0 first commit |
보통은 8자에서 10자 내외로도 충분하다. 이 정도로도 중복되지 않는다. 대규모 프로젝트인 리눅스 커널도 커밋을 가리키는데 해시 값 40자 중에서 12자만 사용한다.
SHA-1 해시 값에 대한 단상
Git을 쓰는 사람 중에서 가능성이 낮긴 하지만 언젠가 SHA-1 값이 중복될까 봐 걱정하는 사람도 있다. 정말 그렇게 되면 어떤 일이 벌어질까?
이미 있는 SHA-1 값을 Git 데이터베이스에 커밋하면 새로운 개체라고 해도 이미 커밋한 것으로 간주된다. 그래서 해당 SHA-1 값의 커밋을 Checkout하면 항상 처음에 저장한 커밋만 Checkout된다.
그러나 해시 값이 중복되는 일은 일어나기 어렵다. SHA-1 값의 크기는 20 바이트(160비트)이다. 해시 값이 중복될 확률이 50%가 되는 데 필요한 개체의 수는
이다. 이 수는 1.2 자(‘자’는 ‘경’의 ‘억’배 - 1
2^80
)이다(충돌 확률을 구하는 공식은 1
10^24
이다). 즉, 지구에 존재하는 모래알의 수에 1200을 곱한 수와 맞먹는다.1
p=(n(n-1)/2) * (1/2^160)
아직도 SHA-1 해시 값이 중복될까 봐 걱정하는 사람들을 위해 좀 더 덧붙이겠다. 지구에서 약 6.5억 명의 인구가 개발하고 각자 매초 리눅스 커널 히스토리 전체와(100만 개) 맞먹는 개체를 쏟아 내고 바로 Push한다고 가정하자. 이런 상황에서 해시 값의 충돌 날 확률이 50%가 되기까지는 5년이 걸린다. 그냥 어느 날 동료가 전부 한순간에 늑대에게 물려 죽을 확률이 훨씬 더 높다.
브랜치로 가리키기
브랜치를 사용하는 것이 커밋을 나타내는 가장 쉬운 방법이다. 커밋 개체나 SHA-1 값이 필요한 곳이면 브랜치 이름을 사용할 수 있다. 만약
브랜치의 최근 커밋을 보고 싶으면 아래와 같이 실행한다. 1
topic1
브랜치가 1
topic1
를 가리키고 있기 때문에 두 명령의 결과는 같다:1
ca82a6d
1 2 | $ git show ca82a6dff817ec66f44342007202690a93763949 $ git show topic1 |
브랜치가 가리키는 개체의 SHA-1 값에 대한 궁금증은
이라는 Plumbing 도구가 해결해 준다. 9장에서 이 도구에 대해 좀 더 자세히 설명한다. 기본적으로 1
rev-parse
은 저수준 명령어이기 때문에 평소에는 전혀 필요하지 않지만 그래도 한번 사용해보고 어떤 결과가 나오는지 알아 두자:1
rev-parse
1 2 | $ git rev-parse topic1 ca82a6dff817ec66f44342007202690a93763949 |
RefLog로 가리키기
Git은 자동으로 브랜치와 HEAD가 지난 몇 달 동안에 가리켰었던 커밋을 모두 기록하는데 이 로그를 Reflog라고 부른다.
를 실행하면 Reflog를 볼 수 있다:1
git reflog
1 2 3 4 5 6 7 8 | $ git reflog 734713b... HEAD@{0}: commit: fixed refs handling, added gc auto, updated d921970... HEAD@{1}: merge phedders/rdocs: Merge made by recursive. 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은 브랜치가 가리키는 것이 변경될 때마다 그 정보를 임시 영역에 저장한다. 그래서 예전에 뭘 가리켰었는지 확인할 수 있다.
규칙을 사용하면 아래와 같이 HEAD가 5번 전에 가리켰던 것을 알 수 있다:1
@{n}
1 | $ git show HEAD@{5} |
순서뿐 아니라 시간도 가능하다. 어제 날짜의
브랜치를 보고 싶으면 아래와 같이 한다:1
master
1 | $ git show master@{yesterday} |
이 명령은 어제 master 브랜치가 가리키고 있던 것이 무엇인지 보여준다. Reflog에 남아있는 것만 조회할 수 있기 때문에 너무 오래된 커밋은 조회할 수 없다.
명령을 사용하면 1
git log -g
결과를 1
git reflog
명령과 같은 형태로 볼 수 있다:1
git log
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $ 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가 하나도 없다.
같은 명령은 적어도 두 달 전에 Clone한 저장소에서나 사용할 수 있다. 그러니까 이 명령을 5분 전에 Clone한 저장소에 사용하면 아무것도 나오지 않는다.1
git show HEAD@{2.months.ago}
계통 관계로 가리키기
계통 관계로도 커밋을 표현할 수 있다. 이름 끝에
를 붙이면 Git은 해당 커밋의 부모를 찾는다. 프로젝트 히스토리가 아래와 같을 때:1
^
1 2 3 4 5 6 7 8 9 | $ 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의 부모”를 의미하므로 바로 이전 커밋을 보여준다:1
HEAD^
1 2 3 4 5 6 7 | $ 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' |
뒤에 숫자도 사용할 수 있다. 예를 들어 1
^
는 “d921970의 두 번째 부모”를 의미하기에 두 번째 부모가 있는 Merge 커밋에만 사용할 수 있다. 첫 번째 부모는 Merge할 때 Checkout했던 브랜치를 말하고 두 번째 부모는 Merge한 대상 브랜치를 의미한다.1
d921970^2
1 2 3 4 5 6 7 8 9 10 11 12 13 | $ 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 |
계통을 표현하는 방법으로
라는 것도 있다. 1
~
와 1
HEAD~
는 똑같이 첫 번째 부모를 가리킨다. 하지만 그 뒤에 숫자를 사용하면 달라진다. 1
HEAD^
는 명령을 실행할 시점의 “첫 번째 부모의 첫 번째 부모”, 즉 “조부모”를 가리킨다. 위의 예제에서 1
HEAD~2
은 아래와 같다:1
HEAD~3
1 2 3 4 5 6 | $ git show HEAD~3 commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d Author: Tom Preston-Werner <tom@mojombo.com> Date: Fri Nov 7 13:47:59 2008 -0500 ignore *.gem |
이 것은
와 같은 표현이다. 다시 말해서 첫 번째 부모의 첫 번째 부모의 첫 번째 부모를 말한다:1
HEAD^^^
1 2 3 4 5 6 | $ git show HEAD^^^ commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d Author: Tom Preston-Werner <tom@mojombo.com> Date: Fri Nov 7 13:47:59 2008 -0500 ignore *.gem |
이 두 표현을 같이 사용할 수도 있다. 위의 예제에서
를 사용하면 증조부모의 Merge 커밋의 두 번째 부모를 조회한다.1
HEAD~3^2
범위로 커밋 가리키기
커밋을 하나씩 조회할 수도 있지만, 범위를 주고 여러 커밋을 한꺼번에 조회할 수도 있다. 범위을 주고 조회하면 브랜치를 관리할 때 유용하다. 브랜치가 상당히 많고 “왜 아직도 주 브랜치에 Merge가 안된 브랜치들은 무엇에 대한 브랜치일까?”라는 의문이 들면 범위를 주고 어떤 브랜치인지 쉽게 알아볼 수 있다.
Double Dot
범위를 표현하는 문법으로 Double Dot(..)을 많이 쓴다. Double Dot은 한쪽에는 있고 다른 쪽에는 없는 커밋이 무엇인지 Git에게 물어보는 것이다. 예들 들어 그림 6-1과 같은 커밋 히스토리가 있다고 가정하자.
그림 6-1. 범위를 설명하는 데 사용할 예제
experiment 브랜치의 커밋들 중에서 아직 master 브랜치에 Merge하지 않은 것만 보고 싶으면
라고 사용한다. 이 표현은 “master에는 없지만, experiment에는 있는 커밋”을 의미한다. 여기에서는 설명을 쉽게 하고자 실제 조회 결과가 아니라 그림 6-1의 문자를 사용한다:1
master..experiment
1 2 3 | $ git log master..experiment D C |
반대로
에는 없고 1
experiment
에만 있는 커밋이 궁금하면 브랜치 순서를 거꾸로 사용한다. 1
master
는 1
experiment..master
에는 없고 1
experiment
에만 있는 것을 알려준다:1
master
1 2 3 | $ git log experiment..master F E |
브랜치를 Merge하기 전에 무엇이 변경됐는지 궁금하다. 그리고 리모트 저장소에 Push할 때에도 마찬가지로 차이가 궁금하다. 이렇게 궁금한 상황에서 굉장히 유용하다:1
experiment
1 | $ git log origin/master..HEAD |
이 명령은
저장소의 1
origin
브랜치에는 없고 현재 Checkout중인 브랜치에만 있는 커밋을 보여준다. Checkout한 브랜치가 1
master
라면 1
origin/master
가 보여주는 커밋이 Push하면 서버에 전송될 커밋이다. 그리고 한쪽의 레퍼런스를 생략하면 Git은 HEAD라고 가정한다. 1
git log origin/master..HEAD
는 1
git log origin/master..
와 같다.1
git log origin/master..HEAD
세 개 이상의 레퍼런스
Double Dot은 간단하고 유용하다. 하지만, 두 개 이상의 브랜치에는 사용할 수 없다. 그러니까 현재 작업 중인 브랜치에는 있지만 다른 여러 브랜치에는 없는 커밋이 보고 싶으면
으로는 확인할 수 없다. 1
..
과 1
^
옵션 뒤에 브랜치 이름을 넣으면 그 브랜치에 없는 커밋을 찾아준다. 다음 명령어는 모두 같은 명령이다:1
--not
1 2 3 | $ git log refA..refB $ git log ^refA refB $ git log refB --not refA |
Double Dot으로는 세 개 이상의 레퍼런스에 사용할 수 없지만 이 옵션은 가능하다. 예를 들어
나 1
refA
에는 있지만 1
refB
에는 없는 커밋을 보려면 다음 중 하나를 사용한다:1
refC
1 2 | $ git log refA refB ^refC $ git log refA refB --not refC |
이 조건을 잘 응용하면 작업 중인 브랜치와 다른 브랜치을 매우 상세하게 비교할 수 있다.
Triple Dot
Triple Dot은 양쪽에 있는 두 레퍼런스 사이에서 공통으로 가지는 것을 제외하고 서로 다른 커밋만 보여준다. 그림 6-1의 커밋 히스토리를 다시 보자. 만약
와 1
master
의 공통부분은 빼고 다른 커밋만 보고 싶으면 아래와 같이 하면 된다:1
experiment
1 2 3 4 5 | $ git log master...experiment F E D C |
우리가 아는
명령의 결과를 최근 날짜순으로 보여준다. 이 예제에서는 커밋을 네 개 보여준다.1
log
그리고
명령에 1
log
옵션을 추가하면 각 커밋이 어느 브랜치에 속하는지도 보여주기 때문에 좀 더 이해하기 쉽다:1
--left-right
1 2 3 4 5 | $ git log --left-right master...experiment < F < E > D > C |
위와 같은 명령들을 사용하면 원하는 커밋을 좀 더 꼼꼼하게 살펴볼 수 있다.