标签: 分支管理

  • “我的代码被同事覆盖了!”——帮群里的同学解决 Git 问题

    “我的代码被同事覆盖了!”——帮群里的同学解决 Git 问题

    本站 2024 年招商持续进行中,欢迎各位老板前来投放:

    https://blog.meathill.com/ad/advertise-on-this-site.html

    刚才,群里有位同学提问:

    我把我的分支合到 B 分支上了,然后另外一个同事将他的代码合到 B 分支将我的代码覆盖了,然后 B 分支又有很多提交记录,不能回退了,我的分支再合到 B 分支不显示冲突和修改了

    大佬们,这种情况怎么解决呀

    可能大家都有过被同事误操作丢失代码的经验吧,群里群情激愤,纷纷谴责起这位同事。也有不少同学七嘴八舌的开始支招,有说找责任人的,有说一个一个还原的,有说 rebase 的。大家很热心,可惜都没说到点子上。

    要用好 Git,绝对不能靠背套路,更不能瞎试,成了也不知道咋成的,败了也不知道为啥不行。要理解 Git 的设计和实现,遇到问题先分析,再寻找最佳方案,然后小心谨慎的实施。

    我们先分析一下问题的原因:

    1. 最大的可能是,同事合并代码时,遇到冲突,他在解决冲突时,选择保留自己的代码,丢弃了同学的代码(最恶心的情况)。
    2. 我们不考虑归责。同学现在要提交自己的代码,但是因为他的代码版本比较“旧”,所以 git 拒绝合并。也无法提交新的 PR。
    3. 如果前面推测没错,那么同事就不是用的 push -f
    4. 所以同学的代码应该有一部分在库里,只是冲突的部分可能没了。

    如此一来,除了一个文件一个文件比对(效率太低),那么就是把所有修改全部找出来,然后做成新的提交。因为部分代码已经在库里,这部分代码大概率会直接被 git 接受;只有少部分之前冲突的代码需要解决之后才能提交,总量应该不多。所以,这该会是最快的方法。

    想清楚该怎么做之后,就该制定方案了:

    1. 先备份代码。
    2. 回退到启动开发时,切出来的分支。假设 commit 是 b1:git reset b1 --mixed。这一步会将 b1 之后的变更,作为未提交的部分,集中起来,排除版本信息。
    3. 保留这部分的代码到暂存区:git stash
    4. 切到开发分支的最新版本:git checkout B
    5. 弹出暂存的代码 git stash pop
    6. 可能会失败,因为存在冲突。那就解决冲突。
    7. 解决冲突后,重新提交 PR,然后合并分支。

    总结一下。Git 原理很简单:

    1. 按行对比。
    2. 每次提交都会产生新版本。
    3. 版本是单向的,Git 负责维护版本历史。
    4. 两个无法分辨先后的版本修改了同一行,就有冲突。Git 无法解决冲突,会交给人处理。
    5. 人处理后,产生新版本。
    6. 分支是指针,指向某个版本,并随提交自动前进。

    在此基础上,可能衍生出各种问题,比如开头的那一幕。但是不管问题如何,都离不开根本原理。所以我们面对 Git 问题,不要背套路,要认真分析前因后果,然后选择解决方案,再根据方案制定流程。

    希望这次分享对大家有帮助。如果你对 Git 操作还有什么不清楚不明白的,欢迎提问。如果我上文有错误,也欢迎指出。希望 2024,大家共同进步,再没有会乱搞别人代码的同事。

  • 2023 Git 必备知识(三):小知识推荐配置与小技巧

    2023 Git 必备知识(三):小知识推荐配置与小技巧

    (本文好像内容较少,我会继续完善。)

    Git 系列文章

    “我们有多个客户,交付给每个客户的功能不一样,要维护几个分支?”

    这是一个很常见的问题,也是大家经常出错的地方。比如我之前待过的某厂,给客户交付部署了 a0 版本。然后我们继续开发,搞出来了 bcd 等功能。客户想增加功能,但是不想多付钱;我们收不到钱,又不想得罪客户,于是跟他们协商,只交付 c 功能。于是,负责版本的同学就 cherry-pick c 功能的每次提交,交付给客户。

    第一次交付比较顺利,后来就越来越麻烦,交付分支和开发分支的差异越来越大,难以维护。

    git 设计用来管理代码的版本,方便我们并行开发、快速迭代。于是它是线性的,基本上是个单向链,我们很难往分支中段插入一个 commit,或者将某个 commit 轻松发布到某几个特定的版本当中。故而,功能模块化和热插拔跟 git 无关,不应该放在一个问题里讨论。

    类似的工作,我们需要使用模块化控制,比如构建脚本等。大部分脚手架工具,比如 webpack、vite 都提供有 define 功能,即定义变量,并注入到最终代码。我们可以利用这个功能,配合环境变量,给功能添加开关,然后在编译阶段进行调整,针对不同的用户,输出包含不同功能的代码。

    以 Vite 为例,大概是这个样子:

    export default defineConfig({
      define: {
        __CLIENT__: process.env.CLIENT,
      },
    });
    const routes = [
      // 通用路由
    ];
    
    // 只有客户是 meathill,才能看到某些路由,配合路由 lazyload,实现打包区分
    if (__CLIENT === 'meathill') {
      routes.push({
        path: '/pay-before-use-feature',
        component: () => import('./pay-before-use-feature.vue');
      })
    } else {
      routes.push({
        path: '/pay-before-use-feature',
        component: SorryYouAreNotAllowed,
      });
    }

    如果你有不同意见的话,可以到 这个问答 里讨论。

    推荐配置

    git config --global pull.ff only

    全局配置 git pull 的时候只使用 fast forward only 模式,即只会在采用快进模式,不会将远程分支通过创建 merge commit 的方式合并到本地。可以避免产生多余的 merge commit

    git config --global core.autocrlf true

    如果你使用 Windows 系统作为主力开发系统,那最好把自动转换换行符的选项钩上,以便和主要用其它系统的同事一起工作。

    Tips

    • GUI 推荐 GitHub Desktop,GitHub 集成很好,其它平台也能用
    • 分支名不要太长,可能导致构建工具失败
    • 依赖变更,翻译更新尽量单独开分支来做,即使跟功能相关的翻译,也要单开分支,这样可以大大减轻 code reviewer 的负担,并且更不容易产生冲突
    • VS Code 处理冲突比较好用,比 JetBrains 系列 IDE 好用
    • rebase 时如果 lock 文件产生冲突,可以直接 git checkout --theirs 回退开发分支的修改,rebase 结束后重新安装即可。

    总结

    至此,我现阶段对 Git 的理解就分享完毕,希望对大家有帮助。未来我收获新知也会继续分享。如果你对 Git 或者版本管理有什么问题或者想法,欢迎留言交流。

  • 2023 Git 必备知识(一):常用团队规范

    2023 Git 必备知识(一):常用团队规范

    我一直以为,时至今日,Git 知识应该已经足够普及,常用操作大家都知道,应该不需要分享。没想到换过几份工作之后,发现还有很多同学不清楚、乱用;一些新出的 git 分享文也写得乱七八糟,槽点满满。于是我决定结合之前在 Code.fun 建立的 Git 规范,和分享过的常见操作整理出来,作为虎年兔年承上启下的文章,贡献给大家。

    Git 分享计划

    本系列计划分成三篇文章:

    Git 使用原则

    1. 保障分布式开发:每个人都在自己的分支上开发;可以随时切换不同分支。
    2. 保障历史记录有价值:只保留有价值的 commit,不必要的 commit 应该在合并前 squash。可以通过回溯 commit message,了解到某行代码的意图,做出合理推断。
    3. 减少未来操作成本:只保留一条历史树,不使用 merge 造成混乱的图谱。以便在发版时不因为和代码耽误时间。

    分支设计

    Code.fun 是一家小厂,采用敏捷开发模式,所以不需要维护太多分支。大体上,我们只维护两大类分支:发布分支:master、dev;开发分支:每个人根据需求切出自己的分支进行开发。

    • 正式版本代码放在 master 分支,并且公开发布上线。
    • 迭代中的代码合并到 dev 分支,可能也会提供 dev 环境给部分喜欢尝鲜的用户。
    • 迭代结束后,将 dev 合并回 master,并发布正式版。
    • 启动新迭代,产生新的 dev 分支。

    单线分支原则

    为了保证版本管理的有效性,我们应尽力保持 只有一条线 的版本历史。为此
    有以下注意事项:

    1. 保证本地 dev 与中心仓库一致。
    2. 开发时,从 dev 分支创建开发分支。
      • 如果需求较大、开发时间较长,建议大家经常 rebase,保持和 dev 同步。
    3. 合并前:
      1. 更新本地的 dev 分支,与中心仓库一致。
      2. 将开发分支 rebase 到最新 dev 分支,即在开发分支上,运行 git rebase dev
    4. 拉代码时,尽量使用 git pull --ff-only,避免出现 merge commit。

    开发规范

    1. 新需求启动时,需要创建一个分支进行开发。如果有多个参与者,可以根据实际情况,选择大家在同一个分支下进行开发,或者基于此分支创建更多的子分支进行开发。
    2. 分支名应该包含用户名,与分支名用 / 分隔,形如:{用户名}/{分支名称}
    3. 其中分支名称应该符合 {前缀}-{需求描述},如 feature-新功能
    4. 常见的前缀有:
      • feat(ure): 新功能
      • (bug)fix: 修复 bug
      • chore:构建过程或辅助工具的变更
      • docs: 文档的变更
      • style: 代码风格的变更
      • ref(actor): 重构
      • test: 测试的变更
      • ver(sion): 版本更新
      • text(ure): 文本的变更
      • deps: 依赖变更,即为适配依赖产生的变更
    5. 提交时,commit message 也需添加合适的前缀,如:feature: 支持点击
    6. 分支应随时推入公司仓库。
    7. 需求开发过程中,应尽早创建 PR。PR 完成前,加 [WIP] 前缀。
    8. 开发完成后,部署到测试环境自测。
      • 请结合各公司不同的基础设施,进行部署和自测。之前我厂主要是 k8s、GitHub Actions 等。
    9. 自测后,邀请产品同学体验,邀请测试同学测试。
    10. 体验+测试通过,请其他同事帮忙做 code review。
    11. 修复问题,code review 通过后,再次更新本地 dev 分支,然后使用 git rebase dev -i,将开发分支变基到最新 dev 分支,并 squash 掉价值较小的 commit,只保留有回顾价值的 commit。
    12. 接下来,建议引入自动化测试,进行回归测试。
    13. 使用 git push -f 将变基后的分支再次推入仓库,准备合并。
    14. 合并分支到 dev。此过程若在 GitHub 上操作,只可以使用“Squash and merge“或”Rebase and merge“,不可以使用“Create a merge commit”。如果只打算保留 一个 commit,就用”Squash“。
    15. 迭代周期结束后,合入 master,等待发版。合入后之前所有的开发分支均作废。

    可以说,Git 是现代化软件开发的基础,各种操作规范都建立在 Git 规范之上,比如 CI/CD、自动化测试、Code Review 等等。学会 Git,采用合理的 Git 流程对稳定发布有很大帮助,建议大家都学好 Git、选用简单有效的 Git 规范构建更好的团队。

    上面的规范不一定适应所有团队,但在我长期的开发实践中,这套规则很好的提升了开发效率、保证了代码质量,推荐给大家参考。

    如果你对上文有问题,或者对 Git 有不同理解,欢迎留言评论。