브랜치에서 로그인 화면을 한창 만지고 있는데, 갑자기 팀장이 "지금 운영 서버에 버그 났는데 급하게 좀 봐줘요" 하고 부르는 순간이 있어요. 근데 내 작업은 아직 반쯤 짜다 만 상태라 커밋하기엔 찝찝하죠. 그렇다고 그냥 두고 다른 브랜치로 옮기자니 변경사항이 졸졸 따라와서 꼬여요. 이럴 때 딱 쓰는 게 stash(작업 임시 보관)예요. 하던 걸 서랍에 쏙 넣어두고 나중에 그대로 다시 꺼내는 기능이라, 실무에서 하루에도 몇 번씩 쓰게 돼요.

79.1 하던 작업 잠깐 치워둘 수 있나요?

있어요. 커밋은 "이 변경을 역사에 남긴다"는 무거운 행동이지만, stash는 "잠깐 서랍에 넣어둔다"는 가벼운 행동이에요. 지금 작업 트리(working tree, 지금 편집 중인 파일들)에 있는 변경사항을 통째로 걷어서 한쪽에 밀어두고, 작업 트리는 마지막 커밋 상태로 깨끗하게 되돌려줘요. 그래서 브랜치를 자유롭게 옮겨다닐 수 있게 되죠. 반쯤 짜다 만 코드를 억지로 커밋해서 지저분한 기록을 남기지 않아도 되고요.


언제 커밋하고 언제 stash 하냐고요? 기준은 간단해요. "이 변경을 나중에 다시 볼 사람이 있나?" 싶으면 커밋이고, "그냥 나 혼자 잠깐 치워두는 거야" 싶으면 stash예요. 며칠 갈 작업이면 임시 브랜치를 파서 커밋하는 게 낫고, 몇 분에서 몇 시간짜리 잠깐이면 stash가 딱이에요. stash는 오래 묵히면 뭐가 들었는지 까먹기 십상이라, 넣었으면 그날 안에 꺼내는 걸 추천해요.


예를 들어 app.js 파일을 수정하고, temp.js라는 새 파일도 만든 상황이라고 해볼게요. 지금 상태를 먼저 확인해볼게요.


$ git status --short
M app.js
?? temp.js


맨 앞의 M은 수정됨(modified), ??는 깃이 아직 모르는 새 파일이라는 뜻이에요. 자, 이 상태를 서랍에 넣어볼게요.

79.2 stash는 어떻게 넣고 빼나요?

넣을 때는 git stash push, 뺄 때는 git stash pop이에요. 넣을 때 나중에 알아보기 쉽게 메시지를 같이 달아두는 습관을 들이면 좋아요. 서랍이 여러 개 쌓이면 뭐가 뭔지 헷갈리거든요. 참고로 git stash만 쳐도 push와 똑같이 동작하지만, 메시지를 못 다니까 저는 늘 push -m을 써요.


$ git stash push -m "로그인 화면 작업중"
Saved working directory and index state On main: 로그인 화면 작업중


이제 다시 상태를 보면 아까 그 app.js 수정이 사라지고 작업 트리가 깨끗해진 걸 볼 수 있어요. 마음 놓고 다른 브랜치로 옮겨서 급한 버그를 잡으면 돼요. 버그를 다 잡고 돌아왔으면 서랍을 다시 열어요.


$ git stash pop
On branch main
Changes not staged for commit:
modified: app.js
Dropped refs/stash@{0} (eb9a42305a...)


아까 치워뒀던 수정이 그대로 돌아왔죠. 그리고 맨 아래에 Dropped라고 뜬 게 보이나요? pop은 서랍에서 꺼내면서 그 서랍을 비워버려요. 만약 꺼내되 서랍은 남겨두고 싶으면 git stash apply를 쓰면 돼요. apply는 내용만 복사해오고 stash 목록에는 그대로 남겨둬요. 같은 변경을 여러 브랜치에 적용해보고 싶을 때 유용하죠.

79.3 여러 개 쌓이면 어떻게 골라 꺼내죠?

실무에서는 stash가 하나만 있으란 법이 없어요. 이거 하다 저거 하다 하면 두세 개씩 쌓이곤 하죠. 목록은 git stash list로 봐요.


$ git stash list
stash@{0}: On main: 작업 B
stash@{1}: On main: 작업 A


여기서 stash@{0}이 가장 최근에 넣은 거예요. 스택(stack, 나중에 넣은 게 위로 쌓이는 구조)이라서 번호가 낮을수록 최신이에요. 특정 서랍을 콕 집어 꺼내려면 번호를 지정하면 돼요.


$ git stash apply stash@{1}


이러면 "작업 A"만 골라서 적용돼요. 꺼내기 전에 안에 뭐가 들었는지 미리 보고 싶으면 git stash show를 써요.


$ git stash show stash@{0}
app.js | 1 +
1 file changed, 1 insertion(+)


어떤 파일이 몇 줄 바뀌었는지 요약이 나와요. 뒤에 -p를 붙이면 실제 바뀐 코드 줄까지 다 보여주고요. 다 쓴 서랍을 정리할 땐 하나만 버리는 git stash drop stash@{0}, 전부 비우는 git stash clear가 있어요. clear는 되돌리기 어려우니 진짜 다 필요 없을 때만 쓰세요.

79.4 추적 안 된 새 파일은 왜 안 따라오죠?

이거 처음 겪으면 진짜 당황해요. 분명 stash 했는데 새로 만든 파일이 작업 트리에 그대로 남아있는 거예요. 기본 git stash는 깃이 이미 추적(track)하는 파일의 변경만 치워요. 아직 한 번도 add 안 한 새 파일(?? 표시)은 건드리지 않죠.


새 파일까지 통째로 치우고 싶으면 -u 옵션(untracked, 추적 안 된 파일 포함)을 붙여요.


$ git stash push -u -m "전부 치우기"
Saved working directory and index state On main: 전부 치우기


이러면 새로 만든 temp.js 같은 파일까지 싹 서랍에 들어가서 작업 트리가 완전히 깨끗해져요. 반대로, 새 파일이 자꾸 stash에 안 딸려온다고 답답했다면 십중팔구 이 -u를 안 붙인 거예요.

79.5 꺼낼 때 충돌 나면 어떡하죠?

내가 서랍에 넣어둔 사이에 다른 사람이 같은 파일을 고쳐서 커밋했다면, 꺼낼 때 충돌(conflict)이 날 수 있어요. 이럴 때 pop 결과가 이렇게 나와요.


$ git stash pop
Auto-merging a.js
CONFLICT (content): Merge conflict in a.js
both modified: a.js
The stash entry is kept in case you need it again.


여기서 핵심은 마지막 줄이에요. 충돌이 나면 pop이 서랍을 지우지 않아요. 혹시 정리하다 꼬여도 다시 꺼낼 수 있게 남겨두는 거죠. 충돌 표시(<<<<<<< 같은 줄)를 손으로 정리하고 git add로 해결을 마친 다음, 서랍이 필요 없어지면 그때 git stash drop으로 직접 비워주면 돼요.

79.6 오늘 정리

정리하면 이래요. 하던 걸 잠깐 치울 땐 git stash push -m "메시지", 다시 꺼낼 땐 git stash pop(서랍 비움) 또는 git stash apply(서랍 남김)예요. 목록은 git stash list, 미리보기는 git stash show, 새 파일까지 치울 땐 -u, 충돌이 나면 서랍은 자동으로 남는다는 것까지 기억하면 돼요. stash는 커밋하기 애매한 순간을 부드럽게 넘겨주는 안전장치예요. "지금 이거 커밋해도 되나?" 망설여질 때 일단 서랍에 넣어두는 습관이 팀 작업에서 브랜치 꼬임을 크게 줄여줘요. 처음엔 pop과 apply 헷갈리는 게 당연하니까, "pop은 꺼내면서 비운다, apply는 꺼내도 남는다" 이 한 줄만 몸에 익혀두세요. 나머지는 쓰다 보면 손에 붙어요.