内容简介:Git是目前最为强大的代码版本管理工具,被开源社区和各大公司所广泛使用。使用Git进行团队协作开发是很便利的一件事情,但是在多人协作的过程中,我们也会面临如何运用好Git的问题。这种情况下,就出现了各种各样的风险总是与利益并存的,使用
Git是目前最为强大的代码版本管理工具,被开源社区和各大公司所广泛使用。使用Git进行团队协作开发是很便利的一件事情,但是在多人协作的过程中,我们也会面临如何运用好Git的问题。这种情况下,就出现了各种各样的 Git Workflow
,而本文将介绍一种基于 rebase
的工作流,这种工作流也是目前开源社区所比较推崇的做法,了解了这种工作流之后可以更好地优化对git的使用、对代码的管理
一、Rebase和Squash
1、Rebase是什么,为什么使用Rebase
rebase
是能够将我们对代码的更改从一个分支集成到另一个分支中的git命令之一(另一个命令是 Merge
)。使用 rebase
的一个风险在于,它会改写 commit
历史,如果操作不当那么会是一种破坏性的操作。
风险总是与利益并存的,使用 rebase
也好处良多,体现在于:
- 能够保持提交记录的清爽,不带来额外的提交历史(而使用
merge
会生成一条merge
的commit
) - 能够保持commit线的线性,保持成一条直线,从而能够容易地看出代码是如何推进的
2、Squash是什么,为什么使用Squash
rebase
可以运行在 “交互(interactive)”
模式下,交互模式下的 rebase
操作允许我们将多次 commit
进行压缩,从而合并成更少的、甚至单一的一次提交。
之所以这么做,是因为在我们有很多 commit
时,在最终合并到 master
分支时,这些 commit
就都会进入 master
的 commit
历史中,那么这会导致 master
中的提交历史不那么可读。
举个例子,假设我们为某个 feature
进行了多次 commit
,每次 commit
都只是做了一点点的更新,那么commit历史看起来会像是这样:
e94d2fb (HEAD -> feat-china) feat: Add Shenzhen 81d2258 feat: Add Guangzhou 1525479 feat: Add Shanghai e8021a2 feat: Add Beijing 40d9fc0 feat: Add headline ce9657d (upstream/master, origin/master, origin/HEAD, master) feat: i18n (#1) ab15f4f feat(i18n): Add support for Japanese a39b290 feat: Add new headline 4774815 Initial commit
我们之所以频繁地提交 commit
,是为了能够及时地存档,以便能够在需要的时候方便地回滚代码。但是为了修复特定问题、开发某个特性的多次小提交,其实是仅仅在当前开发上下文中有意义的,可能我们为此提交了十几个 commit
,但是对于其他同事而言,他们可能就只想知道我们这次做了什么事情。那么通过 Squash
这个操作,我们就能够将这些小 commit
进行合并,组成一个有意义的 commit
。以下是用了 Squash
后的结果:
c27766b (Head -> feat-china) feat: show first-tier cities of China 4774815 (master) Initial commit
二、基本法则
在这套工作流中,有一些重要的法则,即:
- 在fork的仓库上拉分支 每个贡献者都应该
fork
代码仓库并且在该fork
出的仓库上进行开发。通过fork
仓库,就可以拥有独立的工作空间,从而可以放心大胆地进行开发、修改等,对于多人开发而言,还无需事先获得分支权限也能继续开发( Tips: 其实并不建议多人在同一个分支上进行开发,因为这样子很容易导致合并冲突,如果某个feature
需要不止一个人去开发,相比只拉一个feature
分支,把大的feature
拆分为小的可独立工作单元其实是一种更好的实践) - 禁止对一个多人协作的分支进行rebase 这一点 非常重要 ,贡献者只能对自己
fork
仓库的分支进行rebase
,这是因为rebase
会改写commit
历史,是比较危险的一种操作,只有对自己fork
仓库的分支进行rebase
,才不会有任何问题
三、工作角色
在这套工作流中,涉及两个角色:
-
Maintainer(维护者)
:维护者拥有仓库的写权限,他们可对Pull Request
进行Review
来决定通过或者拒绝,并且能创建Git Tag
用于发布 -
Contributor(贡献者)
:贡献者拥有仓库的读和fork
权限,可以查看和创建issue
,也可以提交Pull Request
。贡献者也负责解决合并冲突,此外贡献者仅能推送分支到自己fork
的仓库里
四、准备工作
在开始这套工作流之前,我们需要先做一些起始步骤。首先,需要先 fork
项目仓库(通常原项目仓库惯称为 upstream
,上游仓库);然后,在本地 clone
这个 fork
后的仓库(与此对应的远程仓库惯称为 origin
);之后,在本地的 remote
记录中添加这个 upstream
仓库,具体步骤如下:
-
Fork
上游仓库,如在Github
中可以点击右上角的“Fork”按钮:
- 在本地
clone
这个fork
后的仓库,如:
$ git clone git@github.com:RuphiLau/playgit.git $ cd playgit
- 添加上游仓库
$ git remote add upstream git@github.com:trialground/playgit.git
- 确认信息:通过
git remote -v
确认我们是否成功进行了配置,正常而言需要显示两组记录(一组origin
,一组upstream
),如:
origin git@github.com:RuphiLau/playgit.git (fetch) origin git@github.com:RuphiLau/playgit.git (push) upstream git@github.com:trialground/playgit.git (fetch) upstream git@github.com:trialground/playgit.git (push)
五、工作流
好了,说了这么多前置知识,总算应该开始进入正题了。在工作流中,建议所提交的代码是关联一个 user story(用户故事)
或者一个 issue
的,可以和一些外部系统相关联(如 JIRA
、 VersionOne
),本套工作流的步骤总结如下:
- 第一步:
fetch
上游仓库的更新 - 第二步: 将
upstream/master
分支和本地master
合并 - 第三步: 在本地新建分支
- 第四步: 写代码、提交代码
- 第五步: 再次
fetch
上游仓库的更新(以同步在拉取分支之后upstream/master
中更新的内容) - 第六步: 对分支基于
upstream/master
进行rebase
和squash
,并且解决合并冲突(如果有) - 第七步: 推送分支
- 第八步: 发起一个
Pull Request
,进行Code Review
,Code Review
通过后,合并到master
,此后可以删除本地分支
以下是具体的细节:
1、 fetch
上游仓库的更新
我们开发时,应当基于最新版本的代码库进行开发。国际惯例,我们一般将原始被fork的那个仓库成为 upstream
仓库(上游仓库),通过 fetch
命令,我们可以将 upstream
中的内容获取下来存在本地,即执行:
$ git fetch upstream
示例流程图:
ORIGIN: master: A->B UPSTREAM: master: A->B->C LOCAL: master: A->B origin/master: A->B upstream/master: A->B->C # 获取结果
2、将 upstream/master
分支和本地 master
合并
一般情况下,我们都是先拉取远程分支,然后基于此创建本地新分支,这样子就能够拥有最新的代码。不过在创建本地新分支前,我们可以先执行以下命令:
$ git checkout master $ git merge upstream/master
这样子将会执行一个 快速合并(Fast-Forward)
来让 master
和 upstream/master
指向同一个提交,示例图:
ORIGIN: master: A->B UPSTREAM: master: A->B->C LOCAL: master: A->B->C # 新增了C origin/master: A->B upstream/master: A->B->C
3、创建本地新分支
现在 master
分支是最新的了,因此我们可以基于此来拉新分支:
$ git checkout -b feat-xyz
示例图:
ORIGIN: master: A->B UPSTREAM: master: A->B->C LOCAL: master: A->B->C origin/master: A->B upstream/master: A->B->C feat-xyz: A->B->C # 新增了 feat-xyz 分支
4、编写代码、提交代码
这一步就是开发的主要步骤了,开发过程中我们可以时不时地提交 commit
(当然也要确保 commit
是有意义的),示例图:
ORIGIN: master: A->B UPSTREAM: master: A->B->C LOCAL: master: A->B->C origin/master: A->B upstream/master: A->B->C feat-xyz: A->B->C->D->E->F # 新增了 D、E、F 三次 commit
5、再次拉取代码
编码结束后,在发起一个 Pull Request
合并上游仓库的 master
分支之前,我们需要抓取一些 upstream
里的新提交记录(这是因为对于原仓库而言,我们不是唯一的一个开发者),示例图:
ORIGIN: master: A->B UPSTREAM: master: A->B->C->C2->C3 # 其他人新提交了 C2、C3 两次 commit LOCAL: master: A->B->C origin/master: A->B upstream/master: A->B->C feat-xyz: A->B->C->D->E->F
为了保持和 upstream/master
的同步,我们需要使用 git fetch
:
$ git fetch upstream
拉取之后,示例图:
LOCAL: master: A->B->C origin/master: A->B upstream/master: A->B->C->C2->C3 # 拉取结果 feat-xyz: A->B->C->D->E->F
6、Rebase和Squash
rebase
会改变原 commit
所基于的分支(简称 变基
),并且创建带着新 SHA-1
哈希的新 commit
(与此同时会保留原有提交信息)。而 squash
会将多个 commit
压缩为一个新的 commit
(也可以是多个 commit
)并且带上新的 SHA-1
哈希值。通常情况下,我们会想要对目标合并分支进行 rebase
,当最终创建 Pull Request
的时候,这个 rebase
和 squash
后的分支就会从 origin
仓库的分支进入到 upstream
仓库的master分支里,所以我们需要 rebase upstream/master
,为了执行 squash
操作,我们需要运行交互模式的 rebase
,如下:
$ git rebase --interactive upstream/master
这将会打开默认的编辑器,然后呈现将被 rebase
的 commit
列表,如:
pick 40d9fc0 feat: Add headline pick e8021a2 feat: Add Beijing pick 1525479 feat: Add Shanghai pick 81d2258 feat: Add Guangzhou pick e94d2fb feat: Add Shenzhen # Rebase ce9657d..e94d2fb onto ce9657d (5 commands) # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # d, drop = remove commit # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out
开头即为 commit
列表,而 #
中的内容则是对一些操作的解释。 commit
列表中列出了从旧到新的每次 commit
,然后我们可以使用说明里的命令选择对这些 commit
执行怎样的一个操作,当我们操作完了后,可以保存文件然后退出编辑器。这之后, rebase
就会基于所选择的命令进行处理。
在 rebase
过程中,可能会有合并冲突(比如你和 upstream/master
里都修改了同个文件的同一部分),那么这时候,需要手动解决冲突,步骤如下:
- 通过
git status
查看哪些文件发生了冲突 - 手动解决冲突
- 运行
git add
来暂存文件 - 运行
git rebase --continue
来继续合并过程(不需要用git commit
来解决合并冲突)
rebase
之后的示例图如下:
ORIGIN: master: A->B UPSTREAM: master: A->B->C->C2->C3 LOCAL: master: A->B->C origin/master: A->B upstream/master: A->B->C->C2->C3 feat-xyz: A->B->C->C2->C3->D->E->F # Rebase结果
如果我们还执行了 squash
操作,那么这多次的 commit
会被压缩, git
会提示我们输入 commit message
来作为这次压缩后的 commit message
,所以我们通常需要让这条 message
能够概括多次 commit
的内容,示例图:
LOCAL: master: A->B->C origin/master: A->B upstream/master: A->B->C->C2->C3 feat-xyz: A->B->C->C2->C3->DEF # Squash结果
7、推送分支
为了创建一个 Pull Request
,我们需要将分支推送到 origin
仓库,可以运行:
$ git push --set-upstream origin feat-china
如果你已经推送过你的分支了,并且想要更新它,那么以上的命令会执行失败。这是因此 rebase
改写了 commit
历史,所以不再有一个公共的 commit
,因此需要使用 --force
选项来告诉 git
放弃并覆盖远程分支,即:
$ git push --force origin feat-china
Tips:以上也是为什么我们要在 fork
分支上独立写代码的主要原因
8、发起一个Pull Request
到这里,是时候对上游仓库的 master
分支发起一个来自 origin
仓库 feat-china
分支的 PR
了,通常在我们 push
之后,在 github
中可以看到如下图所示:
这时候点击 “Compare && pull request”
便可进入发起 PR
的流程
ORIGIN: master: A->B feat-xyz: A->B->C->C2->C3->DEF ---- | Pull Request UPSTREAM: | master: A->B->C->C2->C3 <----
进入 PR
创建页面后,我们可以填写对这个 PR
的描述信息,然后点击 Create pull request
便可成功发起 PR
我们可以通过填写 Reviewers
邀请同事进行 Code Review
(与此同时,还可以通过 CI
处理一些流程,比如校验代码格式、判断是否已经 Review
通过,必须完成这项步骤才能合并代码):
在 CI
(如果有)通过和 Code Review
通过后,我们便可以点击 Squash and merge
或者 Rebase and merge
来合并代码到 master
(这里我们推荐 Squash and merge
,可以压缩多次 commit
为一次):
此后,维护者接受PR,代码就会被自动合并到 master
分支里,并且关闭该 PR
。然后,我们也可以做一些后期清理工作:删除本地分支和远程分支(如 feat-china
和 origin/feat-china
),可以执行如下命令:
$ git branch -D feat-china $ git push origin --delete feat-china
最终效果为:
ORIGIN: master: A->B UPSTREAM: master: A->B->C->C2->C3->DEF LOCAL: master: A->B->C origin/master: A->B upstream/master: A->B->C->C2->C3->DEF
六、成果
现在,让我们来看一下这个工作流带来的成果,以下是以上步骤完成后,所呈现出的一个 commit
历史:
怎么样,是不是又干净又清爽呢?I do believe that you'll enjoy it !
参考资料
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 使用webpack4打造自己的前端工作流
- 从0开始使用webpack搭建react工作流
- 如何使用 Vue CLI 3 加速你的开发工作流?
- [小团队自动化](二) Drone CI使用Vault作为凭据存储 —— 打造自己的CI/CD工作流
- 为什么你应该在项目中使用pyenv+Pipenv:为你的Python项目设置超棒的本地开发工作流之秘籍
- [JWFD开源工作流]JWFD开源工作流-矩阵引擎设计初步
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Programming PHP
Rasmus Lerdorf、Kevin Tatroe、Peter MacIntyre / O'Reilly Media / 2006-5-5 / USD 39.99
Programming PHP, 2nd Edition, is the authoritative guide to PHP 5 and is filled with the unique knowledge of the creator of PHP (Rasmus Lerdorf) and other PHP experts. When it comes to creating websit......一起来看看 《Programming PHP》 这本书的介绍吧!