前两天写完一篇文章,但是还没有发布,因为一直在调试hexo和next-theme的配置。然后就发生了一件很悲催的事情,执行了git add .
,正准备git commit
时,发现有很多文件不想加到git仓库,也就是在工作区执行了add,但是还没有commit,此时脑袋一热,执行了git reset --hard HEAD
,然后就悲剧了,工作区的修改全没了。
脑袋嗡的一下,敲完回车的那一刹那我就感觉哪里不对了,但为时已晚。
先说一下如果想恢复缓存区里工作区的修改,其实很简单,git status
的提示也很清楚:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ❯ git status On branch main Your branch is up to date with 'origin/main' . Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: _config.next.yml modified: _config.yml new file: source/_posts/一次危险的git操作.md Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: source/_posts/一次危险的git操作.md
注意,这里我已经在git add
后又修改了一次危险的git操作.md
这个文件,这里的修改当然包括删除操作。
此时只需要git restore .
,就可以把工作区恢复到上次add
时的状态,即取消当前工作区的修改。
而如果加上--staged
参数,则把缓存区的内容恢复到工作区,注意此时工作区的修改不会被取消,即如果工作区删除了文件,那执行git restore --staged
是无法恢复的。
要是git add .
后删除了文件,然后又执行了git reset --hard HEAD
,那就无法简单恢复了。
如何能恢复呢?
我在网上一通搜索,某乎上这个帖子 还是提供了很好的参考,但不是很详细。
大致上的解决方法有两个:git reflog
,git fsck --lost-found
。
git reflog
1 2 3 4 5 6 7 8 9 ❯ git reflog ffe9822 (HEAD -> main) HEAD@ {0 }: reset: moving to HEAD ffe9822 (HEAD -> main) HEAD@ {1 }: reset: moving to HEAD ffe9822 (HEAD -> main) HEAD@ {2 }: commit: mofify: 修改配置文件 a8de715 (origin/main) HEAD@ {3 }: commit: add: 添加文章 e3bb4d8 HEAD@ {4 }: reset: moving to HEAD e3bb4d8 HEAD@ {5 }: commit: modify _config d7e58c5 HEAD@ {6 }: Branch: renamed refs/heads/master to refs/heads/main d7e58c5 HEAD@ {8 }: commit (initial): init commit
这个命令,我的理解可以解决丢步的情况,比如reset到了更早的commit,把那之后的commit给整没了,此时可以用这个命令恢复。
git fsck --lost-found
1 2 3 4 5 6 7 8 ❯ git fsck --lost-found Checking object directories: 100 % (256 /256 ), done. dangling tree 051912 e095bb9d741aeec78c41afc051c6bb98db dangling blob 8 ef0c755cba8a88e113c90f378902f0b7914c32c dangling blob 3 cd9b74819ac88752c9e1a6b3c95b71e0860cdb0 dangling blob dcc06571a9d69efea1046f3f7d2b7e9303aa5509 dangling blob ee5beaf30617b77b02db09b71d7eca0039c8f76c dangling blob f857e88e2aed2cf87dc877ebe3c29b53fd21aaad
看到这个,问题基本就解决了。接下来就是把对应文件进行恢复。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ❯ git show 8 ef0c755cba8a88e113c90f378902f0b7914c32c --- title: 一次危险的git操作 date: 2022 -11-15 14 :03 :34 tags: - git --- 前两天写完一篇文章,但是还没有发布,因为一直在调试hexo和next-theme 的配置。然后就发生了一件很悲催的事情,执行了`git add .`,正准备`git commit`时,发现有很多文件不想加到git仓库,也就是在工作区执行了add,但是还没有commit,此时脑袋一热,执行了`--- title: 一次危险的git操作 date: 2022 -11-15 14 :03 :34 tags: - git --- 前两天写完一篇文章,但是还没有发布,因为一直在调试hexo和next-theme 的配置。然后就发生了一件很悲催的事情,执行了`git add .`,正准备`git commit`时,发现有很多文件不想加到git仓库,也就是在工作区执行了add,但是还没有commit,此时脑袋一热,执行了`git reset --hard HEAD`,然后就悲剧了,工作区的修改全没了。 脑袋嗡的一下,敲完回车的那一刹那我就感觉哪里不对了,但为时已晚。 先说一下如果想恢复缓存区里工作区的修改,其实很简单,`git status`的提示也很清楚: ``` powershell ❯ git status On branch main Your branch is up to date with 'origin/main' . Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: _config.next.yml modified: _config.yml new file: source/_posts/一次危险的git操作.md Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) :
不同的文件对应不同的文件和状态,找到自己需要的状态恢复即可。
我当时用这个命令并没有起作用,转而使用的另一个方法。git的所有操作都是增量操作,除非特别刻意地去删除,否则要想让一个文件消失得无影无踪还是有点儿困难的。这个方法是直接去.git/objects
目录查找。参考了这篇文章 。
如果用git bash的话可以使用find
命令进行查找:find .git/objects -type f | xargs ls -lt | sed 30q
,这条命令输出最近add
的30个文件,可以根据情况设置相应的值。Windows中没有find
命令,需要在git bash中执行 。
我使用的是powershell,用fd
命令替代find
:
1 2 3 4 5 6 7 8 9 10 11 12 ❯ fd . '.\.git\objects' -t f --changed-within '2022-11-15' .git\objects\09 \2 c9306cd5b2761048b8020a0802a6a4a068ed7 .git\objects\3 c\d9b74819ac88752c9e1a6b3c95b71e0860cdb0 .git\objects\6 a\36 a8a9c4b02c92684bc67ed65cdd12ee055de3 .git\objects\8 a\c69f0b1d0032ace1e6e18f70d2bfc103591560 .git\objects\8 e\f0c755cba8a88e113c90f378902f0b7914c32c .git\objects\a0\37 eb5a4333bdcc625f9ea4e5df13b0487e470b .git\objects\b8\36 d4b7235fe833fee00ea9278082d5ba4354f7 .git\objects\dc\c06571a9d69efea1046f3f7d2b7e9303aa5509 .git\objects\ee\5 beaf30617b77b02db09b71d7eca0039c8f76c .git\objects\f8\57 e88e2aed2cf87dc877ebe3c29b53fd21aaad .git\objects\ff\e9822967eac8c6e6845fbc133b18a13d11d446
从这里可以看出结果跟上面的git fsck --lost-found
是基本一致的。这个方法需要把结果处理下:.git\objects\09\2c9306cd5b2761048b8020a0802a6a4a068ed7 → 092c9306cd5b2761048b8020a0802a6a4a068ed7,可以使用脚本,也可以重定向到文件然后用编辑器批量修改的方法,然后再用git show
就可以了。
如果文件比较多的话,可以编写脚本
1 2 $f = Get-Content .\files.txt foreach ($l in $f ){git show $l | out-file x$l .txt}
这里用powershell有一个大坑,就是如果文件中有中文的话,得到的文本文件会有乱码。用git bash和cmd则没有这个问题。问题是很容易绕过去解决,但是不找到原因怎么睡得着?因为git show
的输出是没有问题的,所以想来想去,觉得问题应该就是出在powershell的默认编码上,后来一通搜索,在github这个issue 得到了确认。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 ❯ [console ]::inputencoding;[console ]::outputencoding EncodingName : Chinese Simplified (GB2312) WebName : gb2312 HeaderName : gb2312 BodyName : gb2312 Preamble : WindowsCodePage : IsBrowserDisplay : IsBrowserSave : IsMailNewsDisplay : IsMailNewsSave : IsSingleByte : False EncoderFallback : System.Text.InternalEncoderBestFitFallback DecoderFallback : System.Text.InternalDecoderBestFitFallback IsReadOnly : True CodePage : 936 EncodingName : Chinese Simplified (GB2312) WebName : gb2312 HeaderName : gb2312 BodyName : gb2312 Preamble : WindowsCodePage : IsBrowserDisplay : IsBrowserSave : IsMailNewsDisplay : IsMailNewsSave : IsSingleByte : False EncoderFallback : System.Text.InternalEncoderBestFitFallback DecoderFallback : System.Text.InternalDecoderBestFitFallback IsReadOnly : False CodePage : 936
对,问题就出在这,应该跟安装powershell时系统语言有关系。要解决这个问题,建议还是不要修改默认配置,只修改当前控制台的变量就好(只在当前终端生效)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ❯ [console ]::inputencoding = [console ]::outputencoding = [System.Text.Encoding ]::UTF8 ❯ [console ]::inputencoding;[console ]::outputencoding Preamble : BodyName : utf-8 EncodingName : Unicode (UTF-8 ) HeaderName : utf-8 WebName : utf-8 WindowsCodePage : 1200 IsBrowserDisplay : True IsBrowserSave : True IsMailNewsDisplay : True IsMailNewsSave : True IsSingleByte : False EncoderFallback : System.Text.EncoderReplacementFallback DecoderFallback : System.Text.DecoderReplacementFallback IsReadOnly : False CodePage : 65001 Preamble : BodyName : utf-8 EncodingName : Unicode (UTF-8 ) HeaderName : utf-8 WebName : utf-8 WindowsCodePage : 1200 IsBrowserDisplay : True IsBrowserSave : True IsMailNewsDisplay : True IsMailNewsSave : True IsSingleByte : False EncoderFallback : System.Text.EncoderReplacementFallback DecoderFallback : System.Text.DecoderReplacementFallback IsReadOnly : False CodePage : 65001
这时再执行前面的脚本,输出应该就正常了。如果文件不多,直接复制一份出来直接对照文件内容改回原来的文件名也行。
总结
git仓库中还是不要做shift+del
这种操作
git reset --hard
要谨慎
遇到问题别慌,耐心找方法
git的3个区还是要加深理解
学无止境