DevOps/GIT

github#8 git stash

aliceintr 2020. 11. 20. 14:09
반응형

내가 현재 여러 브랜치를 가지고 작업을 하고 있으나 내가 작업하는 브랜치 working directory에서 작업 중에 작업을 다 마치지 못했을 경우 commit을 할 수 도 없고 그렇다고 그냥 남겨 둔 채로 다른 브랜치로 체크아웃을 하면 다른 브랜치에서 작업한 내용이 다른 브랜치에 영향을 준다 git status 로 확인할 수 있다.

 

이럴 때 사용할 수 있는 명령어가 stash 이다.

NAME
       git-stash - Stash the changes in a dirty working directory away

SYNOPSIS
       git stash list [<options>]
       git stash show [<options>] [<stash>]
       git stash drop [-q|--quiet] [<stash>]
       git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]
       git stash branch <branchname> [<stash>]
       git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
                    [-u|--include-untracked] [-a|--all] [-m|--message <message>]
                    [--pathspec-from-file=<file> [--pathspec-file-nul]]
                    [--] [<pathspec>...]]
       git stash clear
       git stash create [<message>]
       git stash store [-m|--message <message>] [-q|--quiet] <commit>

DESCRIPTION
       Use git stash when you want to record the current state of the working directory and the index, but want to go back to a clean working
       directory. The command saves your local modifications away and reverts the working directory to match the HEAD commit.

       The modifications stashed away by this command can be listed with git stash list, inspected with git stash show, and restored
       (potentially on top of a different commit) with git stash apply. Calling git stash without any arguments is equivalent to git stash
       push. A stash is by default listed as "WIP on branchname ...", but you can give a more descriptive message on the command line when you
       create one.

       The latest stash you created is stored in refs/stash; older stashes are found in the reflog of this reference and can be named using
       the usual reflog syntax (e.g. stash@{0} is the most recently created stash, stash@{1} is the one before it, stash@{2.hours.ago} is also
       possible). Stashes may also be referenced by specifying just the stash index (e.g. the integer n is equivalent to stash@{n}).

COMMANDS
       push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>]
       [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<pathspec>...]
           Save your local modifications to a new stash entry and roll them back to HEAD (in the working tree and in the index). The <message>
           part is optional and gives the description along with the stashed state.

           For quickly making a snapshot, you can omit "push". In this mode, non-option arguments are not allowed to prevent a misspelled
           subcommand from making an unwanted stash entry. The two exceptions to this are stash -p which acts as alias for stash push -p and
           pathspec elements, which are allowed after a double hyphen -- for disambiguation.

       save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]
           This option is deprecated in favour of git stash push. It differs from "stash push" in that it cannot take pathspec. Instead, all
           non-option arguments are concatenated to form the stash message.
           
       store
           Store a given stash created via git stash create (which is a dangling merge commit) in the stash ref, updating the stash reflog.
           This is intended to be useful for scripts. It is probably not the command you want to use; see "push" above.

       list [<options>]
           List the stash entries that you currently have. Each stash entry is listed with its name (e.g.  stash@{0} is the latest entry,
           stash@{1} is the one before, etc.), the name of the branch that was current when the entry was made, and a short description of the
           commit the entry was based on.

               stash@{0}: WIP on submit: 6ebd0e2... Update git-stash documentation
               stash@{1}: On master: 9cc0589... Add git-stash

           The command takes options applicable to the git log command to control what is shown and how. See git-log(1).

       show [<options>] [<stash>]
           Show the changes recorded in the stash entry as a diff between the stashed contents and the commit back when the stash entry was
           first created. By default, the command shows the diffstat, but it will accept any format known to git diff (e.g., git stash show -p
           stash@{1} to view the second most recent entry in patch form). You can use stash.showStat and/or stash.showPatch config variables
           to change the default behavior.

       pop [--index] [-q|--quiet] [<stash>]
           Remove a single stashed state from the stash list and apply it on top of the current working tree state, i.e., do the inverse
           operation of git stash push. The working directory must match the index.

           Applying the state can fail with conflicts; in this case, it is not removed from the stash list. You need to resolve the conflicts
           by hand and call git stash drop manually afterwards.

       apply [--index] [-q|--quiet] [<stash>]
           Like pop, but do not remove the state from the stash list. Unlike pop, <stash> may be any commit that looks like a commit created
           by stash push or stash create.

       branch <branchname> [<stash>]
           Creates and checks out a new branch named <branchname> starting from the commit at which the <stash> was originally created,
           applies the changes recorded in <stash> to the new working tree and index. If that succeeds, and <stash> is a reference of the form
           stash@{<revision>}, it then drops the <stash>.

           This is useful if the branch on which you ran git stash push has changed enough that git stash apply fails due to conflicts. Since
           the stash entry is applied on top of the commit that was HEAD at the time git stash was run, it restores the originally stashed
           state with no conflicts.

       clear
           Remove all the stash entries. Note that those entries will then be subject to pruning, and may be impossible to recover (see
           Examples below for a possible strategy).

       drop [-q|--quiet] [<stash>]
           Remove a single stash entry from the list of stash entries.

       create
           Create a stash entry (which is a regular commit object) and return its object name, without storing it anywhere in the ref
           namespace. This is intended to be useful for scripts. It is probably not the command you want to use; see "push" above.
          
OPTIONS
       -a, --all
           This option is only valid for push and save commands.

           All ignored and untracked files are also stashed and then cleaned up with git clean.

       -u, --include-untracked
           This option is only valid for push and save commands.

           All untracked files are also stashed and then cleaned up with git clean.

       --index
           This option is only valid for pop and apply commands.

           Tries to reinstate not only the working tree's changes, but also the index's ones. However, this can fail, when you have conflicts
           (which are stored in the index, where you therefore can no longer apply the changes as they were originally).

       -k, --keep-index, --no-keep-index
           This option is only valid for push and save commands.

           All changes already added to the index are left intact.

       -p, --patch
           This option is only valid for push and save commands.

           Interactively select hunks from the diff between HEAD and the working tree to be stashed. The stash entry is constructed such that
           its index state is the same as the index state of your repository, and its worktree contains only the changes you selected
           interactively. The selected changes are then rolled back from your worktree. See the "Interactive Mode" section of git-add(1) to
           learn how to operate the --patch mode.

           The --patch option implies --keep-index. You can use --no-keep-index to override this.

       --pathspec-from-file=<file>
           This option is only valid for push command.

           Pathspec is passed in <file> instead of commandline args. If <file> is exactly - then standard input is used. Pathspec elements are
           separated by LF or CR/LF. Pathspec elements can be quoted as explained for the configuration variable core.quotePath (see git-
           config(1)). See also --pathspec-file-nul and global --literal-pathspecs.

       --pathspec-file-nul
           This option is only valid for push command.

           Only meaningful with --pathspec-from-file. Pathspec elements are separated with NUL character and all other characters are taken
 	  -q, --quiet
           This option is only valid for apply, drop, pop, push, save, store commands.

           Quiet, suppress feedback messages.

       --
           This option is only valid for push command.

           Separates pathspec from options for disambiguation purposes.

       <pathspec>...
           This option is only valid for push command.

           The new stash entry records the modified states only for the files that match the pathspec. The index entries and working tree
           files are then rolled back to the state in HEAD only for these files, too, leaving files that do not match the pathspec intact.

           For more details, see the pathspec entry in gitglossary(7).

       <stash>
           This option is only valid for apply, branch, drop, pop, show commands.

           A reference of the form stash@{<revision>}. When no <stash> is given, the latest stash is assumed (that is, stash@{0}).

 

 


 

 

 

이제 실습을 해보자

#apple 이라는 브랜치에 체크아웃 한다. 
% git checkout apple
M	a.txt
Switched to branch 'apple'

#apple 브랜치의 a.txt 파일은 apple 을 가지고 있다. 
% cat a.txt
apple
#이 브랜치 파일 내용을 수정하자
% vim a.txt 
% cat a.txt
banana

% git branch
* apple
  master

#마스터 브랜치로 옮기자
% git checkout master
M	a.txt
Switched to branch 'master'

% git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   a.txt

#마스터 브랜치의 a.txt 파일도 바껴버렸다. 
% cat a.txt
banana

 

이제 apple 브랜치에만 파일 변경이 적용되게 하자

% git checkout apple
M	a.txt

Switched to branch 'apple'

#apple 브랜치에 있는 수정내용을 stash 로 저장 : git stash save 라고 해도됨
% git stash
Saved working directory and index state WIP on apple: 9dfcfaa commit : 2


% git status
On branch apple
nothing added to commit but untracked files present (use "git add" to track)

#파일내용이 banana 아닌것을 볼 수 있다. 
% cat a.txt
apple

#이제 마스터 a.txt파일도 변경되지 않고 원본의 모양을 갖게 되었다. 
% git checkout master
Switched to branch 'master'
% cat a.txt
apple

 

이제 감추었던 stash를 복원

% git checkout apple
Switched to branch 'apple'

#다시 원래 작업으로 돌아가자 
% git stash apply

On branch apple
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   a.txt
#a.txt파일의 상태가 바뀐것을 볼 수 있다. 

#stash 기록들
% git stash list 
stash@{0}: WIP on apple: 9dfcfaa commit : 2

#stash 적용을 없애고 싶으면 git reset --hard HEAD 로 가장 최신 commit 상태로 돌아가면됨
% git reset --hard HEAD
HEAD is now at 9dfcfaa commit : 2

#아무 파일도 modified 된게 없는 것을 볼 수 있다. 
% git status
On branch apple
nothing added to commit but untracked files present (use "git add" to track)


#reset 을 해도 stash 기록은 남아있어서 언제든 돌아갈 수 있다. 
% git stash list
stash@{0}: WIP on apple: 9dfcfaa commit : 2

1개의 git stash 를 더 추가해 본다.

% git branch
* apple
  master
% vim b.txt
% cat b.txt
banana
% git status   
On branch apple
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   a.txt
	modified:   b.txt

no changes added to commit (use "git add" and/or "git commit -a")

% git stash  
Saved working directory and index state WIP on apple: 9dfcfaa commit : 2

% git stash list
stash@{0}: WIP on apple: 9dfcfaa commit : 2
stash@{1}: WIP on apple: 9dfcfaa commit : 2

이전의 stash 기록은 아직 삭제가 되지 않는다. 여기서 다시 reset 으로 돌아간다면

항상 stash@{0}: 이 가지고 있는 stash 상태로 가게 된다.

% git stash apply 
On branch apple
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   a.txt
	modified:   b.txt

이런경우 stash@{1}: 의 상태로 갈 수 없게 된다. 이럴 경우 가장 최신의 stash 를 drop 해주면 된다.

 

% git stash drop
Dropped refs/stash@{0} (a6f0582a228ba3b8a5b177ad00ce0bf489c8a3be)

% git stash list
stash@{0}: WIP on apple: 9dfcfaa commit : 2

그 후에 git stash apply 해주면 stash@{1}: 의 상태로 돌아 갈 수 있게 한다.

stash@{1}은 상태는 a.txt 내용이 a 에서 apple 로 바꼈었고 b.txt 파일은 없었다.

% git stash apply
Removing b.txt
On branch apple
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   a.txt
	deleted:    b.txt

이를 한번에 해줄 수 있는 명령어는 다음과 같다.

git stash pop
#먼저 지금 상태는 아무 파일도 수정되지 않고, commit 도 되지 않고 , stash 도 없다. 
#먼저 기존에 있던 a.txt파일을 수정하고
% vi a.txt
% cat a.txt
cat

#git add를 해준다음 stash로 임시저장한다. 
% git add a.txt
% git stash
Saved working directory and index state WIP on apple: 9dfcfaa commit : 2

#stash 상태가 되어서 수정된 부분에 대한 언급이 없음
% git status
On branch apple
nothing added to commit but untracked files present (use "git add" to track)

#stash 를 해줬기 때문에 list 에 나오는 것을 알 수 있다. 
% git stash list
stash@{0}: WIP on apple: 9dfcfaa commit : 2

#이제 다시 stash 전 상태로 돌리려면 pop 옵션을 쓰면된다. 
% git stash pop
On branch apple
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   a.txt

no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (90c3de8291d682cb6301bb9b757ef77ddffae56f)
% git stash list
#아무 stash 도 없음 pop 할 때 이미 다 지워짐 

하나의 파일이 추가 된 상황을 고려해보자

#현재 디렉토리에는 a.txt 파일 밖에 없음
gitproject % ls -ltr
-rw-r--r--  1 alice  staff     6 19 Nov 23:57 a.txt
#a.txt를 수정해주고
% vi a.txt
#b.txt를 새로 만들어주고
% vi b.txt

#git status 체크
% git status
On branch apple

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   a.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	b.txt

결과를 보면 a.txt는 기존에 있었기 때문에 track 이 되지만 b.txt는 새로운 파일이라 트랙이 안되는 걸 알 수 있다.

이 상황에서 git stash 를 하면

% git stash
Saved working directory and index state WIP on apple: 9dfcfaa commit : 2

#status 에서 stash 가 됬어도 적용이 안된 것을 알 수 있다. 
% git status
On branch apple
Untracked files:
  (use "git add <file>..." to include in what will be committed)
  
	b.txt

nothing added to commit but untracked files present (use "git add" to track)

결과적으로 버전관리가 되고 있는 파일만 stash 가 적용된다는 사실 꼭 기억하자 , 한번이라도 add 가 됬거나 commit이 된 파일들을 말하는 것 같다.

 

 

 

내용이 도움이 되셨다면 블로그 구독하기 부탁드리겠습니다.

* 이 글의 모든 저작권은 aliceintr에 있으며 무단 배포 및 사용은 자제해 주시기 바랍니다. *

반응형

'DevOps > GIT' 카테고리의 다른 글

github#10 git push and pull  (0) 2020.11.24
github#9 git remote repository  (0) 2020.11.21
github#7 3 ways merge  (0) 2020.11.20
github#6 git merge conflict  (0) 2020.11.20
github#5 git HEAD file  (0) 2020.11.20