- Published on
Git rebase 실수? reflog로 커밋 복구하는 법
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
Git을 쓰다 보면 rebase는 피할 수 없는 도구입니다. 히스토리를 깔끔하게 만들고, 병합 커밋을 줄이며, PR 리뷰를 쉽게 만드는 데 큰 도움이 되죠. 하지만 그만큼 “실수했을 때”의 체감 난이도도 높습니다. rebase --onto를 잘못 썼거나, 인터랙티브 리베이스에서 커밋을 drop 해버렸거나, 리베이스 도중 충돌을 해결하다가 엉뚱한 방향으로 진행해 커밋이 사라진 것처럼 보이는 일이 자주 생깁니다.
다행히도 대부분의 경우 커밋은 실제로 사라지지 않습니다. Git은 객체를 바로 지우지 않고, 참조가 끊어진 커밋도 한동안 저장합니다. 그리고 “내가 방금 어디에 있었는지”를 추적하는 가장 강력한 힌트가 reflog입니다.
이 글에서는 git reflog로 리베이스 실수로 잃어버린 커밋을 복구하는 방법을 시나리오별로 정리합니다.
관련해 강제 푸시까지 엮인 PR 복구가 필요하다면, 아래 글도 함께 참고하세요.
reflog가 뭔가요: “브랜치 로그”가 아니라 “HEAD 이동 기록”
많은 분들이 git log로는 커밋이 안 보이면 “커밋이 날아갔다”고 생각합니다. 하지만 git log는 현재 브랜치가 가리키는 커밋 그래프만 보여줍니다. 반면 git reflog는 로컬 저장소에서 HEAD와 브랜치 레퍼런스가 어디로 이동했는지의 기록을 보여줍니다.
즉, 리베이스로 인해 브랜치 포인터가 다른 곳으로 이동했더라도, “이전 위치”를 reflog에서 찾아 되돌릴 수 있습니다.
핵심 포인트는 다음입니다.
reflog는 기본적으로 로컬에만 존재합니다(서버에 푸시되지 않음).reflog는 브랜치가 아니라 레퍼런스(주로HEAD) 이동을 기록합니다.- 커밋 객체는 당장 삭제되지 않으므로, 레퍼런스가 끊겨도 일정 기간 복구 가능성이 높습니다.
가장 먼저 할 일: 현재 상태를 보존하는 안전장치
복구 작업은 “되돌리기”를 포함하기 때문에, 실수로 더 망가뜨릴 수 있습니다. 시작 전에 현재 상태를 보존해두면 마음이 편합니다.
# 현재 브랜치 상태를 임시로 보존
git branch backup-before-reflog-recovery
# 작업 트리가 더러우면(수정 파일이 있으면) stash로 잠시 치워두기
git stash -u
stash는 선택이지만, 리베이스 도중 충돌 해결 파일이 남아 있는 상황이라면 추천합니다.
reflog로 “사라진 커밋” 찾기
1) reflog 확인
git reflog
출력은 대략 이런 형태입니다.
HEAD@{0}: 가장 최근 상태HEAD@{1},HEAD@{2}: 그 이전 상태- 메시지에
rebase (start),rebase (pick),rebase (finish)같은 힌트가 남는 경우가 많음
원하는 시점은 보통 다음 중 하나입니다.
- 리베이스 시작 직전
- 인터랙티브 리베이스에서 커밋을 지우기 직전
rebase --onto를 잘못 적용하기 직전
2) 후보 커밋을 눈으로 검증
reflog에서 의심되는 항목의 커밋 해시를 복사한 뒤, 해당 커밋이 맞는지 확인합니다.
git show abcdef12
여기서 커밋 메시지, 변경 파일, diff가 “내가 찾던 커밋”인지 확인하세요.
복구 시나리오 1: 리베이스 이전 상태로 브랜치 되돌리기
가장 흔한 요구는 “리베이스 하기 전 상태로 돌아가고 싶다”입니다. 이때는 reflog에서 리베이스 직전 커밋을 찾고, 브랜치를 그 커밋으로 이동시키면 됩니다.
방법 A: reset --hard로 되돌리기(로컬 브랜치 포인터를 이동)
# 예: 리베이스 직전 커밋이 abcdef12 라고 가정
git reset --hard abcdef12
주의할 점:
--hard는 작업 트리 변경사항을 날립니다. 필요한 변경이 있다면 먼저stash또는 커밋으로 보관하세요.
방법 B: reflog의 상대 표기 사용
해시를 직접 쓰기 싫다면 HEAD@{n} 형태로도 가능합니다.
# 예: HEAD@{6}이 리베이스 직전 상태라면
git reset --hard HEAD@{6}
복구 시나리오 2: 리베이스에서 특정 커밋만 “드롭”해버림
인터랙티브 리베이스(git rebase -i)에서 커밋을 drop하거나, 실수로 라인을 삭제해 특정 커밋이 히스토리에서 빠지는 경우가 있습니다.
이때는 “브랜치 전체를 되돌리기”보다, “잃어버린 커밋 하나만 다시 붙이기”가 더 적절할 때가 많습니다.
1) reflog에서 드롭된 커밋의 해시 찾기
git reflog
드롭된 커밋이 직접적으로 reflog에 보이지 않더라도, 리베이스 과정 중 rebase (pick) 단계나 리베이스 직전 상태에서 커밋을 따라가면 찾을 수 있습니다.
2) 커밋을 현재 브랜치에 다시 적용
가장 단순한 방법은 cherry-pick입니다.
# 드롭된 커밋이 1234abcd 라고 가정
git cherry-pick 1234abcd
충돌이 나면 해결 후 아래를 진행합니다.
git add -A
git cherry-pick --continue
이 방식의 장점:
- 현재 히스토리를 크게 흔들지 않고, 필요한 커밋만 복구 가능
복구 시나리오 3: rebase --onto를 잘못해서 커밋이 다른 베이스로 이동
rebase --onto는 강력하지만 실수하기 쉽습니다. 예를 들어, 기준(base) 커밋을 잘못 지정하면 엉뚱한 커밋들이 떨어져 나가거나, 다른 브랜치 위로 올라가면서 “내 커밋이 사라졌다”처럼 보입니다.
이 경우에도 접근은 같습니다.
- reflog에서 “명령 실행 직전” 상태를 찾는다
- 그 커밋으로 되돌린다
- 올바른 인자로 다시 리베이스한다
# 1) reflog로 직전 상태 확인
git reflog
# 2) 직전 커밋으로 되돌리기
git reset --hard HEAD@{n}
# 3) 올바르게 다시 실행(예시)
git rebase --onto new-base old-base feature-branch
여기서 new-base, old-base, feature-branch는 상황마다 달라 정확한 값은 반드시 확인해야 합니다.
복구 시나리오 4: 리베이스 도중 꼬였을 때(중단/취소)
리베이스 도중 충돌이 반복되거나, 내가 해결한 방향이 맞는지 확신이 없을 때는 일단 중단하고 안전한 지점으로 돌아가는 게 좋습니다.
리베이스 자체를 취소
git rebase --abort
대부분의 경우 리베이스 시작 전 상태로 돌아갑니다.
이미 끝난 리베이스를 되돌리고 싶다면
이미 rebase --abort가 불가능한 상태(리베이스가 종료됨)라면, reflog로 되돌립니다.
git reflog
# 리베이스 시작 전 커밋을 찾아서
git reset --hard abcdef12
복구한 뒤 원격 브랜치까지 맞춰야 한다면(강제 푸시 주의)
로컬에서 복구가 끝났는데, 이미 원격에 잘못된 히스토리를 푸시한 상태라면 원격 브랜치도 정정해야 합니다. 이때는 강제 푸시가 필요할 수 있습니다.
가능하면 아래 형태를 권장합니다.
# 단순 --force 보다 안전장치가 있는 옵션
git push --force-with-lease
--force-with-lease는 “원격이 내가 마지막으로 봤던 상태에서 바뀌지 않았다”는 전제 하에만 강제 푸시를 허용해, 동료의 커밋을 덮어쓰는 사고를 줄입니다.
강제 푸시가 PR에 미치는 영향, 안전한 복구 순서는 아래 글이 더 자세합니다.
reflog로도 못 찾을 때: 마지막 보루들
대부분은 reflog로 해결되지만, 다음 상황에서는 난이도가 올라갑니다.
- 해당 작업이 다른 PC에서 발생했고, 그 PC 로컬 reflog가 없다
- 로컬에서
git gc가 돌면서 오래된 객체가 정리되었다 - 매우 오래 전에 레퍼런스가 끊겨 만료되었다
그래도 시도해볼 수 있는 옵션이 있습니다.
1) dangling 커밋/객체 찾기
git fsck --lost-found
출력에 dangling commit이 보이면, 그 해시를 git show로 확인해 유실 커밋인지 검증합니다.
2) 원격/다른 클론에서 찾기
동료의 로컬 저장소, CI가 체크아웃한 커밋, 원격에 남아있는 다른 브랜치(예: origin/feature-old)에 커밋이 남아 있을 수 있습니다.
재발 방지 체크리스트
리베이스 자체를 피할 필요는 없지만, 실수 비용을 줄이는 습관은 중요합니다.
- 리베이스 전
git branch backup-...로 임시 브랜치 생성 - 원격에 푸시된 공유 브랜치에서는 리베이스를 신중히(팀 규칙 합의)
- 강제 푸시는
--force-with-lease우선 - 인터랙티브 리베이스는 범위를 좁게(최근 몇 개 커밋만)
마무리: “커밋이 사라졌다”는 대부분 착시다
리베이스는 히스토리를 재작성하기 때문에, git log 기준으로는 커밋이 사라진 것처럼 보일 수 있습니다. 하지만 커밋 객체가 즉시 삭제되는 경우는 드물고, git reflog는 그 사이를 잇는 강력한 타임머신 역할을 합니다.
정리하면 복구의 기본 흐름은 다음 3단계입니다.
git reflog로 올바른 시점의HEAD를 찾는다git show로 커밋이 맞는지 확인한다git reset --hard또는git cherry-pick으로 복구한다
이 패턴만 익혀두면, 리베이스 실수는 “치명적인 사고”가 아니라 “몇 분짜리 복구 작업”으로 바뀝니다.