继续暂存区未完成的实践

经过了前面的实践,现在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. HhashMap HashTable ConcurrentHashMap

    hashMap hashTable concurrentHashMap hashMap的效率高于hashTable,hashMap是线程不安全的,并发时hashMap put方法容易引起死循环,导致c ...

  2. cf314E. Sereja and Squares(dp)

    题意 题目链接 给你一个擦去了部分左括号和全部右括号的括号序列,括号有25种,用除x之外的小写字母a~z表示.求有多少种合法的括号序列.答案对4294967296取模.合法序列不能相交,如()[],( ...

  3. VUE打包发布后无法访问js、css资源

    在vue开发中,本地测试以及测试环境中都没有遇到问题,当发布生产,有虚拟路径时,便出现js.css均报错404: 首先在config的index.js文件中,将assetsPublicPath修改为' ...

  4. Paoding-Rose学习

    * HttpServletRequest.getContextPath 获取web程序root.如果是默认位置,返回””空串,否则返回 /根路径名 * rose是如何扫描到资源的 利用spring提供 ...

  5. (转) HTTP Request header

    HTTP Request header 当今web程序的开发技术真是百家争鸣,ASP.NET, PHP, JSP,Perl, AJAX 等等. 无论Web技术在未来如何发展,理解Web程序之间通信的基 ...

  6. Nginx+Keepalived双主轮询负载均衡

    双主模式使用两个VIP,前段有2台服务器,互为主从,两台服务器同时工作,不存在资源浪费情况.同时在前端的DNS服务器对网站做多条A记录,实现了Nginx的负载均衡,当一台服务器故障时候,资源会转移到另 ...

  7. POJ 3233 Matrix Power Series (矩阵分块,递推)

    矩阵乘法是可以分块的,而且幂的和也是具有线性的. 不难得到 Si = Si-1+A*Ai-1,Ai = A*Ai-1.然后矩阵快速幂就可以了. /*************************** ...

  8. 【洛谷2216】[HAOI2007] 理想的正方形(二维RMQ)

    点此看题面 大致题意: 求出一个矩阵中所有\(n*n\)正方形中极差的最小值. 另一种做法 听说这题可以用单调队列去做,但是我写了一个二维\(RMQ\). 二维\(RMQ\) \(RMQ\)相信大家都 ...

  9. 2017.12.20 Java中的 IO/XML学习总结 File类详细

    IO / XML 一.File类 1.定义/概念 Java是面向对象的语言,要想把数据存到文件中,就必须要有一个对象表示这个文件.File类的作用就是代表一个特定的文件或目录,并提供了若干方法对这些文 ...

  10. python_25_string

    name="my name is 齐志光qizhiguang" print(name.capitalize())#首字母变大写 print(name.count('i'))#统计字 ...