갓태희

[Git] git stash에 대하여.. 본문

Git

[Git] git stash에 대하여..

갓태희 2021. 6. 2. 23:11

들어가기 앞서..

stash는 꼭 자기가 stash했던 내역을 stash했던 시점에 다시 적용시킬 필요는 없습니다. 한번 stash한 내용은 어느 커밋, 어느 브랜치에도 적용가능합니다 그 와중에 충돌이 생긴다면 해결을 해주면 그만입니다. 저는 stash를 하고나면 꼭 다시 stash했던 시점으로 돌아와 stash스택에 저장된 내역을 적용시켜야 하는줄 알았습니다 밑에서 들 예는 같은 브랜치에 다시 돌아와서 적용시키는 예이지만 꼭 저렇지 않고 다른 브랜치, 다른 커밋에 stash를 적용시켜도 무방합니다.

현재 HEAD가 가리키는 main 브랜치에서 갑자기 develop브랜치에 볼일이 있어서 develop 브랜치로 checkout 하려는 상황이라고 가정 해보겠습니다. 예를들어 develop브랜치에는 내가 main브랜치에서 수정하려는 파일의 상태가 어땠는지를 확인하기위해서 잠시 develop브랜치로 checkout하고 금방 main브랜치에 다시 돌아오려고 합니다.

그런데 저는 one이라는 파일을 수정중이였고 stash의 성질을 전혀 모르는 저는 바로 develop 브랜치로 checkout을 하려고 하는데 error가 발견됩니다 에러의 내용은 지금 one이라는 파일에 변경사항이 생겨서 checkout을 하게되면 그 파일이 그냥 덮혀 씌워져버릴것이니 chekcout하기 전에 커밋하거나 stash하라는 내용의 에러를 발생시킵니다. 왜 이러한 에러를 발생시킬까요 ?

착한 깃

비록 우리에게 오류를 내보내 주지만 이것은 우리의 소중한 파일을 깃이 덮혀씌워지게 하지 않게 하기위해 애초에 checkout을 막고 있는 것입니다. main브랜치의 one이라는 파일에 어떤 작업을 거의 마친상태이며 develop브랜치에는 one이라는 파일을 이제 막 만들었다고 생각하면 당연히 develop브랜치로 checkout하면 안됩니다. 왜냐하면 만약 checkout을 허용한다면 one이라는 파일을 작업 다해놨는데 완전 초기상태로 돌려보내기 때문입니다.

그럼 만약 main 브랜치에는 있지만 develop브랜치에는 없는 파일이 변경사항이 생기면 checkout이 가능할까 ?

지금의 궁금증을 해결하기위해 폴더를 새로 파서 환경을 다시 만들었다 상황은 main브랜치와 develop가 있는상황과 똑같다.

무슨말이냐면 앞서 상황에서는 main브랜치와 develop브랜치 둘다 가지고 있는 one이라는 파일의 변경사항이 덮혀씌워질까봐 checkout을 안시켜준것이였다 그럼 다음 사진을 먼저 보자

위의 상황은 develop브랜치에 현재 위치해 있고 파일의 목록이 one, two, three로 3개가 있는 것을 확인할수 있으며 main브랜치에는 four까지 총 4개가 있는것을 확인할수 있는데 이때 내가 four파일에 변경사항을 준 상태로 develop브랜치로 checkout하려고 했다. 내가 왜 이런 상황을 만든지 이해가 됬나 ? 지금 일부로 main브랜치에는 있고 develop브랜치에는 없는 파일을 수정한것이다. 둘이 겹치는 파일이 아니니 덮혀 씌울게 없으니까 checkout이 될것이다라고 궁금증이 생겼기때문이다 결론은 똑같이 error를 발생시킨다. 이는 git checkout은 있는 파일이든 없는 파일이든 Tracked상태인 파일을 수정하거나 Staged Area에 파일이 있다면 git checkout을 허용하지 않기 때문이다. 사실 우리 눈에야 저 파일이 four라는 영어로 보이는것이지 사실 컴퓨터 입장에서 보면 four라는 파일은 그냥 이진수로 이름 지어진 파일일 뿐이다. 컴퓨터는 0과 1밖에 모르기 때문이다. 그렇기 때문에 git checkout의 기준이 이름이 같은게 있는가? 가 아닌 위에서 말한 Tracked 상태의 파일을 수정하거나 Staging Area에 파일이 있다면 git checkout은 일어날 수 없다.

그래서 왜 stash를 사용하는가?

변경사항이 있는데 잠시 어느 시점의 브랜치나 커밋으로 돌아가고싶을때 우리가 받은 에러 메시지 처럼 커밋을 하기에는 조금 부담감(?)이 있습니다 왜냐하면 보통 커밋은 완성된 한개의 기능, 한개의 모듈 단위로 하는데(정해진거 아님) 완성되지도 않은채로 커밋을 하기에는 찝찝하기 때문이다. 그럴때 우리는 이 변경사항을 임시로 저장할수 있으며 저장 후 checkout으로 다른 커밋과 브랜치를 자유롭게 왔다갔다 하다가 다시 돌아와 방금 저장한 임시저장내역을 다시 적용시키거나 심지어 같은 브랜치가 아닌 다른 브랜치에도 stash에 저장해놓은 내역을 적용시킬수 있다. 자 이제 여러가지 상황을 보았으니 이제 본격적으로 명령어 사용법을 알아보자.

git stash

git stash명령어는 새로운 stash를 스택에 만들어 작업하던 변경사항을 임시로 저장한다. 변경사항을 임시로 저장한다라는 말을 잘 생각해보면 변경사항을 찾을 수 없는 파일, 즉 Git이 Untracked하는 파일은 stash하지 않는다. 커밋처럼 영구히 저장이 아닌 말그대로 임시로 저장을 해주는것 뿐이다. 물론 git stash pop 혹은 git stash drop하지 않는 이상 절대로 사라지지 않을것이다 하지만 git log명령어로 로그를 살펴보았을때 괜히 한개의 커밋만 더 추가되서 보일것이다. 그러니 로그를 깨끗하게 유지하기위해서라도 stash를 비워주는 것이 좋다.(의무는 아님)

나의 말이 이 사진으로 설명이된다. git stash를 하게되면 그 변경내용은 커밋처럼 하나의 해쉬값을 갖게 되므로 git logrefs/stash라는 이름을 가진채 출력이 된다. 이런 stash된 내역들이 많이많이 쌓이고 커밋도 많이쌓이다보면 git log의 출력 내용은 겉잡을수 없이 알아보기 힘들어 질 것이다.

git stash apply

git stash apply명령어는 스택에 담겨있던 0번째의 stash를 꺼내 적용시켜준다. 이는 stash를 저장할때 스택이라는 자료구조에 저장하기 때문이다. 하지만 특정 stash를 꺼낼수도 있는데 git stash apply stash@{2}처럼 2번째의 stash를 적용시킬수 있따. 또한 변경내역이 원래는 staged된 상태였다면 git은 staged 상태로 되돌리면서 적용시키지 않는다. 그래서 만약 staged된 상태까지 유지시키며 적용시키고 싶다면 git stash apply --index명령어를 통해 staged된 내용은 그대로 staged된 상태와 내용을 복구시킬수 있다. 그런데 apply명령어는 단순히 적용시킬 뿐이지 stash스택에서 방금 적용시킨 stash를 제거하지는 않는다. 따라서 git stash drop을 해주어야 스택에 저장된 stash가 사라지며 drop역시 git stash drop stash@{2}처럼 특정 stashdrop할수 있다. 혹시 만약 적용과 동시에 삭제를 하고싶다면 git stash pop명령어를 사용하면 된다.

--keep-index

--keep-index 옵션으로 staged된 내용은 stash하지 않을수 있는데 단, 이러면 아직 해당 브랜치 혹은 커밋에 변경사항이 여전히 남아있기때문에 checkout하지 못한다. 아래사진 처럼 말이다.

옵션의 이름이 왜 --keep-index인줄 아는가? git-scm에서는 staging영역을 index라고 하기때문에 인덱스는 지킨다 라는 뜻이 되므로 staging 영역의 변경사항은 stash스택에 올라가지 않는다.

--include-untracked 혹은 -u

위에서도 언급했듯이 git stash는 기본적으로 Tracked 상태의 파일만을 임시 저장 해주는데, Tracked상태의 파일 뿐만이 아닌 Untracked상태의 파일까지 stash하고 싶다면 위의 옵션을 붙혀주면 된다.

다음과 같이 git stash는 Tracked 상태의 파일인 one, two파일만 stash 스택에 저장했으며 Untracked상태의 파일인 fivestash 스택에 저장하지 않았다. 아래의 사진은 똑같은 상황을 만들고 -u옵션을 붙혔을때의 git status로 상태를 확인한 사진이다.

이와 같이 -u옵션을 붙히면 Untracked 상태의 파일까지 stash스택에 저장해 준다.

--patch

git stash --patch는 변경된 모든 사항들을 저장하는 것이 아니며 대화형 프롬프트를 통해 자신이 stash에 저장할 것과 저장하지 않을 것을 지정할수 있다.

Stash를 적용한 브랜치 만들기

보통 Stash에 저장하면 한동안 그대로 유지한 채로 그 브랜치에서 새로운 일을 계속 한다. 그러면 여태 수정해왔던 파일에 다시 stash를 적용하려고 한다면 충돌이 생길수 있다. 그러면 이 충돌을 또 해결해야하는 번거로움이 생기는데 이럴땐 stash를 한 시점에 새로운 브랜치를 만들어주고 그 브랜치로 checkout후 적용까지 해줄수 있는데 git stash branch <브랜치>로 이러한 기능을 이용할수 있다. 만약 명령이 성공하면

one, two, three의 3개의 파일을 새로 만들고 커밋 한개를 만들었다. 그리고 one파일에 변경사항 one이라는 텍스트로 변경사항을 주고 커밋하지 않고 stash스택에 담았다. 그리고는 몇개의 변경사항을 주고 커밋을 했으며 이제 git stash branch develop이라는 명령어로 stash했던 시점인 4ca5a181커밋에 develop브랜치를 생성하고 develop 브랜치로 checkout하여 stash했던 내용인 one이라는 내용이 정상적으로 적용까지 시켜지는지 볼 것이다. 아래는 명령을 실행하기 이전의 상태이다. one이라는 파일은 abcd라는 내용을 가지고있다.

결과는 예상과 정확하게 맞아 떨어졌다. 결과도 중요하지만 명령어 바로 아래의 Switched to a new branch 'develop'Dropped refs/stash@{0} (adfd53247bc3c17e6a9dbcd18879ec0219230039)을 통해 git stash branch명령어로 했던 모든 명령들을 한눈에 보여주고 있다.

정말 어마어마한 기능인것 같다. 단순히 버전관리를 넘어 이건 뭐 깃을 점점 알면 알수록 관리를 못할게 없어질것 같다. 또한 아래 사진에서는 stash스택에 있던 내용까지 drop된것 까지 알수 있다.

참고

Git - Stash

'Git' 카테고리의 다른 글

[Git] Git의 내부 - Git개체  (0) 2021.06.24
[Git] Git 내부 - Plumbing명령과 Porcelain 명령  (0) 2021.06.23
[Git] git rm에 대하여..  (3) 2021.06.01
[Git] Bard-repository and Work-repository  (0) 2021.05.31
[Git] git rebase  (0) 2021.05.30
Comments