文件修改的记录居然没有了!!!
大家好,有到了Git那些事系列了,近期笔者遇到了一个十分奇怪的事情,一个Git仓库的文件被莫名其妙的修改了,问题是,一点修改记录都没有!!!经过仔细的溯源,终于发现原来Git对文件的记录也不是万能,这里做个沉淀,供大家参考
在一个岁月静好的一天,笔者使用一个开发分支在测试环境做编包和部署,发布的时候突然报错,查看原因是某个配置文件异常,一些部署相关代码被删除了!
当然,这件事情看起来很离谱
但在多人频繁开发的项目团队
也很正常!
,顺便问问修改的同学,删除的背景是啥
然后!
居然没有找到该配置代码修改记录!!!
只找到两次该文件异常的修改记录
如上如所示,在下面红框这里,需要的配置代码还在,但在上面红框里面,配置代码已经没有了
同时,除了这两个红框的提交,中间的提交并没有对该配置代码所在的配置文件进行修改
然而,在上面的红框里面,没有该配置代码的修改变更记录!
也就是说,这个文件的部分代码的变更
因为Git对文件的跟踪是连续的,当删除之后再次添加就会导致Git对该文件的跟踪失效,在这里的场景下,虽然,该文件的部分代码丢失没有记录,但该文件的另一部分修改被记录了(下图),所以,Git对整个文件的跟踪并没有丢失,所以并不是一个删除后又添加的场景
这里就是本地的多次修改没有逐项提交,或者完整提交,导致中间部分修改没有修改记录,但在这里中间的修改记录指的是本地的修改记录。对于远端的提交记录来说,它们是连续的。所以也不是这种情况
这是一个相对常用的一个命令,
git reset [--soft | --mixed | --hard] [HEAD]
简单做下介绍:
git reset HEAD^
# 回退所有内容到上一个版本
git reset HEAD^ hello.php
# 回退 hello.php 文件的版本到上一个版本
git reset 052e
# 回退到指定版本
这里比较有意思的是三个参数:
hard
:重置位置的同时,直接将 working Tree工作目录、 index 暂存区及 repository 都重置成目标Reset节点的內容,所以效果看起来等同于清空暂存区和工作区。
soft
:重置位置的同时,保留working Tree工作目录和index暂存区的内容,只让repository中的内容和 reset 目标节点保持一致,因此原节点和reset节点之间的【差异变更集】会放入index暂存区中(Staged files)。所以效果看起来就是工作目录的内容不变,暂存区原有的内容也不变,只是原节点和Reset节点之间的所有差异都会放到暂存区中。
mixed
:(默认)重置位置的同时,只保留Working Tree工作目录的內容,但会将 Index暂存区 和 Repository 中的內容更改和reset目标节点一致,因此原节点和Reset节点之间的【差异变更集】会放入Working Tree工作目录中。所以效果看起来就是原节点和Reset节点之间的所有差异都会放到工作目录中。
如图所示:
这篇文章写的比较好:Git Reset 三种模式
hard
:1.要放弃目前本地的所有改变時 2.真的想抛弃目标节点后的所有commit
soft
:想合并「当前节点」与「reset目标节点」之间不具太大意义的 commit 记录(比如解一个BUG的前前后后的提交,这里如果分支合并的场景用checkout – path效果一样)
mixed
:(默认)那些提交还想好好看看,留一些,删一些,改一些,总之就是不确定的情况
**
下面是一个示例:
假设你的 Git 仓库有以下提交历史:
commit 1234567 (HEAD -> master)
Author: John Doe <[email protected]>
Date: Mon Jul 26 14:00:00 2021 -0500
Add new feature
commit abcdefg
Author: John Doe <[email protected]>
Date: Fri Jul 23 10:00:00 2021 -0500
Update README.md
commit 9876543
Author: John Doe <[email protected]>
Date: Wed Jul 21 16:00:00 2021 -0500
Initial commit
现在你想回退到上一个提交,使用以下命令:
$ git reset HEAD~1
现在你的提交历史变成了这样:
commit abcdefg (HEAD -> master)
Author: John Doe <[email protected]>
Date: Fri Jul 23 10:00:00 2021 -0500
Update README.md
commit 9876543
Author: John Doe <[email protected]>
Date: Wed Jul 21 16:00:00 2021 -0500
Initial commit
然后你可以使用 git reflog 命令查看提交历史的变化:
$ git reflog
1234567 HEAD@{0}: reset: moving to HEAD~1
abcdefg HEAD@{1}: commit: Update README.md
9876543 HEAD@{2}: commit (initial): Initial commit
在这个示例中,git reflog 显示了 HEAD@{0},它是你最近的操作,即使用 git reset 回退到上一个提交。 HEAD@{1} 是回退之前的提交,即 abcdefg。
回到问题本身在笔者遇到的这个场景下,并没有发现异常的Git reset的操作
git reset HEAD是用来重置暂存区和工作区的,并没有重置HEAD
git push --force origin master
这个也是一个偶尔会用到的命令,一般用来填坑,就是把本地的强制覆盖到远端,而远端的一些修改记录(本地没有的修改记录)就没有了,这样也会导致某个文件的修改记录不完善
例如,假设你在本地仓库中执行了以下操作:
$ git add main.js
$ git commit -m "Add new feature"
$ git push --force
然后,你执行了 git reflog 命令,可以看到类似以下的输出:
$ git reflog
7b1c3f9 (HEAD -> master) HEAD@{0}: push: forced-update
a3b2e8d HEAD@{1}: commit: Add new feature
回到这个问题,根据上一章的截图,也没有看到push --force
的操作
代码合并是一个老生常谈的点,也是绝大部分出现问题的根因
git merge [-n] [--stat] [--no-commit] [--squash] [--[no-]edit]
[-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
[--[no-]allow-unrelated-histories]
[--[no-]rerere-autoupdate] [-m <msg>] [<commit>…]
git merge --abort
git merge --continue
这里的-s,和-X都是一个策略,具体可以参考:Git 合并策略选项和示例
同时,由于Merge中,可能会有冲突,这就导致在这个中间状态如果增加了一部分额外的修改,并不会被记录进来,而且如果额外的修改就在这个冲突的文件里面的话,确实可能会出现,文件有修改记录,但额外代码没有修改记录
针对这次问题,会不会是这个原因呢?
首先,git的操作是比较复杂的,仅仅是看git log是看不出来的。如果要查看这种无记录的修改,得分析下每次push产生的最新版本里,文件是否已经被改坏,从而查到是哪次push有问题
git show abc123:xxx.go
这里的push比较多,而且是多分支的,所以排查起来会比较慢
这里有两种情况
1.某次合并时,两个父节点,一个父节点对问题代码有删除,一个父节点对问题代码没有删除
这种一般是正常的合并
2.某次合并时,两个父节点,两个父节点对问题代码都没有修改,但合并后却删除了,那问题就在这里了
经过笔者多分支一次一次push的回溯,终于找到了这个疑似的合并
本地复现一下:
git checkout 93f13bqqa1 && git merge 8f9c013f8e
发现该文件确实需要解冲突,但丢失的代码其实不在解决冲突的范围内
那在解冲突的时候,顺手把丢失的代码删除下,是不是就没有记录了?
解完冲突后,git add和git commit之后,发现删除的diff确实没有了:
看来是合并的时候,解决冲突的时候误删了一些代码
好吧
问题解决
这里给出的经验:
1.合并是一个容易出问题的地方
2.合并的冲突解决是一个容易出问题的地方
3.避免冲突的方案是分支快拉快合,避免分支长时间不合入(一周以上)
4.每次合并不要进行大量代码修改,每次拉分支只做一件事
5.每一次记录的修改都能找到问题的点,只是比较麻烦。。。。