继续暂存区未完成的实践

经过了前面的实践,现在DEMO版本库应该处于master分支上,看看是不是这样。

$ cd /path/to/my/workspace/demo
$ git status -sb # Git 1.7.2 及以上版本才支持 -b 参数哦
## master
$ git log --graph --pretty=oneline --stat
* 2b31c199d5b81099d2ecd91619027ab63e8974ef Merge commit 'acc2f69'
|\
| * acc2f69cf6f0ae346732382c819080df75bb2191 commit in detached HEAD mode.
| | 0 files changed, 0 insertions(+), 0 deletions(-)
* | 4902dc375672fbf52a226e0354100b75d4fe31e3 does master follow this new commit?
|/
| 0 files changed, 0 insertions(+), 0 deletions(-)
* e695606fc5e31b2ff9038a48a3d363f4c21a3d86 which version checked in?
| welcome.txt | 1 +
| 1 files changed, 1 insertions(+), 0 deletions(-)
* a0c641e92b10d8bcca1ed1bf84ca80340fdefee6 who does commit?
* 9e8a761ff9dd343a1380032884f488a2422c495a initialized.
welcome.txt | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

还记得在之前“Git暂存区”的结尾,是如何保存进度的么?翻回去看一下,用的是git stash命令。这个命令用于保存当前进度,也是恢复进度要用的命令。

查看保存的进度用命令git stash list。

$ git stash list
stash@{0}: WIP on master: e695606 which version checked in?

现在就来恢复进度。使用git stash pop从最近保存的进度进行恢复。

$ git stash pop
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: a/b/c/hello.txt
#
# 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: welcome.txt
#
Dropped refs/stash@{0} (c1bd56e2565abd64a0d63450fe42aba23b673cf3)

先不要管git stash pop命令的输出,后面会专题介绍git stash命令。通过查看工作区的状态,可以发现进度已经找回了(状态和进度保存前稍有不同)。

$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: a/b/c/hello.txt
#
# 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: welcome.txt
#

此时再看Git状态输出,是否别有一番感觉呢?有了前面三章的基础,现在可以游刃有余的应对各种情况了。

  • 以当前暂存区状态进行提交,即只提交a/b/c/hello.txt,不提交welcome.txt。

    • 执行提交:
    $ git commit -m "add new file: a/b/c/hello.txt, but leave welcome.txt alone."
    [master 6610d05] add new file: a/b/c/hello.txt, but leave welcome.txt alone.
    1 files changed, 2 insertions(+), 0 deletions(-)
    create mode 100644 a/b/c/hello.txt
    • 查看提交后的状态:
    $ git status -s
    M welcome.txt
  • 反悔了,回到之前的状态。

    • 用重置命令放弃最新的提交:
    $ git reset --soft HEAD^
    • 查看最新的提交日志,可以看到前面的提交被抛弃了。
    $ git log -1 --pretty=oneline
    2b31c199d5b81099d2ecd91619027ab63e8974ef Merge commit 'acc2f69'
    • 工作区和暂存区的状态也都维持原来的状态。
    $ git status -s
    A a/b/c/hello.txt
    M welcome.txt
  • 想将welcome.txt提交。

再简单不过了。

$ git add welcome.txt
$ git status -s
A a/b/c/hello.txt
M welcome.txt
  • 想将a/b/c/hello.txt撤出暂存区。

也是用重置命令。

$ git reset HEAD a/b/c
$ git status -s
M welcome.txt
?? a/
  • 想将剩下的文件(welcome.txt)从暂存区撤出,就是说不想提交任何东西了。

还是使用重置命令,甚至可以不使用任何参数。

$ git reset
Unstaged changes after reset:
M welcome.txt
  • 想将本地工作区所有的修改清除。即清除welcome.txt的改动,删除添加的目录a即下面的子目录和文件。

    • 清除welcome.txt的改动用检出命令。实际对于此例执行git checkout .也可以。
    $ git checkout -- welcome.txt
    • 工作区显示还有一个多余的目录a。
    $ git status
    # On branch master
    # Untracked files:
    # (use "git add <file>..." to include in what will be committed)
    #
    # a/
    • 删除本地多余的目录和文件,可以使用git clean命令。先来测试运行以便看看哪些文件和目录会被删除,以免造成误删。
    $ git clean -nd
    Would remove a/
    • 真正开始强制删除多余的目录和文件。
    $ git clean -fd
    Removing a/
    • 整个世界清净了。
    $ git status -s

使用git stash

命令git stash可以用于保存和恢复工作进度,掌握这个命令对于日常的工作会有很大的帮助。关于这个命令的最主要的用法实际上通过前面的演示已经了解了。

  • 命令:git stash

保存当前工作进度。会分别对暂存区和工作区的状态进行保存。

  • 命令:git stash list

显示进度列表。此命令显然暗示了git stash可以多次保存工作进度,并且在恢复的时候进行选择。

  • 命令:git stash pop [–index] [<stash>]

如果不使用任何参数,会恢复最新保存的工作进度,并将恢复的工作进度从存储的工作进度列表中清除。

如果提供<stash>参数(来自于git stash list显示的列表),则从该<stash>中恢复。恢复完毕也将从进度列表中删除<stash>。

选项--index除了恢复工作区的文件外,还尝试恢复暂存区。这也就是为什么在一开始恢复进度的时候显示的状态和保存进度前略有不同。

实际上还有几个用法也很有用。

  • 命令:git stash [save [–patch] [-k|–[no-]keep-index] [-q|–quiet] [<message>]]

    • 这条命令实际上是第一条git stash命令的完整版。即如果需要在保存工作进度的时候使用指定的说明,必须使用如下格式:
    git stash save "message..."
    • 使用参数--patch会显示工作区和HEAD的差异,通过对差异文件的编辑决定在进度中最终要保存的工作区的内容,通过编辑差异文件可以在进度中排除无关内容。

    • 使用-k或者--keep-index参数,在保存进度后不会将暂存区重置。缺省会将暂存区和工作区强制重置。

  • 命令:git stash apply [–index] [<stash>]

除了不删除恢复的进度之外,其余和git stash pop命令一样。

  • 命令:git stash drop [<stash>]

删除一个存储的进度。缺省删除最新的进度。

  • 命令:git stash clear

删除所有存储的进度。

  • 命令:git stash branch <branchname> <stash>

基于进度创建分支。

探秘git stash

了解一下git stash的机理会有几个好处:当保存了多个进度的时候知道从哪个进度恢复;综合运用前面介绍的Git知识点;了解Git的源码,Git将不再神秘。

在执行git stash命令时,Git实际调用了一个脚本文件实现相关的功能,这个脚本的文件名就是git-stash。看看git-stash安装在哪里了。

$ git --exec-path
/usr/lib/git-core

如果检查一下这个目录,会震惊的。

$ ls /usr/lib/git-core/
git git-help git-reflog
git-add git-http-backend git-relink
git-add--interactive git-http-fetch git-remote
git-am git-http-push git-remote-ftp
git-annotate git-imap-send git-remote-ftps
git-apply git-index-pack git-remote-http
..................
... 省略40余行 ...
..................

实际上在1.5.4之前的版本,Git会安装这些一百多个以git-<cmd>格式命名的程序到可执行路径中。这样做的唯一好处就是不用借助任何扩展机制就可以实现命令行补齐:即键入git-后,连续两次键入<Tab>键,就可以把这一百多个命令显示出来。这种方式随着Git子命令的增加越来越显得混乱,因此在1.5.4版本开始,不再提供git-<cmd>格式的命令,而是用唯一的git命令。而之前的名为git-<cmd>的子命令则保存在非可执行目录下,由Git负责加载。

后面的章节中偶尔会看到形如git-<cmd>字样的名称,以及同时存在的git <cmd>命令。可以这样理解:git-<cmd>作为软件本身的名称,而其命令行为git <cmd>。

最早很多Git命令都是用Shell或者Perl脚本语言开发的,在Git的发展中一些对运行效率要求高的命令用C语言改写。而git-stash(至少在Git 1.7.3.2版本)还是使用Shell脚本开发的,研究它会比研究用C写的命令要简单的多。

$ file /usr/lib/git-core/git-stash
/usr/lib/git-core/git-stash: POSIX shell script text executable

解析git-stash脚本会比较枯燥,还是通过运行一些示例更好一些。

当前的进度保存列表是空的。

$ git stash list

下面在工作区中做一些改动。

$ echo Bye-Bye. >> welcome.txt
$ echo hello. > hack-1.txt
$ git add hack-1.txt
$ git status -s
A hack-1.txt
M welcome.txt

可见暂存区中已经添加了新增的hack-1.txt,修改过的welcome.txt并未添加到暂存区。执行git stash保存一下工作进度。

$ git stash save "hack-1: hacked welcome.txt, newfile hack-1.txt"
Saved working directory and index state On master: hack-1: hacked welcome.txt, newfile hack-1.txt
HEAD is now at 2b31c19 Merge commit 'acc2f69'

再来看工作区恢复了修改前的原貌(实际上用了 git reset –hard HEAD 命令),文件welcome.txt的修改不见了,文件hack-1.txt整个都不见了。

$ git status -s
$ ls
detached-commit.txt new-commit.txt welcome.txt

再做一个修改,并尝试保存进度。

$ echo fix. > hack-2.txt
$ git stash
No local changes to save

进度保存失败!可见本地没有被版本控制系统跟踪的文件并不能保存进度。因此本地新文件需要执行添加再执行git stash命令。

$ git add hack-2.txt
$ git stash
Saved working directory and index state WIP on master: 2b31c19 Merge commit 'acc2f69'
HEAD is now at 2b31c19 Merge commit 'acc2f69'

不用看就知道工作区再次恢复原状。如果这时执行git stash list会看到有两次进度保存。

$ git stash list
stash@{0}: WIP on master: 2b31c19 Merge commit 'acc2f69'
stash@{1}: On master: hack-1: hacked welcome.txt, newfile hack-1.txt

从上面的输出可以得出两个结论:

  • 在用git stash命令保存进度时,提供说明更容易找到对应的进度文件。

  • 每个进度的标识都是stash@{<n>}格式,像极了前面介绍的reflog的格式。

实际上,git stash的就是用到了前面介绍的引用和引用变更日志(reflog)来实现的。

$ ls -l .git/refs/stash .git/logs/refs/stash
-rw-r--r-- 1 jiangxin jiangxin 364 Dec 6 16:11 .git/logs/refs/stash
-rw-r--r-- 1 jiangxin jiangxin 41 Dec 6 16:11 .git/refs/stash

那么在“Git重置”中学习的reflog可以派上用场了。

$ git reflog show refs/stash
e5c0cdc refs/stash@{0}: WIP on master: 2b31c19 Merge commit 'acc2f69'
6cec9db refs/stash@{1}: On master: hack-1: hacked welcome.txt, newfile hack-1.txt

对照git reflog的结果和前面git stash list的结果,可以肯定用git stash保存进度,实际上会将进度保存在引用refs/stash所指向的提交中。多次的进度保存,实际上相当于引用refs/stash一次又一次的变化,而refs/stash引用的变化由reflog(即.git/logs/refs/stash)所记录下来。

新的一个疑问又出现了,如何在引用refs/stash中同时保存暂存区的进度和工作区中的进度呢?查看一下引用refs/stash的提交历史能够看出端倪。

$ git log --graph --pretty=raw  refs/stash -2
* commit e5c0cdc2dedc3e50e6b72a683d928e19a1d9de48
|\ tree 780c22449b7ff67e2820e09a6332c360ddc80578
| | parent 2b31c199d5b81099d2ecd91619027ab63e8974ef
| | parent c5edbdcc90addb06577ff60f644acd1542369194
| | author Jiang Xin <jiangxin@ossxp.com> 1291623066 +0800
| | committer Jiang Xin <jiangxin@ossxp.com> 1291623066 +0800
| |
| | WIP on master: 2b31c19 Merge commit 'acc2f69'
| |
| * commit c5edbdcc90addb06577ff60f644acd1542369194
|/ tree 780c22449b7ff67e2820e09a6332c360ddc80578
| parent 2b31c199d5b81099d2ecd91619027ab63e8974ef
| author Jiang Xin <jiangxin@ossxp.com> 1291623066 +0800
| committer Jiang Xin <jiangxin@ossxp.com> 1291623066 +0800
|
| index on master: 2b31c19 Merge commit 'acc2f69'

可以看到在提交关系图可以看到进度保存的最新提交是一个合并提交。最新的提交说明中有WIP字样(是Work In Progess的简称),说明代表了工作区进度。而最新提交的第二个父提交(上图中显示为第二个提交)有index on master字样,说明这个提交代表着暂存区的进度。

但是上图中的两个提交都指向了同一个树——tree 780c224...,这是因为最后一次做进度保存时工作区相对暂存区没有改变,这让关于工作区和暂存区在引用refs/stash中的存储变得有些扑朔迷离。别忘了第一次进度保存工作区、暂存区和版本库都是不同的,可以用于验证关于refs/stash实现机制的判断。

第一次进度保存可以用reflog中的语法,即用refs/stash@{1}来访问,也可以用简称stash@{1}。下面就用第一次的进度保存来研究一下。

$ git log --graph --pretty=raw  stash@{1} -3
* commit 6cec9db44af38d01abe7b5025a5190c56fd0cf49
|\ tree 7250f186c6aa3e2d1456d7fa915e529601f21d71
| | parent 2b31c199d5b81099d2ecd91619027ab63e8974ef
| | parent 4560d76c19112868a6a5692bf9379de09c0452b7
| | author Jiang Xin <jiangxin@ossxp.com> 1291622767 +0800
| | committer Jiang Xin <jiangxin@ossxp.com> 1291622767 +0800
| |
| | On master: hack-1: hacked welcome.txt, newfile hack-1.txt
| |
| * commit 4560d76c19112868a6a5692bf9379de09c0452b7
|/ tree 5d4dd328187e119448c9171f99cf2e507e91a6c6
| parent 2b31c199d5b81099d2ecd91619027ab63e8974ef
| author Jiang Xin <jiangxin@ossxp.com> 1291622767 +0800
| committer Jiang Xin <jiangxin@ossxp.com> 1291622767 +0800
|
| index on master: 2b31c19 Merge commit 'acc2f69'
|
* commit 2b31c199d5b81099d2ecd91619027ab63e8974ef
|\ tree ab676f92936000457b01507e04f4058e855d4df0
| | parent 4902dc375672fbf52a226e0354100b75d4fe31e3
| | parent acc2f69cf6f0ae346732382c819080df75bb2191
| | author Jiang Xin <jiangxin@ossxp.com> 1291535485 +0800
| | committer Jiang Xin <jiangxin@ossxp.com> 1291535485 +0800
| |
| | Merge commit 'acc2f69'

果然上面显示的三个提交对应的三棵树各不相同。查看一下差异。用“原基线”代表进度保存时版本库的状态,即提交2b31c199;用“原暂存区”代表进度保存时暂存区的状态,即提交4560d76;用“原工作区”代表进度保存时工作区的状态,即提交6cec9db。

  • 原基线和原暂存区的差异比较。
$ git diff stash@{1}^2^ stash@{1}^2
diff --git a/hack-1.txt b/hack-1.txt
new file mode 100644
index 0000000..25735f5
--- /dev/null
+++ b/hack-1.txt
@@ -0,0 +1 @@
+hello.
  • 原暂存区和原工作区的差异比较。
$ git diff stash@{1}^2 stash@{1}
diff --git a/welcome.txt b/welcome.txt
index fd3c069..51dbfd2 100644
--- a/welcome.txt
+++ b/welcome.txt
@@ -1,2 +1,3 @@
Hello.
Nice to meet you.
+Bye-Bye.
  • 原基线和原工作区的差异比较。
$ git diff stash@{1}^1 stash@{1}
diff --git a/hack-1.txt b/hack-1.txt
new file mode 100644
index 0000000..25735f5
--- /dev/null
+++ b/hack-1.txt
@@ -0,0 +1 @@
+hello.
diff --git a/welcome.txt b/welcome.txt
index fd3c069..51dbfd2 100644
--- a/welcome.txt
+++ b/welcome.txt
@@ -1,2 +1,3 @@
Hello.
Nice to meet you.
+Bye-Bye.

从stash@{1}来恢复进度。

$ git stash apply stash@{1}
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: hack-1.txt
#
# 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: welcome.txt
#

显示进度列表,然后删除进度列表。

$ git stash list
stash@{0}: WIP on master: 2b31c19 Merge commit 'acc2f69'
stash@{1}: On master: hack-1: hacked welcome.txt, newfile hack-1.txt
$ git stash clear

删除进度列表之后,会发现stash相关的引用和reflog也都不见了。

$ ls -l .git/refs/stash .git/logs/refs/stash
ls: cannot access .git/refs/stash: No such file or directory
ls: cannot access .git/logs/refs/stash: No such file or directory

通过上面的这些分析,有一定Shell编程基础的读者就可以尝试研究git-stash的代码了,可能会有新的发现。

GIt-恢复进度的更多相关文章

  1. Git学习之Git恢复进度

    ================================================ 继续暂存区未完成的实践 ======================================= ...

  2. 使用git stash命令保存和恢复进度

    使用git stash命令保存和恢复进度 git stash 保存当前工作进度,会把暂存区和工作区的改动保存起来.执行完这个命令后,在运行git status命令,就会发现当前是一个干净的工作区,没有 ...

  3. git 恢复工作区删除的所有文件

    /********************************************************************* * git 恢复工作区删除的所有文件 * 说明: * 今天 ...

  4. Git恢复指定文件

    Git恢复指定文件 修改这个文件的commit有哪些? git log -- <文件路径> 猜测需要还原的commit. 这个文件作了哪些更改? git diff <需要还原的com ...

  5. DG查看恢复进度

    查看恢复进度 (1)查看进程的活动状态 V$MANAGED_STANDBY视图专用于显示物理Standby数据库相关进程的当前状态,该视图中的列也很有特点,查看进程状态时,通常我们会关注PROCESS ...

  6. rman备份有效性验证/恢复进度监控

    故障一定会发生,只是早晚的问题!作为一名DBA时刻要记着备份,备份的有效性同样重要,不要当某一天最需要的时候,发现悲剧了...验证rman备份是否可以成功还原,11g后可以通过命令验证但,验证全备份一 ...

  7. git恢复已删的分支

    git恢复已经删除的分支 执行git命令, 找回之前提交的commit git log -g 执行效果 commit 80fd3a3e1abeab52030ee9f6ec32b5c815de20a9 ...

  8. Git恢复之前版本的两种方法reset、revert

    实战 回退 1.删除之前的提交 git reset --hard id 推送到远程 git push -f [git log中确实删除了,但是拿到可以恢复] 2.不删除之前的提交 git revert ...

  9. git stash 保存和恢复进度

    1. stash当前修改 git stash会把所有未提交的修改(包括暂存的和非暂存的)都保存起来,用于后续恢复当前工作目录. 比如下面的中间状态,通过git stash命令推送一个新的储藏,当前的工 ...

  10. git恢复误删文件及省去密码提交

    自己遇到这种情况:自己将某文件在网页的控制面板上直接删除了,再pull下来.或者一个成员误删除了某个文件,然后push到远程库了,其他成员也都pull了,结果就是所有人的本地库当前版本中这个文件都不见 ...

随机推荐

  1. Django之model基础(查询补充)

    学习完简单的单表查询外,是远远不够的,今天我们对查询表记录做一个补充,接下来来看看基于对象的跨表查询.基于双下划线的跨表查询,聚合查询和分组查询,F查询与Q查询. 比如我们有如下一张表,在model中 ...

  2. 微信小程序:从本地相册选择图片或使用相机拍照。

    wx.chooseImage(OBJECT) 从本地相册选择图片或使用相机拍照. OBJECT参数说明: 示例代码: wx.chooseImage({ count: 1, // 默认9 sizeTyp ...

  3. <Android 应用 之路> 天气预报(四)

    前言 第二次尝试完成天气预报应用,与上次不同的是,个人感觉这次的Ui不那么丑陋,整体的实用性和界面效果,用户体验相较上一次有所提升,但是还是有很多地方需要完善. 这次使用到的内容比较丰富,包括聚合数据 ...

  4. 跨平台移动开发phonegap/cordova 3.3全系列教程-app启动画面

    1.app启动画面设计 用photoshop设计启动画面,图片分辨率为720*1280 保存的文件名为splash.png 将splash.png复制到res\drawable,如图 PS:要先添加闪 ...

  5. leetcode 355 Design Twitte

    题目连接 https://leetcode.com/problems/design-twitter Design Twitte Description Design a simplified vers ...

  6. linux日常1-踢出用户

    踢掉自己不用的终端 1.查看系统在线用户 w 2.查看哪个属于此时自己的终端(我开了两个连接) who am i 3.pkill掉自己不适用的终端 pkill -kill -t pts/1 注意: 如 ...

  7. C#实现正则表达式

    如果想了解正则表达式的基础知识:http://www.cnblogs.com/alvin-niu/p/6430758.html 一.C#中的Regex类 1.在C#中开发正则表达式,首先要引用Syst ...

  8. SQL Server 08版与14版处理重复行的方式

    在项目中,利用循环拼接成了插入多行数据的SQL语句: Insert into table(col1,col2)vaules(value11,value21); Insert into table(co ...

  9. 使用Java程序消费SAP Leonardo的机器学习API

    以sap leonardo作为关键字在微信上搜索,能搜到不少文章.但是我浏览了一下,好像没有发现有从具体编程角度上来介绍的.所以我就贡献一篇. 需求 开发一个Java程序,用户可以指定一张图片,该Ja ...

  10. 高级vim 配置

    [root@chenbj ~]# pwd /root [root@chenbj ~]# cat .vimrc set nocompatible set pastetoggle=<F9> s ...