通过 MyFlow 加速
正如你所看到的,Git 工作流只是针对不同用例的解决方案集合。它们的主要区别在于是否基于主干(trunk-based),以及是否对某些方面进行了明确的规定。由于我发现所有的工作流都有一些不足之处,因此我创建了自己的工作流:MyFlow。
MyFlow 是一个轻量级的、基于主干的工作流,基于 PR(Pull Requests)。MyFlow 并不是一种新发明!许多团队已经在以这种方式工作。如果你专注于通过 PR 进行协作,这是一种非常自然的分支和合并方式。我只是给它起了个名字,并且我认为人们会很容易采纳这种方式。
主分支(main branch)
由于 MyFlow 是基于主干的,因此只有一个名为 main
的主分支,并且它应该始终保持干净的状态。main
分支应该始终可以构建,并且在任何时候都能够发布到生产环境。因此,应该通过分支保护规则来保护 main
分支。一个好的分支保护规则应至少包括以下条件:
-
在合并之前要求至少两次 PR 审核
-
当新提交被推送时,撤销过期的 PR 审核
-
要求来自代码所有者的审核
-
在合并之前要求状态检查通过,这包括 CI 构建、测试执行、代码分析和代码检查
-
将管理员包含在限制内
-
允许强制推送
通过 CI 构建的自动化程度越高,您就越能保持分支的干净状态。
所有其他分支总是从 main
分支派生出来。由于 main
是默认分支,您在创建新分支时无需指定源分支。这简化了流程并消除了错误来源。
私有主题分支
如图 11.5 所示,MyFlow 的基本概念:

私有主题分支可以用来处理新功能、文档、错误、基础设施以及其他在您的仓库中的内容。它们是私有的,意味着它们只属于一个特定的用户。其他团队成员可以检出该分支以测试解决方案,但他们不能直接推送更改到该分支,而是必须通过 PR 中的建议来向 PR 的作者提议更改。
为了表明分支是私有的,我建议使用类似 users/
或 private/
的命名约定,这样可以清楚地表明该分支是私有的。我还建议在分支名称中包括问题或错误的标识符(ID)。这使得以后在提交信息中引用它非常方便。一个好的约定示例如下:
users/<username>/<id>_<topic>
要开始处理一个新主题,您可以创建一个新的本地分支,如下所示:
$ git switch -c <branch> main
以下是一个示例:
$ git switch -c users/kaufm/42_new-feature main
> Switched to a new branch 'users/kaufm/42_new-feature'
然后,您可以创建您的第一次修改,提交并推送到服务器。修改内容并不重要——即使只是给某个文件添加一个空格也可以,反正之后可以覆盖它。您可以参见以下示例:
$ git add .
$ git commit
$ git push --set-upstream origin <branch>
这是带有更多信息的前述示例:
$ git add .
$ git commit -m "New feature #42"
$ git push --set-upstream origin users/kaufm/42_new-feature
我使用的是 GitHub 命令行界面(GitHub CLI)(https://cli.github.com/)来与 PR 交互,因为我觉得它比使用网页用户界面(UI)截图更易于阅读和理解。您也可以通过 Web UI 来执行相同操作。 |
创建 PR 并将其标记为草稿,如下所示:
$ gh pr create --fill --draft
这样,团队就知道您正在处理这个主题。通过查看打开的 PR 列表,您可以很快看到团队当前正在处理的主题。
您可以在提交更改时省略 |
现在,您可以开始处理您的主题,并可以充分利用 git 的强大功能。如果您想要将更改添加到之前的提交中,可以使用 --amend
选项,如下所示:
$ git commit --amend
或者,如果您想要将最后三个提交合并为一个提交,可以运行以下命令:
$ git reset --soft HEAD~3
$ git commit
如果您想要将分支中的所有提交合并为一个提交,可以运行以下命令:
$ git reset --soft main
$ git commit
或者,如果您想完全自由地重新排列并压缩所有提交,可以使用交互式 rebase,如下所示:
$ git rebase -i main
要将更改推送到服务器,您可以使用以下命令:
$ git push origin +<branch>
以下是带有分支名称的前述示例:
$ git push origin +users/kaufm/42_new-feature
请注意,分支名称前面的加号(+
)。这会导致强制推送,但仅对特定分支。如果您没有修改分支历史记录,您可以执行常规的 git push 操作,如果您的分支已很好地保护,且知道自己在做什么,常规的强制推送可能会更方便,如下所示:
$ git push -f
如果您已经希望得到团队成员对代码的帮助或意见,您可以在 PR 中的评论里提到他们。如果他们希望提出更改,可以使用 PR 评论中的建议功能。这样,您可以应用这些更改,并确保在执行此操作之前,您的仓库状态是干净的。
每当您觉得自己的工作准备好时,您可以将 PR 的状态从草稿更改为已准备好,并启用自动合并,如下所示:
$ gh pr ready
$ gh pr merge --auto --delete-branch --rebase
我指定了 |
您的审阅者仍然可以在评论中创建建议,并且您可以继续协作。但是,一旦所有批准和自动化检查完成,PR 将自动合并,并且分支将被删除。自动化检查会在 pull_request
触发器上运行,可以包括在隔离环境中安装应用并运行各种测试。
如果您的 PR 已被合并且分支已被删除,您可以通过以下命令清理本地环境:
$ git switch main
$ git pull --prune
这将把您的当前分支切换到 main
,从服务器拉取更改的分支,并删除本地已在服务器上删除的分支。
发布
一旦您的更改被合并到 main
分支,main
上的推送触发器将开始将应用部署到生产环境,无论您是使用环境还是基于环的部署方法。
如果您需要维护多个版本,可以使用标签与 GitHub 发布一起使用(如第 8 章《使用 GitHub Packages 管理依赖》所示)。在工作流中使用发布触发器并部署应用,同时使用 GitVersion 自动生成版本号,示例如下:
$ gh release create <tag> --notes "<release notes>"
例如:
$ gh release create v1.1 --notes "Added new feature"
您还可以利用自动生成发布说明的功能。不幸的是,目前通过 CLI 还无法使用此功能。为了使用这个功能,您必须通过 UI 来创建发布。
由于我们已经按照上游优先原则修复了错误,因此如果不需要进行热修复,实际上没有必要为每个版本创建一个发布分支。创建发布时生成的标签就足够了。
热修复(Hotfix)
如果您需要为旧版本提供热修复,您可以检出标签并创建一个新的热修复分支,操作如下:
$ git switch -c <hotfix-branch> <tag>
$ git push --set-upstream origin <branch>
例如:
$ git switch -c hotfix/v1.1.1 v1.1
$ git push --set-upstream origin hotfix/1.1.1
现在,切换回 main
分支并在正常的主题分支(例如,users/kaufm/666_fix-bug
)中修复错误。接下来,使用 cherry-pick
命令将修复提交应用到热修复分支,操作如下:
$ git switch <hotfix-branch>
$ git cherry-pick <commit SHA>
$ git push
您可以使用要 cherry-pick
的提交的安全哈希算法(SHA)。或者,如果提交是分支的最新提交,您也可以使用分支的名称,操作如下:
$ git switch hotfix/v1.1.1
$ git cherry-pick users/kaufm/42_fix-bug
$ git push
这将 cherry-pick
主题分支的最新提交。图 11.6 显示了如何对旧版本执行热修复:

您还可以先将修复合并到 main
分支,然后再从那里 cherry-pick
提交。这可以确保代码符合所有分支策略。
您也可以将修复 cherry-pick
到一个基于热修复分支的临时分支,并通过另一个 PR 合并该修复。这取决于您的环境复杂度以及 main
和热修复分支之间的差异有多大。
自动化
如果您有一个带有命名约定的工作流,那么有一些命令序列您会经常使用。为了减少打字错误并简化工作流,您可以使用 Git 别名来自动化这些操作。最好的方法是使用您喜欢的编辑器编辑 .gitconfig
文件,如下所示:
$ git config --global --edit
如果 [alias]
部分不存在,您可以添加它,并为别名添加条目,如下所示:
[alias]
mfstart = "!f() { \
git switch -c users/$1/$2_$3 && \
git commit && \
git push --set-upstream origin users/$1/$2_$3 && \
gh pr create --fill --draft; \
};f"
这个别名叫做 mfstart
,它将使用用户名、问题 ID 和主题作为参数,示例如下:
$ git mfstart kaufm 42 new-feature
它会切换到一个新的分支,提交当前索引中的更改,推送到服务器,并创建一个 PR。
您可以引用单个参数($1,$2,…)或使用 $@
引用所有参数。如果您希望命令不管退出代码如何都独立执行,必须使用分号(;
)结束命令。如果您希望下一个命令仅在第一个命令成功时执行,可以使用 &&
。请注意,每行的结尾需要使用反斜杠(\
),这也是您用来转义引号的字符。
您还可以添加 if
语句来分支您的逻辑,例如:
mfrelease = "!f() { \
if [[ -z \"$1\" ]]; then \
echo Please specify a name for the tag; \
else \
gh release create $1 --notes $2; \
fi; \
};f"
或者,您可以将值存储在变量中,以便稍后使用,例如获取当前分支名称(HEAD
指向的分支):
mfhotfix = "!f() { \
head=$(git symbolic-ref HEAD --short); \
echo Cherry-pick $head onto hotfix/$1 && \
git switch -c hotfix/$1 && \
git push --set-upstream origin hotfix/$1 && \
git cherry-pick $head && \
git push; \
};f"
这些只是示例,自动化的实现方式很大程度上取决于您的工作方式,但它是一个非常强大的工具,可以帮助您提高生产力。