写点什么

这些 Git 事故灾难, 你经历过几个?

作者:前夕
  • 2024-04-17
    上海
  • 本文字数:2433 字

    阅读完需:约 8 分钟

前言

关于 Git, 相信大家最常用的就是 pull 和 push. 但随着协作规模的提升, 遇到的问题也会越来越多. 本篇文章并不科普一些命令的详细用法, 更多的是分享在工作中遇到的 Git 场景问题以及踩过的坑

难办? 那就别办咯

先来个开胃小菜. 一般公司都会有个 dev 分支用来部署测试版的功能. 也因为是非正式环境, 所以比较随意, 更新的也很频繁. 每次我们使用 dev 分支之前都需要保证本地的 dev 分支是最新代码.



可怕, 我非常清楚 pull 一下一定都是冲突.



老实人可能还想着如何一个个去解决冲突.



但是为什么要解决呢? 我们又没在 dev 分支上开发, 我们不过是想获得最新版的分支内容, 所以我们完全可以直接把 dev 删了, 然后再切个 dev 就好了. 当一个分支本地不存在的时候, 他就会从远端切.


Revert 的陷阱

业务背景

相信大家对这个命令并不陌生. 当你想要回退某一条 commit 的时候, 可以这样做.


git revert xxx
复制代码


现在我们来看一个场景问题, 今天是你负责合代码. 你的同事告诉你, feat/talk 分支的任务需要上线, 于是你熟练地敲下合并命令



刚刚才执行完, 你的同事就跑来和你说弄错了, 这个分支不应该被合并的. 此时你捏紧了拳头, 非常想揍他. 但是你忍了. 你想到 revert 可以解决这个问题. 于是你找到了合并的 commit id 化解危机.



轻轻松松~ 此刻的你犹如杀敌的将军般英姿飒爽. 而你的同事又跑来和你说, 不好意思又搞错了. 这个任务确实是要上的. 此刻你非常后悔, 后悔刚刚为什么没揍他. 但是这可怎么办呢? 于是你就故技重施.



非常神奇的是, 这次的 merge, 却提示已经是最新代码了. 于是你查看文件变更、log 日志, 都和刚刚 revert 完的一模一样. 卧槽, 鬼打墙了?!

解决方案

问题其实就出在merge这个命令, 你理解错了. merge, 对比的是 2 个分支的 commit. 什么意思呢. 我们分别看看 2 个分支的 commit 有什么不一样


master:



feat/talk:



那么当你想在 master 分支 merge feat/talk 的时候, 是不是 feat/talk 有的 commit 在 master 都已经有了? 虽然你 revert 了, 但是也并没有影响他该有的 commit 还在啊. 所以你 merge 的时候他认为没有新的 commit 产生, 也没毛病.

cherry pick

可以单独 cherry pick 相应的 commit id. 因为 cherry pick 是针对单个提交的操作, 它不会关心或受到该提交是否已被撤销的影响, 它只关注将指定提交的更改重新引入到当前分支.


revert 你的 revert

这句话听起来有点绕, 但是仔细想想应该能理解, 简单说就是负负得正.


冲突多到爆炸

如果你的团队协作规模在 10 个人以上, 则很有可能出现非常复杂的冲突. 根据大家的发版周期、任务持续周期等多个因素影响, 会有各种不同类型的冲突出现. 今天我的粉丝群就有个同学问了这样的一个问题


"远端 master 是 v19 的版本, 而本地的 master 是 v18 的版本, 在一个月前, 从 v18 的 master 切出来任务分支开发功能, 我一共提交了 50 次代码, 现在要同步下 master 的代码, 发现冲突有 300 多个文件. 我要崩溃了!"


莫慌! 解决 300 个文件的冲突确实不太现实. 首先我们把他这个分支情况画出来



大概就是上面这个样子, 远端和本地的 1 号和 2 号是共同的祖先 commit. 但是后面就自立山头了. 图中只画了 4 个不同的节点, 按这位同学的说法, 他提交了 50 个节点, 而远端不确定有多少个, 反正冲突是 300 多个文件.


首先, 这 50 个 commit 我们可以使用 squash 压缩成一个 commit.


squash 的作用就是将多个 commit 合并成一个. 用法不解释了, 有点小麻烦, 百度都有.


那此刻, 我们的图就变成了下面这样



先不说你的新代码改动. 单单说你本地的 v18master 和 origin 的 v19master 绝对都是有冲突的. 参考本文的开头案例, 不需要去合并, 只需要把 v18 删了就行了. 那么你的本地 master 也就保持了最新. 最后, 直接 cherry 5 就可以了.


这里再提另一个思路, 其实这个场景最核心的就是将 50 个 commit 转化为 1 个 commit. 除了 squash 还有个办法, 就是 reset. 可以 reset 到第一次提交的内容, 再次 commit, 也可以实现这个效果.

stash 的阳奉阴违

我们经常会在做完一些改动后, 可能由于各种原因, 需要暂存下代码. 这里就会用到 stash. 你可能以为你的代码修改都会被保存下来. 但并不是这样的, stash 只会保存 modify 的文件, 不会保存 untracked 文件. 说人话就是新建的文件是不会被暂存的. 而实际上, 我们几乎不可能出现只想保存文件变更, 不想保存新建的文件. 所以一定要加上参数**--include-untracked**才可以.


另外, 取出 stash 的内容, 很多人喜欢用 stash pop. 我更推荐你用 stash apply. 区别是 pop 在取出 stash 的内容后会直接删除掉暂存的 stash. 这时候一旦你的更改又出现问题, 你就回不去了. 而 apply 则随时可以放弃所有变更从头再来.

核弹洗地

及时提交 commit, 避免了代码丢失, 也保证了代码片段的独立性. 我们有时候会在编辑代码后发现改了不少东西, 但是这些都是不需要了的, 想要全部放弃掉. 比较常见的情况是给同事写个 demo 临时演示下, 搞完就不需要了. 可以使用以下命令.


git checkout . && git clean -fd
复制代码


前半部分会恢复你的代码变更, 后半部分则会删除新建的文件. 但是这么长的命令谁记得住啊. 所以我建议使用 git 的别名设置


别名的配置用法网上很多, 搜索 git alias


ddd = !git checkout . && git clean -fd
复制代码


为什么我设置的是 3 个 d 呢? 这里想和大家说个趣事儿. 几年前我的名别设置如下


ad = add .d = !git checkout . && git clean -fd
复制代码


因为 discard 这个单词, 所以我把这个命令设置了为单字母 d. 和往常一样, 写完代码我的肌肉记忆就敲出了如下操作


git adgit commit
复制代码


但是当时可能键盘的 a 键有点不灵敏, 就敲出了 git d 并且回车了. 当场心态崩了. 大家可能说现在的 vsc 就算撤销了代码, cmd+z 还是会回来部分代码的. 但是几年前并没有. 而且当时我在开发的任务非常复杂, 我写了一整天, 就这样没了. 最后找回来了吗? 没有, 我重新写了一遍. 之后我就改为了 3 个 d, 确保绝对不可能误触.


如果是现在发生这种情况, 也许 reflog 还能救一下, 也只是也许, 因为防范于未然一定优于亡羊补牢.

总结

以上都是笔者在多年的实际工作中出了事故踩了坑而得出的总结, 不知道你是否也有类似的 git 事故经验呢? 欢迎在评论区分享.




我是前夕, 专注于前端和成长. 公众号: 前夕小课堂



转载需注明出处且不得删减内容

发布于: 刚刚阅读数: 3
用户头像

前夕

关注

专注于前端和成长. 公众号: 前夕小课堂 2019-01-15 加入

还未添加个人简介

评论

发布
暂无评论
这些Git事故灾难, 你经历过几个?_git_前夕_InfoQ写作社区