Git Workflow学习(一)
本文内容主要翻译自Atlassian公司的GIT Tutorial系列文章。
A Git workflow is a recipe or recommendation for how to use Git to accomplish work in a consistent and productive manner.
Git工作流是关于如何使用 Git 以一致且高效的方式完成工作的秘诀或建议。目前市面上有很多Git工作流,需要根据开发特点选择定制适合团队的Git工作流,从而保证研发协助的高效。
什么是成功的Git工作流
在评估团队的工作流程时,最重要的是考虑团队的文化。我们希望工作流程能够提高团队的效率,而不是成为限制生产力的负担。评估Git工作流时要考虑的一些事项包括:
- 此工作流程是否随团队规模可扩展?
- 使用此工作流程撤消失误和错误是否容易?
- 此工作流程是否强加给团队任何不必要的认知开销?
关于第一个问题,一般团队规模稳定的话,这个不是太大问题。关于第三个问题,我认为这是很重要的问题,可能会导致工作流无法在团队内部被采用。
集中式工作流程
集中式工作流是一个适合团队从集中式版本管理软件(比如SVN,Perforce)过渡到 Git 的工作流。与 Subversion 一样,集中式工作流使用中央存储库(central repository)作为项目所有更改的单一入口点。Git默认开发分支(branch)是main
,所有更改提交到此分支中,不同于SVN的trunk
。这种工作流不需要除main
外的其它分支。
过渡到分布式版本控制系统似乎是一个艰巨任务,但您不必更改现有工作流即可利用 Git。您的团队可以像使用Subversion一样开发项目。
但是,与 SVN 相比,使用 Git 为您的开发工作流提供支持具有一些优势。首先,它为每个开发人员提供了整个项目的本地副本(分布式VCS特色)。这种隔离的环境让每个开发人员都可以独立对项目进行更改,即可以将提交添加到他们的本地存储库,并完全忘记上游开发直到他们需要集成时。
其次,它使你能够使用 Git 强大的分支和合并(merge)模型。与 SVN 不同,Git 分支被设计为一种故障安全机制,用于集成代码和在存储库之间共享更改。集中式工作流与其他工作流类似,它利用开发人员从远程服务器端托管存储库中推送和拉取。与其他工作流相比,集中式工作流没有pull request(PR) 和 forking模式。集中式工作流通常更适合从 SVN 迁移到 Git 的团队以及较小规模的团队。
工作方式
开发人员首先克隆中央存储库。在他们自己的项目本地副本中,他们编辑文件并提交更改,就像使用 SVN 一样。但是,这些新提交更改记录存储在本地,与中央存储库完全隔离。这样一来,开发人员就可以推迟与上游同步,直到他们处于方便时候。
为了发布对官方项目的更改,开发人员将他们的本地分支main推送(push)到中央存储库。这相当于 svn commit
(或者perforce的p4 submit),只是它添加了所有尚未在服务器端中央仓库main
分支中的本地提交。
初始化中央存储库
首先,需要有人在服务器上创建中央存储库。如果是新项目,则可以初始化空存储库。否则,您需要导入现有的 Git 或 SVN 存储库。
中央存储库应始终是裸(bare)存储库(它们不应该有工作目录),可以按如下方式创建:
ssh user@host git init --bare /path/to/repo.git
需要有效的ssh用户名和密码。注意.git
后缀通常附加到存储库名称中,表明它是裸存储库。
托管的中央存储库
中央存储库通常是通过第三方 Git 托管服务(如Bitbucket Cloud、Github或自建服务器)创建的。上面讨论的初始化裸存储库的过程可由托管服务处理。然后,托管服务将提供一个地址,供中央存储库从本地存储库访问。
克隆中央存储库
接下来,每个开发人员创建整个项目的本地副本。这是通过git clone
命令:
git clone ssh://user@host/path/to/repo.git
当你克隆一个仓库时,Git 会自动添加一个名为origin
的快捷方式指向“父”仓库,基于未来会与它交互的假设。
进行更改并提交
在本地克隆存储库后,开发人员可以使用标准的 Git 提交过程进行更改:编辑(edit)、暂存(stage)和提交(commit)。如果您不熟悉暂存区域,这是一种准备提交的方法,无需在工作目录中包含每个更改。这样一来,即使你已经进行了大量的本地更改,也可以创建高度集中的提交。
git status # View the state of the repo
git add <some-file> # Stage a file
git commit # Commit a file</some-file>
请记住,由于这些命令只是创建本地提交,因此你可以根据需要多次重复此过程,而不必担心中央存储库中发生了什么。这对于需要分解为更简单、更多原子块的大型功能非常有用。
将新提交推送到中央存储库
一旦本地存储库提交了新的更改。这些更改需要推送以与项目中的其他开发人员共享。
git push origin main
此命令会将新提交的更改推送到中央存储库。将更改推送到中央存储库时,之前可能推送了来自其他开发人员的更新,其中包含与预期推送更新冲突的代码。Git 将输出一条消息,指示此冲突。在这种情况下,首先需要执行git pull
。
管理冲突
中央仓库代表官方项目,因此其提交历史应被视为神圣的和(前面的提交记录)不变的。如果开发人员的本地提交与中央存储库不同,Git 将拒绝推送他们的更改,因为这会覆盖官方版本。
在开发人员发布其功能之前,他们需要获取(fetch)最新的中心仓库版本,并在其上重新创建(rebase)更改。这就像在说,“我想将我的更改添加到其他人已经完成的工作中。其结果是完美的线性历史记录,就像在传统的 SVN 工作流程中一样。
如果本地更改直接与上游提交冲突,Git 将暂停变基(rebase)过程,并给你一个手动解决冲突的机会。Git 的好处在于它使用相同的命令git status
和git add
来生成提交和解决合并冲突。这使得新开发人员可以轻松管理自己的合并。另外,如果陷入困难,Git 可以很容易地中止整个 rebase 并重试(或寻求帮助)。
示例
下面通过虚拟两个开发人员John和Mary说明集中式工作流场景。
John开发他的功能
在他的本地存储库中,John 可以使用标准的 Git 提交过程开发功能:编辑、暂存和提交。
请记住,由于这些命令只会创建本地提交,因此 John 可以根据需要多次重复此过程,而不必担心中央存储库中发生了什么。
玛丽开发她的功能
与此同时,Mary 正在使用相同的编辑/暂存/提交过程在她自己的本地存储库中开发自己的功能。像 John 一样,她不关心中央存储库中发生了什么,她也不关心John在他的本地存储库中做了什么,因为所有本地存储库都是私有的。
John提交了他的功能更改
一旦 John 完成了他的功能,他应该将他的本地提交发布到中央存储库,以便其他团队成员可以访问它。他可以用git push
命令,如下所示:
git push origin main
请记住origin
是 Git在克隆中央存储库时创建的与中央仓库的远程连接。mian
参数告诉 Git 尝试使origin
的main
分支和他的本地分支一致。由于中央存储库在John克隆后一直没有更新,因此这不会导致任何冲突,push推送将按预期工作。
Mary尝试提交她的功能更改
如果 Mary 在 John 成功将他的更改发布到中央存储库后尝试推送她的功能,会发生什么。她可以使用完全相同的推送命令:
git push origin main
但是,由于她的本地历史记录已经与中央存储库不同,Git 将拒绝请求,并显示一条相当冗长的错误消息:
error: failed to push some refs to '/path/to/repo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
这样可以防止 Mary 覆盖官方提交。她需要将 John 的更新拉到她的本地存储库中,将它们与她的本地更改集成,然后重试。
Mary 在 John 的提交之上重新变基(rebase)
Mary可以使用git pull
将上游(即中央存储库)更改合并到她的存储库中。这个命令有点像svn update
— 它将整个上游提交历史记录拉入 Mary 的本地存储库,并尝试将其与她的本地提交集成:
git pull --rebase origin main
--rebase
选项告诉 Git 在与中央存储库中的更改同步后,将 Mary 的所有提交移动到分支的顶端,如下所示:
如果您忘记了此选项,拉取(pull)仍然有效。但是,每当有人需要与中央存储库同步时,您最终都会遇到多余的“合并提交”。对于此工作流,最好是变基,而不是生成合并提交。
Mary 解决了合并冲突
变基工作原理是将每个本地提交一次性转移到更新的main
分支。这意味着您可以在逐个提交(commit-by-commit)的基础上捕获合并冲突,而不是在一次大规模合并提交中解决所有冲突。这样可以使您的提交尽可能集中,并使项目历史记录清晰明了。反过来,这样可以更容易地找出错误被引入的位置,并在必要时回滚更改,同时将对项目的影响降至最低。
如果 Mary 和 John 正在处理不相关的功能,则变基过程不太可能产生冲突。但如果存在冲突,Git 将在当前提交时暂停 rebase 并输出以下消息,以及一些相关说明:
CONFLICT (content): Merge conflict in <some-file>
Git 的伟大之处在于任何人都可以解决自己的合并冲突。在我们的示例中,Mary 只需运行git status
以查看问题所在。冲突的文件将显示在“未合并的路径”部分:
# Unmerged paths:
# (use "git reset HEAD <some-file>..." to unstage)
# (use "git add/rm <some-file>..." as appropriate to mark resolution)
#
# both modified: <some-file>
然后,她会根据自己的喜好编辑文件。一旦她对结果感到满意,她就可以以正常的方式暂存文件,并让git rebase
完成剩下的:
git add <some-file>
git rebase --continue
这就是它的全部内容。Git 将继续进行下一个提交,并对产生冲突的任何其他提交重复该过程。
如果你走到这一步并意识到你不知道发生了什么,不要惊慌。只需执行以下命令,即可返回到开始的位置:
git rebase --abort
Mary 成功发布了她的功能更改
完成与中央存储库的同步后,Mary 将能够成功发布她的更改:
git push origin main
扩展
正如你所看到的,只使用少量的 Git 命令就可以复制传统的 Subversion 开发环境。这对于将团队从 SVN 过渡非常有用,但它没有利用 Git 的分布式特性。
集中式工作流非常适合小型团队。随着团队规模的扩大,上面详述的冲突解决过程可能会形成瓶颈(中央存储库会频繁的被提交更改)。如果您的团队对集中式工作流感到满意,但希望简化其协作工作,那么绝对值得探索功能分支工作流(Feature Branch Workflow).通过为每个功能指定一个独立的分支,可以在将它们集成到官方项目之前围绕新添加的内容进行深入讨论。
其他常见工作流
集中式工作流本质上是其他 Git 工作流的基础。大多数流行的 Git 工作流都会有某种集中式存储库,各个开发人员将从中推送和拉取。下面我们将简要讨论其他一些流行的 Git 工作流。这些扩展的工作流在管理功能开发、热修复和最终发布的分支方面提供了更专业的模式。
功能分支(Feature branching)
功能分支是集中式工作流的逻辑扩展。背后的核心思想功能分支工作流,所有功能开发都应该在专用分支而不是在main
分支中进行。这种封装使多个开发人员可以轻松地在不干扰主代码库的情况下处理特定功能。这也意味着main
分支永远不应该包含损坏的代码,这对于持续集成环境来说是一个巨大的优势。
Gitflow 工作流
Gitflow工作流首次发表在 2010 年一篇备受推崇的博客文章中(https://nvie.com/posts/a-successful-git-branching-model/)。Gitflow 工作流定义了一个围绕项目版本(project release)设计的严格分支模型。此工作流不增加任何超出功能分支工作流所需内容的新概念或命令。相反,它将非常具体的角色分配给不同的分支,并定义它们应该如何以及何时进行交互。
分叉工作流程(Forking workflow)
Forking工作流与本教程中讨论的其他工作流本质上不同。不同于使用单个服务器端存储库充当“中心”代码库,它为每个开发人员提供了一个服务器端存储库。这意味着每个贡献者拥有不是一个而是两个 Git 存储库:一个私有的本地存储库和一个公共服务器端存储库。
常见于基于Github的开发协助就是该工作流,适合分布式开发。
指导原则
没有一种普适的Git工作流程。如前所述,开发一个提高团队工作效率的Git 工作流非常重要。除了团队文化之外,工作流程还应该与企业文化相辅相成。分支和标签等 Git 功能应该补充企业的发布计划。如果您的团队正在使用任务跟踪项目管理软件您可能希望使用与正在进行的任务相对应的分支。此外,在决定工作流时要考虑的一些准则是:
生存期较短的分支
分支与生产分支分离的时间越长,合并冲突和部署挑战的风险就越高。生存期较短的分支可促进更简洁的合并和部署。
最小化和简化还原
拥有一个有助于主动阻止合并回退的工作流非常重要。例如,在允许将分支合并到main
分支之前对其进行测试的工作流。然而,事故确实会发生。话虽如此,拥有一个允许轻松恢复的工作流程是有益的,不会中断其他团队成员的流程。
匹配发布计划
工作流应补充企业的软件开发发布周期。如果您计划每天发布多次,您将需要保持main
分支稳定。如果您的发布计划不太频繁,您可能需要考虑使用 Git 标记将分支标记为版本。
总结
在本文档中,我们讨论了 Git 工作流。我们通过实际示例深入研究了集中式工作流程。在集中式工作流的基础上,我们讨论了其他专用工作流。本文档的一些关键要点是:
- 没有放之四海而皆准的 Git 工作流
- 工作流程应该简单,并提高团队的生产力
- 你的业务需求塑造你的 Git 工作流程。
原文链接

