关于使用git进行代码托管下文件的恢复

引言

git是一款很好用的代码托管工具。但是如果发生了误操作,丢失的文件如何找回呢?本小节就通过亲身经历来解决这个问题。


1,情景

产生误操作的如下:

首先,甲和乙两个人都在一个分支上进行代码修改,假设分支为develop。

其次,两人最后一次同步是在2016年1月8号。

在1月12号上午乙已经把自己修改的代码push到了远方服务器上。一切正常。

误操作是甲做的,时间是2016年1月12号下午。操作如下:

1>修改了好多代码,并且一直没有commit!!没有备份!!

2>下午快下班的时候,甲打算把代码提交到服务器上,悲剧即将发生:

1) git pull origin develop,失败,因为乙也修改了代码,直接pull肯定失败。

2) git stash,这一步肯定成功了,因为stash是一种很常见的在不提交本地修改时,临时切换到别的分支的手段。

3) git pull origin develop,由于前面执行了stash,所以这次pull成功了,本地是乙的修改结果。

4) git stashpop,这时部分文件进行了自动merge,但是很多文件都需要手动merge(两个人都修改了相同文件的相同位置)。

5) ls,看了一眼,没过大脑!

6) git checkout --,悲剧在此时发生了!那些需要手工merge的文件全部变成了乙修改的结果,甲修改的所有内容消失了!

7) git push origin develop

8) gitk,发现远方的代码不是自己修改的!!

9) 慌了。。。

10) 之后又执行了一些不痛不痒的操作

 

上面的情景中甲犯了几个严重的错误:

1>长时间没有和乙进行代码同步

2>两个人共同在同一分支,修改相同文件

3>甲没有任何人为备份

4>甲在打算上传代码时,没有先commit一次。

5>甲在晕晕乎乎的情况下执行了要命的git checkout

 

上面5个错误,甲只要少犯其中任何一个都不会造成文件丢失,但是,就是那么寸,全部都发生了。一旦发生上述情况,肿么办?

 

2,解决方法:

1>在执行任何操作之前先将整个git project进行备份!否则后果自负。

2>下面是我写的一个脚本:


关于使用git进行代码托管下文件的恢复_第1张图片


其核心思想是试图通过查找git的object来找到丢失的文件。


3>修改一下脚本中的路径

4>执行脚本

5>到info目录下,执行脚本最后提示的“grep”指令,查找你想恢复的文件名或者module的名字等关键字。

6>分析grep命令的输出信息(文件太多的话,这一步需要点耐心)。

7>一旦找到你想要的文件,确认其对应的哈希值(那一长串数字),

8>到info目录下找到相同文件名的那个文件,里面,就是你想要的文件。

 

需要注意的是,有时候,有些文件会有多个备份,这时去.git/objects目录下查看哪个文件的修改时间最新即可。(ls和find都可以看文件时间戳)

通过上述办法,大部分代码都是可以恢复的,但是如果点儿背的话,就不好说了。

 

9>无论有没有恢复,最后一步都是:记住这次教训!


3,小结

通过努力,终于把丢失的文件恢复过来了,不过还是浪费了本不该浪费的时间。

如果你通过这种方法还没有把文件恢复的话,可以告诉我,咱们一起解决。

附A:

脚本执行完之后的情景:

关于使用git进行代码托管下文件的恢复_第2张图片


某个blob对象的内容:就是某个你千辛万苦改完,刚刚打算提交的文件内容!

关于使用git进行代码托管下文件的恢复_第3张图片


附B:

关于git object,摘录如下:

Git中有四种基本对象类型,组成了Git更高级的数据结构:


blobs
每个blob代表一个(版本的)文件,blob只包含文件的数据,而忽略文件的其他元数据,如名字、路径、格式等。
trees
每个tree代表了一个目录的信息,包含了此目录下的blobs,子目录(对应于子trees),文件名、路径等元数据。因此,对于有子目录的目录,git相当于存储了嵌套的trees。
commits
每个commit记录了提交一个更新的所有元数据,如指向的tree,父commit,作者、提交者、提交日期、提交日志等。每次提交都指向一个tree对象,记录了当次提交时的目录信息。一个commit可以有多个(至少一个)父commits。
tags
tag用于给某个上述类型的对象指配一个便于开发者记忆的名字, 通常用于某次commit。
在.git/objects/中存储了所有的对象,如用命令


find .git/objects -type f
列举出所有对象如下(这里只列四个作为例子)


.git/objects/06/cd8f74b5114222c74d2042b7d7b6a57feb7a74
.git/objects/08/a69220e57387cd1b6207d147be44d54b356af4
.git/objects/0e/e6ff926999e39a1c61c470bc435224a74ba0a5
.git/objects/56/f0a1291c46803558687eeea807e07ae1a861ef


git用SHA1值的前两个数字来分目录存储对象,因此上述四个对象分别为


06cd8f74b5114222c74d2042b7d7b6a57feb7a74
08a69220e57387cd1b6207d147be44d54b356af4
0ee6ff926999e39a1c61c470bc435224a74ba0a5
56f0a1291c46803558687eeea807e07ae1a861ef
用命令:

git cat-file -t
git cat-file -p
可以分别查看对象的值和内容。一般地,使用SHA1值的前面几位(而不是全部)就可以区分不同的对象,因此上述命令中可以用如git cat-file -t 06cd8f。

 查看文件的SHA1值,用命令:
git hash-object file
查看SHA1值对应的对象的内容,

也可用命令:
git show
查看commits,可用命令:
git log --pretty=oneline
这会给出commit的历史记录。每行行首显示了每次提交的SHA1值。然后用:

git show
显示commit的内容。


查看每次commit指向的tree,用命令(如对commit 5ca6fc):
git rev-parse 5ca6fc^{tree}
会显示5ca6fc指向的tree的SHA1。如果想进一步查看tree的内容,用命令:

git show 5ca6fc^{tree}
或者
git cat-file -p 5ca6fc^{tree}
后者输出形如


100644 blob 76018072e09c5d31c8c6e3113b8aa0fe625195ca    file1
100644 blob 5716ca5987cbf97d6bb54920bea6adde242d87e6    file2
100644 blob 10e2d16377371e6436ba3779e62381fdaf8fcfbf    file3
040000 tree edf40c2308b255c4aebc34aa9cce134131778a14    src
可以看出,这个tree包含三个blob和一个子tree。


查看tag,可用:
git tag
列出所有的tag。对于某个tag “v1”,用命令:
git rev-parse v1
查看tag的SHA1值。



你可能感兴趣的:(misc)