git stash 的一次惊心动魄的误删操作

简介:行走在互联网最低端的小熊

问题--源起:

小熊和所有混迹在互联网中的开发一样,公司里面用git来管理项目,由于可能经常有几个问题要开发,要频繁在多分支中切换,但是经常会遇到以下情况:

小熊当前正在分支A上干活,突然有一个紧急任务要去分支B上操作,但是由于分支A的活还没有做完,小熊又不想做一次无畏的提交,所以小熊就将分支A上修该的文件使用git stash save 'message' 保存起来,再切换到分支B去处理紧急任务

HINT:要切换分支,必去当前的分支没有改动的文件,否者可能需要处理冲突

刚开始,小熊使用git stash 命令用的不亦乐乎,由于以前都是用git commit 去提交之后在切换分支,之后且会有又取消这次的提交,操作麻烦;现在使用git stash完全可以满足小熊的需求,并且操作简单。

git stash 使用教程

git stash用于将当前工作区的修改暂存起来,就像堆栈一样,可以随时将某一次缓存的修改再重新应用到当前工作区。一旦用好了这个命令,会极大提高工作效率。

举例说明:

1、准备工作,首先初始化一个git仓
随便建立一个目录,进去,然后使用 :
$: git init .
添加一个文件:
$: touch hello
$: git add .
$: git commit -m "first add"
2、暂存当前修改内容(git stash)
假设我们在写一个C函数,如下:
void func1(void) {
printf("this is func1");
}
$:~/code/linux/git$ vim hello.c
$:~/code/linux/git$ git diff
diff --git a/hello.c b/hello.c
index e69de29..bdc92a5 100644
--- a/hello.c
+++ b/hello.c
@@ -0,0 +1,2 @@
+void func1(void) {printf("this is func1");}
+void main(void) {return func1();}

调试OK,发现func1功能OK,但是应该优化一下,可能效率更高,这个时候怎么办?

直接改func1的话,如果发现修改不合适,想回退的话很麻烦,这个时候可以用git stash将将修改暂存起来。

$: ~/code/linux/git$ git stash
Saved working directory and index state WIP on master: 452b08d rename hello as hello.c
HEAD is now at 452b08d rename hello as hello.c
$:~/code/linux/git$ git status
On branch master
nothing to commit, working directory clean
3、弹出修改内容(git stash pop)

这个时候你重新编写func1, 发现效果不好,后悔了,于是可以用git stash pop命令,弹出刚才的内容(注意先用git checkout . 清空工作区)

$:~/code/linux/git$ vim hello.c
$:~/code/linux/git$ git diff
diff --git a/hello.c b/hello.c
index e69de29..9c5bff3 100644
--- a/hello.c
+++ b/hello.c
@@ -0,0 +1 @@
+some bad chenges....
$:~/code/linux/git$ git checkout .
$:~/code/linux/git$ git stash pop
On branch master
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: hello.c no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (208ca2e2c0c455da554986a6770a74ad0de5b1e0)
$:~/code/linux/git$ git diff
diff --git a/hello.c b/hello.c
index e69de29..bdc92a5 100644
--- a/hello.c
+++ b/hello.c
@@ -0,0 +1,2 @@
+void func1(void) {printf("this is func1");}
+void main(void) {return func1();}

注意,git stash pop 弹出成功后,暂存列表里面就没有了,如果当前工作区不干净,弹出时有冲突,则暂存列表会继续保留修改。

4、可以保存多个修改

假设你在实现一个功能,有好几种算法可以实现,你想逐个尝试看效果。

现在你在func1中实现了一种方法,准备尝试写func2,用另一种方法。

那么可以将func1的修改入栈,去写fun2,等fun2写好后,你又想试试func3,那么没关系,可以用同样的方法保存func2的修改:

$:~/code/linux/git$ git diff
diff --git a/hello.c b/hello.c
index e69de29..bdc92a5 100644
--- a/hello.c
+++ b/hello.c
@@ -0,0 +1,2 @@
+void func1(void) {printf("this is func1");}
+void main(void) {return func1();}
$:~/code/linux/git$ git stash
Saved working directory and index state WIP on master: 452b08d rename hello as hello.c
HEAD is now at 452b08d rename hello as hello.c
$:~/code/linux/git$ git status
On branch master
nothing to commit, working directory clean
$:~/code/linux/git$ vim hello.c
$:~/code/linux/git$ git diff
diff --git a/hello.c b/hello.c
index e69de29..7fd0a13 100644
--- a/hello.c
+++ b/hello.c
@@ -0,0 +1,2 @@
+void func2(void) {printf("this is func2");}
+void main(void) {return func2();}
$:~/code/linux/git$ git stash
Saved working directory and index state WIP on master: 452b08d rename hello as hello.c
HEAD is now at 452b08d rename hello as hello.c
$:~/code/linux/git$ git status
On branch master
nothing to commit, working directory clean
5、查看保存的内容列表(git stash list)

现在我们保存了两个修改,一个func1,一个func2,可以通过git stash list去查看保存内容列表:

$:~/code/linux/git$ git stash list
stash@{0}: WIP on master: 452b08d rename hello as hello.c
stash@{1}: WIP on master: 452b08d rename hello as hello.c

可以清楚的看到这两次修改,stash@{0}和stash@{1}, 那么哪个对应func1,哪个对应func2的修改呢?

这时我们需要使用git stash show stash@{X}命令来查看,其中‘X’表示列表号。

$:~/code/linux/git$ git show stash@{0}
commit 72e6a391bcad186ab24676aa1db8d5831c99cec9
Merge: 452b08d 6c95c30
Author: hiekay
Date: Sat Mar 12 19:56:18 2016 +0800 WIP on master: 452b08d rename hello as hello.c diff --cc hello.c
index e69de29,e69de29..7fd0a13
--- a/hello.c
+++ b/hello.c
@@@ -1,0 -1,0 +1,2 @@@
++void func2(void) {printf("this is func2");}
++void main(void) {return func2();}
$:~/code/linux/git$ git show stash@{1}
commit 7fcca4b66640c51ca76e637df03264b7c41885be
Merge: 452b08d 1c37881
Author: hiekay
Date: Sat Mar 12 19:54:35 2016 +0800 WIP on master: 452b08d rename hello as hello.c diff --cc hello.c
index e69de29,e69de29..bdc92a5
--- a/hello.c
+++ b/hello.c
@@@ -1,0 -1,0 +1,2 @@@
++void func1(void) {printf("this is func1");}
++void main(void) {return func1();}

发现stash@{0}对应func2的修改, stash@{1}对应func1的修改,原来新入栈的修改,其代号为0,循环命名。

6、应用任意一次修改到当前目录(git apply stash@{x})

如果现在又想回到func1的修改,怎么办呢?在工作区干净的情况下,要使用git stash apply stash@{1}。

注意这时不能使用git stash pop, 它将最栈顶,即stash@{0}的修改弹出来,而func1现在已经是stash@{1}了。

$:~/code/linux/git$ git stash apply stash@{1}
On branch master
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: hello.c no changes added to commit (use "git add" and/or "git commit -a")
$:~/code/linux/git$ git diff
diff --git a/hello.c b/hello.c
index e69de29..bdc92a5 100644
--- a/hello.c
+++ b/hello.c
@@ -0,0 +1,2 @@
+void func1(void) {printf("this is func1");}
+void main(void) {return func1();}

可见git stash apply可以将列表中任何一次修改应用到当前工作区,我们再次git stash list一把:

$:~/code/linux/git$ git stash list
stash@{0}: WIP on master: 452b08d rename hello as hello.c
stash@{1}: WIP on master: 452b08d rename hello as hello.c

我们发现,虽然func1的修改已经被弹出应用到当前工作区,其修改内容还继续保留在暂存列表,并未丢弃。

当然,我们可以使用git stash drop stash@{1}来丢掉stash@{1}

7、保存时打标记(git stash save)

假设现在我们又开始尝试写func3, 这样越来越多,这样列表会越来越大,你要想搞清楚某次修改对应哪个函数,就要挨个用git stash show看一遍,很麻烦。

那么,这个时候git stash 的save参数就有用了,它可以为这次暂存做个标记,使得你用git stash list的时候显示这些标记,方便你回忆是修改的什么:

$:~/code/linux/git$ vim hello.c
$:~/code/linux/git$ git diff
diff --git a/hello.c b/hello.c
index e69de29..786c214 100644
--- a/hello.c
+++ b/hello.c
@@ -0,0 +1,2 @@
+void func3(void) {printf("this is func3");}
+void main(void) {return func3();}
$:~/code/linux/git$ git stash save "this is func3"
Saved working directory and index state On master: this is func3
HEAD is now at 452b08d rename hello as hello.c
$:~/code/linux/git$ git stash list
stash@{0}: On master: this is func3
stash@{1}: WIP on master: 452b08d rename hello as hello.c
stash@{2}: WIP on master: 452b08d rename hello as hello.c

我们在save后面指定一个字符串,作为提醒,这样在git stash list查看时就能知道每一个代号对应的修改了。

可是由于一次的误操作事件,让小熊瞬间陷入了恐慌;那是一个夜黑风高的夜晚,小熊刚刚改完这几天的bug,还没commit,只是用stash 保存起来了,但是由于小熊的一个手滑操作,本打算删除以前使用stash保存的废弃记录,可是由于手滑的失误让小熊这几天修改的BUG,被删除了;突然小熊一下子就懵了,还好小熊深呼吸一口,沉着稳定的去查找是否有,回退的操作。



问题--修复

小熊,本着忐忑的心情,再百度输入了 git stash 误删 想要找到相关的回退操作文档,果然皇天不负有心人,一篇精彩绝伦的回退操作文档,完美的解决了,小熊这次的手贱操作。

1. 使用git fsck --unreachable命令查找所有unreachable的记录

这条命令会打印出所有不能从任何索引节点访问但是确存在的对象,回车之后我们会看到如下的显示:

如图中所示,大概有三种类型的内容,blobtreecommit。我们这样看的话是看不出任何有用信息的,我们需要另外一条命令将其内容show出来。

记录太多,还好小熊把当时误删除的stash id 记录下来了,可以用删除的stash id直接定位到,那条记录,可以看到是一个commit 类型的记录。

2. 使用git show命令显示记录内容

Shows one or more objects (blobs, trees, tags and commits).

git show后面跟上要show的id,就可以展示这个id所对应的blobtreetagcommit。比如:git show f19aa7d5a056b4f1fe9f30ec86137431d063db57。比较诡异的是,这些id所对应的记录并不是有序的,如果想要找到之前误删的内容,需要我们一条条的去show出这些内容,然后判断是否是要找回的。万幸的是有两点如果能善加利用的话,会起到事半功倍的效果。一是我们只需要关心commit类型的内容,这些才是我们保存过或者提交过的内容;二是show出来的内容是有日期的,我们只要能大概记住误删内容的保存或提交日期,那么找起来自然轻松很多。

3. 使用git stash apply命令恢复记录内容

根据第二步找到我们所要恢复内容的id,使用git stash apply就可以完美治愈本次的手残,命令执行完后会发现,之前的stash内容又在工作区出现了,是不是很有趣呢,git果然是无比强大!这个地方需要注意的是git stash apply只能恢复commit类型的记录,如果使用这条命令来恢复blobtreetag可能会报错。

在使用 git fsck –unreachable 命令输出的很多文件里面,有很多是带有 committree 的标识的,这些可以使用 git stash apply 加标记号进行找回。而 blob 的文件是只能手动拷贝的,或者使用> 输出到指定的路径去 [ 例如git show 302063e31742cbce7c5fdb917edf520183154cc1 > D:\recovery\backup.txt]

小记

git使用了这么久,其实还是有很多东西没能深入了解,比如blobtreetagcommit都代表了什么含义,各有什么作用,查找了一些资料,学习记录一下。

每个object包含三个部分:类型,大小和内容。大小就是指内容的大小,内容取决于对象的类型,有四种类型的对象,也就是上面提到的blobtreetagcommit

  • blob 用来存储文件数据,通常是一个文件
  • tree 有点像一个目录,它管理一些treeblob,就像文件和子目录
  • tag 标签,用来标记某一次的commit
  • commit 只指向一个tree,它用来标记项目某一个特定时间点的状态。它包括一些关于时间点的元数据,比如时间戳、最近一次提交的作者,指向上次提交的指针等等。

git stash 常用命令

  • git stash save "message"
  • git stash list
  • git stash drop <stash@{num}>
  • git stash apply <stash@{num}>

参考链接:

git stash 的一次惊心动魄的误删操作的更多相关文章

  1. git stash 暂存恢复和文件误删恢复

    git commit提交文件,服务器返回本地文件有修改. 1.git stash :暂存本地代码 2.git pull origin develop : 获取远程分支代码 3.git stash po ...

  2. Git Stash紧急处理问题,需要切分支

    在开发过程中,大家都遇到过bug,并且有些bug是需要紧急修复的. 当开发人员遇到这样的问题时,首先想到的是我新切一个分支,把它修复了,再合并到master上. 当时问题来了,你当前正在开发的分支上面 ...

  3. git stash 用法

    git stash用于将当前工作区的修改暂存起来,就像堆栈一样,可以随时将某一次缓存的修改再重新应用到当前工作区. 一旦用好了这个命令,会极大提高工作效率.   直接举例说明: 1.准备工作,首先初始 ...

  4. git stash和git stash pop

    git stash 可用来暂存当前正在进行的工作, 比如想pull 最新代码, 又不想加新commit, 或者另外一种情况,为了fix 一个紧急的bug,  先stash, 使返回到自己上一个comm ...

  5. git stash -u 添加新文件

    git 提交 有新文件执行    git stash -u ------ 如果已经执行git stash,会发现有UNtracked这个单词 说明新文件没有添加进去,此时 执行  git stash ...

  6. 每天一命令 git stash

    git stash  命令是用于保存当前进度的命令.该命令会保存当前工作区的改动.保存的改动是已经跟踪的文件的改动,对于未跟踪的改动stash是不会保存的. git stash 命令常用于分支切换的 ...

  7. git stash提交PR的正确步骤&git squash技术

    1.git stash梳理 1.1git stash的克隆与同步 首先整理下git stash的逻辑是这样 在本地做出了新的修改,提交时显示当前的版本不是最新版本,这时就需要先pull一下自己代码仓库 ...

  8. git merge git pull时候遇到冲突解决办法git stash

    在使用git pull代码时,经常会碰到有冲突的情况,提示如下信息: error: Your local changes to 'c/environ.c' would be overwritten b ...

  9. git stash简介

    原文:http://gitbook.liuhui998.com/4_5.html 一.基本操作 当你正在做一项复杂的工作时, 发现了一个和当前工作不相关但是又很讨厌的bug. 你这时想先修复bug再做 ...

随机推荐

  1. docker入门_image、container相关命令

    docker入门_image.container相关命令 镜像仓库服务.镜像仓库.镜像相关概念 镜像仓库服务:docker镜像仓库服务.阿里云镜像服务 镜像仓库:docker镜像仓库服务中会有很多仓库 ...

  2. 深度剖析text-align家族

    大家好,我是半夏,一个刚刚开始写文的沙雕程序员.如果喜欢我的文章,可以关注 点赞 加我微信:frontendpicker,一起学习交流前端,成为更优秀的工程师-关注公众号:搞前端的半夏,了解更多前端知 ...

  3. 技术分享 | WEB 端常见 Bug 解析

    对于 WEB 产品来说,有一些常见的 Bug,本章节挑选一些比较典型的 Bug 进行举例介绍. UI Bug 页面展示的时候,需要根据长度的边界值去设计用例进行验证.   一般来说都会有超长内容的验证 ...

  4. [AcWing 862] 三元组排序

    点击查看代码 #include <iostream> #include <algorithm> using namespace std; const int N = 1e5 + ...

  5. 再见 FTP/SFTP!是时候拥抱下一代文件传输利器了!

    关注「开源Linux」,选择"设为星标" 回复「学习」,有我为您特别筛选的学习资料~ 两台电脑之间该如何传送档案,其实方法有超多种的,像是 FTP 或透过 SSH 方式来传送档案, ...

  6. Linux 运维工程师面试问答录(推荐阅读)

    一个执着于技术的公众号 本文整理了一些比较常见的 Linux 相关的面试题目,该问答录主要分为基础知识篇和服务器篇.内容主要涉及 Linux 基本原理.常用命令操作.服务器应用等部分的内容. Linu ...

  7. 实战 | 一文带你读懂Nginx反向代理

    一个执着于技术的公众号 前言 在前面的章节中,我们已经学习了nginx基础知识: 给小白的 Nginx 10分钟入门指南 Nginx编译安装及常用命令 完全卸载nginx的详细步骤 Nginx 配置文 ...

  8. XPath语法和lxml模块

    XPath语法和lxml模块 什么是XPath? xpath(XML Path Language)是一门在XML和HTML文档中查找信息的语言,可用来在XML和HTML文档中对元素和属性进行遍历. X ...

  9. intelij idea 好用的插件

    简介 记录一下平时使用的插件 Foldable ProjectView 隐藏目录或文件 One Dark theme 主题比较好用 Gitmoji Plus: Commit Button 在 comm ...

  10. windows获取高精度时间戳 精度100ns

    #include <stdio.h> #include <Windows.h> int main(void){ LARGE_INTEGER ticks,Frequency; Q ...