有效管理 Git 分支与提交

本文档亦在说明工作中使用 Git 和 GitLab 时,进行提交、推送、合并等 Git 操作的原则。从而达到有效管理分支与提交,维护提交历史的目的。

1 合并与变基

Git 分支分为本地分支和远端分支,每个分支之间都存在相互合并和变基的能力。

合并与变基分为在本地进行,和在远端使用合并请求进行。

  • 本地操作推荐使用 Visual Studio 进行
  • 远端即通过 GitLab 的合并请求进行

1.1 原理

GitLab 的合并请求,分为如下三种方法:

  1. 默认非快进式合并方法,每次合并都会创建一个合并提交。如下图:

    • 目标分支(destination)产生了 C 和 D 两个提交
    • 源分支(source)产生了 K 一个提交,与 C 和 D 的变更没有任何交集
    • source 合并至 destination,除了提交 K,额外产生一个合并提交 L
    • 从分支图上能够看出提交 K 是 source 产生的,K 在 source 的分支线上,C 和 D 在 destination 的分支线上,有一个 source 指向 destination 的合并历史
    %%{init: { 'logLevel': 'debug', 'theme': 'default', 'gitGraph': {'rotateCommitLabel': false, 'mainBranchName': 'destination'}} }%% gitGraph commit id: "A" commit id: "B" branch source checkout destination commit id: "C" commit id: "D" checkout source commit id: "K" checkout destination merge source tag: "L"

    |900

  2. 快进式合并,不会创建合并提交。如下图:

    • 目标分支(destination)产生了 C 和 D 两个提交
    • 源分支(source)产生了 K 一个提交,与 C 和 D 的变更没有任何交集
    • source 变基为 destination
    • source 快进式合并至 destination,仅新增一个提交 K
    • 从分支图中将看不出提交 K 是 source 产生的,C、D、K 都在一条分支线上,观察不到合并历史
    %%{init: { 'logLevel': 'debug', 'theme': 'default', 'gitGraph': {'rotateCommitLabel': false, 'mainBranchName': 'destination source'}} }%% gitGraph commit id: "A" commit id: "B" commit id: "C" commit id: "D" commit id: "K"

    |800

  3. 暂时不对【合并提交与半线性历史记录】进行说明与使用。

1.2 原则

  1. 为观察提交和合并的历史信息,我们采取默认的合并方法:非快进式的合并提交。
  2. 为避免提交和合并历史的繁复混乱,我们对合并操作采取如下限制:
    • 严格控制从远端新建的分支、本地推送到远端的新分支。若发现分支创建的不适宜,采取相应的记过措施,后续将会根据记过情况跟进惩罚措施。
    • 不应无限制地使用合并操作,需遵循下述合并原则。若发现远端产生了不必要的合并提交,采取相应的记过措施。
    • 本地应先拉取,再提交或推送,避免无意义的合并提交。任何发生在本地的无意义合并提交,推送前都有补救措施,所以推送一定要谨慎。若发现远端产生了不必要的合并提交,采取相应的记过措施。
    • 分支不宜过多,提交不宜过多,部分快速变更、临时修复的分支,合并后应当删除。
    • 分支之间不宜来回切换、合并。
  3. 针对同一功能进行代码开发或缺陷修复时使用了不同的源分支(source),向目标分支(destination)合并时存在冲突,一般不介意将 destination 的变更引入 source 分支,则可以先将 source 分支变基到 destination 分支,解决冲突,再进行合并。切记避免将 destination 分支合并到 source 分支,而应当利用变基操作来进行。
    • code -> feature
    • bug -> develop
    • hotfix -> release
  4. 对于发布类分支的合并操作,在本地完成,无需在 GitLab 新建合并请求来进行合并。原因是合并请求主要目的之一是其对评审工作的高效支持,而此类因发布需求所产生的合并无需评审。另外,源分支领先于目标分支而没有任何落后于目标分支的提交,在本地直接合并时不会产生合并提交,从提交与合并历史可以看出,Visual Studio 对此类合并采取了快进式合并。
    • develop -> test
    • develop -> release
  5. 对于主干分支的合并操作,在本地完成,无需在 GitLab 新建合并请求来进行合并。原因是合并请求主要目的之一是其对评审工作的高效支持,而此类主干分支合并无需评审。在本地直接合并,解决合并冲突,同时可能将产生合并提交,Visual Studio 对此类合并采取了非快进式的合并提交。
    • feature -> develop
    • release -> develop

1.3 操作说明

  1. 通过 Visual Studio 对本地仓库执行变基的方法,见参考资料【在 Visual Studio 中管理 Git 存储库】。对于源分支提交未推送远端的情况,变基后可直接进行推送。对于源分支提交已推送远端的情况,变基后的推送需要启用强制推送 --force-with-lease 选项。否则仍然需要一次远端源分支到本地的合并。

  1. 当前不使用 GitLab 合并请求设为快进式合并时,合并请求中所提供的变基功能。
  2. 一般情况下,推送前均需拉取远端最新的变更到本地,或本地变基到远端,才允许推送。当拉取操作和本地提交顺序颠倒,或在本地提交但未推送远端前,远端又有新的变更,导致拉取时出现一个从远端到本地的合并提交。显然 Visual Studio 对此采取了一个非快进式的合并提交。下述步骤说明如何消解此合并提交:
    • (A)产生了一个或多个本地提交(commit)
    • (B)此时应尝试提取(fetch)远端分支最新的变更,若远端存在新的提交,前往步骤(C);若远端没有新的提交,前往步骤(D)
    • (B-1-1)若此时并非提取而直接执行了拉取(pull),并且远端也有未同步到本地的新变更,本地分支将产生一个远端分支至本地分支的合并提交
    • (B-1-2)若已产生合并提交,重置并删除远端拉取下来的更改(reset --hard),包括新产生的合并提交。重置成功后传入、传出、本地历史记录将还原到(B)的状态,此时可以前往步骤(C)
    • (B-2)若此时未拉取,直接尝试推送,提示无法推送,请先拉取最新。切记不要执行【拉取然后推送】。不能且无法【强制推送】。选择【取消】。前往步骤(C)
    • (C)将本地分支变基(rebase)为远端分支
    • (D)将本地分支的提交推送(push)至远端

2 压缩提交

压缩提交的目的是将频繁的微小变动或中间态变更,整合为一个最终形态,减少提交历史中频繁琐碎的小提交个数。

2.1 原则

  1. 若分支上的提交仍在本地,未推送远端,可以对该分支直接进行压缩。若提交已推送远端,不建议对该分支直接进行压缩。原因是远端的提交历史无法被改写,压缩提交的操作只是将所有提交又额外生成另一个提交,包含在分支当中,只会显得杂乱无章。
  2. 若提交已推送远端,需要压缩提交时,建议合并时进行压缩,且需要在合并后删除源分支。
  3. 对于提交已推送远端的分支,压缩提交适用于 Bug、Hotfix、部分 Code 分支,等编写完成后可以随即删除的分支。其不会反复使用,不会与其他分支发生更多的合并(无论作为源分支或目标分支)。否则此分支的提交历史是包含所有小提交的,无法被改写,而目标分支仅包含一个压缩提交。两个分支提交历史对不上,不仅造成不必要的困惑,后续再次发生合并时还会产生不必要的合并冲突,使提交历史更加杂乱无章。
  4. 压缩提交无法压缩合并提交。
  5. 适合进行压缩的提交:
    • 功能开发前期产生的初始化、代码库搭建提交
    • 针对同一功能或 Bug 对同一文件进行的多次调整,如评审意见处理,自己开发过程中产生的纠错变动
    • 为解决冲突所产生的提交

2.2 未推送远端的本地提交

未推送远端的本地提交可以在 Visual Studio 的 git 工具内选中所有相关的最近 n 次提交,右键菜单选中【Squash 提交】进行压缩。输入压缩为 1 次提交后的提交消息,点击【Squash】压缩。

2.3 推送至远端的提交

合并时利用压缩提交,如下图勾选【压缩提交】的复选框,可以自动将源分支的合并进行压缩,即使源分支的多次提交已经推送到了远端。同时应确保勾选【删除源分支】选项,以保证提交历史合理有效。不希望删除的源分支,或不具备压缩条件的提交,禁止勾选【压缩提交】。

3 总结

  1. 本地变更提交前,先拉取远端,获取最新的变更,再进行提交。
  2. 本地提交推送前,先拉取远端,获取最新的变更,再进行推送。
  3. 本地分支产生一些提交是正常的,同时也可以结合暂存(add),或储藏(stash),管理变更内容。但不允许出现远端和本地之间的合并提交,需结合重置、变基、快进式合并等操作保持本地提交历史的干净整洁,再进行推送。推送远端的提交历史实施严格把控,禁止出现不符合规范的分支和提交。
  4. 若不小心先进行了本地提交,后拉取远端,或其他原因导致产生了多余的远端到本地的合并提交。需使用重置并删除更改,消除这些不必要的合并提交。再通过变基来保持本地和远端分支提交历史一致,以便于下一步将本地的提交推送至远端。
  5. 本地完成的变更提交后,不急于推送。待功能具备一定的形态,形成特定版本,或完成某个议题、里程碑,再通过必要的压缩和变基操作,优化提交结构,最终推送至远端。具体如何优化本地的提交结构,压缩提交、分支变基的操作说明请查看文档前述章节。
  6. 对于均已推送远端的分支,其分支之间的合并,根据分支的属性、评审需求,采用不同的策略:
source destination 合并 解决冲突
code feature GitLab 合并请求 Visual Studio 变基 + 强制推送
bug develop GitLab 合并请求 Visual Studio 变基 + 强制推送
hotfix release GitLab 合并请求 Visual Studio 变基 + 强制推送
develop test Visual Studio 快进式
develop release Visual Studio 快进式
feature develop Visual Studio 合并提交 Visual Studio 合并
release develop Visual Studio 合并提交 Visual Studio 合并
  1. 在 GitLab 对合并请求进行合并操作时,根据压缩原则,勾选【压缩提交】的选项进行合并过程中的压缩操作。详见文档第 2 章节。

4 参考资料

posted @ 2023-06-19 16:54  TXRock  阅读(131)  评论(0编辑  收藏  举报