Git Workflow学习(二)
功能分支工作流背后的核心思想是,所有功能开发都应该在一个专用的分支中进行,而不是在main
分支中进行。这种封装使多个开发人员可以轻松地在不干扰主代码库的情况下处理特定功能。这也意味着main
分支永远不会包含损坏的代码,这对于持续集成环境来说是一个巨大的优势。
封装功能开发还可以利用拉取请求(pull requests),这是围绕某个分支发起讨论的一种方式。它们让其他开发人员有机会在将功能集成到正式项目之前确认该功能。或者,如果你开发某个功能遇到困难,你可以打开一个拉取请求,向你的同事征求建议。关键是,拉取请求使您的团队可以非常轻松地对彼此的工作发表评论。
Git 功能分支工作流是一个可组合的工作流,可由其他高级 Git 工作流利用。Git Feature Branch Workflow 以分支模型为中心,这意味着它是管理和创建分支的指导框架。其他工作流更侧重于存储库。Git 功能分支工作流可以合并到其他工作流中。Gitflow工作流和Git forking工作流传统上在其分支模型方面使用 Git 功能分支工作流。
工作方式
Feature Branch Workflow 假定一个中央存储库,并且main
表示正式的项目历史记录。开发人员不是直接在本地main
分支上提交,而是在每次开始开发新功能时创建一个新分支。功能分支应具有描述性名称,例如 animated-menu-items 或 issue-#1061。意图是给每个分支一个明确、高度集中的目的。Git 在技术上没有区分main
分支和功能分支,因此开发人员可以编辑、暂存和提交对功能分支的更改。
此外,功能分支可以(并且应该)推送到中央存储库。这样就可以在不接触任何官方代码的情况下与其他开发人员共享功能。由于main
是唯一的“特殊”分支,因此在中央存储库上存储多个功能分支不会造成任何问题。当然,这也是备份每个人的本地提交的便捷方式。以下是功能分支生命周期的演练。
从主分支开始
所有功能分支都是根据项目的最新代码状态创建的。本指南假定main
在分支中维护和更新了这一点。
创建存储库
git checkout main
git fetch origin
git reset --hard origin/main
这会将存储库切换到main
分支,拉取最新的提交并重置存储库的本地副本main
分支以匹配最新版本。
创建新分支
为您处理的每个功能(feature)或问题(issue)使用单独的分支。创建分支后,在本地签出(checkout)它,以便您所做的任何更改都将在该分支上。
git checkout -b new-feature
这将基于main
创建并进入一个名为 new-feature 的分支,并且 -b 标志告诉 Git 创建该分支(如果该分支尚不存在的话)。
更新、添加、提交和推送更改
在此分支上,以通常的方式编辑、暂存和提交更改,并根据需要可以多次提交来构建功能。处理该功能并像使用 Git 时一样进行提交。准备就绪后,推送提交,更新远程中央存储库的功能分支。
git status
git add <some-file>
git commit
将功能分支推送到远程
最好将功能分支推送到中央存储库。这是一个方便的备份,当与其他开发人员协作时,这将使他们能够查看对新分支的提交。
git push -u origin new-feature
此命令将new-feature分支推送到中央存储库(origin),并且 -u 标志将其添加为远程跟踪分支。设置跟踪分支后,后续git push
无需任何参数即可调用,自动将新功能分支推送到中央仓库。若要获取有关新功能分支的反馈,请在存储库管理中创建拉取请求。在PR页面您可以添加审阅者并确保在合并之前一切正常。
解决反馈问题
现在,团队成员可以评论并批准推送的提交。在本地解决他们的评论,提交建议的更改,并将其推送到中央存储库。您的更新将显示在拉取请求PR中。
合并拉取请求
在合并之前,如果其他人对中央存储库进行了更改,则可能需要解决合并冲突。当您的拉取请求获得批准且没有冲突时,您可以将代码添加到main
分支。从 Bitbucket 中的拉取请求合并。
拉取请求
除了隔离功能开发之外,分支还可以通过拉取请求来讨论更改。一旦有人完成了一项功能,他们不会立即将其合并到main
。取而代之的是,他们将功能分支推送到中心服务器,并提交一个拉取请求PR,要求将他们的添加内容合并到main
。这使其他开发人员有机会在更改成为主代码库的一部分之前对其进行审查。
代码审查是拉取请求的一大好处,但它们实际上被设计为一种讨论代码的通用方式。您可以将拉取请求视为专用于特定分支的讨论。这意味着它们也可以在开发过程的早期使用。例如,如果开发人员需要特定功能的帮助,他们所要做的就是提交拉取请求。感兴趣的各方将自动收到通知,他们将能够在相关提交旁边看到问题。
一旦拉取请求被接受,发布功能的实际行为与在集中式工作流相同。首先,您需要确保您的本地main
与上游同步。然后,将功能分支合并到main
,并将更新的main
内容推送回中央存储库。
示例
以下是使用功能分支工作流的示例。该场景是团队对新功能拉取请求进行代码审查的场景。这是该模型可用于的众多目的之一。
Mary 开始了一项新功能开发
在开始开发功能之前,Mary 需要一个独立的分支来工作。她可以使用以下命令请求新分支:
git checkout -b marys-feature main
这里有个前提:本地main分支与中央存储库main分支同步
这将检出一个基于main的marys-feature分支,并且 -b 标志告诉 Git 创建该分支(如果该分支尚不存在)。在此分支上,Mary 以通常的方式编辑、暂存和提交更改,并根据需要使用多个提交来构建她的功能:
git status
git add <some-file>
git commit
玛丽去吃午饭
Mary 在上午的过程中向她的功能添加了一些提交。在她去吃午饭之前,最好将她的功能分支推送到中央存储库。这是一个方便的备份(推送到功能分支不会影响其他人使用中央存储库),但如果 Mary 正在与其他开发人员协作(相同功能开发),这也将允许他们访问她的初始提交。
git push -u origin marys-feature
此命令推送marys-feature到中央存储库(origin),-u 标志将其添加为远程跟踪分支。设置跟踪分支后,Mary 可以在没有任何参数的情况下调用git push
来推送她的功能。
玛丽完成了她的功能开发
当玛丽吃完午饭回来时,她完成了她的功能。在将其合并到main
之前,她需要提交一个拉取请求PR,让团队的其他成员知道她已经完成了。但首先,她应该确保中央存储库有她最近的提交:
git push
然后,她在 Git GUI 中提交拉取请求,要求合并到main
,团队成员将自动收到通知。拉取请求的好处在于,它们在相关提交旁边显示注释,因此很容易提出有关特定更改集的问题。
Bill 收到拉取请求
Bill 收到了拉取请求,并查看了一下marys-feature。他决定在将其集成到正式项目之前进行一些更改,他和 Mary 通过拉取请求进行了一些来回交流。
Mary 做出改变
为了进行更改,Mary 使用与创建功能的第一次迭代完全相同的过程。她编辑、暂存、提交更新并将其推送到中央存储库。她的所有活动都显示在拉取请求中,Bill 仍然可以在此过程中发表评论。
如果他愿意,Bill 可以将marys-feature
拉入他的本地存储库并自己处理它。他添加的任何提交也会显示在拉取请求中。
玛丽发布她的功能更改
一旦 Bill 准备好接受拉取请求,就需要有人将该功能合并到稳定项目中(这可以由 Bill 或 Mary 完成):
git checkout main
git pull
git pull origin marys-feature
git push
此过程通常会导致一个合并提交。一些开发人员喜欢这样,因为它就像是功能与代码库其余部分的符号连接。但是,如果您偏爱线性历史记录,则可以在执行合并之前将feature变基到main
顶端,从而实现快进合并。
一些 GUI 将通过单击“Accept”按钮运行所有这些命令来自动执行拉取请求接受过程。如果你的没有,它至少应该能够在功能分支合并到main
时自动关闭PR。
与此同时,John也在做同样的事情。
当 Mary 和 Bill 正在研究 marys-feature 并在她的拉取请求中讨论它时,John 正在用他自己的 feature 分支做同样的事情。通过将功能隔离到单独的分支中,每个人都可以独立工作,但在必要时与其他开发人员共享更改。
总结
在本文档中,我们讨论了 Git 功能分支工作流。此工作流有助于组织和跟踪专注于业务功能的分支。其他 Git 工作流(如 Git forking工作流和 Gitflow 工作流)以存储库为中心,可以利用 Git 功能分支工作流来管理其分支模型。本文档演示了用于实现 Git 功能分支工作流的高级代码示例和虚构示例。要与功能分支工作流建立的一些关键关联包括:
- 专注于分支模式
- 可由其他面向存储库的工作流利用
- 通过拉取请求和合并评审促进与团队成员的协作
利用git rebase
在功能分支的审查和合并阶段,将创建强制执行功能合并的有凝聚力的 Git 历史记录。功能分支模型是促进团队环境中协作的绝佳工具。
资料来源

