stash 를 drop 해 버린 뒤 알았을 때, 지운 작업을 커밋 해시로 되살리는 순서
환경 Git 2.54 · 2026-10-05 확인
급한 핫픽스 때문에 하던 작업을 stash 로 넣어 두었다. 일을 마치고 돌아와 목록을 정리하다가 git stash drop 을 한 번 더 눌렀거나, 비슷해 보이는 항목이 많아 git stash clear 로 한꺼번에 비웠다. 그리고 몇 분 뒤 그 안에 반나절 작업이 들어 있었다는 걸 깨닫는다. git stash list 는 비어 있고 reflog 에도 보이지 않는다. 그래도 아직 늦지 않았다.
지운 건 목록의 한 줄이고, 작업은 커밋으로 남아 있다
stash 하나는 겉보기와 달리 파일 묶음이 아니라 커밋이다. git stash 를 실행하면 Git 은 작업트리 상태를 담은 커밋을 만들고, 그 부모로 HEAD 와 인덱스 상태를 기록한다. 그래서 log 로 찍어 보면 부모가 둘 이상인 머지 커밋 모양을 하고 있다.
stash 목록은 이 커밋들을 refs/stash 라는 참조와 그 reflog 로 줄 세워 둔 것에 지나지 않는다. drop 은 그 줄에서 한 칸을 빼고, clear 는 줄 전체를 없앤다. 커밋 객체 자체는 저장소에 그대로 남는다. 다만 이제 어떤 브랜치에서도, 어떤 reflog 에서도 닿지 않는 고아가 될 뿐이다.
고아 객체는 영원히 남지 않는다. git gc 가 돌 때 어디서도 닿지 않으면서 기본 2주보다 오래된 객체를 정리한다. 이 2주는 지운 때가 아니라 객체가 생긴 때부터 센다. 3주 전에 만들어 둔 stash 를 오늘 지웠다면 다음 gc 에서 바로 사라질 수 있다는 얘기다. 되살릴 생각이라면 지금 하는 게 맞다.
drop 과 clear 는 목록에서 줄을 지울 뿐, gc 가 돌기 전까지 stash 커밋은 저장소 안에 남아 있다.
손대기 전에 정리 작업부터 멈춰 둔다
복구하는 동안 할 일은 하나다. 고아 객체를 지우는 명령을 돌리지 않는 것이다. git gc --prune=now 나 git prune 을 실행하면 2주 유예 없이 바로 지운다. 저장소 용량을 줄이겠다고 습관처럼 넣어 둔 스크립트나 별칭이 있다면 잠시 멈춘다.
설정에 gc.pruneExpire 가 now 로 잡혀 있어도 유예가 없어진다. 평소 손댈 일이 없는 값이지만 남이 만든 저장소라면 한 번 확인해 두자. 아무것도 안 나오면 기본값 2주가 적용된다.
자동 gc 도 신경 쓰인다면 gc.auto 를 0 으로 잠시 꺼 둔다. 커밋이나 머지 끝에 Git 이 알아서 정리를 돌릴 수 있는데, 이 값을 0 으로 두면 그 자동 실행이 멈춘다. 복구를 마친 뒤에는 다시 지워 원래대로 돌린다.
git config --get gc.pruneExpire # now 가 나오면 유예 없음, 비어 있으면 기본 2주
git config gc.auto 0 # 복구하는 동안 자동 gc 를 잠시 끈다
# 복구가 끝나면
git config --unset gc.auto복구 전에 정리 작업이 끼어들 틈부터 막는다
지운 직후라면 해시는 터미널에 이미 찍혀 있다
가장 빠른 길은 터미널 스크롤을 위로 올려 보는 것이다. git stash drop 은 지우면서 Dropped stash@{1} (a6f0775c…) 같은 줄을 남긴다. 괄호 안이 방금 지운 stash 커밋의 해시다. pop 이 성공했을 때도 Dropped refs/stash@{0} (…) 로 거의 같은 줄이 찍힌다.
이 해시만 있으면 나머지는 평소 쓰던 명령 그대로다. git stash show -p 에 해시를 넘기면 안에 든 변경을 미리 볼 수 있고, git stash apply 에 넘기면 작업트리에 다시 얹힌다. stash@{n} 이 들어갈 자리에 커밋 해시를 넣어도 stash 모양을 한 커밋이면 그대로 받아 준다.
clear 로 비웠다면 이 방법은 쓸 수 없다. clear 는 항목별 해시를 출력하지 않는다. 그럴 때는 다음 단계로 넘어간다.
# 터미널에 남은 줄: Dropped stash@{1} (a6f0775c1671e9a0011b04849303c5a1b45c8336)
git stash show -p a6f0775 # 내용부터 확인
git stash apply a6f0775 # 작업트리에 다시 얹는다drop 이 출력한 해시를 그대로 넘긴다
해시를 놓쳤다면 fsck 로 주인 없는 커밋을 훑는다
터미널을 닫았거나 clear 로 지웠다면 저장소 안을 직접 뒤져야 한다. git fsck --unreachable 은 어떤 참조에서도 닿지 않는 객체를 모두 보여 준다. 여기서 커밋만 골라 머지 커밋 모양인 것만 추리면 stash 후보가 남는다.
Git 의 stash 문서에도 이 용도로 한 줄짜리 명령이 실려 있다. 그런데 그 명령은 마지막에 --grep=WIP 로 메시지를 거른다. 메시지 없이 만든 stash 는 WIP on main: 처럼 기록되니 잘 걸린다. 하지만 git stash push -m 으로 이름을 붙였다면 On main: 로그인 폼 수정 처럼 WIP 가 아예 들어가지 않는다. Git 2.54 에서 두 가지를 만들어 함께 지워 보면, 문서의 명령으로는 메시지를 붙인 쪽이 목록에 나오지 않는다.
그래서 --grep 은 빼고, 대신 날짜와 제목을 한 줄씩 찍어 눈으로 고르는 편이 낫다. 지운 작업이 대개 최근 것이니 날짜만 봐도 금방 좁혀진다. 고아 커밋이 많다면 grep 으로 브랜치 이름이나 기억나는 메시지 일부를 걸러도 된다.
git fsck --unreachable | grep commit | cut -d' ' -f3 \
| xargs git log --merges --no-walk --format='%h %ci %s'
# 8034c8a 2026-10-05 00:06:54 +0900 WIP on main: 35214cb init
# a6f0775 2026-10-05 00:06:54 +0900 On main: 로그인 폼 수정 ← --grep=WIP 로는 빠진다메시지로 거르지 말고 날짜·제목을 모두 찍어 고른다
stash 에 -m 으로 이름을 붙였다면 메시지에 WIP 가 없다. --grep=WIP 로 거르면 바로 그 stash 가 빠진다.
찾은 커밋은 목록에 다시 올려 두고 꺼낸다
후보를 골랐으면 바로 apply 하기 전에 목록에 다시 걸어 두는 편이 안전하다. git stash store 는 커밋 하나를 받아 stash 목록 맨 위에 올린다. 이렇게 해 두면 다시 참조가 생겨 gc 가 건드리지 못하고, 이후로는 stash@{0} 으로 평소처럼 다룰 수 있다.
후보가 여러 개라 무엇이 맞는지 헷갈리면 하나씩 git stash show -p 로 내용을 본다. 추적하지 않던 새 파일까지 -u 로 넣어 둔 stash 였다면 --include-untracked 를 붙여야 그 파일들도 보인다.
지금 작업트리가 깨끗하지 않다면 apply 대신 git stash branch 로 새 브랜치에 꺼내는 방법도 있다. stash 를 만든 시점의 커밋에서 브랜치를 새로 따고 그 위에 얹기 때문에 지금 작업과 충돌할 일이 없다.
git stash store -m "되살림: 로그인 폼 수정" a6f0775
git stash list # stash@{0}: 되살림: 로그인 폼 수정
git stash apply # 지금 브랜치에 얹거나
git stash branch recover-login # 새 브랜치로 꺼낸다참조를 먼저 걸어 gc 로부터 지켜 두고 꺼낸다
돌아왔는지 확인하고, 다시 잃는 자리를 피한다
확인은 git diff --stat 으로 한다. 기억하는 파일들이 바뀐 줄 수와 함께 나오면 제대로 돌아온 것이다. 새 파일이 들어 있던 stash 라면 git status 의 추적하지 않는 파일 목록에도 다시 보여야 한다.
복구 중에 자주 걸리는 데가 몇 곳 있다. 작업트리에 같은 파일의 변경이 남아 있으면 apply 나 pop 이 덮어쓰게 된다며 멈춘다. 이때는 지금 변경을 먼저 커밋하거나 stash 해 두고 다시 시도한다. pop 이 충돌로 끝났을 때는 항목이 목록에서 지워지지 않으니, 충돌을 정리한 뒤 직접 drop 해야 한다.
그리고 같은 사고를 막는 가장 쉬운 방법은 stash 를 오래 묵히지 않는 것이다. 하루 넘게 둘 작업이라면 임시 브랜치에 커밋해 두는 편이 낫다. 브랜치 커밋은 reflog 에 남고 지워도 찾기 쉽다.
git diff --stat # 바뀐 파일과 줄 수가 기억과 맞는지
git status --short # ?? 로 새 파일까지 돌아왔는지
git stash list # 되살린 항목이 아직 남아 있는지(apply 는 지우지 않는다)되살린 뒤 세 가지만 확인한다
조치 후 확인할 것
- 복구가 끝날 때까지 git gc --prune=now·git prune 을 돌리지 않았는지
- fsck 결과를 --grep=WIP 로 거르지 않고 날짜·제목을 모두 훑었는지
- 찾은 커밋을 git stash store 로 목록에 다시 걸어 두었는지
- git diff --stat·git status 로 수정 파일과 새 파일이 모두 돌아왔는지
- 잠시 꺼 둔 gc.auto 설정을 git config --unset gc.auto 로 되돌렸는지
stash 는 손이 가볍다. 그래서 지울 때도 가볍게 지운다. 다행히 Git 은 지운 줄만 없앨 뿐 커밋은 한동안 붙잡고 있다. 터미널의 Dropped 줄을 먼저 보고, 없으면 fsck 로 날짜를 훑고, 찾으면 store 로 다시 걸어 둔다. 이 세 걸음이면 대부분의 stash 는 돌아온다.