[Git/Github] Git 심화 명령어5 — git revisions(git object 표현하기)

dEpayse
dEpayse_publication
12 min readAug 25, 2022

이번 포스트에서는 git revision 에 대해서 알아본다.

git 에서 revision 은 명령어는 아니고, git 의 object 하나를 의미한다. revision 의 사전적인 의미는 ‘수정, 검토, 변경, 어떤 것의 새로운 버전’이라는 뜻이 있는데, git 에서 사용하는 revision 의 뜻은 ‘어떤 것의 새로운 버전’이라는 뜻이 더 어울릴 것 같다.

revision은 SHA-1 함수를 사용하기 때문에 40자의 16진수로 나타나는데, 특정 revision 을 명령어를 이용하여 조작하고 싶을 때 우리가 직접 40자의 16진수를 입력하는 것은 생산성을 크게 저하시킬 수 있기 때문에 표현할 수 있는 방법들을 제공한다. 예전 포스트에서 다뤄봤듯이 git 의 object 는 blob, tree, commit, annotated tag 4가지가 있고, 우리가 주로 다루게 될 object는 commit 이다. 기본 개념에 대한 내용은 아래 링크를 참고하자.

이제부터 revision 을 표현할 수 있는 방법에 대해 다뤄보자.

Sha-1 줄여쓰기

git 저장소에서 겹치는 것이 없다면, 40자의 SHA-1 을 줄여서 표현할 수 있다.

git show 1af426118211296c930aa6ec6ec451b73ce228ba

위의 명령을 아래와 같이 사용해도, 만약 겹치는 hash id 가 없다면 아래와 같이 40자 중 앞자리의 몇 개만 입력하여 사용할 수 있다.

git show 1af4261

hash id 는 최소 4자리까지 줄여쓸 수 있지만, 다른 hash id 와 겹칠 확률이 커지기 때문에 7자리를 보통 사용한다. 7자리를 사용했을 때 충돌 확률이 1%가 되려면 2323개의 revision 이 필요하다.

git log --abbrev-commit

위의 명령어를 사용하면 git 저장소 내에 충돌하지 않도록 하고, 최소 7자의 짧은 길이의 hash id 들을 보여준다.

Fig1. revision hash id 를 7자리로 사용했을 때 충돌 확률 (출처 : https://github.com/source-foundry/font-v/issues/2)

브랜치로 가리키기

branch 는 커밋을 가리키고, 커밋들이 각각 부모 커밋을 가리키고 있기 때문에 커밋 이력을 알 수 있다. 여기서 branch 가 가리키는 커밋을 branch tip 이라고 하는데, 아래와 같이 branch tip 커밋을 명령어에 사용하기 위해 branch 이름을 써서 간단히 표현할 수 있다.

Fig2. 브랜치로 가리키기

Fig2 의 branch_a 의 브랜치팁이 ca82a 커밋과 같을 때 아래 두 명령어는 같은 의미이다.

git show ca82a6dff817ec66f44342007202690a93763949
git show branch_a

reflog 를 참고하여 @{n} 으로 가리키기

git은 HEAD 포인터와 branch 의 변경사항을 자동으로 추적하는데, 이를 reference log (reflog) 라고 한다. HEAD 포인터의 reflog 는 commit 의 변경사항 혹은 branch 를 바꾸는 것을 포함하고, branch 의 reflog 는 commit 의 변경사항만을 포함한다. git 저장소가 reflog 를 자동으로 기록하는 기본 기간은 3달이다.

아무 인자 없이 reflog 명령어를 사용한다면, HEAD 의 reflog 를 볼 수 있다. 아래의 세 개의 명령어는 같은 의미이다.

git reflog
git reflog HEAD
git reflog show HEAD

위 명령어를 수행하면 아래와 같은 결과를 볼 수 있다.

eff544f HEAD@{0}: commit: migrate existing content 
bf871fd HEAD@{1}: commit: Add Git Reflog outline
9a4491f HEAD@{2}: checkout: moving from main to git_reflog
9a4491f HEAD@{3}: checkout: moving from Git_Config to main
39b159a HEAD@{4}: commit: expand on git context
9b3aa71 HEAD@{5}: commit: more color clarification
f34388b HEAD@{6}: commit: expand on color support
9962aed HEAD@{7}: commit: a git editor -> the Git editor

특정 브랜치의 reflog 를 보고 싶다면 HEAD 대신 해당 브랜치의 이름을 사용하면 된다.

git reflog branch_a
git reflog show branch_a

(참고로, git reflog show 명령어는 git log 에 옵션을 붙인 것과 같다.

git reflog show
git log -g --abbrev-commit --pretty=oneline

위 두 명령어는 같은 일을 수행한다.)

revision 의 관점에서 다룰 내용은, HEAD 혹은 특정 브랜치의 reflog 의 n 번째 로그와 관련된 커밋을 지칭할 수 있다는 것이다. 위의 ‘git reflog HEAD’ 의 결과에서 봤듯이, HEAD@{n}은 HEAD reflog 의 최신 기록에서 n 번째 떨어진 로그에 대한 정보를 의미한다. 따라서 아래와 같은 명령어를 통해 로그와 관련된 커밋의 정보를 볼 수 있다.

git show HEAD@{n}
git show branch_a@{n}

@{n}에서 n 대신 시간 표현을 사용할 수 있다. 아래는 사용 가능한 시간 표현들이며, 1대신 다른 값을 사용할 수 있다.

  • 1.minute.ago
  • 1.hour.ago
  • 1.day.ago
  • yesterday
  • 1.week.ago
  • 1.month.ago
  • 1.year.ago
  • 2011-05-17.09:00:00

예시로 아래와 같이 사용할 수 있다.

git show HEAD@{2.days.ago}
Fig3. timed reflog

해당 시간에 정확히 해당하는 reflog 를 지칭한다기보다, Fig3 과 같이 첫 reflog 부터 지칭한 시간 사이에 있는 reflog 중에 가장 최근의 reflog 를 의미한다.

계통 관계로 가리키기

^ (캐럿 기호)

HEAD 포인터 혹은 branch 이름의 끝에 ^ (캐럿 기호)를 붙이면 HEAD 포인터의 부모 혹은 branch tip 의 부모를 의미한다.

git show HEAD^
git show branch_a^

Window OS 에서 실행한 cmd 에서는 이미 캐럿 기호가 다른 뜻으로 사용되므로 위와 같은 의미로 사용하려면 아래와 같이 사용해야한다. 그러나 본 포스트에서는 캐럿 기호의 의미 전달을 위해 mac os 를 기준으로 다루기로 하자.

git show "HEAD^"

그러나 merge 커밋의 경우, 부모 커밋이 여러 개일 수 있다. branch_b 를 branch_a 에 병합한다면, merge 커밋의 첫 번째 부모는 branch_a 의 커밋이고, 두 번째 부모가 branch_b 의 커밋이 된다. 이 경우 캐럿 기호 뒤에 몇 번째 부모인지 숫자를 입력하여 커밋을 표현할 수 있다. (1부터 시작)

git show HEAD^2
git show branch_a^2

만약 부모 커밋이 한 개인데 ‘^2’ 와 같은 표현을 사용한다면 오류가 발생한다. 바로 뒤에 숫자가 없는 하나의 캐럿 기호는 첫 번째 부모를 의미하여, 아래의 두 명령어는 같은 뜻이다.

git show HEAD^
git show HEAD^1

캐럿 기호를 두 번 사용하여 부모의 부모를 나타낼 수도 있고, 두 번째 부모의 부모를 나타낼 수도 있다.

git show HEAD^^ #HEAD포인터가 가리키는 커밋의 첫 번째 부모의 첫 번째 부모(=조부모)
git show HEAD^2^ #HEAD포인터가 가리키는 커밋의 첫 번째 부모의 첫 번째 부모(=조부모)

~ (물결표)

캐럿 기호(^)가 한 단계 조상의 몇 번째 부모인지를 찾는다면, 물결표(~)는 첫 번째 부모의 몇 단계 조상인지를 찾을 수 있다. 만약 부모 중에 두 개 이상의 부모 커밋을 갖고 있는 부모가 있어도, 첫 번째 부모만을 따라간다.

Fig4. ^(캐럿 기호)와 ~(물결표) 비교

물결표도 뒤에 숫자를 붙여 몇 번째 부모 커밋인지 표현할 수 있고, 숫자를 표기하지 않는다면 첫 번째 부모 커밋을 의미한다.

# 아래 두 명령어는 같은 의미이다.
git show HEAD~
git show HEAD~1
# HEAD 포인터가 가리키는 커밋의 증조부모(3단계 부모)
git show HEAD~3
git show HEAD~~~
# 브랜치도 마찬가지이다.
git show branch_a~
git show branch_a~1
git show branch_a~3
git show branch_a~~~

Fig5 가 캐럿 기호와 물결표에 관한 많은 것을 함축적으로 보여줄 수 있을 것 같아 참고하면 좋을 것 같다. Fig5 의 로그 그래프의 수평 방향에서 부모 커밋의 순서는 왼쪽에서 오른쪽이고(맨 왼쪽이 첫 번째 부모 커밋을 의미), 수직 방향에서는 부모가 위쪽, 자식이 아래쪽이다(맨 아래쪽이 가장 최신 커밋을 의미).

Fig5. ^(캐럿 기호)와 ~(물결표) 표현 정리 (출처 : https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=buildkdom&logNo=221245384545)

범위 내의 커밋들 가리키기

Double Dot(..)

double dot 을 이용하면 아래와 같이 한 쪽 브랜치에만 속하는 커밋들을 가리킬 수 있다.

Fig6. double dot(..) 을 이용하여 범위 커밋 나타내기

Fig6 과 같은 경우 아래와 같이 명령어를 작성하여 얻을 수 있다. 아래 명령어를 이용하면 해당 범위의 최신 커밋부터 로그를 볼 수 있다.

git log branch_b..branch_a

double dot 을 해석하자면, branch_b 에는 없지만 branch_a 에는 있는 커밋들을 의미한다.

세 개 이상의 브랜치를 비교하고 싶을 때는 double dot 을 이용하기 힘들다. 이 때는 ‘^(캐럿 기호)’ 혹은 ‘--not’ 옵션을 활용할 수 있다. Fig6 과 같은 결과를 얻기 위해 다음과 같은 명령어를 사용할 수 있고, 위의 명령어와 동일한 의미이다.

git log branch_a ^branch_b
git log branch_a --not branch_b

세 개 이상의 branch 를 비교하기 위해서 아래와 같이 명령어를 사용할 수 있다.

git log branch_a branch_b ^branch_c
git log branch_a branch_b --not branch_c

Triple Dot(…)

triple dot 을 이용하면 두 브랜치가 공통으로 갖고 있는 브랜치를 제외한 커밋들을 가리킬 수 있다.

Fig7. triple dot(…) 을 이용하여 범위 커밋 나타내기

Fig7 과 같은 경우 아래와 같이 명령어를 작성하여 얻을 수 있다. 아래 명령어를 이용하면 해당 범위의 최신 커밋부터 로그를 볼 수 있다.

git log branch_a...branch_b

다음과 같은 명령어를 이용하면 결과 커밋이 어느 브랜치에 속하는지 ‘<’, ‘>’ 기호를 통해 알려준다. 뾰족한 부분이 가리키는 쪽이다.

git log --left-right branch_a...branch_b

Reference

  1. [Git official] “리비전 조회하기" — https://git-scm.com/book/ko/v2/Git-%EB%8F%84%EA%B5%AC-%EB%A6%AC%EB%B9%84%EC%A0%84-%EC%A1%B0%ED%9A%8C%ED%95%98%EA%B8%B0
  2. [Git official] “ git-rev-parse Documentation” — https://git-scm.com/docs/git-rev-parse
  3. [John’s Home] “git rev-parse” — https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=buildkdom&logNo=221245384545
  4. [eungjoon] “git의 revision에 대해” — https://blog.npcode.com/2012/09/20/git%EC%9D%98-revision%EC%97%90-%EB%8C%80%ED%95%B4/
  5. [Devhints.Io] “Git revisions cheatsheet” — https://devhints.io/git-revisions
  6. [Atlassian] “git reflog” — https://www.atlassian.com/git/tutorials/rewriting-history/git-reflog
  7. [Git official] “gitrevisions” — https://git-scm.com/docs/gitrevisions

--

--

dEpayse
dEpayse_publication

나뿐만 아니라 다른 사람들도 이해할 수 있도록 작성하는, 친절한 블로그를 목표로.