git reset --hard를 잘못 쳐서 커밋이 사라졌다면 아직 늦지 않았습니다. git은 HEAD가 거쳐 간 자리를 reflog에 따로 적어 둡니다. 거기서 커밋 번호를 찾아 다시 가면 됩니다.
근거: git 문서 git-reflog, git-config의 gc.reflogExpire·gc.reflogExpireUnreachable. 실행 결과는 git 2.53에서 직접 확인했습니다(2026년 11월 3일).
직접 날려 봤습니다
새 저장소에 커밋 세 개를 쌓고, 두 칸 뒤로 돌아갔습니다.
$ git log --oneline
53c9e32 fix pay
5b1bd4c pay
2dc3667 login
$ git reset --hard HEAD~2
HEAD is now at 2dc3667 login
$ git log --oneline
2dc3667 login
git log에는 첫 커밋만 남았습니다.
reflog에서 찾기
$ git reflog
2dc3667 HEAD@{0}: reset: moving to HEAD~2
53c9e32 HEAD@{1}: commit: fix pay
5b1bd4c HEAD@{2}: commit: pay
2dc3667 HEAD@{3}: commit (initial): login
HEAD@{0}이 방금 한 reset이고, 바로 그 전 자리 HEAD@{1}이 날린 마지막 커밋입니다. 그리로 다시 가면 됩니다.
$ git reset --hard HEAD@{1}
HEAD is now at 53c9e32 fix pay
$ git log --oneline
53c9e32 fix pay
5b1bd4c pay
2dc3667 login
세 개가 그대로 돌아왔습니다. HEAD@{1} 대신 해시 53c9e32를 써도 같습니다.
reset이 겁나면 새 브랜치로 꺼내 두는 방법도 있습니다. 현재 브랜치는 건드리지 않습니다.
git branch rescue 53c9e32
브랜치를 지웠을 때
git branch -D로 지운 브랜치도 그 브랜치에서 커밋한 적이 있으면 HEAD의 reflog에 남습니다. 해 보면 이렇게 나옵니다.
$ git branch -D feat
Deleted branch feat (was 80d729b).
$ git reflog
4ad9eb9 HEAD@{0}: checkout: moving from feat to main
80d729b HEAD@{1}: commit: feat work
checkout: moving from feat 바로 아랫줄이 지운 브랜치의 마지막 커밋입니다. git branch feat 80d729b로 되살립니다. 지울 때 git이 찍어 준 (was 80d729b)를 봐 둬도 됩니다.
얼마나 남아 있나
| 설정 | 기본값 | 뜻 |
|---|---|---|
gc.reflogExpire | 90일 | 지금 브랜치에서 닿는 커밋의 기록 |
gc.reflogExpireUnreachable | 30일 | reset으로 날린 커밋처럼 어디서도 닿지 않는 커밋의 기록 |
날린 커밋은 두 번째 경우라 기본 30일입니다. 그 뒤 git gc가 돌면 지워집니다.
reflog로도 못 살리는 것
커밋하지 않은 변경입니다. reflog는 커밋만 기억합니다. 파일을 고치고 커밋하지 않은 채 git reset --hard를 치면 그 수정은 남지 않습니다. 해 보니 reset 뒤에 고친 내용이 사라졌습니다.
그래서 reset --hard 전에는 커밋부터 하거나, 커밋하기 애매하면 git stash로 치워 두세요. stash는 git stash list로 다시 찾습니다.
쇼츠로 보기
이 글은 비치콤버스 쇼츠 No.20의 짝 글입니다. 짧은 주소 beachcombers.co.kr/s/20.