如何使用Git提高研发团队工作效率?
为什么使用Git
随着互联网时代的来临与发展,尤其分布式开发的大力引入,对于开发工程师来说,代码管理变成了头等难题。10多个人或者更多的成员的研发团队如何管理同一份代码,异地办公如何跟同事有效的维护同一份代码?下面直接介绍Git,就不对Git和其他的版本管理工具进行比较了。
Git属于分布式的版本控制系统,它具有以下特点:
- Git中每个克隆(clone)的版本库都是平等的。你可以从任何一个版本库的克隆来创建属于你自己的版本库,同时你的版本库也可以作为源提供给他人,只要你愿意。
- Git的每一次提取操作,实际上都是一次对代码仓库的完整备份。
- 提交完全在本地完成,无须别人给你授权,你的版本库你作主,并且提交总是会成功。
- 甚至基于旧版本的改动也可以成功提交,提交会基于旧的版本创建一个新的分支。
- Git的提交不会被打断,直到你的工作完全满意了,PUSH给他人或者他人PULL你的版本库,合并会发生在PULL和PUSH过程中,不能自动解决的冲突会提示您手工完成。
- 冲突解决:在需要的时候才进行合并和冲突解决。
- Git版本库统一放在服务器中
- 可以为 Git 版本库进行授权:谁能创建版本库,谁能向版本库PUSH,谁能够读取(克隆)版本库,即权限配置
- 团队的成员先将服务器的版本库克隆到本地;并经常的从服务器的版本库拉(PULL)最新的更新;
- 团队的成员将自己的改动推(PUSH)到服务器的版本库中,当其他人和版本库同步(PULL)时,会自动获取改变
- 你完全可以在脱离Git服务器所在网络的情况下,如移动办公/出差时,照常使用代码库
- 你只需要在能够接入Git服务器所在网络时,PULL和PUSH即可完成和服务器同步以及提交
选择适合团队的工作流
分布式工作流程:
同传统的集中式版本控制系统(CVCS)不同,Git 的分布式特性使得开发者间的协作变得更加灵活多样。在集中式系统中,每个开发者就像是连接在集线器上的节点,彼此的工作方式大体相像。而在 Git 中,每个开发者同时扮演着节点和集线器的角色——也就是说,每个开发者既可以将自己的代码贡献到其他的仓库中,同时也能维护自己的公开仓库,让其他人可以在其基础上工作并贡献代码。由此,Git 的分布式协作可以为你的项目和团队衍生出种种不同的工作流程,接下来的章节会介绍几种利用了 Git 的这种灵活性的常见应用方式。我们将讨论每种方式的优点以及可能的缺点;你可以选择使用其中的某一种,或者将它们的特性混合搭配使用。
Git 提供的有以下三种工作流程
- 集中式工作流
- 集成管理者工作流
- 司令官与副官工作流
目前我们团队使用的是最简单的方式,集中式工作流程,随着研发团队的壮大可能会选择使用第二种,下面我们分别介绍下三种工作流
1.集中式工作流
集中式系统中通常使用的是单点协作模型——集中式工作流。一个中心集线器,或者说仓库,可以接受代码,所有人将自己的工作与之同步。若干个开发者则作为节点——也就是中心仓库的消费者——并且与其进行同步。
这意味着如果两个开发者从中心仓库克隆代码下来,同时作了一些修改,那么只有第一个开发者可以顺利地把数据推送回共享服务器。第二个开发者在推送修改之前,必须先将第一个人的工作合并进来,这样才不会覆盖第一个人的修改。这和 Subversion (或任何 CVCS)中的概念一样,而且这个模式也可以很好地运用到 Git 中。如果在公司或者团队中,你已经习惯了使用这种集中式工作流程,完全可以继续采用这种简单的模式。只需要搭建好一个中心仓库,并给开发团队中的每个人推送数据的权限,就可以开展工作了。Git 不会让用户覆盖彼此的修改。例如 John 和 Jessica 同时开始工作。 John完成了他的修改并推送到服务器。接着 Jessica 尝试提交她自己的修改,却遭到服务器拒绝。她被告知她的修改正通过非快进式(non-fast-forward)的方式推送,只有将数据抓取下来并且合并后方能推送。这种模式的工作流程的使用非常广泛,因为大多数人对其很熟悉也很习惯。当然这并不局限于小团队。利用 Git 的分支模型,通过同时在多个分支上工作的方式,即使是上百人的开发团队也可以很好地在单个项目上协作。
2.集成管理者工作流
Git 允许多个远程仓库存在,使得这样一种工作流成为可能:每个开发者拥有自己仓库的写权限和其他所有人仓库的读权限。这种情形下通常会有个代表``官方''项目的权威的仓库。要为这个项目做贡献,你需要从该项目克隆出一个自己的公开仓库,然后将自己的修改推送上去。接着你可以请求官方仓库的维护者拉取更新合并到主项目。维护者可以将你的仓库作为远程仓库添加进来,在本地测试你的变更,将其合并入他们的分支并推送回官方仓库。这一流程的工作方式如下所示:
- 项目维护者推送到主仓库。
- 贡献者克隆此仓库,做出修改。
- 贡献者将数据推送到自己的公开仓库。
- 贡献者给维护者发送邮件,请求拉取自己的更新。
- 维护者在自己本地的仓库中,将贡献者的仓库加为远程仓库并合并修改。
- 维护者将合并后的修改推送到主仓库。
这是 GitHub 和 GitLab 等集线器式(hub-based)工具最常用的工作流程。人们可以容易地将某个项目派生成为自己的公开仓库,向这个仓库推送自己的修改,并为每个人所见。这么做最主要的优点之一是你可以持续地工作,而主仓库的维护者可以随时拉取你的修改。贡献者不必等待维护者处理完提交的更新——每一方都可以按照自己节奏工作。
3.司令官与副官工作流
这其实是多仓库工作流程的变种。一般拥有数百位协作开发者的超大型项目才会用到这样的工作方式,例如著名的 Linux 内核项目。被称为副官(lieutenant)的各个集成管理者分别负责集成项目中的特定部分。所有这些副官头上还有一位称为司令官(dictator)的总集成管理者负责统筹。司令官维护的仓库作为参考仓库,为所有协作者提供他们需要拉取的项目代码。整个流程看起来是这样的
- 普通开发者在自己的特性分支上工作,并根据master分支进行变基。这里是司令官的master分支。
- 副官将普通开发者的特性分支合并到自己的master分支中。
- 司令官将所有副官的master分支并入自己的master分支中。
- 司令官将集成后的master分支推送到参考仓库中,以便所有其他开发者以此为基础进行变基。
这种工作流程并不常用,只有当项目极为庞杂,或者需要多级别管理时,才会体现出优势。利用这种方式,项目总负责人(即司令官)可以把大量分散的集成工作委托给不同的小组负责人分别处理,然后在不同时刻将大块的代码子集统筹起来,用于之后的整合。
介绍了上面三种工作流,想必你一定有想法使用哪一种了,选择一个适合自己团队的工作流,只要是严格按照这种规则执行的话,相信你的研发团队对于代码的管理一定不会再混乱了,这也会大大提升团队的工作效率。
管理 Git 分支
几乎所有的版本控制系统都以某种形式支持分支。使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线。在很多版本控制系统中,这是一个略微低效的过程——常常需要完全创建一个源代码目录的副本。对于大项目来说,这样的过程会耗费很多时间。 有人把 Git 的分支模型称为它的``必杀技特性'',也正因为这一特性,使得 Git 从众多版本控制系统中脱颖而出。为何 Git 的分支模型如此出众呢? Git 处理分支的方式可谓是难以置信的轻量,创建新分支这一操作几乎能在瞬间完成,并且在不同分支之间的切换操作也是一样便捷。与许多其它版本控制系统不同,Git 鼓励在工作流程中频繁地使用分支与合并,哪怕一天之内进行许多次。理解和精通这一特性,你便会意识到 Git 是如此的强大而又独特,并且从此真正改变你的开发方式。
Git 创建分支
Git 是怎么创建新分支的呢?很简单,它只是为你创建了一个可以移动的新的指针。比如,创建一个 testing
分支,你需要使用git branch
命令:
$ git branch testing
这会在当前所在的提交对象上创建一个指针
那么,Git 又是怎么知道当前在哪一个分支上呢?也很简单,它有一个名为HEAD
的特殊指针。请注意它和许多其它版本控制系统(如 Subversion 或 CVS)里的HEAD
概念完全不同。在 Git 中,它是一个指针,指向当前所在的本地分支。在本例中,你仍然在master
分支上。因为git branch
命令仅仅创建一个新分支,并不会自动切换到新分支中去。
你可以简单地使用git log
命令查看各个分支当前所指的对象。提供这一功能的参数是--decorate
。
$ git log --oneline --decoratef30ab (HEAD, master, testing) add feature #32 - ability to add new 34ac2 fixed bug #1328 - stack overflow under certain conditions 98ca9 initial commit of my project
正上,当前master
和testing
分支均指向校验和以f30ab
开头的提交对象。
分支切换
要切换到一个已存在的分支,你需要使用git checkout
命令。我们现在切换到新创建的testing
分支去:
$ git checkout testing
这样HEAD就指向testing分支了。
那么,这样的实现方式会给我们带来什么好处呢?现在不妨再提交一次:
$ vim test.rb $ git commit -a -m 'made a change'
如上图所示,你的testing
分支向前移动了,但是master
分支却没有,它仍然指向运行git checkout
时所指的对象。这就有意思了,现在我们切换回master
分支看看:
$ git checkout master
这条命令做了两件事。一是使 HEAD 指回master分支,二是将工作目录恢复成master分支所指向的快照内容。也就是说,你现在做修改的话,项目将始于一个较旧的版本。本质上来讲,这就是忽略testing分支所做的修改,以便于向另一个方向进行开发。
现在我们稍微再做些修改并commit
:
$ vim test.rb $ git commit -a -m 'made other changes'
现在,这个项目的提交历史已经产生了分叉。因为刚才你创建了一个新分支,并切换过去进行了一些工作,随后又切换回 master
分支进行了另外一些工作。上述两次改动针对的是不同分支:你可以在不同分支间不断地来回切换和工作,并在时机成熟时将它们合并起来。而所有这些工作,你需要的命令只有branch
、checkout
和commit
。
Git 合并分支
接下来咱们举个稍微复杂点的例子,三个分支的分别处理不同的事情,最后合并到一块
首先,我们假设你正在你的项目上工作,并且已经有一些提交, 如下图:
现在,你已经决定要解决你的公司使用的问题追踪系统中的 #53
问题。想要新建一个分支并同时切换到那个分支上,你可以运行一个带有-b
参数的git checkout
命令:
$ git checkout -b iss53 Switched to a new branch "iss53"
它是下面两条命令的简写:
$ git branch iss53 $ git checkout iss53
你继续在 #53
问题上工作,并且做了一些提交。在此过程中,iss53
分支在不断的向前推进,因为你已经检出到该分支(也就是说,你的HEAD
指针指向了iss53
分支)
$ vim index.html $ git commit -a -m 'added a new footer [issue 53]'
当你在iss53
这个分支上正在顺畅的工作,这时候有个特别紧急的问题需要你来修复,那么为了不影响iss53
的正常工作,你需要做的是重新切换到master
分支上来,在master
分支的基础上再创建一个新的分支 hotfix
,然后在hotfix
分支解决紧急的问题。
$ git checkout -b hotfix Switched to a new branch 'hotfix' $ vim index.html $ git commit -a -m 'fixed the broken email address' [hotfix 1fb7853] fixed the broken email address 1 file changed, 2 insertions(+)
这个时候hotfix
分支上的问题彻底解决了,你需要合并到master
分支,并且部署上线, 那么你只需要在master
分支使用git merge
命令就可以了
$ git checkout master $ git merge hotfix Updating f42c576..3a0874c Fast-forward index.html | 2 ++ 1 file changed, 2 insertions(+)
在合并的时候,你应该注意到了"快进(fast-forward
)"这个词。由于当前master
分支所指向的提交是你当前提交(有关 hotfix
的提交)的直接上游,所以 Git 只是简单的将指针向前移动。换句话说,当你试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候,只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward
)”。现在,最新的修改已经在master分支所指向的提交快照中了。
关于这个紧急问题的解决方案发布之后,你准备回到被打断之前时的工作中。然而,你应该先删除hotfix
分支,因为你已经不再需要它了, master
分支已经指向了同一个位置。你可以使用带-d
选项的git branch
命令来删除分支:
$ git branch -d hotfix Deleted branch hotfix (3a0874c).
现在你可以切换回你正在工作的分支继续你的工作,也就是针对iss53
分支
$ git checkout iss53 Switched to branch "iss53" $ vim index.html $ git commit -a -m 'finished the new footer [issue 53]' [iss53 ad82d7a] finished the new footer [issue 53] 1 file changed, 1 insertion(+)
这个时候 #53
问题解决后你就可以把代码合并到master了,操作跟刚才的htofix
分支处理方式一样
$ git checkout master Switched to branch 'master' $ git merge iss53 Merge made by the 'recursive' strategy.index.html | 1 + 1 file changed, 1 insertion(+)
但是这和你之前合并hotfix分支的时候看起来有一点不一样。在这种情况下,你的开发历史从一个更早的地方开始分叉开来(diverged
)。因为,master
分支所在提交并不是iss53
分支所在提交的直接祖先,Git 不得不做一些额外的工作。出现这种情况的时候,Git 会使用两个分支的末端所指的快照(C4和C5)以及这两个分支的工作祖先(C2),做一个简单的三方合并。
和之间将分支指针向前推进所不同的是,Git 将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它。这个被称作一次合并提交,它的特别之处在于他有不止一个父提交。
这个时候如何不需要iss53
分支的话,你也可以删除iss53
分支了
远程分支
以github
为例,目前很多开源项目以及公司的研发项目代码一般都托管在github
,那么就出现了远程仓库,远程分支等这些概念。
下面我们从github
远程仓库clone
下来一份代码
如果你在本地的master分支做了一些工作,然而在同一时间,其他人推送提交到[email protected]:glj1102/git_test.git
并更新了它的master分支,那么你的提交历史将向不同的方向前进。也许,只要你不与 origin 服务器连接,你的origin/master指针就不会移动。
如果要同步你的工作,运行git pull
命令。这个命令是抓取远程分支数据到本地分支,并且更新本地数据库,移动origin/master指针指向新的、更新后的位置。同时你也可以执行 git push
命令把你本地修改的数据提交到远程分支。
实时跟踪和监控团队代码操作记录
我们使用的是 Worktile + github
来管理团队的,平时团队沟通,任务分配都是通过Worktile来做的,那么 Worktile 与github如何关联的呢?
Worktile绑定github代码仓储
在Worktile的后台管理 > 服务管理
中找到github
,点击添加
下一步会让你选择Worktile的一个聊天群组
,之后github的提交记录就会发送到这个群组中:
添加服务之后需要进行一些配置,配置方式有两种,一种是授权模式
,选择仓储,选择事件, 另一种是普通模式
,这种模式Worktile会生成一个Webhook
链接,拿到这个链接可以直接在github仓储中进行Webhook配置:
具体的配置Worktile后台github服务设置中有教程。
Worktile接收github发送的操作记录
根据不配置时选择的事件不同,github会发送不同的记录消息
总结
通过上面对Git的介绍,对于团队管理者来说,如何使用Git来有效的管理团队代码应该会有最基本的概念,那接下来就需要你真正的花时间来实践了。那么对于Git的命令集网上有很多例子的,下面我这里介绍两本Git的基本入门教程:
Git 简易指南 http://www.bootcss.com/p/git-...
Git 图解 http://marklodato.github.io/v...
本文作者:龚林杰
文章来源:Worktile技术博客
文章转载请注明出处。