赵走x博客
首页
书籍
软件
工具
古诗词
搜索
登录
12、命令指南
11、单人团队:连接远程仓库
10、单人团队:使用标签
9、单人团队:在仓库中添加更改
8、单人团队:使用分支工作
7、单人团队:创建本地仓库
6、单人团队:基于 issue 的版本控制
5、工作流
4、分支策略
3、访问模型
2、项目治理
1、团队作战
3、访问模型
资源编号:76624
Git团队协作
书籍
热度:40
如果你使用版本控制系统已经很长时间了,那么或许会想起 CVS、Subversion 等使用中央 仓库的版本控制系统。图 2-1 展示了在 Subversion 的集中式系统中更改是如何发生和流动 的。在这个系统中,当你每次想要保存一份仓库中的工作快照时,你和所有人的快照其实 都存放到了同一个地方。当你想要共享工作,或提交代码审查时,如果有人刚刚在同一分 支更新他们的工作,你可能就无法完成操作。 ![image.png](http://file.handsomemark.com/file/2020/05/15/0419fc30-9665-11ea-b6ea-00163e12d51c.png) 图 2-1:Subversion 中的文件操作 在另一方面, Git 是一个分布式的版本控制系统。也就是说,与强制将更改记录在中央仓 库的集中式代码托管系统相比,使用 Git 每个人都能够独立工作,并且在自己本地的仓库 副本中提交更改。这意味着其他开发者的修改永远不会强制进入你的作品。相反,你可以 决定什么时候引入外部代码以及什么时候共享你的工作。 >注1: 由于 GitHub 页面改版, Wiki 的入口从右侧边栏已移至上方的选项卡。 尽管在使用 Git 时,人们经常提到在没有网络的飞机上编写代码这个例子,我 认为 Git 真正的优点在于你可以私下里更多地尝试你的想法。你可以创建新 的分支,思考代码中的新想法,并且在自己准备好了之后再连接其他仓库。 如果你做过 MBTI 职业性格测试(https://en.wikipedia.org/wiki/Myers%E2% 80%93Briggs_Type_Indicator) , Git 大概是 INTP(内向/直觉/理性/理解) 类型的,而 Subversion 大概是 ESFJ(外倾/感觉/情感/判断)类型的。 每当你开始使用 Git 时,对于你的电脑来说你使用的是集中式系统开发方式,如图 2-2 所 示,仓库中的更改全部存放在你自己的机器上。你完成一些工作,然后将这些工作保存到 你本地的仓库。当你准备好将工作共享给别人时,连接到远程仓库,将你特定分支的副本 推送上去。 ![image.png](http://file.handsomemark.com/file/2020/05/15/35c5dcea-9665-11ea-b6ea-00163e12d51c.png) 图 2-2:Git 中的文件操作 把工作完全存放在本地就太大材小用了!相反,我们连接到其他系统,通过远程仓库可以 共享我们的代码。 Git 不提供访问控制,允许任何开发者拥有仓库的完整读写权限。在最外层,你通过登录 控制来限制访问。 我在自己的机器上开发, 而你没有权限访问, 因此你无法更改我的仓 库。一旦我将仓库放在共享的区域,比如集中式代码托管服务器,我们需要共同协商如何 管理仓库的访问权限。 一些 Git 托管系统,如 Bitbucket, 允许细粒度、分支独立的权限控制;但是,大多数托管 系统只允许你对每个仓库设置访问控制。 也就是说, 你要么可以在仓库中任意分支上提 交,要么只能通过拉取请求来提交贡献。 在本节中,我们介绍了下面三种最流行的访问模型。 • 单一仓库共同维护模型:团队中的每个人都是维护者,有权限向项目仓库上传更改。 • 并列贡献者仓库模型:提交贡献的开发者创建一个项目的远程副本,等待项目维护者接 受他们的更改。 • 分散贡献者仓库模型:代码通过文本格式的补丁包共享。 在本节结束时,你会学会如何将这些方法贯穿起来,建立一个适合你的团队的访问模型。 # 2.2.1 适合分散贡献者仓库的模型 当 Git 面世时,开源软件项目通常在公开的邮件列表中讨论代码库的更改,而不是在集中 式网站中。这个模型如今仍用于 Git 的开发团队。这个模型不太可能适合你的团队的开发, 但是,理解这个模型有助于理解使用 rebase (第 6 章)和 bisect (第 9 章)命令时遇到的 一些更高级的概念。 为了在社区中共享工作,开发者需要使用 diff 程序创建一个补丁文件。开发者接下来给 讨论组发送一份邮件,将图 2-3 所示的补丁文件添加为附件。为了审核提出的更改,邮件 列表成员将会下载附件中的补丁文件,并使用系统自带的 patch 命令将更改应用到本地代 码库。 通过邮件列表共享补丁文件,开发者得以隔离和贡献他们的工作;而通过有效地限制补丁 文件共享的内容,审查者可以轻松地看到在共享代码库中两个特定时间点之间的更改。 >形式服从于功能 为了让邮件发送补丁文件更加方便, Git 增加了 am 命令来支持通过邮件列表 发送的补丁。 这个模型如今仍然被 Git 项目自身使用,它仍然使用邮件列表来共享补丁,并讨论哪些功 能应该被加入 Git, 哪些 bug 应该被消灭。 尽管这个模型看上去有些过时,但它确实具有以下这些优点。 • 你不需要在本地拥有特定的版本控制系统, 因为补丁文件不需要在本地安装版本控制 软件。 • 开发者可以轻松地使用邮件应用来审查提出的更改。 • 这个模型鼓励完整的想法。如果你每次提交更改都需要给一群人发邮件,你更可能会确 保所有地方都是正确的,以避免“我又想起一件事”的尴尬。 • 将你提出的更改上传到一个单独的系统,而不是上传到代码托管系统,确保让软件项目 中的成员来实施审查的过程。换言之,开发者不能直接将更改上传到主仓库,而必须宣 布工作完成并等待其他人将它合并。 ![image.png](http://file.handsomemark.com/file/2020/05/15/5555bf10-966c-11ea-b6ea-00163e12d51c.png) 图 2-3:社区对补丁的审查过程 使用分立的仓库不仅仅适用于使用邮件列表沟通的项目。在本书编写时, Drupal 项目正在 使用这个模型的一个变种。与使用邮件列表来共享补丁相比, Drupal 项目使用一个自行管 理、集中式代码托管和工单 issue 系统。图 2-4 展示了一个包含补丁文件的 issue 屏幕截图。 ![image.png](http://file.handsomemark.com/file/2020/05/15/6f7efbcc-966c-11ea-b6ea-00163e12d51c.png) 图 2-4:一个带有补丁文件的 Drupal issue 队列 在这个模型中,你可以在共享之前为每个提交署名;但是,如果有多人参与,则很难搞清 提交历史中谁做了哪些更改。团队不得不使用补丁格式政策(不论是否签署)以及一个提 交消息格式。 Drupal 使用了严格的提交消息规范(https://www.drupal.org/node/52287)来确 保每个人的工作都被认可。 对于现在开始的大多数项目来说,这个模型是不合适的。但如果你将每个提交视为一个完 整的想法, 这个模型确实有助于理解一些更复杂的命令,比如 bisect 。这个模型的一个更 现代的做法是在单一的代码托管系统中来派生、克隆仓库。 # 2.2.2 适合并列贡献者仓库的模型 如今,软件开发者不再交换补丁文件,而是通常使用集中式代码托管系统来帮助他们管理 打补丁的过程。使用单一的代码托管系统使得在仓库间可编程地创建和提交补丁变得更加 容易。补丁的管理方法是每个版本控制系统的独家秘方。 Git 的 pre-commit 钩子确保在这 个过程中能够遵守访问控制。 在并列的系统中,“上游”项目保留了完整的控制, 决定谁拥有项目主仓库的写入权限。 每个贡献者使用代码托管系统将项目克隆、派生至他们的本地仓库。如图 2-5 所示,贡献 者更改本地的副本,然后通过合并请求或拉取请求提交这些更改。如果你参与的开源项目 拥有很多贡献者,你最可能使用的就是这个模型。 GitHub 将这种开发模型推广到了现在很多的开源项目中。我见过一些职能严格分离的内部 项目同样应用了这个模型。如果 QA 团队专门负责将最终的代码并入稳定的预发分支,他 们可能会在这个模型的基础上做一些修改。如果你使用外部承包商并且不希望他们越过审 查直接向仓库提交更改,那么这个模型同样适合你。 >Git和GitHub术语的比较 有时候很难知道应该使用哪个术语, 因为现在普遍采用的 GitHub 术语并 不总是与对应的 Git 命令保持一致。 比如, GitHub 的术语 fork 使用 Git 命 令 clone 创建了一个仓库的副本。 因为这本书的重点是 Git 本身, 而不是 GitHub 的 Git 实现,所以我们将会使用 Git 命令。我们偶尔会同时用到两种 术语,因为有时我们更熟悉 GitHub 的术语,而不是单独的命令。 ![image.png](http://file.handsomemark.com/file/2020/05/15/c0437b3c-966c-11ea-b6ea-00163e12d51c.png) 图 2-5:创建一个克隆仓库 当 GitHub 创建一个派生的仓库时,它等同于使用 Git 命令 clone 来创建一个仓库的副本。 一旦你创建了一个派生仓库,你就可以在 GitHub 的网页上直接将更改应用于仓库,但对 于比较复杂的更改来说,这不是一个好习惯。相比之下,你更有可能会再克隆一次仓库, 而这次是从派生出来的仓库克隆到本地的工作区。这个做法高效地建立了从一个仓库副本 到另一个的克隆链。保持所有仓库同步有些费事,但是,与直接操作补丁相比,需要记忆 的命令会少很多。正所谓“有得有失”。 架构相同的仓库应该比分立的仓库更易于使用,因为你使用封装软件更加方便。除了让更 新工作更为方便之外,封装软件使你能够更有效地控制谁能够提交工作并获得认可。 一般来说,克隆链中的第一个仓库应该只能被少数核心提交者改变,他们有权限向仓库中 添加新的提交或合并分支。大多数项目的参与者将会从仓库的本地克隆开始工作。在这个 本地的克隆仓库中,每个人都拥有完整的控制权限。他们可以添加新的分支、添加新的代 码,并将工作推送到主仓库的公开克隆仓库,将提出的更改共享给别人。一旦工作被推送 到了公共的克隆仓库,编写者可以请求社区对最新工作进行反馈。一旦工作被审查完成并 由社区测试之后,编写者可以发起一个从公开的克隆仓库到主仓库的合并请求或拉取请求。 如果某人不打算让他们的工作贡献回到主仓库, 他们可以跳过创建公开的克隆仓库这一 步,直接将主仓库克隆至本地环境。如果你意识到有一些更改需要提交回到项目中,并且 还做了一些不希望被共享的工作时,事情会变得有些麻烦。 不过,认识到你做的事能帮到别人并不总是那么容易的。比如,我正在使用一个开源的演 示文稿框架 reveal.js(https://github.com/hakimel/reveal.js)为 OSCON 制作幻灯片。 对你来 说,你可能正在使用一个 WordPress 主题,或是一个前端框架,或是其他的起始模板。 以前在使用 reveal.js 制作幻灯片时,我觉得我不会在使用时升级 reveal.js 软件,若停下来 升级,我会担心与上游项目的 Git 连接是否还存在。我在自己的仓库中翻遍了所有文件夹, 我的工作才得以完成。 一个自定义的主题完成了, 我做了一些修改, 我的仓库变成了一 个派生仓库,和它的起点不再相连。(有开源项目经验的开发者这时会抓狂,因为他们已 经意识到了我即将发现的事。)当我开始工作时,我发现幻灯片排版成讲义时出现了格式 错误, 我希望我的演讲者备注能显示在幻灯片的旁边(https://github.com/hakimel/reveal.js/ pull/963) ,而不是堆积在下方。我在项目的 GitHub 页面上创建了一个 bug 报告,然后继续 工作。有些人给了我一些重新排版的建议。啊!我知道怎么解决这个问题了。我想我的问 题已经解决了,但其他人仍然有可能对我的解决方案感兴趣。现在我陷入了两难的境地, 我创建自己的项目时并没有打算共享我的工作。 如果你提交了一个补丁, 你或许可以只共享部分工作, 但当你和并列贡献者一起工作 时,你需要通过已有的仓库将你的工作共享回去。我自己的项目没有为上游的工作准备分 支,因为我从来没想过要将我的工作共享回这个演示文稿框架。我开始创建一个新的仓库 链。图 2-6 显示了我操作的顺序。在 GitHub 上,我创建了 reveal.js 主项目的一个派生仓库 (https://github.com/emmajane/reveal.js)。然后我将派生的仓库克隆到本地。我在本地的克隆 仓库中为我的更改创建了一个新的分支。之后我将这些更改从 OSCON 的幻灯片中(因为 只有很少的幻灯片,所以我不介意打一个补丁,我只要用我习惯的复制粘贴工具即可)复 制到克隆的演示文稿框架的仓库。当更改完成时,我将更改推送回 GitHub 上的远程仓库, 并且创建拉取请求将我的更改并入项目。 reveal.js 仓库公开的克隆是必需的, 因为我没有 reveal.js 仓库的写入权限。 如果我有写入 权限,我可以不必创建公开的克隆仓库,直接创建一份本地的克隆仓库。 # 2.2.3 共同维护的模型 最后,我们会介绍内部团队(和个人团队)中最常见的权限模型:共同维护。在这个模型 中,团队成员之间有着天生的信任。我们假设代码在提交至主项目分支之前已经经过检查 和确认,即开发者是受信任的。在这个模型中,在推送至项目共享的仓库之前,每个开发 者在本地完成自己的工作。如图 2-7 所示,与内部团队协作时,我们的起点通常是单个共 享的仓库,每个人都拥有对该仓库的共享写入权限。 ![image.png](http://file.handsomemark.com/file/2020/05/15/56f78474-966d-11ea-b6ea-00163e12d51c.png) 图 2-6:并列仓库之间的项目更改流向 Git 不提供权限控制,而是依赖其他系统来授权或阻止对项目的写入访问。如果你想要阻 止别人将代码上传至共享仓库,需要使用托管系统的访问控制才能实现。如果你使用的托 管平台并非 Git, 那么访问可能由 SSH 账户管理。 除此之外, 与 Subversion 不同, Git 不允许你锁定特定的分支。 如果不借助额外的软 件, 团队成员默认在充分测试之前不会将更改提交到特定分支。 Bitbucket(第 11 章)和 GitLab(第 12 章)提供了分支独立的访问限制。如果你倾向于使用一个更加轻量的系统, 可以看一看 Gitolite(http://gitolite.com/gitolite/index.html) 。 ![image.png](http://file.handsomemark.com/file/2020/05/15/83f014aa-966d-11ea-b6ea-00163e12d51c.png) 图 2-7:团队中每个成员都拥有从本地仓库向中央仓库写入的权限 # 2.2.4 自定义访问模型 除了这些策略之外,团队还可以为一个项目选择多个访问模型。官方仓库的代码提交有着 严格规定的项目,这个模型将会特别有用。确实,大多数开源项目对不同的贡献者提供了 不同的访问权限。 一个常见的工作流如下所示。 * 一个官方项目仓库,只有极少数人可以向该仓库提交代码。在开源项目中,这个角色即 项目维护者;在闭源或合作的项目中,这个角色可能是 QA 团队。 * 一份限制更为宽松的仓库内部副本,每个贡献者和项目团队都使用该副本进行集成。这 个仓库或许会遵循共同维护的模型,作为代码审查流程中的一环,每个人都可以将他们 的分支并入仓库,甚至在一个特定的起点上继续开发。 * 锁定到各个贡献者的单独创建的个人仓库。通常这些仓库会使用与官方仓库相同的代码 托管系统,因为大多数现代代码托管系统都拥有易于集成的功能(通常称为“拉取请求” 或“合并请求”)。 这种项目组织方式常见于拥有初级开发者、QA 团队或者也许有外部承包商的团队。 # 2.3 小结 在本章中,你学到了授权和限制访问项目仓库的各种方式。 • 清晰地定义项目治理模型将有助于所有贡献者理解项目的所有权。 • 代码的版权通常属于作者,除非因为贡献者协议或作为雇用作品将版权重新分配给另一 个法人。 • 限制分发及代码衍生品的规则由软件许可证决定。 • Git 只是一个简单的内容跟踪工具,它不提供开箱即用的控制机制。一些代码管理系统 整合了 pre-commit 钩子,用来限制分支独立时的访问。 • 任意仓库的访问可以是受限的,也可以是开放的。提交至仓库的更改由补丁组成。在代 码托管系统中,你可以使用图形化界面来管理补丁提交流程。 仓库中的权限结构已经搭建完成,我们接下来将会探索如何分隔仓库,让你正在进行的工 作和已经完成的工作都可以在团队成员之间共享。