git stash 사용하기

git으로 코드 형상을 관리하면서 로컬에서 열심히 작업하는 도중에, 아래와 같은 상황들이 종종 생긴다.

  • 현재 하고 있는 일을 잠깐 내려두고 다른 커밋부터 빠르게 만들어야하는 경우
  • 최신 코드를 pull로 땡겨와야 하는 경우

이 때 결국 working directory와 index(또는 staging area)를 깨끗하게 비우고, 새로운 작업을 시작해야 한다. working directory 또는 index에 아직 내가 커밋하지 않은 작업이 올라가 있는 경우, pull을 하려고 했을 때 아래와 같은 에러 메시지가 발생하는 것을 자주 보았을 것이다.

$ git pull origin new-branch
error: cannot pull with rebase: Your index contains uncommitted changes.  
error: please commit or stash them.  

이를 해결하기 위한 가장 단순한 방법으로는 아래 방법 중 하나를 선택하면 된다.

  • 내가 작업한 코드 되돌리기(즉, working directory와 index 비우기)
  • 내가 작업한 코드 커밋하기

하지만 내가 작업한 코드량이 많고 아직 미완성인 경우에는, 이를 모두 지우기에는 작업한 게 너무 아깝고 그렇다고 아직 완성된 작업은 아니라 커밋하기에는 애매하다.

이 때 편리하게 사용할 수 있는 것이 바로 $ git stash 이다. stash는 영어로 "숨기는 장소"란 의미로 git에서 내 작업사항을 잠시 어딘가에 숨겨놓는다의 의미로 풀이할 수 있다.


stash 저장: git stash (git stash push)

내가 현재 작업한 사항이 아래처럼 working directory에 있다고 하자.

이 때 다른 branch를 pull로 땡겨 오려고 하면, 에러가 발생할 것이다.

이 때 이 변경사항을 stash로 로컬에 임시 저장해놓을 수 있다.

$ git stash # 또는 git stash push

정식 명령어는 $ git stash push 이지만, 그냥 $ git stash 만 입력해도 된다. ($ git stash save 도 가능하지만 save는 이제 deprecate되었다고 한다.)

stash로 저장한 후에는 이제 working directory와 index가 깨끗하게 비워졌음을 확인할 수 있다. (이젠 pull로 다른 branch를 땡겨올 수도 있다!!)

참고로 untracked file(즉, 기존에 없었던 새로 생성한 파일)들에 대해서는 기본적으로 git stash로는 저장되지 않는데,

이 때 두 가지 방법을 사용해 stash로 저장할 수 있다.

  1. $ git add 를 통해 tracked file로 만들어 index에 올린 후, $ git stash
  2. -u 옵션을 이용해 모든 untracked file들도 함께 stash 저장, $ git stash -u

stash 목록 조회: git stash list

stash로 저장해놓은 내용들은 list 명령어로 조회할 수 있다.

$ git stash list

내가 방금 저장한 stash가 하나 출력된다. 아까 위에서처럼 어떠한 옵션도 없이 그냥 stash로 저장하면 기본적으로 아래 포맷으로 출력된다.

stash@{[stash의 index]} WIP on [현재 checkout한 branch]: [현재 HEAD commit의 hash] [현재 HEAD commit의 message]

여기서 stash의 index란 0부터 증가하는 값이고, 가장 마지막에 push된 stash의 index가 0이 되고 그전에 push된 것의 index들은 1씩 늘어난다.

물론 저장한 stash가 몇개 없고 거의 바로 꺼내쓰는 상황이라면 index만 볼수 있어도 딱히 큰 불편함은 없겠지만, 만약 저장한 stash가 많다면

나의 기억력이 슈퍼컴퓨터가 아닌 이상 stash의 순서와 commit, branch 만으로는 각 stash가 어떤 작업을 의미하는지 알기 힘들 것이다.

그래서 가급적이면 $ git stash push 를 할 때, --message(또는 -m) 옵션을 이용해 각 stash에 대한 짧은 설명을 남겨놓는 습관을 들여놓는 것도 좋을 것 같다.

$ git stash -m "[stash message]" # 또는 git stash push -m "[stash message]"


stash 가져오기: git stash pop

push, pop 명령어로 알수 있듯이, stash는 기본적으로는 stack과 상당히 닮아있다.

그래서 별다른 index를 지정하지 않는다면 0번째(stash@{0}), 즉 가장 마지막에 push된 것이 pop이 된다. (쉽게 stack의 LIFO를 생각하면 된다.)

하지만 그렇다고 가장 마지막 push한 것만 가져올 수 있는 건 아니다. 특정 index 옵션을 주면 특정 위치의 stash를 가져올 수 있다.

$ git stash pop [stash의 index]

참고로 apply 라는 명령어도 있다.

$ git stash apply [stash의 index]

위의 pop 명령어 결과 뒤에 출력된 Dropped refs/stash@… 메시지에서 알 수 있듯이, pop은 stash를 꺼냄과 동시에 삭제(drop)시킨다. 이와 다르게 apply는 stash의 작업 사항을 꺼내기만 하고, 저장된 건 그대로 유지시킨다. (하지만 나는 stash를 가져올 때는 보통 drop해도 되는 경우가 대부분이라 apply를 사용한 적은 거의 없었던 듯 하다.)

아 모르겠고, 그냥 stash에서 삭제만 하고 싶다면? $ git stash drop를 사용하면 된다.

$ git stash drop [stash의 index]

참고로 $ git stash pop 을 한 작업이 현재 HEAD의 작업과 충돌이 발생할 수도 있다.

보통 충돌 해결할 때와 마찬가지로 파일에서 직접 충돌을 해결한 이후에 커밋하면 된다.

이 때, 아까 위의 pop 명령어 출력결과에서도 알 수 있듯이

The stash entry is kept in case you need it again.

커밋을 한 뒤에도, pop을 한 작업은 stash에 그대로 유지된다.

아무래도 충돌이 발생하였다는 것 자체가 찝찝한 부분이고, 혹시 충돌 해결을 잘못하였거나 아까 pop한 작업을 다시 확인이 필요한 경우도 있기 때문에 stash에는 그대로 유지해주는 git의 작은 배려가 아닐까 싶다.