git stash

Published: 08 May 2014 Category: git

不知道使用git的同学有没有遇到这种情况:在当前分支下修改了一些代码,还未来的急提交(commit),但是遇到紧急情况,需要切换到另外一个分支上工作。这时候如果强行进行切换的话还出现什么情况呢?我们来举个例子演示一下:

1.新建一个目录git-test,初始化一下仓库,做一次简单的提交:

mkdir git-test
cd git-test/
git init
echo 0 > test
git add .
git commit -am "init"  

2.新建一个分支:

git checkout -b master1  

简单地修改test文件:

linux-geek:/home/tmp/git-test # echo 1 >> test
linux-geek:/home/tmp/git-test # cat test 
0
1 

我们用git status看一下:

linux-geek:/home/tmp/git-test # git status
# On branch master1
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   test
#
no changes added to commit (use "git add" and/or "git commit -a")  

如果这个时候我们有紧急情况要处理需要切换到分支master上,但是此时分支master1的改动我们并不想立即提交,该怎么办呢?如果此时我们强行切换到分支master上,如下:

linux-geek:/home/tmp/git-test # git checkout master
M       test
Switched to branch 'master'   
linux-geek:/home/tmp/git-test # cat test
0
1

在分支master1上的修改会被强制应用到分支master上,这是我们所不想发生的事情。那这时我们该怎么处理呢?这个时候git stash就派上用场了。我们回到第2步切换分支之前。

3.这时候如果我们直接敲入命令git stash,会发生什么呢?

linux-geek:/home/tmp/git-test # git stash
Saved working directory and index state WIP on master1: 5c28393 init
HEAD is now at 5c28393 init  

再来用git status看一下:

linux-geek:/home/tmp/git-test # git status
# On branch master1
nothing to commit (working directory clean) 
linux-geek:/home/tmp/git-test # cat test 
0 

发现工作目录是干净的,我们把在分支master1的一些改动暂存起来了。这时候我们切换到分支master是安全的,没有任何问题的。

4.好了,假设我们在分支master上的工作完成了,并且也提交了,我们又回到了分支master1上,想要应用之前的改动该怎么办呢?我们可以用一系列的命令来实现这个功能:

4.1 我们先来git stash list来看一下我们的暂存区里面有哪些存贮的东西:

linux-geek:/home/tmp/git-test # git stash list
stash@{0}: WIP on master1: 5c28393 init  

现在,我们只有一个提交,那我们直接git stash apply看一下:

linux-geek:/home/tmp/git-test # git stash apply
# On branch master1
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   test
#
no changes added to commit (use "git add" and/or "git commit -a")  
linux-geek:/home/tmp/git-test # cat test
0
1  

直接git stash apply会挑选最近一次git stash(stash@{0})的内容加以应用。如果我们之前有好几次git stash(注意:每一次git stash都会做一次压栈处理),在这里我们可以挑选一个stash加以应用:

linux-geek:/home/tmp/git-test # git stash list
stash@{0}: WIP on master1: 5c28393 init
stash@{1}: WIP on master1: 5c28393 init
linux-geek:/home/tmp/git-test # git stash apply stash@{1}
# On branch master1
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   test
#
no changes added to commit (use "git add" and/or "git commit -a")  

如果我们直接git stash pop会出现什么情况呢?

linux-geek:/home/tmp/git-test # git stash pop 
# On branch master1
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   test
#
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (34972248ca71c1e4cf2b5937398649dfcb3c00b5)
linux-geek:/home/tmp/git-test # cat test 
0
2
linux-geek:/home/tmp/git-test # git stash list 
stash@{0}: WIP on master1: 5c28393 init  

你会发现stash@{1}不见了。其实不然,实际上是做了个这样的处理:把stash@{0}出栈,把原来的stash@{1}替换stash@{0},所以stash@{1}并没有消失,只不过换了个名字而已。

如果我们不想要栈存里面的某一个stash,我们可以直接git stash drop掉:

linux-geek:/home/tmp/git-test # git stash list 
stash@{0}: WIP on master1: 5c28393 init
linux-geek:/home/tmp/git-test # git stash drop stash@{0} 
Dropped stash@{0} (cde55b11b448d1ecd8ca0751bad6b9f10f878100)
linux-geek:/home/tmp/git-test # git stash list  

我们会发现此时,栈空了。如果我们想更彻底些,直接把所有的stash扔掉,可以直接git stash clear