常见工作流比较
中心化的工作流
中心化的工作流将中央仓库作为项目中所有修改的唯一入口.默认的开发分支叫做master,所有更改都被提交到这个分支。这种工作流不需要 master 之外的其它分支.为了向官方项目发布修改,开发者将他们的本地 master 分支「推送」到中央仓库
例子
#所有人将仓库克隆到本地
git clone ssh://user@host/path/to/repo.git
#在他的本地仓库中,John 可以用标准的 Git 提交流程开发功能:编辑、缓存、提交
git status # 查看仓库状态
git add <some-file> # 缓存一个文件
git commit # 提交一个文件</some-file>
#John 发布了他的功能
git push origin master
Mary as试图发布她的功能
Git 防止 Mary 覆盖官方的修改。她需要将 John 的更新拉取到她的仓库,和她的本地修改整合后,然后重试。
Mary在John的提交之上rebase
git pull --rebase origin master
-rebase 选项告诉 Git,在同步了中央仓库的修改之后,将 Mary 所有的提交移到 master 分支的顶端.
Mary 解决了合并冲突(如果有)
Rebase 的工作是将每个本地提交一个个转移到更新后的 master 分支。也就是说,你可以一个个提交分别解决合并冲突,而不是在一个庞大的合并提交中解决。
如果 Mary 和 John 开发的功能没有关联,rebase的过程不太可能出现冲突。但如果出现冲突时,Git 在当前提交会暂停 rebase
Mary 只需运行一下 git status 就可以发现问题是什么。冲突的文件会出现在未合并路径中.
如果她对结果满意了,和往常一样缓存这些文件,然后让 git rebase 完成接下来的工作:
git add <some-file>
git rebase --continue
就是这样。Git 会继续检查下个提交,对冲突的提交重复这个流程。
你就会回到开始之前的状态:
git rebase --abort
Mary 成功发布了她的分支
git push origin master
Feature 分支的工作流
在你的开发流程中添加功能分支是一个简单的方式,来促进协作和开发者之间的交流。这种封装使得多个开发者专注自己的功能而不会打扰主代码库.它还保证 master 分支永远不会包含损坏的代码,给持续集成环境带来了是很大的好处.
封装功能的开发使得 Pull Request 的使用成为可能,用来启动围绕一个分支的讨论。它给了其他开发者在功能并入主项目之前参与决策的机会。或者,如果你开发功能时卡在一半,你可以发起一个 Pull Request,向同事寻求建议。重点是,Pull Request 使得你的团队在评论其他人的工作时变得非常简单。
如何工作
Feature 分支工作流同样使用中央仓库,master 同样代表官方的项目历史.开发者每次进行新的工作时创建一个新的分支,比如 animated-menu-items(菜单项动画)或 issue-#1061.每个分支都应该有一个清晰、高度集中的目的.
在中央仓库中储存多个 feature 分支不会引出什么问题。当然了,这也是备份每个开发者本地提交的好办法。
Pull Request
除了隔离功能开发之外,分支使得通过 Pull Request 讨论修改成为可能。一旦有人完成了一个功能,他们不会立即将它并入master。他们将 feature 分支推送到中央服务器上,发布一个 Pull Request,请求将他们的修改并入 master。这给了其他开发者在修改并入主代码库之前审查的机会。代码审查是 Pull Request 的主要好处,但他们事实上被设计为成为讨论代码的一般场所.
一旦 Pull Request 被接受了,发布功能的行为和中心化的工作流是一样的。首先,确定你本地的 master
和上游的 master
已经同步。然后,将 feature分支并入 master,将更新的 master 推送回中央仓库。
例子
Mary 开始了一个新功能
#创建新分支:
git checkout -b marys-feature master
一个基于 master、名为 marys-feature 的分支将会被checkout,-b 标记告诉Git在分支不存在时创建它.
git status
git add <some-file>
git commit
Mary 去吃饭了
在她去吃午饭前,将她的分支推送到中央仓库是个不错的想法。这是一种方便的备份.
git push -u origin marys-feature
这个命令将 marys-feature 推送到中央仓库(origin),-u 标记将它添加为远程跟踪的分支。在设置完跟踪的分支之后,Mary 调用不带任何参数的 git push 来推送她的功能.
Mary 完成了她的工作
当 Mary 吃完午饭回来,她完成了她的功能。在并入 master 之前,她需要发布一个 Pull Request,让其他的团队成员知道她所做的工作.
git push
然后,她在她的 Git 界面上发起了一个 Pull Request,请求将 marys-feature 合并进 master,团队成员会收到自动的通知.
Bill 收到了 Pull Request
Bill 收到了 Pull Request,并且查看了 marys-feature。他决定在并入官方项目之前做一些小修改,通过 Pull Request 和 Mary 进行了沟通。
Mary 作了修改
她编辑、缓存、提交、将更新推送到中央仓库。她所有的活动显示在 Pull Request 中.
如果 Bill 想要的话,也可以将 marys-feature 分支 pull 到他自己的本地仓库,继续工作.
Mary 发布了她的功能
git checkout master
#更新合并meger 当前分支
git pull
#更新 marys-feature 分支并合并 meger
git pull origin marys-feature
git push
这个过程导致了一个合并提交。一些开发者喜欢它,因为它是功能和其余代码合并的标志。但,如果你希望得到线性的历史,你可以在执行 merge 之前将功能 rebase 到 master 分支的顶端,产生一个快速向前的合并。
只需点击一下「Merge Pull Request」。如果你的没有的话,它至少在合并之后应该可以自动地关闭 Pull Request。
GitFlow 工作流
GitFlow 工作流是管理功能开发、发布准备、维护的常见模式。
如何工作
和其他工作流一样,开发者在本地工作,将分支推送到中央仓库。唯一的区别在于项目的分支结构。
历史分支
这种工作流使用两个分支来记录项目历史。master
分支储存官方发布历史,develop
分支用来整合功能分支。同时,这还方便了在 master 分支上给所有提交打上版本号标签。
功能分支
每个新功能都放置在自己的分支中,可以在备份/协作时推送到中央仓库。但是,与其合并到 master,功能分支将开发分支作为父分支。当一个功能完成时,它将被合并回 develop。功能永远不应该直接在 master 上交互。
发布分支
一旦 develop分支的新功能足够发布(或者预先确定的发布日期即将到来),你可以从 develop 分支 fork 一个发布分支。这个分支的创建开始了下个发布周期,只有和发布相关的任务应该在这个分支进行,如修复 bug、生成文档等。一旦准备好了发布,发布分支将合并进 master
,打上版本号的标签。另外,它也应该合并回 develop
,后者可能在发布启动之后有了新的进展.
通常我们约定:
- 从 develop 创建分支
- 合并进 master 分支
- 命名规范 release-* or release/*
维护分支
维护或者「紧急修复」分支用来快速给产品的发布打上补丁。这是唯一可以从 master 上 fork 的分支。一旦修复完成了,它应该被并入 master
和 develop 分支
(或者当前的发布分支),master 应该打上更新的版本号的标签。
例子
创建一个开发分支
你要做的第一步是为默认的 master 分支创建一个互补的 develop 分支。最简单的办法是在本地创建一个空的 develop 分支,将它推送到服务器上:
git branch develop
git push -u origin develop
这个分支将会包含项目中所有的历史,而 master 将包含不完全的版本。其他开发者应该将中央仓库克隆到本地,创建一个分支来追踪 develop 分支:
git clone ssh://user@host/path/to/repo.git
git checkout -b develop origin/develop
Mary 和 John 开始了新功能
他们的功能分支都应该基于develop,而不是 master:
git checkout -b some-feature develop
他们都使用「编辑、缓存、提交」的一般约定来向功能分支添加提交:
git status
git add <some-file>
git commit
Mary 完成了她的功能
如果她的团队使用 Pull Request,现在正是发起 Pull Request 的好时候,请求将她的功能并入 develop 分支。否则,她可以向下面一样,将它并入本地的 develop 分支,推送到中央仓库:
### 更新并合并 develop 分支
git pull origin develop
git checkout develop
### 将some-feature分支合并到 develop分支上
git merge some-feature
git push
### 删除some-feature分支
git branch -d some-feature
第一个命令在尝试并入功能分支之前确保 develop 分支已是最新。
Mary 开始准备发布
她新建了一个分支来封装发布的准备工作。这也正是发布的版本号创建的一步:
git checkout -b release-0.1 develop
这个分支用来整理提交,充分测试,更新文档,为即将到来的发布做各种准备。它就像是一个专门用来完善发布的功能分支。不在 develop 分支中的功能将被推迟到下个发布周期。
Mary完成了她的发布
一旦发布准备稳妥,Mary 将它并入 master
和 develop
,然后删除发布分支.
git checkout master
# 将 release-0.1 分支合并到master
git merge release-0.1
git push
git checkout develop
#将 release-0.1 分支合并到develop
git merge release-0.1
git push
# 删除 release-0.1 分支
git branch -d release-0.1
发布分支是功能开发(develop)和公开发布(master)之间的过渡阶段。不论什么时候将提交并入 master 时,你应该为提交打上方便引用的标签
git tag -a 0.1 -m "Initial public release" master
git push --tags
Git 提供了许多钩子,即仓库中特定事件发生时被执行的脚本。当你向中央仓库推送 master 分支或者标签时,你可以配置一个钩子来自动化构建公开发布。
终端用户发现了一个 bug
正式发布之后,Mary 回过头来和 John 一起为下一个发布开发功能。这时,一个终端用户开了一个 issue 抱怨说当前发布中存在一个 bug。为了解决这个 bug,Mary(或 John)从 master 创建了一个维护分支,用几个提交修复这个 issue,然后直接合并回 master。
git checkout -b issue-#001 master
# Fix the bug
git checkout master
git merge issue-#001
git push
git checkout develop
git merge issue-#001
git push
git branch -d issue-#001
Fork 工作流
它给予 每个 开发者一个服务端仓库。也就是说每个贡献者都有两个 Git 仓库.
Fork 工作流的主要优点在于贡献可以轻易地整合进项目,而不需要每个人都推送到单一的中央仓库。开发者推送到他们 自己的 服务端仓库,只有项目管理者可以推送到官方仓库。这使得管理者可以接受任何开发者的提交,却不需要给他们中央仓库的权限。它同时也是开源项目理想的工作流。
如何工作
他们 fork 一份官方项目,在服务端创建一份副本。这份新建的副本作为他们私有的公开仓库——没有其他开发者可以在上面推送,在他们创建了服务端副本之后,开发者执行 git clone 操作,在他们的本地机器上复制一份。这是他们私有的开发环境,正如其他工作流中一样。
他们将提交推送到自己的公开仓库——而非官方仓库。然后,他们向主仓库发起一个 Pull Request,让项目维护者知道一个更新做好了合并的准备。如果贡献的代码有什么问题的话,Pull Request 可以作为一个方便的讨论版。
为了将功能并入官方代码库,维护者将贡献者的修改拉取到他们的本地仓库,确保修改不会破坏项目,将它合并到本地的 master 分支,然后将 master 分支推送到服务端的官方仓库。贡献现在已是项目的一部分,其他开发者应该从官方仓库拉取并同步他们的本地仓库
例子
项目维护者初始化了中央仓库
公开的仓库应该永远是裸的,不管它们是否代表官方代码库。公开的仓库应该永远是裸的,不管它们是否代表官方代码库。所以项目维护者应该运行下面这样的命令来设置官方仓库:
ssh user@host
git init --bare /path/to/repo.git
开发者 fork 仓库
接下来,所有开发者需要 fork 官方仓库。你可以用 SSH 到服务器,运行 git clone 将它复制到服务器的另一个地址—— fork 其实只是服务端的 clone.GitHub上开发者只需点一点按钮就可以 fork 仓库。
开发者将 fork 的仓库克隆到本地
git clone https://user@github.com/user/repo.git
Fork 工作流需要两个远程连接,一个是中央仓库,另一个是开发者个人的服务端仓库。你可以给这些远端取任何名字,约定的做法是将 origin 作为你 fork 后的仓库的远端(运行 git clone 是会自动创建)和 upstream 作为官方项目。
## 添加中央仓库地址 ,命合为 upstream
git remote add upstream https://github.com/maintainer/repo
它使得你轻易地保持本地仓库和官方仓库的进展同步。
开发者进行自己的开发
git checkout -b some-feature
# 编辑代码后提交
git commit -a -m "Add first draft of some feature"
如果官方项目已经向前进展了,他们可以用 git pull 获取新的提交:
git pull upstream master
因为开发者应该在专门的功能分支开发,这一般会产生一个快速向前的合并。
开发者发布他们的功能
一旦开发者准备好共享他们的新功能,他们需要做两件事情。
第一,他们必须将贡献的代码推送到自己的公开仓库,让其他开发者能够访问到。他们的 origin 远端应该已经设置好了.
git push origin feature-branch
第二,他们需要通知项目维护者,他们想要将功能并入官方代码库。GitHub 提供了一个「New Pull Request」按钮
当项目维护者收到 Pull Request 时,决定是否将它并入官方的代码库,他们可以使用下面两种方式之一:
- 直接检查 Pull Request 中检查代码
- 将代码拉取到本地仓库然后手动合并
如果 Pull Request 会导致合并冲突,第二个选项就有了必要。在这个情况中,维护者需要从开发者的服务端仓库 fetch 功能分支,合并到他们本地的 master 分支,然后解决冲突
#获取 feature-branch 最新的代码
git fetch https://bitbucket.org/user/repo feature-branch
# 检查修改
git checkout master
# 将 feature-branch 合并到 master 中
git merge FETCH_HEAD
一旦修改被整合进本地的 master,维护者需要将它推送到服务器上的官方仓库
git push origin master
开发者和中央仓库保持同步
因为主代码库已经取得了新的进展,其他开发者应该和官方仓库同步:
git pull upstream master