Git分支工作流的一些笔记

Posted 山河已无恙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Git分支工作流的一些笔记相关的知识,希望对你有一定的参考价值。

写在前面


  • 今天和小伙伴们分享一些Git分支工作流的笔记
  • 学习的原因,希望通过学习了解大型项目的如何使用Git管理
  • 博文为《Pro Git》读书笔记整理
  • 感谢开源这本书的作者和把这本书翻译为中文的大佬们
  • 理解不足小伙伴帮忙指正,书很不错,感兴趣小伙伴可以去拜读

傍晚时分,你坐在屋檐下,看着天慢慢地黑下去,心里寂寞而凄凉,感到自己的生命被剥夺了。当时我是个年轻人,但我害怕这样生活下去,衰老下去。在我看来,这是比死亡更可怕的事。--------王小波


分支开发工作流

大多数的时候,使用Git的开发者都喜欢只在master分支上保留完全稳定的代码,一般为已经发布即将发布的代码。

还有一些名为develop或者next平行分支,被用来做后续开发或者测试稳定性,这些分支不必保持绝对稳定,但是一旦达到稳定状态,它们就可以被合并入master分支了。

类似下的样子,当然这里的分支周期很短

这样,在确保这些已完成的主题分支(短期分支)能够通过所有测试,并且不会引入更多bug之后,就可以合并入主干分支中,等待下一次的发布。

事实上我们刚才讨论的,是随着你的提交而不断右移的指针。稳定分支的指针总是在提交历史中落后一大截,而前沿分支的指针往往比较靠前。

通常把他们想象成流水线(work silos)可能更好理解一点,那些经过测试考验的提交会被遴选到更加稳定的流水线上去。

可以用这种方法维护不同层次的稳定性。一些大型项目还有一个proposed(建议)pu:proposed updates(建议更新)分支,它可能因包含一些不成熟的内容而不能进入next或者master分支。这么做的目的是使你的分支具有不同级别的稳定性

当它们具有一定程度的稳定性后,再把它们合并入具有更高级别稳定性的分支中。通过分支实现的工作流不是必须,但是对于复杂的项目往往很有帮助

主题分支

在master分支上工作到C1,这时为了解决一个问题而新建iss91分支,在iss91分支上工作到C4,然而对于那个问题你又有了新的想法,于是你再新建一个iss91v2分支试图用另一种方法解决那个问题,接着你回到master分支工作了一会儿,你又冒出了一个不太确定的想法,你便在C10的时候新建一个dumbidea分支,并在上面做些实验。你的提交历史看起来像下面这个样子:

现在,我们假设两件事情:你决定使用第二个方案来解决那个问题,即使用在iss91v2分支中方案

另外,你将dumbidea分支拿给你的同事看过之后,结果发现这是个惊人之举。这时你可以抛弃iss91分支(即丢弃C5和C6提交),然后把另外两个分支合并入主干分支。最终你的提交历史看起来像下面这个样子:

$ git checkout master
$ git merge dumbidea
$ git merge dumbidea

当然这么多操作的时候,这些分支全部都存于本地。 当你新建和合并分支的时候,所有这一切都只发生在你本地的 Git 版本库中,没有与服务器发生交互。

远程分支

远程引用是对远程仓库的引用(指针),你可以通过git 1s-remote<remote>来显式地获得远程引用的完整列表,或者通过git remote show <remote>获得远程分支的更多信息。然而,一个更常见的做法是利用远程跟踪分支

远程跟踪分支是远程分支状态的引用。通俗的讲,希望在本地可以看到远程分支的状态,它们是你无法移动的本地引用。一旦你进行了网络通信,Git就会为你移动它们以精确反映远程仓库的状态

远程跟踪分支以<remote>/<branch> 的形式命名。例如,如果你想要看你最后一次与远程仓库origin 通信时master分支的状态,你可以查看origin/master分支。

当你在Github或者Gitlab,Gitee上克隆一个项目,Git的clone命令会为你自动将其命名为origin,拉取它的所有数据,创建一个指向它的master分支的指针,并且在本地将其命名为origin/master。Git也会给你一个与origin的master分支在指向同一个地方的本地master分支,这样你就有工作的基础。

下图上面为远程厂库的分支情况,下面为克隆到本地的情况。

master 是当你运行git init时默认的起始分支名字,原因仅仅是它的广泛使用,origin是当你运行git clone时默认的远程仓库名字。如果你运行git clone -o booyah,那么你默认的远程分支名字将会是booyah/master

如果你在本地的master分支做了一些工作,在同一段时间内有其他人推送提交到 git.ourcompany.com 并且更新了它的master分支,这就是说你们的提交历史已走向不同的方向。即便这样,只要你保持不与origin 服务器连接(并拉取数据),你的origin/master 指针就不会移动。

如果要与给定的远程仓库同步数据,运行git fetch <remote>命令(在本例中为git fetch origin)。这个命令查找“origin”是哪一个服务器(在本例中,它是git.ourcompany.com),从中抓取本地没有的数据,并且更新本地数据库,移动origin/master指针到更新之后的位置。

添加远程仓库到当前Git版本库

为了演示有多个远程仓库与远程分支的情况,我们假定你有另一个内部Git服务器,仅服务于你的某个敏捷开发团队。这个服务器位于git.teaml.ourcompany.com。你可以运行git remote add命令添加一个新的远程仓库引用到当前的项目,将这个远程仓库命名为teamone

现在,可以运行git fetch teamone来抓取远程仓库teamone有而本地没有的数据。因为那台服务器上现有的数据是origin服务器上的一个子集,所以Git并不会抓取数据而是会设置远程跟踪分支teamone/master 指向teamone的master分支

推送本地分支到远程

当你想要公开分享一个分支时,需要将其推送到有写入权限的远程仓库上。如果你在本地新建的分支并做了commit,服务端会有一个申请合并的消息,在我日常的开发中,大都也是以这种方式来提交代码,

本地的分支并不会自动与远程仓库同步—-你必须显式地推送想要分享的分支。这样,你就可以把不愿意分享的内容放到私人分支上,而将需要和别人协作的内容推送到公开分支。

如果希望和别人一起在名为serverfix的分支上工作,你可以像推送第一个分支那样推送它。运行git push<remote><branch>

$ git push origin serverfix
Counting objects: 24, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (24/24), 1.91 KiB | 0 bytes/s, done.
Total 24 (delta 2), reused 0 (delta 0)
To https://github.com/schacon/simplegit
 * [new branch] serverfix -> serverfix

也可以运行git push origin serverfix:serverfix,推送本地的 serverfix分支,将其作为远程仓库的serverfix分支

如果并不想让远程仓库上的分支叫做 serverfix,可以运行git push origin serverfix:awesomebranch 来将本地的serverfix分支推送到远程仓库上的awesomebranch分支

使用推送的远程分支

下一次其他协作者从服务器上抓取数据时 git fetch origin,他们会在本地生成一个远程分支 origin/serverfix,指向服务器的serverfix分支的引用:

$ git fetch origin
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://github.com/schacon/simplegit
 * [new branch] serverfix -> origin/serverfix

要特别注意的一点是当抓取到新的远程跟踪分支时,本地不会自动生成一份可编辑的副本(拷贝)。换一句话说,这种情况下,不会有一个新的serverfix分支——只有一个不可以修改的origin/serverfix指针。

可以运行git merge origin/serverfix将这些工作合并到当前所在的分支

如果想要在自己的serverfix分支上工作可以新建分支在远程跟踪分支之上

$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

这会给你一个用于工作的本地分支serverfix,并且起点位于 origin/serverfix

跟踪分支

从一个远程跟踪分支检出一个本地分支会自动创建所谓的“跟踪分支”(它跟踪的分支叫做“上游分支”)。跟踪分支是与远程分支有直接关系的本地分支。如果在一个跟踪分支上输入git pu11,Git能自动地识别去哪91个服务器上抓取、合并到哪个分支。

当克隆一个仓库时,它通常会自动地创建一个跟踪origin/mastermaster分支。然而,如果你愿意的话可以设置其他的跟踪分支,或是一个在其他远程仓库上的跟踪分支,又或者不跟踪master分支。最简单的实例就是像之前看到的那样,运行git checkout-b <branch><remote>/<branch>

$ git checkout --track origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

这是一个十分常用的操作所以Git 提供了--track快捷方式,该捷径本身还有一个捷径,如果你尝试检处的分支不存在且刚好有一个远程分支与之对应,那么Git就会为你创建一个远跟踪分支。

$ git checkout serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

设置已有的本地分支跟踪一个刚刚拉取下来的远程分支,或者想要修改正在跟踪的上游分支,你可以在任意时间使用-u或--set-upstream-to选项运行git branch来显式地设置。

$ git branch -u origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.

拉取 fetch和pull的区别

git fetch命令从服务器上抓取本地没有的数据时,它并不会修改工作目录中的内容。它只会获取数据然后让你自己合并

git pull在大多数情况下它的含义是一个git fetch紧接着一个git merge命令

由于git pull的魔法经常令人困惑所以通常单独显式地使用fetch与merge命令会更好一些。

删除远程分支

可以运行带有--delete选项的git push命令来删除一个远程分支。 如果想要从服务器上删除 serverfix 分支,运行下面的命令:

$ git push origin --delete serverfix
To https://github.com/schacon/simplegit
 - [deleted] serverfix

基本上这个命令做的只是从服务器上移除这个指针。 Git 服务器通常会保留数据一段时间直到垃圾回收运行,所以如果不小心删除掉了,通常是很容易恢复的。

这一章中,你将会学习如何作为贡献者或整合者,在一个分布式协作的环境中使用Git。你会学习为一个项目成功地贡献代码,并接触一些最佳实践方式,让你和项目的维护者能轻松地完成这个过程。另外,你也会学到如何管理有很多开发者提交贡献的项目。

分布式工作流程

与传统的集中式版本控制系统(CVCS)相反,Git的分布式特性使得开发者间的协作变得更加灵活多样。

在集中式系统中,每个开发者就像是连接在集线器上的节点,彼此的工作方式大体相像。而在分布式Git中,每个开发者同时扮演着节点和集线器的角色,也就是说,每个开发者既可以将自己的代码贡献到其他的仓库中,同时也能维护自己的公开仓库,让其他人可以在其基础上工作并贡献代码。

Git的分布式协作可以为项目和团队衍生出种种不同的工作流程,一起来学习下

集中式工作流

集中式系统中通常使用的是单点协作模型——集中式工作流。一个中心集线器,或者说仓库,可以接受代码,所有人将自己的工作与之同步。若干个开发者则作为节点,即作为中心仓库的消费者与中心仓库同步。

例如John和Jessica同时开始工作。John完成了他的修改并推送到服务器。接着Jessica尝试提交她自己的修改,却遭到服务器拒绝。她被告知她的修改正通过非快进式(non-fast-forward)的方式推送,只有将数据抓取下来并且合并后方能推送。这种模式的工作流程的使用非常广泛,因为大多数人对其很熟悉也很习惯。

当然这并不局限于小团队。利用Git的分支模型,通过同时在多个分支上工作的方式,即使是上百人的开发团队也可以很好地在单个项目上协作。

集成管理者工作流

Git允许多个远程仓库存在,使得这样一种工作流成为可能:

每个开发者拥有自己仓库的写权限和其他所有人仓库的读权限。这种情形下通常会有个代表“官方”项目的权威的仓库

要为这个项目做贡献,你需要从该项目克隆出一个自己的公开仓库,然后将自己的修改推送上去。接着你可以请求官方仓库的维护者拉取更新合并到主项目。

维护者可以将你的仓库作为远程仓库添加进来,在本地测试你的变更,将其合并入他们的分支并推送回官方仓库。这一流程的工作方式如下所示

基本流程

  1. 项目维护者推送到主仓库。
  2. 贡献者克隆此仓库,做出修改。
  3. 贡献者将数据推送到自己的公开仓库。
  4. 贡献者给维护者发送邮件,请求拉取自己的更新。
  5. 维护者在自己本地的仓库中,将贡献者的仓库加为远程仓库并合并修改
  6. 维护者将合并后的修改推送到主仓库。

这也是GitHub和GitLab等集线器式(hub-based)工具最常用的工作流程。

人们可以容易地将某个项目派生成为自己的公开仓库,向这个仓库推送自己的修改,并为每个人所见。这么做最主要的优点之一是你可以持续地工作,而主仓库的维护者可以随时拉取你的修改。贡献者不必等待维护者处理完提交的更新——每一方都可以按照自己的节奏工作。

主管与副主管工作流

这其实是多仓库工作流程的变种。一般拥有数百位协作开发者的超大型项目才会用到这样的工作方式,例如著名的Linux内核项目。被称为副主管(lieutenant)的各个集成管理者分别负责集成项目中的特定部分。所有这些副主管头上还有一位称为主管(dictator)的总集成管理者负责统筹。主管维护的仓库作为参考仓库,为所有协作者提供他们需要拉取的项目代码。整个流程看起来是这样的(见主管与副主管工作流。):

  1. 普通开发者在自己的主题分支上工作,并根据master分支进行变基。这里是主管推送的参考仓库的master分支。
  2. 副主管将普通开发者的主题分支合并到自己的master分支中。
  3. 主管将所有副主管的master分支并入自己的master分支中。
  4. 最后,主管将集成后的master分支推送到参考仓库中,以便所有其他开发者以此为基础进行变基

这种工作流程并不常用,只有当项目极为庞杂,或者需要多级别管理时,才会体现出优势。利用这种方式,项目总负责人(即主管)可以把大量分散的集成工作委托给不同的小组负责人分别处理,然后在不同时刻将大块的代码子集统筹起来,用于之后的整合。

博文参考


《Pro Git》

以上是关于Git分支工作流的一些笔记的主要内容,如果未能解决你的问题,请参考以下文章

分布式git

使用git命令创建分支到团队项目

Git 工作流

Git之深入解析如何使用Git的分布式工作流程与如何管理多人开发贡献的项目

Git之深入解析如何贮藏工作分支与清理工作目录

关于Git分支基础知识的一些笔记