介绍

Git 精干分支(lean branching)是指使用变基(rebase)的方式同步分支改动,最后使用非快进式合并提交方式合并分支中改动的一种工作流。

Git Fork 客户端介绍了这种工作流:

这里借用 Git Fork 的一张图片展示最终的效果:

Git Fork Lean Branching

更新

  • 2024-11-03 更新关键的服务器限制方法
  • 2022-09-11 初次发布

Git 工作流

面对不同的需求,产生了许多不同的 Git 工作流。

Git Flow

主要用于构建明确版本化的软件,或者需要支持市场中多个版本的软件。

GitHub Flow

主要用于开源软件协作,充分利用了 GitHub 提供的完善的基础设施,即各种网页上的功能(Pull Request、Issues、代码评论等等)。

GitLab Flow

主要用于企业产品发布,通过将不同稳定程度的内容分为不同的分支(production、develop 等等),进行软件的发布。

集成修改

Git 使用分支来管理需要隔离的改动,而将分支集成到主干或开发分支的过程叫作集成。

集成分支中修改有多种方式:合并(merge)、变基(rebase)、压缩(merge –squash)

GitHub 支持使用以上方式合并 Pull Request:

合并

合并支持将分支的改动放到要合并的分支中,默认行为是 fast-forward if possible,意味着如果提交图没有分叉,Git 会使用 fast-forward 快进式方式进行合并;如果提交图有分叉,则会创建一个合并提交,父提交分别是当前分支与要合并的分支,同时将分支中的所有改动放到此次提交中,如果有任何冲突则会提示用户处理。如果想要强制创建一个合并提交,可以指定 merge --no-ff 参数来禁用默认行为。

合并只会产生一次提交,因此只会有一次产生合并冲突的机会。

变基

变基是将特性分支重置到当前分支所在的位置,并将特性分支之前的所有提交重新应用到特性分支中。类似于 reset 之后进行 cherry-pick 操作。

由于变基会将所有提交重新应用一遍,因此会提交多次,就会有多次冲突需要处理。

流程

  1. main 上新建功能分支
  2. 提交修改,尽可能原子化提交修改内容
  3. 同步 main 上的修改,使用变基(rebase on main),并强制推送到服务器
  4. 功能分支完成后合并(merge –no-ff)到 main

上述操作涉及到了 Git 中相对高级复杂的命令,并且实际使用时需要考虑到各种情况,会导致操作变复杂,例如:

  1. 工作目录中存在修改时需要临时将其储藏(stash),在操作完成后将储藏弹出(stash pop)。
  2. 在变基(rebase)时会遇到冲突,需要使用外部的合并工具处理,并且需要能轻易地选择保留某个版本。
  3. 同时冲突处理过程需要支持跳过、中断等操作。
  4. 合并分支到 main 时需要显式指定 --no-ff 参数。

以上所有操作在命令行下输入起来都比较麻烦。

服务器支持

这个流程最关键的就是如何从根本上杜绝错误的合并提交,那么就需要在 Git 服务器上增加对应的检查。

自建服务器

如果使用的是 GitLab 等自己维护的服务器,可以使用 Git 推荐钩子进行管理:

托管服务器

如果使用的是 GitHub、GitLab 等第三方服务器,那么就需要查看官方有没有对应的方案。

GitHub 通过 Rules 来实现此功能:

Require linear history

Enforcing a linear commit history prevents collaborators from pushing merge commits to the branch. This means that any pull requests merged into the protected branch must use a squash merge or a rebase merge. A strictly linear commit history can help teams revert changes more easily. For more information about merge methods, see “About pull request merges.”

Before you can require a linear commit history, your repository must allow squash merging or rebase merging. For more information, see “Configuring pull request merges.”

软件支持

Git Fork 在 2022 年初新增了对于 Git 精干分支工作流的支持,通过 GUI 上几个菜单项就可以实现上述流程中复杂的操作,极大地降低了使用 Git 精干分支工作流的难度,易于让不熟悉 Git 的人快速使用。

截止到 2022-09-11,Git Fork 的 Windows 与 macOS 最新版本都支持此功能。

下面是 Fork 在做相关操作时的命令输出,从中可以了解到整个流程是比较麻烦的,手动操作易出错,而工具集成了正确的工作流可以方便地处理各种情况。

创建功能分支

1
$ git checkout --no-track -b feature-branch xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

同步功能分支

1
2
3
4
5
$ git stash save --message "Fork sync autostash 2022/9/11 10:18:20"
$ git -c core.commentChar=^ rebase refs/remotes/origin/feature-branch
$ git -c core.commentChar=^ rebase refs/remotes/origin/master
$ git push origin refs/heads/feature-branch:refs/heads/feature-branch --force-with-lease --verbose
$ git stash pop stash@{0}

结束功能分支

1
2
$ git checkout master
$ git merge feature-branch --no-ff

自定义流程

在功能分支开发的过程中,推荐同步操作每天上午进行一次:

  1. 同步过程中的冲突都是与昨天其他人的提交产生的,可以快速找到其他人了解如何解决冲突。
  2. 同步完成后需要手动测试功能分支中的内容是否正常,确认正常后可以将当前分支进行存档。
  3. 存档分支名字格式:archives/{featurre-branch-name}/{date}
  4. 存档分支的意义在于每天都有一个可工作的功能分支,如果主干有需要可以随时将其合并到主干进行发布。

自定义命令

Git Fork 增加存档选中分支功能,可以手动创建当前仓库的自定义命令,将其提交到仓库进行同步,这样其他人也可以随时使用此功能。

.fork/custom-commands.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
[
  {
    "name": "Archive selected branch",
    "target": "ref",
    "refTargets": [
      "localbranch"
    ],
    "action": {
      "type": "sh",
      "script": "set -x\r\ndate=$(date '+%Y-%m-%d')\r\ngit branch archives/$name/$date $name",
      "showOutput": false,
      "waitForExit": true
    }
  }
]