前言
偶然间发现这个命令,正好解决了最近遇到的问题,使用 Git
管理代码时有这样一种场景,你正在分支 branch2
上开发新功能,突然刚刚提交测试的 branch1
分支上报了严重的BUG,需要尽快修改,这时候就需要切换到 branch1
分支上去修复BUG,但是你刚刚在分支 branch2
修改的文件还没有提交,接下来该怎么办?
如果本地的修改正好到达一个比较完整的阶段,可以直接提交,然后切换分支改BUG,那再好不过了。可是发生这种情况的时候往往是函数写了一半,或者功能大致写完但是还没来得及测试,这样的代码你敢提交吗?我感觉最好还是不要提交吧,那么如果这时候切换分支会有什么后果呢?一般会遇到两种情况:第一种是你在 branch2
分支上所做的修改与 branch1
上做过的修改不冲突,这时切换分支会将本地修改带到 branch1
分支,如果冲突了就是第二种情况,git checkout branch1
命令会被拒绝,当然你可以添加 -f
参数强行切换分支是能成功切换的,代价就是你会丢掉本地的所有修改。
git stash
上面提到的切换分支时遇到的两种情况一般都不是我们想要的,之前说过“在 Git
中没有真正的方法来做任何事情,这就是它的妙处!”,但是关于切换分支有这样一个建议,那就是在切换分支时尽量保证你的工作区和暂存区是干净的,而 git stash
命令就是用来做这件事的。
当我们遇到这种状况,本地的修改我不能提交,不想带到新切换的分支,更不想直接丢掉,只想把他们暂存到一个地方,等我切换完分支修改好BUG,再切换回来迎接他们。使用 SVN
想保存本地修改可以使用 patch
,而使用 Git
想要解决这种情况更加方便,那就是利用 git stash
命令。
使用方法
这个命令的使用方法非常简单,最常用的 git stash push
和 git stash pop
就能应付大部分情况了,其中 push
这个单词还可以省略,使用起来可以说是相当方便了,接下来尝试一下具体用法。
本地有修改时切换分支的两种情况
之前提到过这两种情况,一种是将当前分支修改带到要切换的分支,另一种是切换会导致冲突,本次切换操作被拒绝,下面具体操作一下。
将当前分支修改带到要切换的分支
首先以 dev
分支为基础新建 feature
分支
1 | albert@homepc MINGW64 /d/gitstart (dev) |
在 feature
分支上修改文件,再切换回 dev
分支,可以正常切换,git status
查看状态,发现修改的文件被带到了 dev
分支上
1 | albert@homepc MINGW64 /d/gitstart (feature) |
这里要注意一点,在切换到 dev
分支的时候,有一行 M README.md
的内容,表示这个文件在切换过来的时候就是修改的。
如果你想要的效果就是这样,就可以直接提交了,比如修改了代码发现分支弄错了,可以这样带着修改的内容切换分支,假设就是这种情况,我们直接在 dev
分支提交修改。
1 | albert@homepc MINGW64 /d/gitstart (dev) |
切换分支操作被拒绝
上面一种情况,在 feature
分支的修改被带到 dev
分支提交,我们在此基础上切换回 feature
分支看一下:
1 | albert@homepc MINGW64 /d/gitstart (dev) |
发现此时 feature
分支上没有任何修改了,我们再改一次,然后切换到 dev
分支上试试:
1 | albert@homepc MINGW64 /d/gitstart (feature) |
看到了吧,切换分支的操作被拒绝了,原因是这次切换可能导致本地的修改被覆盖,你可以在切换分支前尝试 commit
你的修改或者 stash
你的修改,等等,这里出现了 stash
这个单词,其实之前我都没注意到,不是修改好提交了就是直接加 -f
参数放弃了所做的修改,没想到还有这样神奇 stash
命令帮我渡过难关。
stash
一般操作
接下来展示一下 git stash
最常用的操作,也就是标题中提到的——在切换分支前暂存不想提交的修改,继续在上面的环境下操作,现在 feature
分支上修改了 README.md
文件,切换到 dev
分支时因为可能产生冲突而被拒绝,我们先来看一下文件状态。
1 | albert@homepc MINGW64 /d/gitstart (feature) |
存储临时修改
对比显示我们增加了一行,然后执行 git stash
命令,再查看一下文件状态:
1 | albert@homepc MINGW64 /d/gitstart (feature) |
执行完 git stash
之后我们发现,刚才的修改不见了,本地状态提示为 nothing to commit, working tree clean
,这时我们再来切换分支:
1 | albert@homepc MINGW64 /d/gitstart (feature) |
这次切换就没有被拒绝,成功的切换到了 dev
分支,你可以在 dev
分支上进行想要的操作,全部操作完成后再切换回 feature
分支,我们这里就不操作了,直接切回 feature
分支查看一下状态:
1 | albert@homepc MINGW64 /d/gitstart (dev) |
还原临时修改
我们看到工作区很干净,这时如果想还原刚才在 feature
分支的修改,可以使用 git stash pop
命令,我们执行一下然后查看状态:
1 | albert@homepc MINGW64 /d/gitstart (feature) |
刚刚被临时暂存的修改又恢复了,我们可以在 feature
分支上继续愉快地开发了。
stash
进阶操作
作为这么神奇的命令,stash
肯定不止这么一点点用法,接下来再列举几个较为常用的参数组合:
查看临时存储的所有条目 git stash list
当你使用几次 git stash
命令之后就会发现,这个命令有点像建立还原点,所以暂存命令不止可以用一次,当使用多次暂存命令之后就会形成一个暂存列表,这时可以使用 git stash list
命令查看所有的暂存操作,执行命令后大概就是下面的样子:
1 | albert@homepc MINGW64 /d/gitstart (feature) |
这个列表的构成很像一个栈,stash@{0}
是栈顶元素,stash@{1}
是栈顶下面的一个元素,当使用 git stash
命令时会把新的临时存储信息压入栈顶,原来的信息向栈底移动,当使用 git stash pop
命令的时候又会把栈顶的元素弹出,恢复到工作区和暂存区。
临时存储未追踪的新文件 git stash -u
在开发过程中新添加的文件不属于任何一个分支,在不冲突的文件情况下也可以在切换分支的时候带到新的分支,默认在使用 git stash
命令的时候不会把这些文件临时存起来,如果想要存起来加上 -u
参数就可以了,执行之后你会发现这个新加的文件在工作区中消失了。
临时存储被忽略的文件 git stash -a
被忽略的文件在默认情况下也不会被 git stash
命令存储,想要临时存储这部分文件只要使用 -a
参数就可以了,这样不仅会把忽略的文件临时存储,连未追踪的文件也存储了起来。
stash
操作的标号
前面的 git stash list
命令也提到了,使用 git stash
命令的结果会形成一个栈形式的列表,其中 stash@{n}
就是每次临时存储对应的标号,针对于这些标号的操作也有很多,如果不加这些标号默认使用 stash@{0}
,也就是栈顶元素。
查看临时修改的具体内容 git stash show stash@{0}
这个查询过程和查询提交日志的形式有点像,主要展示了某次临时存储时改了哪些内容:
1 | albert@homepc MINGW64 /d/gitstart (feature) |
恢复指定标号的临时修改 git stash apply stash@{0}
在恢复临时存储的修改时不仅可以使用 git stash pop
命令来恢复栈顶那一次修改,也可以按照标号恢复指定的某次修改,测试如下:
1 | albert@homepc MINGW64 /d/gitstart (feature) |
这个命令在执行后,指定标号的修改会被恢复到工作区和暂存区,但是临时存储的列表不会被删除,这时可以尝试再次恢复相同标号的修改到工作区,你会发现本次操作因为修改了相同的文件而被拒绝。
刪除指定标号的临时存储的修改 git stash drop stash@{0}
可以在临时存储列表中删除指定标号的一些修改,可以测试一下看看效果:
1 | albert@homepc MINGW64 /d/gitstart (feature) |
利用临时存储的修改内容新建分支 git stash branch <branchname> [<stash>]
一般这种情况就是使用过多次 git stash push
命令,而本地分支还修改了其他内容,直接恢复之前的修改不太合适,所以利用这个命令新建一个分支,分支的内容以指定的存储标号 <stash>
对应的提交 commit-id
为基础,然后应用 <stash>
的修改,实际上就是新建了一个对应 <stash>
的分支,继续之前未完成的工作,<stash>
默认为 stash@{0}
,测试如下:
1 | albert@homepc MINGW64 /d/gitstart (feature) |
这个操作会消耗掉对应 <stash>
标号的临时存储的内容,将这些内容从存储列表中移除。
stash
的注意事项
这个命令不仅可以在同一分支上存储和还原,也可用于不同分支之间,这时就可以有一个应用,当我们发现在错误的分支上开发了代码,可以先 git stash push
将这些修改临时存储起来, 然后切换到正确的分支,再执行 git stash pop
命令将刚才的修改引用到现在的分支上。
git stash push
命令默认是存储工作区和暂存区的修改内容的,但是 git stash pop
命令在还原是默认将所有的修改还原到工作区,如果想还原到对应的暂存区,需要加额外的参数,像这样 git stash pop --index
。
总结
- 这个命令挺有用的,在合作开发的时候经常碰到临时问题需要处理,切换分支暂存一下很方便
- 感觉这个命令其实和
commit
也很像的,在操作过程中你会发现,它也有自己的hash-id
,但是不会放到commit
列表中 - 这个命令参数也有好多个,不过记住常用的就可以面对大多数情况了,简单列举下
git stash push
会将当前本地的修改临时保存起来,push
可以省略git stash list
查看当前stash push
操作的记录git stash pop
取出最近一次修改,并应用到本地git stash apply stash@{n}
应用stash@{n}
对应的修改,但是不删除这条记录git stash show stash@{n}
展示stash@{n}
对应的修改的实际修改内容