reflog란?
reflog란 git에서 가리키는 referenced commit이 변경된 내역이다. reflog를 기록하는 것은 대표적으로 HEAD와 branch 두가지이다.
HEAD의 reflog
HEAD의 reflog 경우 새로운 커밋이 생기거나, branch가 switch 될 때마다 해당 브랜치의 가장 최신 커밋으로 reference가 바뀌게 된다.
예를 들어 master 브랜치에 test, test2란 커밋을 만들고, feature-branch 브랜치를 새로 만든다음 feature-branch에서 new commit 커밋을 새로 만들면 다음과 같이 reflog가 쌓이게 된다.
$ git reflog show HEAD
d42e22f (HEAD -> feature-branch) HEAD@{0}: commit: new commit # 4. feature-branch에서 new commit 커밋 생성
6e3a608 (master) HEAD@{1}: checkout: moving from master to feature-branch # 3. 브랜치 변경
6e3a608 (master) HEAD@{2}: commit: test2 # 2. master branch에 test2 커밋 생성
b2f3c96 HEAD@{3}: commit (initial): test # 1. master branch에 test 커밋 생성
branch의 reflog
branch의 reflog는 해당 브랜치의 commit이 변경되었을 때만 기록된다.
예를 들어 master 브랜치에 test, test2란 커밋을 만들고, feature-branch 브랜치를 새로 만든다음 feature-branch에서 new commit 커밋을 새로 만들면 master 브랜치의 커밋인 test, test2는 master 브랜치의 reflog에만 나타나고 feature-branch 브랜치의 reflog는 feature-branch 브랜치에서만 나타난다.
$ git reflog show master
6e3a608 (master) master@{0}: commit: test2
b2f3c96 master@{1}: commit (initial): test
$ git reflog show feature-branch
d42e22f (HEAD -> feature-branch) feature-branch@{0}: commit: new commit
6e3a608 (master) feature-branch@{1}: branch: Created from HEAD
reflog를 이용해 삭제된 브랜치 복구하기
위의 상황에서 만약 feature-branch가 삭제되었다고 해보자.
$ git switch master
'master' 브랜치로 전환합니다
$ git branch -D feature-branch
feature-branch 브랜치 삭제 (과거 d42e22f).
삭제된 브랜치의 reflog는 더이상 볼 수 없다.
$ git reflog show feature-branch
fatal: 애매한 인자 'feature-branch': 알 수 없는 리비전 또는 작업 폴더에 없는 경로.
경로와 리비전을 구분하려면 다음과 같이 '--'를 사용하십시오:
'git <명령> [<리비전>...] -- [<파일>...]'
하지만 HEAD의 reflog는 그대로 남아있다. 우리는 이 로그에서 feature-branch의 마지막 커밋인 d42e22f 을 확인할 수 있다.
$ git reflog show HEAD
6e3a608 (HEAD -> master) HEAD@{0}: checkout: moving from feature-branch to master
d42e22f HEAD@{1}: commit: new commit
6e3a608 (HEAD -> master) HEAD@{2}: checkout: moving from master to feature-branch
6e3a608 (HEAD -> master) HEAD@{3}: commit: test2
b2f3c96 HEAD@{4}: commit (initial): test
이 커밋은 .git/objects 폴더에 저장되어 있기 때문에 언제든지 커밋을 체크아웃 함으로써 복구가 가능하다.
$ git checkout d42e22f
Note: switching to 'd42e22f'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD의 현재 위치는 d42e22f new commit
따라서 위와 같이 checkout 하면 feature-branch의 마지막 커밋이 복구되며 이 checkout된 커밋을 다시 branch로 만드는 switch -c 명령어를 통해 브랜치를 복구할 수 있게된다.
$ git switch -c feature-branch
새로 만든 'feature-branch' 브랜치로 전환합니다
git reflog를 통해 삭제된 브랜치의 복구가 가능한 이유
git은 commit, tree, blob를 .git/objects 폴더에 저장한다. 브랜치가 삭제되더라도 이곳에 저장된 commit, tree, blob는 삭제되지 않는다. 따라서 commit hash 값만 있다면 브랜치가 삭제되더라도 언제든지 commit을 복구할 수 있다.
만약 이것에 대해 궁금하다면 아래 두 글을 추가로 읽도록 하자.
- commit의 동작원리
- git이 파일을 저장하고 복구하는 원리
git reflog의 한계점
git은 항상 로컬 브랜치의 reflog만을 저장한다. 따라서 만약 GitHub나 GitLab 등의 remote 브랜치를 삭제했다면 복구가 불가능하다. 물론 해당 브랜치를 체크아웃 한 적이 있다면 복구가 가능하다.
또한 git은 항상 모든 파일을 쌓아두지 않는다. 따라서 브랜치가 삭제된 후 일정 이상의 시간이 지났을 경우 복구가 불가능하다.