Git 실수로 코드 날린 날
— 복구기
git reset --hard 한 방에 3일치 작업이 사라진 그 날의 기록
그날 무슨 일이 있었나
금요일 오후 4시였다. 배포를 앞두고 브랜치 정리를 하고 있었다. main에서 작업 브랜치를 최신으로 맞추려고 했다. 명령어를 입력했다.
❯ git checkout main
Switched to branch 'main'
❯ git pull origin main
Already up to date.
❯ git checkout feat/payment-refactor
Switched to branch 'feat/payment-refactor'
# 여기서 실수 — rebase 하려다 reset --hard를 쳤다
❯ git reset --hard origin/main
HEAD is now at a3f82c1 fix: 결제 모듈 에러 처리 수정
# 0.5초 침묵
# 그리고
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3일치 작업이 사라졌다
feat/payment-refactor 브랜치 전체가 main으로 덮어씌워졌다
커밋 안 된 변경사항 포함해서 전부
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

처음 30분 — 패닉 상태
화면을 보고 10초 동안 아무것도 못 했다. "내가 지금 뭘 한 거지." 손이 떨렸다. Ctrl+Z를 눌렀다. Git 명령은 되돌아가지 않았다. 당연히. 터미널을 닫았다가 다시 열었다. 브랜치는 그대로 main과 같은 상태였다.
팀장한테 말해야 하나. 배포가 내일인데. 3일 동안 만든 결제 리팩토링이 다 사라졌다. 눈앞이 하얘졌다. 5분 동안 아무것도 못 하고 앉아 있었다.
당황해서 뭔가를 해야 한다는 생각에 명령어를 막 쳤다. git checkout으로 다른 브랜치를 갔다 왔다. git stash를 해봤다. git status를 스무 번 봤다. 이 과정에서 상황이 더 꼬일 뻔했다.
나중에 알게 됐다. Git 실수를 당했을 때 가장 먼저 해야 할 일은 아무것도 안 하는 것이다. 추가 명령어를 치면 복구 가능성이 줄어든다. 일단 멈추고 상황을 파악하는 게 먼저였다. 그때는 그 사실을 몰랐다.
패닉해서 git push, git reset을 추가로 치는 것. 특히 git push --force는 원격 저장소도 덮어씌울 수 있다. 실수 직후에는 git status와 git log만 보고, 추가 변경 없이 상황을 파악하는 것이 최우선이다.
복구 과정 — 단계별 실제 기록
15분 패닉 후 겨우 정신을 차렸다. 구글에 "git reset --hard 취소"를 검색했다. 가장 많이 나온 단어가 git reflog였다. reflog가 뭔지 알고는 있었는데 써본 적이 없었다. 일단 쳐봤다.
git reflog는 HEAD의 이동 기록을 전부 보여준다. reset --hard를 해도 이 기록은 남는다. 화면에 내가 했던 작업들이 나왔다. reset 하기 전의 커밋 해시가 보였다.
❯ git reflog
a3f82c1 HEAD@{0}: reset: moving to origin/main ← 이게 실수
7e9d3b2 HEAD@{1}: commit: feat: 카드 결제 재시도 로직 추가 ← 이게 3일 전
4c1a8f5 HEAD@{2}: commit: feat: 결제 실패 에러 메시지 분기
b2e7d1c HEAD@{3}: commit: refactor: PaymentService 클래스 분리
8f3a9b4 HEAD@{4}: commit: chore: 타입 정의 추가
1d5c2e8 HEAD@{5}: commit: feat: 결제 모듈 초기 구조 ← 시작점
a3f82c1 HEAD@{6}: checkout: moving from main to feat/payment-refactor
# HEAD@{1}이 reset 직전의 상태다
# 해시 7e9d3b2으로 복구할 수 있다!
reflog에서 reset 직전 커밋 해시를 찾았다. 7e9d3b2이었다. 그 해시로 HEAD를 돌렸다. 파일들이 돌아왔다. 손이 떨렸다. 커밋된 내용 전부가 복구됐다. 3개의 커밋이 살아났다.
# reflog에서 찾은 해시로 되돌리기
❯ git reset --hard 7e9d3b2
HEAD is now at 7e9d3b2 feat: 카드 결제 재시도 로직 추가
# 확인
❯ git log --oneline -5
7e9d3b2 feat: 카드 결제 재시도 로직 추가
4c1a8f5 feat: 결제 실패 에러 메시지 분기
b2e7d1c refactor: PaymentService 클래스 분리
8f3a9b4 chore: 타입 정의 추가
1d5c2e8 feat: 결제 모듈 초기 구조
✓ 커밋된 작업 전부 복구 완료!
커밋된 것은 살아났다. 그런데 마지막 커밋 이후에 작업 중이던 것들이 있었다. 커밋을 안 한 상태였다. git stash도 안 했다. 그날 오후에 3~4시간 작업한 내용이었다.
이건 git reflog만으로는 못 찾는다. 커밋이 안 됐으니 커밋 해시가 없다. 여기서 다른 방법이 필요했다.
git fsck --lost-found 명령으로 dangling blob(고아 객체)을 검색했다. 운 좋게도 일부 객체가 남아 있었다. 또한 VSCode의 로컬 히스토리 기능이 파일별 변경 이력을 자동으로 저장하고 있었다. 이 두 가지를 조합해서 70% 정도를 복구했다.
# 방법 1: git fsck로 고아 객체 찾기
❯ git fsck --lost-found
Checking object directories: 100% done.
dangling blob 9f8c2a1e3b7d5f4e2c8b6a3d9f7e1c5b
dangling blob 3d7f9a2c8e1b5d6f4a9c2e8b7d3f1e5a
# 고아 객체 내용 보기
❯ git cat-file -p 9f8c2a1e
// PaymentService.ts 내용이 나왔다!
# .git/lost-found/other/ 에 파일로 저장됨
❯ ls .git/lost-found/other/
9f8c2a1e3b7d5f4e2c8b6a3d9f7e1c5b
3d7f9a2c8e1b5d6f4a9c2e8b7d3f1e5a
✓ 일부 작업 내용 발견! 복사해서 파일로 복원
# 방법 2: VSCode 로컬 히스토리
파일 우클릭 → 타임라인 → Local History 확인
→ 30분 단위 자동 저장 기록 발견!
→ 추가 30% 복구 완료
2시간 작업 끝에 대부분을 살렸다. 커밋된 것은 100% 복구. 마지막 오후 작업분은 70% 정도. 나머지 30%는 다시 짰다. 배포일을 하루 미뤘다. 팀장에게 솔직하게 말하고 이해를 구했다.
git reset --hard를 치는 데 0.5초 걸렸다. 복구하는 데 2시간 걸렸다. 이 3.5초의 교훈이 이후 Git을 대하는 방식을 완전히 바꿨다.
Git 복구 치트시트 — 상황별 정리
## 상황 2: 커밋 안 된 변경사항이 날아간 경우 git fsck --lost-found # 고아 객체 찾기 git cat-file -p [해시] # 내용 확인 # .git/lost-found/other/ 에서 파일 찾기 # VSCode: 파일 → 타임라인 → Local History
## 상황 3: git branch -D로 브랜치 삭제한 경우 git reflog # 브랜치 최후 커밋 해시 찾기 git checkout -b [브랜치명] [해시] # 브랜치 재생성
## 상황 4: git commit --amend로 커밋 덮어쓴 경우 git reflog # amend 전 커밋 해시 찾기 git checkout [해시] -- . # 그 시점 파일만 복구
## 상황 5: git push --force로 원격 덮어쓴 경우 (최악) # 동료 로컬에 남은 브랜치가 있으면 git push origin [브랜치] # 동료 로컬에서 다시 올리기 # GitHub에서 Force Push 전 백업이 있으면 복원 가능
## 황금 원칙: git reflog는 90일간 기록을 보관한다 ## 대부분의 실수는 reflog로 복구 가능하다
이 사건 이후 바꾼 것들
reset, rebase, force push 전에 반드시 git status와 git branch로 현재 위치를 확인한다. 0.5초짜리 확인이 2시간을 아꼈을 것이다.
WIP 커밋이라도 남긴다. "작업 중" 커밋이 나중에 구원이 된다. 커밋이 많으면 reflog에 더 많은 선택지가 생긴다.
큰 리팩토링 전에 git tag backup-[날짜]를 만든다. 태그는 reflog와 달리 명시적으로 남아서 찾기 쉽다.
설정에서 files.localHistory.enabled를 true로. 커밋 안 해도 파일 변경을 자동 저장한다. 이번에 이게 30% 추가 복구를 도왔다.
# reset --hard 전 확인 강제 alias grh='echo "현재 브랜치: $(git branch --show-current)" && echo "진짜 reset --hard 할까요? (y/n)" && read -r yn && [ "$yn" = "y" ] && git reset --hard'
# 중요 시점 백업 태그 한 방에 alias gbackup='git tag backup-$(date +%Y%m%d-%H%M)'
# reflog 보기 좋게 alias grl='git reflog --format="%C(auto)%h %'\(20)%gd %s"'
# stash 자동 메시지와 함께 alias gs='git stash push -m "$(date +%Y%m%d-%H%M): wip"'
# 이 alias들이 생기고 나서 git 사고가 없어졌다
팀장에게 말하던 순간
2시간 복구 후 팀장한테 Slack 메시지를 보냈다. "git reset --hard 실수로 작업을 날렸고, 복구 작업 중입니다. 배포를 하루 미뤄야 할 것 같아요." 손이 떨렸다. 바로 답이 왔다. "고생했다. 나도 비슷한 경험 있어요. 내일 배포로 조정할게요."
말하기 전에 두려웠다. 혼날 것 같았다. 하지만 솔직하게 말하고 상황을 설명하니까 오히려 팀장이 비슷한 경험을 공유해줬다. 누구나 Git 실수를 한다는 걸 그때 알았다. 실수를 숨기는 것보다 빠르게 공유하고 해결하는 게 팀에 훨씬 낫다는 것도.
첫째, 당황하면 아무것도 하지 않는다. git reflog를 열고 상황을 파악하는 것이 먼저다. 추가 명령어가 상황을 악화시킬 수 있다. 둘째, git reflog는 거의 모든 것을 복구할 수 있다. reset --hard, 브랜치 삭제, 잘못된 rebase — 커밋이 된 것은 reflog가 있는 한 살아난다. 90일 안에는.
그날 이후 git을 대하는 방식이 달라졌다. 두려움이 생긴 게 아니라 존중이 생겼다. reset --hard는 진짜 강력한 명령이다. 그 강력함을 이해하고 쓰는 것과 모르고 쓰는 것은 다르다. 이제는 위험한 명령 전에 항상 한 번 더 확인한다. 그 습관이 그날 사건이 남긴 가장 큰 유산이다.
그리고 git reflog는 반드시 외워두는 것을 권한다. 언젠가 반드시 필요한 날이 온다.
💬 Git 실수 경험담이 있으신가요?
reset --hard, force push, 브랜치 삭제 — 어떤 실수를 어떻게 복구했는지 댓글로 나눠주세요. 비슷한 상황에 처한 분들에게 가장 현실적인 도움이 될 것 같습니다.
'개발자 > 실무 개발 현장' 카테고리의 다른 글
| 1인 개발자로 사이드 프로젝트 런칭하며 배운 것 (0) | 2026.08.17 |
|---|---|
| 기술 부채를 처음 실감한 순간 (0) | 2026.08.10 |
| 개발자의 하루 루틴, 실제로 어떻게 생겼나 (0) | 2026.08.03 |
| 야근이 일상인 스타트업에서 버틴 방법 (0) | 2026.07.27 |
| 내가 짠 코드가 운영 중 터졌을 때의 경험담 (0) | 2026.07.20 |