git stash: 변경 사항을 임시로 보관했다가 되돌리는 방법
git stash는 커밋하지 않은 변경을 별도 저장소에 넣고 작업 폴더를 마지막 커밋 상태로 되돌립니다. 변경을 다시 꺼낼 때는 git stash pop(적용 후 목록에서 삭제) 또는 git stash apply(적용하고 목록에 유지)를 씁니다. git unstash라는 명령은 없습니다.
git stash가 필요한 이유
전형적인 상황입니다. 작업 중간이라 파일이 수정되어 있고, 아직 커밋하기에는 이릅니다. 그런데 다른 브랜치의 버그를 급히 고쳐 달라는 요청이 옵니다. 브랜치를 바꿀 수도 없고, 수정 사항을 잃을까 걱정됩니다. git stash는 커밋하지 않은 모든 변경을 "주머니"에 넣고, 작업 폴더를 마지막 커밋 상태로 되돌립니다.
$ git stash
Saved working directory and index state WIP on feature/login: 3f2a1c4 Форма входа
$ git status
On branch feature/login
nothing to commit, working tree clean
이제 브랜치를 바꾸고, 버그를 고치고, 돌아와서 보관해 둔 것을 꺼내면 됩니다. 저장할 때마다 stash 스택에 항목이 하나 생기며, Git은 이를 특수한 커밋으로 저장합니다. 그래서 데이터는 안전하고 브랜치와 무관하게 유지됩니다.
변경 사항을 저장하는 방법
설명을 붙여서 저장하기
기본 설명인 "WIP on 브랜치"는 일주일 뒤에는 아무 정보도 주지 않습니다. 설명을 붙이세요.
git stash push -m "이메일 검증 추가 중, 아직 미완성"
git stash에 인수가 없으면 git stash push와 같습니다. 예전 형식 git stash save "메시지"도 동작하지만, 새로 쓰는 경우에는 push -m을 쓰세요.
새 파일까지 함께 저장하기
기본적으로 stash는 추적 중인 파일만, 즉 수정되었거나 인덱스에 추가된 파일만 가져갑니다. 한 번도 git add하지 않은 새 파일은 작업 폴더에 남습니다. 자주 놀라는 부분입니다.
git stash -u # --include-untracked: 추적되지 않는 파일도 포함
git stash -a # --all: .gitignore의 파일도 포함
-a는 조심해서 쓰세요. node_modules, 빌드 결과물, 로컬 설정까지 함께 보관됩니다. 보통은 -u면 충분합니다.
일부 변경만 저장하기
git stash push -m "설정" src/config.js # 지정한 파일만
git stash -p # 조각을 대화형으로 선택
git stash --staged # 인덱스에 있는 것만 (Git 2.35+)
git stash --keep-index # 모두 보관하되 인덱스는 작업 폴더에 남김
--keep-index는 커밋 직전에 유용합니다. 작업 폴더에 커밋할 내용만 남기고 테스트를 돌려서, 그 커밋이 혼자서도 완전한지 확인할 수 있습니다.
보관한 변경 되돌리기
Git에는 git unstash라는 명령이 없습니다. 보관한 것을 꺼내려고 찾는 사람이 많지만, 실제로는 아래 두 명령을 씁니다.
| 명령 | 동작 | 스택 항목 |
|---|---|---|
git stash pop | 가장 최근 보관 내용을 적용 | 충돌 없이 적용되면 삭제 |
git stash apply | 가장 최근 보관 내용을 적용 | 유지됨 |
git stash pop stash@{2} | 특정 항목을 적용 | 삭제 |
git stash apply --index | 인덱스와 작업 폴더 구분까지 복원 | 유지됨 |
$ git stash pop
On branch feature/login
Changes not staged for commit:
modified: src/auth.js
modified: src/form.js
Dropped refs/stash@{0} (8c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d)
주의: 기본적으로는 보관 당시 인덱스에 있던 것도 일반 변경으로 돌아옵니다. 인덱스 상태를 그대로 유지하고 싶다면 --index를 추가하세요.
pop과 apply 중 무엇을 쓸까
일상 작업에는 pop이 편합니다. 꺼내면 끝입니다. apply는 같은 변경을 여러 브랜치에 적용하거나, 잘 적용될지 확신이 없을 때 씁니다. 항목이 남아 있어서 보험이 되며, 나중에 git stash drop으로 지우면 됩니다.
보관 목록 관리하기
$ git stash list
stash@{0}: On feature/login: 이메일 검증 추가 중, 아직 미완성
stash@{1}: WIP on main: 9b7e0d2 Обновить зависимости
stash@{2}: On feature/api: эксперимент с кэшем
스택은 "마지막에 넣은 것을 먼저 꺼낸다" 방식으로 동작합니다. stash@{0}은 항상 가장 최근 항목이며, push나 drop을 할 때마다 번호가 밀립니다.
| 명령 | 용도 |
|---|---|
git stash show | 가장 최근 항목의 변경 요약 |
git stash show -p stash@{1} | 특정 항목의 전체 diff |
git stash drop stash@{1} | 항목 하나 삭제 |
git stash clear | 모든 항목을 한 번에 삭제 |
git stash branch 이름 stash@{0} | stash를 만든 커밋에서 브랜치를 만들고, 항목을 적용한 뒤 삭제 |
git stash show가 요약 대신 전체 diff를 기본으로 보여 주게 하거나, 추적되지 않는 파일도 포함하게 하려면 stash.showPatch와 stash.showIncludeUntracked 설정을 사용합니다.
단계별 예: 작업 중 긴급 버그
- 현재 작업을 새 파일까지 포함해 보관합니다:
git stash push -u -m "로그인: 초안". - 주 브랜치로 이동해 수정 브랜치를 만듭니다:
git switch main,git pull,git switch -c hotfix/null-email. - 버그를 고치고, 커밋하고, 리뷰를 요청합니다.
- 작업으로 돌아갑니다:
git switch feature/login. - 보관한 것을 꺼냅니다:
git stash pop.git status로 모든 것이 있는지 확인합니다.
이 시나리오에서는 git worktree를 쓰는 사람도 많습니다. 같은 저장소의 두 번째 작업 폴더를 만들면 아무것도 보관할 필요가 없습니다. 하지만 5분 정도의 짧은 전환에는 여전히 stash가 더 간단합니다.
보관한 변경과 충돌이 날 때
stash 이후 브랜치에서 같은 줄이 바뀌었다면, 적용하는 과정에서 충돌이 납니다.
$ git stash pop
Auto-merging src/auth.js
CONFLICT (content): Merge conflict in src/auth.js
The stash entry is kept in case you need it again.
이 경우에는 pop이라도 항목이 삭제되지 않습니다. 파일의 충돌을 평소처럼 해결하고, git add로 해결을 표시한 뒤(인덱스에 올리고 싶지 않다면 git restore --staged), 더 이상 필요 없는 항목은 git stash drop으로 지우세요.
충돌이 복잡하다면 우회로가 편합니다. git stash branch tmp-login을 실행하세요. Git은 stash를 만든 바로 그 커밋에서 브랜치를 만들고, 거기서는 충돌 없이 적용됩니다. 그다음 일반적인 병합으로 주 브랜치와 합치면 됩니다.
실수로 지운 stash 복구하기
git stash drop이나 clear를 실수로 실행해도 모든 것이 끝난 것은 아닙니다. 항목은 커밋이었고, 가비지 컬렉터가 지우기 전까지는 "떠다니는" 객체 중에서 찾을 수 있습니다.
git fsck --no-reflog | grep "dangling commit"
git show 8c1d2e3 # stash가 맞는지 확인
git stash apply 8c1d2e3
방금 drop이나 pop으로 지웠다면 해시가 명령 출력에 바로 나와 있습니다. Dropped refs/stash@{0} (...) 줄입니다. 그 값을 git stash apply에 넘기면 됩니다.
stash는 내부에서 어떻게 생겼나
구조를 알면 데이터를 잃을까 하는 두려움이 줄어듭니다. stash의 각 항목은 refs/stash 참조가 가리키는 일반 커밋입니다. 이 커밋은 부모를 두 개 가집니다. 하나는 여러분이 서 있던 HEAD 커밋이고, 다른 하나는 인덱스 상태를 담은 별도 커밋입니다. -u를 쓰면 추적되지 않는 파일을 담은 세 번째 부모가 추가됩니다. stash@{N} 목록은 refs/stash의 리플로그일 뿐입니다.
이로부터 몇 가지 실용적인 결과가 나옵니다. stash는 커밋을 받는 어떤 명령에도 넘길 수 있습니다. 예를 들어 git diff stash@{0}, 한 파일의 내용을 보는 git show stash@{0}:src/auth.js, 항목 전체를 적용하지 않고 파일 하나만 가져오는 git checkout stash@{0} -- src/auth.js가 있습니다. 삭제된 항목도 가비지 컬렉션 전까지는 객체 저장소에 남습니다.
stash와 다른 Git 명령
일부 명령은 변경을 스스로 보관합니다. git rebase --autostash와 git pull --rebase --autostash는 시작 전에 커밋하지 않은 수정을 치워 두고, 끝나면 되돌립니다. 설정 rebase.autoStash true와 merge.autoStash true로 이 동작을 항상 켤 수 있습니다. 되돌릴 때 충돌이 나면 Git은 일반 stash 스택에 항목을 남기고 이를 알려 줍니다.
git switch로 브랜치를 바꿀 때도 항상 stash가 필요한 것은 아닙니다. 변경한 파일이 두 브랜치에서 다르지 않다면, Git은 변경을 그대로 가지고 갑니다. stash가 필요한 때는 Git이 "Your local changes would be overwritten by checkout"이라고 말할 때, 또는 변경을 다른 브랜치로 가져가고 싶지 않을 때입니다.
흔한 실수
- 새 파일이 보관되지 않음. 추적되지 않는 파일에는
-u가 필요합니다. - stash를 저장소처럼 사용. 한 달 뒤에는 이름 없는 "WIP on main"이 열 개쯤 쌓여 있고, 무엇이 들어 있는지 아무도 기억하지 못합니다. 오래 걸리는 작업은 별도 브랜치에 커밋하세요. 최소한 push는 할 수 있습니다.
- 충돌 후 항목이 남아 있다는 사실을 잊음. 나중에 같은 항목을 다시 적용하게 됩니다. 충돌을 해결한 뒤에는 항상
drop하세요. - stash가 서버와 동기화되지 않음. 로컬 저장소에만 있으며,
git push로 보낼 수 없습니다. 프로젝트 폴더를 지우면 stash도 사라집니다. - 잘못된 항목을 적용.
pop전에git stash list와git stash show -p로 확인하세요.
정리
git stash는 커밋하지 않은 변경을 잃지 않고 작업 폴더를 비우는 빠른 방법입니다. git stash push -u -m "설명"으로 보관하고, git stash pop으로 되돌리세요. apply는 보험이 필요할 때, git stash list와 show -p는 헷갈리지 않으려고 씁니다. 오래 걸리는 작업을 stash에 쌓아 두지 마세요. 그런 작업에는 커밋과 브랜치가 있습니다. 작업 전환과 stash 활용은 Git 강의에서 실습으로 다룹니다.
자주 묻는 질문
stash를 다른 브랜치에서 적용할 수 있나요?
네, stash는 특정 브랜치에 묶여 있지 않습니다. 원하는 브랜치로 이동해서 git stash pop이나 apply를 실행하세요. 변경이 맞지 않는다면 일반적인 충돌이 납니다.
git stash가 인덱스의 변경도 저장하나요?
네. 하지만 보통의 pop이나 apply는 모든 것을 인덱스에 올리지 않은 변경으로 되돌립니다. 인덱스 상태까지 복원하려면 git stash apply --index를 쓰세요.
stash를 몇 개까지 보관할 수 있나요?
정해진 상한은 없지만, 도달할 수 없는 reflog 항목은 가비지 컬렉션으로 지워질 수 있습니다. 실무에서는 최근 항목 몇 개만 두세요.
파일 하나만 stash할 수 있나요?
네. push 뒤에 두 개의 하이픈과 경로를 적으세요: git stash push -m 설명 -- src/config.js. 그 파일만 보관되고 나머지 변경은 작업 폴더에 남습니다.
stash와 임시 커밋은 무엇이 다른가요?
커밋은 브랜치에 속하며 서버로 보내고 amend나 rebase로 고칠 수 있습니다. stash는 로컬의 별도 스택에만 있으며, 짧은 전환에 적합합니다.