手把手带你玩git之 git stash

背景

git相对svn有许多好的设计,其中一个就是git stash功能。许多教程在介绍git stash的使用场景时,经常举例是:当你开发着新功能时,写到一半时(既没办法提交,又舍不得撤销),突然报告了一个BUG,你必须立马FIX这个BUG。

如何理解这个应用场景?

基础知识

首先得对git有基础知识,如下图所示:

git三个区和状态变迁

git 是分3个区的,分别是:

  • 工作区(working dir): 简单说就是我们看得见摸得着的目录和文件。
  • 索引区(index/stage): 又叫 暂存区,是被git管理(暂存)了,但尚未提交的。
  • 版本区(repository): 就是我们常说的版本,仓库。

状态变迁是这样的:

  • Untracked:当我们新建一个文件a.txt,并且编辑内容时,这个a.txt仅仅处于工作区,状态是untracked或叫unstaged,这个状态是不被git管理的,只被OS的文件系统管理。
  • Staged: 我们执行 git add a.txt后,文件才进入索引区,状态是staged,开始被git管理。
  • Committed: 再执行git commit a.txt -m 'add a.txt',状态是committed,纳入仓库。

可见git跟svn不同,git的提交,是要经历两个阶段的,它不会直接从工作区跳跃到版本区,中间隔着索引区/暂存区

git stash 内涵

有了上述分区概念后,我们用个示意图来表达下 git stash 具体做了什么:

git stash 的内涵

重点关注左半部分用橙色标注的部分,注意编号1、2、…、n和n+1。
这些步骤只有两个操作:

  • 一个是git stash,表示把索引区的内容转存到stash栈里面,同时工作区跟索引区保持一致(实际上工作区中的untracked的内容依然存在,不会被清除)。
  • 另一个是git stash pop,表示把转存到stash的弹回索引区。

实验实战

构建实验场景

笔者在做代码教程的时候,常希望利用git,把代码演变过程记录下来。比如我写了代码a.txt,演示了功能点a,并打tag为demo-a;接着写了代码b.txt,演示了功能点b,并打tag为demo-b。以便读者可以git checkout demo-agit checkout demo-b来重现笔者当时的代码场景,而不用一上来就看到太多无关的代码。

但是,笔者时常一不小心就写完了a.txt和b.txt,但是却迟迟没有提交,现在笔者想分开提交,先提交a.txt,再提交b.txt。同时,每次提交后,我都必须跑一边单元测试,以便验证正确,如果不正确应该回退这个提交,调整代码后,再提交。

问题来了:在已经提交a.txt,尚未提交b.txt的时候,为了排除b.txt是否对a.txt单元测试有干扰,必须把b.txt从工作区删了,但之后提交b.txt的时候,又得拿回来。

如何用 git stash 解决?

实验操作过程

  • 编写文件 a.txt和b.txt
➜  GitTutorial echo "aaa" > a.txt
➜  GitTutorial echo "bbb" > b.txt
➜  GitTutorial ls
a.txt b.txt
➜  GitTutorial
  • 借助git管理
➜  GitTutorial git init
Initialized empty Git repository in workspace/GitTutorial/.git/
  • 提交 a.txt
➜  GitTutorial git:(master) ✗ git add a.txt
➜  GitTutorial git:(master) ✗ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

    new file:   a.txt

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

    b.txt

➜  GitTutorial git:(master) ✗ git commit a.txt -m 'add a.txt'
[master (root-commit) 9264345] add a.txt
 1 file changed, 1 insertion(+)
 create mode 100644 a.txt
➜  GitTutorial git:(master) ✗ git status
On branch master
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)
➜  GitTutorial git:(master) ✗

上述代码,提交a.txt,需要两个步骤,git add a.txtgit commit a.txt -m 'add a.txt',每次操作后,用git status命令,验证下a.txt经历了从untracked状态到staged (to be committed)状态,再到纳入版本库的变迁过程。

a.txt提交完后,当前情况是:a.txt已经纳入版本仓库,b.txt依然是untracked状态。

用Eclipse Git 可视化看下各区的情况:(Project -> Team -> Commit ... -> Open Git Staging view)

Git可视化各区情况
  • 给a.txt提交打tag,标记为demo-a
➜  GitTutorial git:(master) ✗ git tag demo-a
➜  GitTutorial git:(master) ✗ git tag
demo-a
➜  GitTutorial git:(master) ✗
  • 暂时“删除”b.txt

为了对a.txt进行单元测试,排除b.txt对它可能产生的干扰,需要暂时“删除”b.txt,让工作区只剩下a.txt。执行 git stash,结果:

➜  GitTutorial git:(master) ✗ git stash
No local changes to save
➜  GitTutorial git:(master) ✗

提示的是

No local changes to save

OMG 怎么回事?

因为b.txt是untracked状态,并没进入索引区,git stash是把索引区转存起来。所以我们需要:

  • 把工作区所有内容先纳入索引区;
  • 然后把索引区转存到stash里面。
➜  GitTutorial git:(master) ✗ git add *
把b.txt纳入索引区

执行git stash

➜  GitTutorial git:(master) ✗ git stash
Saved working directory and index state WIP on master: 9264345 add a.txt
HEAD is now at 9264345 add a.txt
➜  GitTutorial git:(master) ls
a.txt

此时工作区只有 a.txt了,b.txt暂时消失了。可以跑只有a.txt的单测了。
可以用命令 git stash list查看stash栈的情况。

  • 恢复b.txt到工作区
➜  GitTutorial git:(master) git stash pop
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   b.txt

Dropped refs/stash@{0} (a6325932f00d22c978617102d9fe6b10b9605e79)
➜  GitTutorial git:(master) ✗ ls
a.txt b.txt
➜  GitTutorial git:(master) ✗

执行 git stash pop 后,b.txt回到工作目录了。

  • 提交b.txt,并打标签demo-b
➜  GitTutorial git:(master) ✗ git commit * -m 'add b.txt'
[master ec78d66] add b.txt
 1 file changed, 1 insertion(+)
 create mode 100644 b.txt
➜  GitTutorial git:(master) git tag demo-b
➜  GitTutorial git:(master) git tag
demo-a
demo-b
➜  GitTutorial git:(master) git log
  • 读者如果想看demo-a怎么办?

执行 git checkout demo-a

➜  GitTutorial git:(master) ls
a.txt b.txt
➜  GitTutorial git:(master) git checkout demo-a
Note: checking out 'demo-a'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

HEAD is now at 9264345... add a.txt
➜  GitTutorial git:(9264345) ls
a.txt
➜  GitTutorial git:(9264345)

注意:git报了一个警告,叫detached HEAD
这是什么意思呢? 请听下回讲解。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,009评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,808评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,891评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,283评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,285评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,409评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,809评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,487评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,680评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,499评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,548评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,268评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,815评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,872评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,102评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,683评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,253评论 2 341

推荐阅读更多精彩内容

  • 1.git的安装 1.1 在Windows上安装Git msysgit是Windows版的Git,从https:/...
    落魂灬阅读 12,645评论 4 54
  • Git是分布式版本控制系统,那么它就没有中央服务器的,每个人的电脑就是一个完整的版本库,这样,工作的时候就不 需要...
    Royno7阅读 446评论 2 3
  • 我今天和爸爸 妈妈还有姐姐去学校的操场上朗读了, 我今天读的是孝弟三百千的第一章。今天下午还学了舞蹈 我感觉今天...
    杨皓宇yang阅读 180评论 0 0
  • 我俯视天地以钟声里的虔诚
    2020号阅读 449评论 20 32
  • 工具不重要,重要的是***。 我猜很多人像我一样经常看到这样的说法,可对于这种简单的说法,我一直不是很满意。刚刚有...
    Rico_阅读 856评论 8 4