커밋하기 애매한 변경이 남은 상태에서 브랜치를 바꾸면 Git이 막는다.

$ git checkout develop
error: Your local changes to the following files would be overwritten by checkout:
	file.txt
Please commit your changes or stash them before you switch branches.
Aborting

git stash는 작업 디렉터리의 변경을 임시 스택에 저장하고 디렉터리를 HEAD 상태로 되돌린다. 변경은 사라지지 않으며 나중에 다시 꺼내 적용할 수 있다. 명령 자체는 단순하지만 applypop의 동작 차이, 그중에서도 pop이 충돌했을 때의 동작에서 실수가 자주 나온다.

기본 사용

git stash push -m "로그인 기능 작업 중"   # 메시지를 붙여 저장
git stash list                            # 저장된 stash 확인
$ git stash list
stash@{0}: On main: 로그인 기능 작업 중

stash@{0}이 가장 최근이고, 새로 저장할 때마다 기존 항목의 인덱스가 하나씩 밀린다.

git stash는 추적 중인(tracked) 파일의 변경만 저장한다. git add 전의 새 파일은 제외되므로 함께 저장하려면 -u를 붙인다.

git stash -u    # untracked 파일까지 포함

apply와 pop

저장한 변경을 꺼내는 명령은 두 가지다.

git stash apply   # 적용하고 스택에 남김
git stash pop     # 적용하고 스택에서 제거

pop은 적용에 성공하면 해당 stash를 스택에서 지우고, apply는 남긴다. 같은 stash를 여러 브랜치에 적용해야 하는 경우가 아니면 보통 pop을 쓴다.

pop이 충돌하면 stash가 남는다

pop은 적용에 성공했을 때만 stash를 제거한다. 충돌로 적용이 끝나지 못하면 stash는 스택에 그대로 남는다.

$ git stash pop
Auto-merging file.txt
CONFLICT (content): Merge conflict in file.txt
On branch main
Unmerged paths:
  (use "git restore --staged <file>..." to unstage)
  (use "git add <file>..." to mark resolution)
	both modified:   file.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가 그 동작을 알려준다. 실제로 목록에 남아 있다.

$ git stash list
stash@{0}: On main: my work

충돌을 해결하고 git add까지 마치면 stash도 함께 정리됐다고 넘어가기 쉽다. 그러나 stash@{0}은 그대로이고, 다음에 다시 pop하면 이미 반영한 변경이 한 번 더 적용된다. 충돌 후에는 다음 순서로 정리한다.

# 1. 충돌 마커(<<<<<<<, =======, >>>>>>>) 해결
# 2. git add file.txt
# 3. git stash drop   # pop이 제거하지 못했으므로 직접 제거

충돌 없이 적용됐다면 pop이 stash를 지우므로 3번은 필요 없다.

적용 전 내용 확인

stash가 여러 개이거나 내용이 분명하지 않을 때는 적용 전에 확인한다.

git stash show -p          # diff로 상세 확인
$ git stash show -p
diff --git a/app.js b/app.js
index de98044..f4b382e 100644
--- a/app.js
+++ b/app.js
@@ -1,3 +1,4 @@
 a
-b
+b modified
 c
+d added

-p를 빼면 변경된 파일 목록만 나온다.

두 가지 사용 예

기능 개발 중 급한 수정이 들어오면 작업을 저장하고 브랜치를 옮긴다.

git stash push -m "feature/login 작업 중"
git checkout hotfix/critical-bug
# 수정 후 커밋
git checkout feature/login
git stash pop

커밋하지 않은 변경이 있는 상태에서 원격을 받아와야 할 때도 방식은 같다.

git stash
git pull origin main
git stash pop   # 충돌 시 해결 후 git stash drop

두 경우 모두 마지막 pop에서 충돌이 나면 stash가 남으므로 직접 drop해야 한다.