Git

[Git] stash pop 하다 충돌나면 stash는 어디 있나

개발자 Noah 2026. 9. 18. 07:42

git stash pop 이 충돌로 멈췄다. 순간 치워 뒀던 작업이 날아간 줄 알았다. 안 날아간다. pop 이 실패하면 stash 는 스택에 그대로 남는다. 대신 성공했을 때와 달리 자동으로 안 지워지므로, 충돌을 푼 뒤 직접 지워야 한다.

현상

Auto-merging f.txt
CONFLICT (content): Merge conflict in f.txt
On branch main
Unmerged paths:
  (use "git restore --staged <file>..." to unstage)
  (use "git add <file>..." to mark resolution)
    both modified: f.txt

no changes added to commit (use "git add" and/or "git commit -a")
The stash entry is kept in case you need it again.

마지막 줄이 답을 이미 말해 주고 있다. The stash entry is kept in case you need it again. 충돌 마커에 눈이 가서 이 줄을 못 봤다.

바로 확인해 보면 남아 있다.

$ git stash list
stash@{0}: WIP on main: 0051ebd base

환경

항목
git 2.50.1 (Apple Git-155)
macOS 26.4

재현

같은 줄을 양쪽에서 다르게 고치면 된다.

git init . && echo "A" > f.txt && git add f.txt && git commit -m base

echo "B" > f.txt && git stash # 작업을 치워 둔다
echo "C" > f.txt && git commit -am "다른 변경"

git stash pop # ← 충돌

exit=1 로 끝난다. 파일에는 충돌 마커가 들어간다.

<<<<<<< Updated upstream
C
=======
B
>>>>>>> Stashed changes

Updated upstream 이 지금 커밋 쪽, Stashed changes 가 치워 뒀던 내 작업이다. 마커를 읽는 법 자체는 충돌 편에서 다뤘다.

원인 추적

처음엔 날아갔다고 생각했다. pop 이라는 이름 때문이다. 스택에서 꺼낸다는 뜻이니 실패해도 꺼내진 줄 알았다.

문서는 반대로 적어 놨다.

Applying the state can fail with conflicts; in this case, it is not removed from the stash list. You need to resolve the conflicts by hand and call git stash drop manually afterwards.

상태를 적용하다가 충돌로 실패할 수 있다. 이 경우 stash 목록에서 제거되지 않는다. 충돌을 직접 해결한 뒤 git stash drop 을 수동으로 호출해야 한다.

git-stash

실패하면 안 지운다. 그리고 직접 지우라고 명시한다.

왜 이렇게 설계됐는지는 반대 경우를 생각하면 바로 이해된다. 지워 버리면 되돌릴 방법이 없다. 충돌 해결에 실패해서 처음부터 다시 하고 싶을 수도 있는데, 그때 원본이 없으면 끝이다.

apply 와의 관계도 문서에 있다.

Like pop, but do not remove the state from the stash list.

pop 과 같지만 stash 목록에서 상태를 제거하지 않는다.

정리하면 pop = apply + drop 이다. 충돌이 나면 apply 가 부분적으로 진행되고 drop 이 안 도는 것뿐이다.

그런데 여기서 새 함정이 생긴다. 충돌을 풀고 커밋한 뒤 drop 을 잊으면 stash 가 그대로 남아 있다. 나중에 또 pop 하면 어떻게 되는지 해봤다.

$ git commit -am "충돌 해결"
$ git stash list
stash@{0}: WIP on main: 0051ebd base ← 아직 있다

$ git stash pop
Auto-merging f.txt
CONFLICT (content): Merge conflict in f.txt ← 또 충돌

이미 해결한 변경을 한 번 더 적용하려다 또 충돌났다. 같은 작업을 두 번 하는 셈이다.

해결

충돌을 풀고, 직접 지운다.

git status # 어느 파일인지 확인
# ... 편집해서 마커 제거 ...
git add f.txt

git stash drop # ← 이 줄을 잊으면 중복 적용된다

상황에 따라 쓰는 명령이 갈린다.

상황 권장
충돌이 날 것 같다 apply 로 먼저 확인 → 문제없으면 drop
트리가 확실히 깨끗하다 pop 으로 한 번에
적용을 되돌리고 싶다 git checkout . 후 다시 apply

치웠는데 안 치워진 파일

같이 알아 둘 게 하나 더 있다. git stash새 파일을 안 담는다.

echo "수정" >> tracked.txt
echo "새 파일" > untracked.txt
git stash
ls
# tracked.txt untracked.txt ← untracked.txt 가 그대로 남아 있다

문서가 정한 기본값이 그렇다. -u 를 붙이면 untracked 까지, -a 를 붙이면 ignored 까지 담는다.

git stash -u
ls
# tracked.txt ← 이제 사라졌다

"분명히 치웠는데 빌드가 깨진다" 의 정체가 대개 이거다.

검증

drop 까지 마치면 목록이 비고, 다시 pop 해도 적용될 게 없다.

$ git stash list
$ git stash pop
No stash entries found.

정리

popapply + drop 이고, 실패하면 drop 만 안 돈다. stash 는 남아 있으니 당황할 필요가 없다. 터미널 마지막 줄이 이미 그렇게 알려 준다.

진짜 조심할 건 pop 실패가 아니라 drop 이다.

If you mistakenly drop or clear stash entries, they cannot be recovered through the normal safety mechanisms.

실수로 stash 항목을 drop 하거나 clear 하면 정상적인 안전장치로는 복구할 수 없다.

reflog 가 없다는 뜻이다. 다만 커밋 객체 자체는 한동안 남아 있어서 우회로가 있다. 문서가 알려 주는 방법을 실제로 돌려 봤다.

git fsck --unreachable | grep commit | cut -d' ' -f3 | \
  xargs git log --merges --no-walk --grep=WIP --format="%H %s"
# 5683e42eef05da248aefab081e93c6062c0887e2 WIP on main: 34d8a48 base

git stash apply 5683e42
# 소중한 작업 ← 되살아났다

--grep=WIP 로 거르는 게 중요하다. fsck 출력을 그냥 head -1 로 집으면 stash 가 아닌 커밋이 잡혀서 is not a stash-like commit 으로 튕긴다. 내가 처음에 그렇게 해서 한 번 헛짚었다.

더 쉬운 길도 있다. drop 할 때 git 이 해시를 출력해 준다.

$ git stash drop
Dropped refs/stash@{0} (5683e42eef05da248aefab081e93c6062c0887e2)

터미널을 안 닫았다면 스크롤만 올리면 된다.

다음에 pop 이 멈추면 마커부터 보지 말고 git stash list 를 먼저 친다. 남아 있는 걸 확인하면 마음이 급하지 않다.