Git那些事系列:从业务场景到高级技巧的完整指南(三)

文件修改的记录居然没有了!!!

如何隐蔽的把一次修改记录搞丢!

大家好,有到了Git那些事系列了,近期笔者遇到了一个十分奇怪的事情,一个Git仓库的文件被莫名其妙的修改了,问题是,一点修改记录都没有!!!经过仔细的溯源,终于发现原来Git对文件的记录也不是万能,这里做个沉淀,供大家参考

配置文件被异常修改了

在一个岁月静好的一天,笔者使用一个开发分支测试环境做编包和部署,发布的时候突然报错,查看原因是某个配置文件异常,一些部署相关代码被删除了!
当然,这件事情看起来很离谱
但在多人频繁开发的项目团队
也很正常!

Git那些事系列:从业务场景到高级技巧的完整指南(三)_第1张图片

那就查查到底哪次提交修改了?

,顺便问问修改的同学,删除的背景是啥
然后!
居然没有找到该配置代码修改记录!!!
只找到两次该文件异常的修改记录
Git那些事系列:从业务场景到高级技巧的完整指南(三)_第2张图片
如上如所示,在下面红框这里,需要的配置代码还在,但在上面红框里面,配置代码已经没有了
同时,除了这两个红框的提交,中间的提交并没有对该配置代码所在的配置文件进行修改
然而,在上面的红框里面,没有该配置代码的修改变更记录
Git那些事系列:从业务场景到高级技巧的完整指南(三)_第3张图片
也就是说,这个文件的部分代码的变更

在某次提交的后,该文件的部分代码突然被删了,且修改记录没这部分代码

**
Git那些事系列:从业务场景到高级技巧的完整指南(三)_第4张图片
难道是Git的BUG?

到底什么时候会导致Git记录丢失呢?

第一种可能:文件删除后又重新进行添加

因为Git对文件的跟踪是连续的,当删除之后再次添加就会导致Git对该文件的跟踪失效,在这里的场景下,虽然,该文件的部分代码丢失没有记录,但该文件的另一部分修改被记录了(下图),所以,Git对整个文件的跟踪并没有丢失,所以并不是一个删除后又添加的场景
Git那些事系列:从业务场景到高级技巧的完整指南(三)_第5张图片

第二种可能:本地的修改未及时提交,导致被覆盖

这里就是本地的多次修改没有逐项提交,或者完整提交,导致中间部分修改没有修改记录,但在这里中间的修改记录指的是本地的修改记录。对于远端的提交记录来说,它们是连续的。所以也不是这种情况

第三种可能:Git reset强制修改

这是一个相对常用的一个命令,
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那些事系列:从业务场景到高级技巧的完整指南(三)_第6张图片
这篇文章写的比较好:Git Reset 三种模式

使用场景:

hard:1.要放弃目前本地的所有改变時 2.真的想抛弃目标节点后的所有commit
soft:想合并「当前节点」与「reset目标节点」之间不具太大意义的 commit 记录(比如解一个BUG的前前后后的提交,这里如果分支合并的场景用checkout – path效果一样)
mixed:(默认)那些提交还想好好看看,留一些,删一些,改一些,总之就是不确定的情况

**

当然,这个场景并不是无痕的,可以通过git reflog来查看Head或者分支的变化

下面是一个示例:

假设你的 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那些事系列:从业务场景到高级技巧的完整指南(三)_第7张图片

git reset HEAD是用来重置暂存区和工作区的,并没有重置HEAD

第四种可能:force push强制修改

git push --force origin master
这个也是一个偶尔会用到的命令,一般用来填坑,就是把本地的强制覆盖到远端,而远端的一些修改记录(本地没有的修改记录)就没有了,这样也会导致某个文件的修改记录不完善

当然,这个场景也不是无痕的,可以通过git reflog来查看

例如,假设你在本地仓库中执行了以下操作:

$ 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那些事系列:从业务场景到高级技巧的完整指南(三)_第8张图片

第五种可能:Merge的脏提交

代码合并是一个老生常谈的点,也是绝大部分出现问题的根因

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.某次合并时,两个父节点,一个父节点对问题代码有删除,一个父节点对问题代码没有删除
Git那些事系列:从业务场景到高级技巧的完整指南(三)_第9张图片
这种一般是正常的合并

2.某次合并时,两个父节点,两个父节点对问题代码都没有修改,但合并后却删除了,那问题就在这里了Git那些事系列:从业务场景到高级技巧的完整指南(三)_第10张图片
经过笔者多分支一次一次push的回溯,终于找到了这个疑似的合并
本地复现一下:
git checkout 93f13bqqa1 && git merge 8f9c013f8e
Git那些事系列:从业务场景到高级技巧的完整指南(三)_第11张图片
发现该文件确实需要解冲突,但丢失的代码其实不在解决冲突的范围内

那在解冲突的时候,顺手把丢失的代码删除下,是不是就没有记录了?
Git那些事系列:从业务场景到高级技巧的完整指南(三)_第12张图片
解完冲突后,git add和git commit之后,发现删除的diff确实没有了:
Git那些事系列:从业务场景到高级技巧的完整指南(三)_第13张图片
Git那些事系列:从业务场景到高级技巧的完整指南(三)_第14张图片

看来是合并的时候,解决冲突的时候误删了一些代码
好吧

问题解决

这里给出的经验:
1.合并是一个容易出问题的地方
2.合并的冲突解决是一个容易出问题的地方
3.避免冲突的方案是分支快拉快合,避免分支长时间不合入(一周以上)
4.每次合并不要进行大量代码修改,每次拉分支只做一件事
5.每一次记录的修改都能找到问题的点,只是比较麻烦。。。。

你可能感兴趣的:(git,elasticsearch)