原文:
annas-archive.org/md5/49af0d85218130f9ed5d5bc40351ac10
译者:飞龙
前言
让我们通过这本必备指南,深入了解 DevOps 的世界。本书专为开发人员、运维专业人员和渴望精通 Git 和 GitHub 的 DevOps 爱好者而编写。这本书不仅仅是理论和教程内容,它是实践智慧和现实应用的宝藏,引导你走上掌握这些现代软件开发和运维工具的道路。
本书的特色:
-
Git 基础揭秘:从扎实的 Git 基础开始你的旅程。了解它在版本控制和协作软件开发中的关键作用,这是任何有志于成为 DevOps 专业人士的基础步骤。
-
探索 GitHub 的高级功能:深入了解 GitHub 的复杂功能。学习它如何促进持续集成/交付(CI/CD)并简化工作流程,弥合开发与运维之间的差距。
-
实践 DevOps 策略:超越基础,结合实际见解和现实场景。见证 Git 和 GitHub 在各个 DevOps 过程中发挥作用,从增强协作到将安全实践无缝集成到你的开发周期中。
《Git 和 GitHub 在 DevOps 中的应用》不仅仅是一本书,它是一个全面的工具包。书中包含了专家技巧、实践练习和有见地的案例研究,是任何努力在动态的 DevOps 世界中脱颖而出的人们的终极指南。
你是否曾经历过这样一种情况,心里想着,“哎呀,我犯了个错误!这太尴尬了”?即使并不是真的很尴尬,难道它没有让你感到有些低落或紧张吗?
尽管我们被建议从错误中学习,但我们都希望尽可能避免错误。
好消息?你失败的次数越多,你学到的东西也就越多。没有失败就没有学习。现在,给你一个坏消息。你将学习的 Git 和 GitHub,本质上是用于记录变化的系统。它们并不是为了轻松隐藏你的错误而设计的。在某些圈子里,精通 Git 是理所当然的,甚至不能使用 GitHub 可能会让你在工程师资格上受到质疑。更糟糕的是,自动化 DevOps 流水线中的错误可能直接导致生产故障。一个小小的失误,可能是更大错误的开端。在一个以协作和快速发展为特征的技术生态系统中,这些工具和方法支撑着现代工程实践。你对 Git、GitHub 和 DevOps 的全面理解是非常值得的。
而这本书正是满足你这种需求的书籍。它赋能开发团队成员在团队内有效沟通,教授现代团队开发的基础,不断改进你的产品。最终,它将显著提升你的开发者体验。
但让我们明确一点,完美是一个神话。然而,卓越无疑是可以实现的。从这个角度来看,这不仅仅是一本关于避免错误的书,更是一本关于拥抱错误并建立一个允许错误发生的流程、工作流和文化的书。它的核心是理解在错误不可避免地发生时,如何反应、适应并成长。这就是一个充满活力的 DevOps 文化的本质。
重要的是,你要开放心态,从自己的失误中学习,与团队公开沟通这些失误,并找到集体的解决方案。你每犯的一个错误并从中学习,都会让你离成为一名不仅仅是合格工程师,而且是无价的团队成员更近一步。
我希望你不仅能将这本书视为一本掌握 Git、GitHub 和 DevOps 的指南,还能视为你职业成长的导师。在翻阅这些页面时,你将遇到真实的场景、实用的练习和深刻的见解,这些将照亮前进的道路。你将学会如何应对团队协作工具的复杂性,如何在共享环境中管理代码,并简化开发过程。
欢迎来到一个持续学习、改进和协作的旅程。欢迎来到 Git、GitHub 和 DevOps 精通的世界。
本书适合的人群
本书是一本为进入 DevOps 动态世界的人们提供的全面指南,特别针对行业内不同角色的需求。书籍的目标读者包括以下群体:
-
即将成为 DevOps 专业人士的你:如果你是第一次参与 DevOps 项目的工程师,这本书就是为你量身定做的。我们理解你对犯错的恐惧。这本指南将帮助你掌握技能,增强信心,熟练地使用 Git 并将其无缝集成到你的团队中。
-
IT 管理员和基础设施工程师:随着行业逐步通过 Git 管理云配置,本书是为习惯于手动管理的基础设施工程师准备的重要资源。它提供了适应这一新范式所需的指导,将你传统的 IT 和基础设施技能与必要的编码和云计算知识相结合。
-
产品经理和产品负责人:这一部分面向的是那些在产品开发中扮演重要角色的非编码专业人士。如果你正在使用 GitHub 进行团队沟通,但对 Git 和 GitHub 的操作知识不足,这本书将为你解开这些工具的神秘面纱。
本书内容
第一章,DevOps 与开发者体验——进入现代开发的世界,介绍了 DevOps 和开发者体验,突出它们在现代软件开发中的重要性。它提供了这些概念的基础知识,包括 Git 和 GitHub 等工具,以及它们在提升开发流程中的作用。
第二章,Git 入门,提供了 Git 的实用介绍,强调了其基本用法以及团队开发中至关重要的沟通方面。内容包括文件管理、分支以及在 Git 驱动的工程环境中的协作原则。
第三章,团队协作的高级 Git 使用,聚焦于高级协作技巧。本章讲解如何管理提交历史、处理复杂分支并解决合并冲突,强调有效的代码库管理策略,以提高团队生产力。
第四章,通过 GitHub 提升团队协作,探讨了 GitHub 在 DevOps 中的作用,超越了其作为代码托管平台的身份。内容涵盖了对于团队协作至关重要的 GitHub 功能,以及从传统系统过渡到现代 DevOps 实践的过程。
第五章,通过 GitHub 推动 CI/CD,深入探讨了 GitHub Actions。本章介绍了其核心概念、工作流优化以及如蓝绿部署和金丝雀部署等高级部署策略,以及功能发布策略。
第六章,丰富 DevOps 实施,全面探讨了 DevOps,讨论了度量指标的重要性、安全实践的集成(DevSecOps),以及在组织内扩展协作的策略。
第七章,通过 AI 加速生产力,聚焦于 AI 在软件开发中的应用。本章深入探讨了 GitHub Copilot 等工具,并介绍了如何借助 AI 协助编程的最佳实践,包括有效的提示语设计和 AI 友好的编程原则。
第八章,反思与总结,反思了 Git、GitHub、DevOps 和 AI 等技术在软件开发中的变革,并考虑了 AI 对软件工程实践未来的影响。
为了从本书中获得最大的收获
本书将涉及在终端中使用命令,以更深入地理解 Git。最好具备初级的终端命令理解能力。
本书涵盖的软硬件 | 操作系统要求 |
---|---|
Git | Windows、macOS 或 Linux |
GitHub | Windows、macOS 或 Linux |
你需要一个最新版本的 Git 和 GitHub 账户的工作环境,才能不仅阅读本书,还能真正体验它。
安装 Git 的说明可以在本书的 Git 仓库中找到(下一节中会提供链接)。
如果你使用的是本书的数字版本,我们建议你自己输入代码或通过本书的 GitHub 仓库获取代码(下节会提供链接)。这样可以帮助你避免与复制和粘贴代码相关的潜在错误。
下载示例代码文件
你可以从 GitHub 下载本书的示例代码文件,链接地址为github.com/PacktPublishing/DevOps-Unleashed-with-Git-and-GitHub
。如果代码有更新,将会在 GitHub 仓库中同步更新。
我们还提供了来自丰富书籍和视频目录中的其他代码包,网址为github.com/PacktPublishing/
。快来看看吧!
使用的约定
本书中使用了多种文本约定。
文本中的代码
:表示文本中的代码、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 用户名。举个例子:“为了准备发布
,会从开发
分支创建一个发布分支。”
代码块设置如下:
name: Node.js CI
on:
push:
branches: [ "main" ]
jobs:
build:
当我们希望你注意到代码块中的特定部分时,相关行或项目会以粗体显示:
on:
push:
branches: [ "main" ]
所有命令行输入或输出如下所示:
$ git checkout main
$ git merge --no-ff add-feature
粗体:表示新术语、重要词汇或你在屏幕上看到的词汇。例如,菜单或对话框中的词汇会以粗体显示。举个例子:“通过点击探索工作流,你将进入市场页面并可以查找 GitHub Actions。”
提示或重要说明
以这种方式显示。
联系我们
我们始终欢迎读者的反馈。
一般反馈:如果你对本书的任何部分有疑问,请通过电子邮件联系我们:customercare@packtpub.com,并在邮件主题中注明书名。
勘误:虽然我们已尽一切努力确保内容的准确性,但错误仍然会发生。如果你发现本书中的错误,我们将非常感激你向我们报告。请访问www.packtpub.com/support/errata并填写表格。
盗版:如果你在互联网上遇到我们作品的任何非法复制形式,我们将非常感激你提供相关的地址或网站名称。请通过 copyright@packt.com 与我们联系,并提供相关资料的链接。
如果你有兴趣成为作者:如果你在某个领域有专业知识,并且有兴趣写书或参与书籍的编写,请访问authors.packtpub.com。
分享你的想法
一旦你阅读完DevOps Unleashed with Git and GitHub,我们希望听到你的想法!请点击这里直接进入 Amazon 书评页面并分享你的反馈。
你的评论对我们以及技术社区非常重要,它将帮助我们确保提供高质量的内容。
下载此书的免费 PDF 副本
感谢购买本书!
你喜欢随时随地阅读,但又不能将纸质书籍带到各个地方吗?
你的电子书购买无法在你选择的设备上使用吗?
别担心,现在购买每本 Packt 书籍,你都可以免费获得该书的无 DRM 保护 PDF 版本。
随时随地,在任何设备上阅读。直接从你喜爱的技术书籍中搜索、复制并粘贴代码到你的应用程序中。
好处不仅仅是这些,你还能独享折扣、新闻通讯以及每天送到你邮箱的精彩免费内容
按照以下简单步骤来获取福利:
- 扫描二维码或访问下面的链接
packt.link/free-ebook/9781835463710
-
提交你的购买证明
-
就是这样!我们将直接通过邮件发送免费的 PDF 文件及其他福利
第一部分:Git、GitHub 与 DevOps 基础
这部分关于现代软件开发的内容从介绍 DevOps 和开发者体验开始,强调它们在当今开发领域中的重要性,并提供基础知识和理论。接着,内容提供了 Git 的实操介绍,涵盖了其基本功能,并重点讲解了团队协作中至关重要的沟通方面,包括文件管理和分支管理。最后,讨论深入到 Git 的高级方法,专门为团队协作量身定制,如管理提交历史、处理复杂分支以及解决合并冲突,所有这些旨在提升团队生产力和有效的代码库管理。
本部分包含以下章节:
-
第一章,DevOps 与开发者体验——进入现代开发的世界
-
第二章,Git 入门
-
第三章,面向团队协作的高级 Git 使用技巧
第一章:DevOps 和开发者体验 – 进入现代开发的世界
本章将涵盖两个主要主题。首先,我们将讨论 DevOps 的定义,这是本书的核心主题。接下来,我们将深入探讨开发者体验,这是一种在组织内保持并持续取得 DevOps 成功的战略。在这个背景下,我们还将介绍 Git 和 GitHub 工具。
本章作为指南,帮助即将开始学习 DevOps、工具和 DevOps 协作实践的人们。它将建立关于这些概念和实践的基础知识,并阐明它们如何提升你的开发过程。
本章将涵盖以下主要内容:
-
DevOps – 通过减少摩擦加速开发周期
-
开发者体验 – 一种追求开发者卓越的战略
-
Git – 代码协作的起点
-
GitHub – 人工智能驱动的开发者平台
DevOps – 通过减少摩擦加速开发周期
让我们从学习 DevOps 基础知识开始。
技术领域有很多术语。有时,这些术语可能比较抽象,人们往往对特定框架有不同的理解。DevOps 也不例外。此外,在谈到 DevOps 时,从个人、团队和整个组织的角度来看,如何看待这些组件的相互关系,有时会让人感到模糊。
我们将回顾 DevOps 的基础知识,澄清一些误解,并简要讨论其典型的实践。在深入探讨 DevOps 之前,让我们先聊聊过去的情况。
DevOps 背景
在过去,软件基本上是你安装的东西。因此,对于一个产品开发团队来说,所需的只是能够开发和测试它的工程师。说到测试,一切都集中在发布前消除漏洞。想象一下那时的情况,它有点像早期电子游戏的发布,类似于任天堂娱乐系统(NES)时代。游戏一旦通过卡带发布,就无法进行更新或添加新特性。游戏中存在的漏洞会一直陪伴消费者。然而,随着 Apache HTTP 服务器的出现以及向网络通信的转变,IT 运维部门需要管理这种新范式。
在许多情况下,IT 运维团队专注于 IT 操作。他们的工具、文化,甚至目标,都与开发团队不同。开发团队想要添加新特性,而 IT 团队则把系统稳定性放在首位。这是一个矛盾。
此外,在这两个团队分裂的世界里,当涉及到部署时,就像是将代码扔过墙一样。开发人员将一个可以部署的完美工件抛给墙另一边的 IT 运维团队。然而,出于某些原因,IT 运维团队收到的却是一个不完整、不能在生产环境中正常工作的版本,导致他们在尽力部署和维护的过程中精疲力尽:
图 1.1 – 工程师将代码扔过墙
近年来,技术发展取得了显著进展,其影响已经渗透到软件领域之外,几乎影响了每个行业。为什么会这样?这是因为无论公司规模如何,软件开发在现代企业管理的战略中扮演着至关重要的角色。受到这股技术浪潮影响的公司发现自己处于一个竞争激烈的市场中,焦点集中在为客户提供快速、尖端和可靠的产品。
尽管市场条件和客户需求在不到一年的时间里可能发生显著变化,但一年只发布几次软件显然是不够的。我们不能因为内部孤岛之间的摩擦而延误软件发布。孤岛指的是缺乏与其他部门或系统的集成或沟通的隔离系统、流程或部门,这可能导致效率低下。我们必须找到一种方式来简化从产品开发到运维的整个过程。这就是全新的开发方法论、组织结构和文化的作用所在。
DevOps 作为一种方法论应运而生,正是从这种情境中诞生的。它代表了开发和运维的融合,开发和运维团队作为一个统一的实体协作,共同培养共享文化、优化流程、部署工具,所有这些都旨在加速发布、提升整体产品质量、收集客户反馈,最终为客户提供更好、更快速的服务。这反过来又促进了更有效的市场表现。
什么是 DevOps?
DevOps 这个术语是由 Development(开发)和 Operations(运维)两个词融合而成的。
那么,DevOps 仅仅是促进开发和运维团队之间的协作吗?
虽然这只是故事的一部分,现实往往更为复杂。这两个团队之间通常存在断层,各自有独特的优先事项和目标。仅仅将它们聚集在一起可能会在某种程度上增强它们的关系和相互理解,但这往往不足以实现有效的协作。实质上,DevOps 不仅仅是整合角色或团队;它代表了组织内部更广泛的文化变革。它关乎对齐优先事项、简化工作流程,并最终打破阻碍有效沟通和进展的壁垒。
简而言之,DevOps 是人、流程和工具的结合,旨在持续为最终用户提供价值,并且本质上是一种文化转变:
图 1.2 – DevOps 是人、流程和工具的结合
“我明白了。我理解了。DevOps 是关于人、流程和工具的。”
很多人以为他们已经理解了 DevOps,并开始着手为 DevOps 实施自动化管道。但是,等等。仅仅替换这些元素并不能确保 DevOps 的成功。为了让 DevOps 成功,你需要更多地了解它是什么,它不是啥,以及为什么它存在。
事实上,DevOps 的定义相当广泛。因此,作为 DevOps 扩展形式的 DevOps* 概念,近年来迅速扩展。各种衍生方法,如 DevSecOps 和 BizDevOps,已被提出,导致了对 DevOps 包含内容的多种不同看法。
不幸的是,DevOps 并没有一个被普遍接受的精确定义,如何实施 DevOps 的问题,不同的人有不同的回答。因此,关于 DevOps 的误解和不准确的解释很常见,往往会导致争论。有时,尽管 DevOps 很重要,当谈到实施 DevOps 时,人们却不愿意讨论。
图 1.3 – DevOps 就像房间里的一头大象,常常被忽视
什么是不是 DevOps?
通常,在定义 DevOps 时,与其尝试明确 DevOps 的真正含义,不如指出什么是不是 DevOps。让我们在这里澄清一个典型的误解。
DevOps 并不仅仅是一个工具、技术或产品
市场上有许多可以应用于 DevOps 的工具。许多云服务商和工具供应商通过说“使用这个工具,你可以构建一个 DevOps 工作流”或“这个工具对一个 DevOps 团队至关重要”来推广它们。
当很多人听到 DevOps 这个词时,他们中的一些人会想到云技术,如 Amazon Web Services(AWS)、Azure 和 Google Cloud Platform(GCP),或者像 Docker 和 Kubernetes 这样的技术。此外,Kubernetes 生态系统还包括许多组件,如 Istio、Flux、Helm、Envoy 和 Prometheus。还有一些平台,包括 GitHub Actions、CircleCI 和 Jenkins,促进了持续、快速和频繁的发布。监控工具的世界同样多样化。如果你还不熟悉这些名字,可能需要一些时间来理解它们的细微差别和好处。
然而,即使你掌握了这些工具,并且遵循了著名的架构或成功案例,这也不意味着你已经实现了 DevOps。因为工具背后始终有着人和流程,单纯改变工具并不会改变它们。
实际上,即使使用了以 DevOps 名义品牌化的工具,仍然常见瀑布式开发的情况。通过将多步骤的审批流程插入自动化工作流中,尊重现有复杂的公司规则,限制客户发布为每三个月一次以避免更改会计或安全检查,或让基础设施团队管理由应用团队使用现代编排系统 Kubernetes 构建的平台创建的容器——做这些事情只会增加运营负担和混乱,而无法创造业务价值。
最终,实现显著的商业目标和转型需要在人员、组织和流程上进行改变。那么,DevOps 是否意味着转型个人和组织?
DevOps 不仅仅是特定的个人、团队或角色
DevOps 是现实,但这个术语本身有些像流行词。公司为这些新活动招聘工程师,职称为 DevOps 工程师或 DevOps 架构师,甚至将其团队称为 DevOps 团队。那些认为自己无法做 DevOps 的人,甚至可能犯下试图将 DevOps 外包给 DevOps 合作伙伴的错误。
当你看待这种情况时,可能会觉得 DevOps 指的是特定的个人、团队或角色,但事实并非如此。在许多情况下,这些术语仅仅是指一些角色,如超级基础设施工程师、超级开发人员或只是云工程师。
公司之所以这么做,是因为为了快速响应不断变化的业务需求并实现快速发布,它们需要新的技术和自动化。此外,它们通常还需要管理复杂的平台,这些平台包括现有组件和新组件。这就包括充分利用 GitHub Actions,本书中将详细介绍这一部分。曾经需要手动安装的基础设施领域、通过界面配置或 CLI 操作的部分,现在需要使用 Git 进行版本控制,并对自动化工作流进行配置和管理。
然而,在实践中,DevOps 处理的事务更为复杂。它不仅仅是掌握这些工具和技术,或为它们配置角色。实际上,它需要强有力的领导者,能够在现有系统和组织中引领变革。通常,他们的行动超出了 DevOps 宽泛框架的范围。我所见过的那些真正实现了 DevOps 的人,往往在追求更大的目标,而不仅仅是 实施 DevOps。
DevOps 本质上是一个变革之旅,旨在转型处理各种复杂技术和产品的组织,它并非仅仅是为现有团队增添拥有新技能的人员,或改变团队和职位的定义。
那么,如果 DevOps 不仅仅是工具,也不仅仅关乎人员和组织,那么是否存在一个专门称为 DevOps 的过程呢?
DevOps 不仅仅是一个过程
DevOps 团队通常会在 2-3 周的短周期内进行迭代开发,通常称为冲刺,并每天早晨举行站立会议。这是真的,采用 DevOps 后,你可能会融入符合敏捷流程和 Scrum 最佳实践的流程。这些方法论是在软件开发中将每项功能分成更小的部分,并通过多个短周期推动开发。
那么,DevOps 是敏捷或 Scrum 的进化概念,还是它们的包容性概念?这个问题的答案既是是,也是不是。
DevOps 拓宽了范围,不仅仅关注像敏捷那样的软件开发,还扩展到软件的发布、反馈收集和改进。这些原则和哲学适用于软件开发生命周期(SDLC)的整个过程。团队关注的是产品的整个生命周期,而不仅仅是开发新特性或设计网页组件:
图 1.4 – DevOps 生命周期的八个阶段
然而,DevOps 的最终目标并不是引入一个新的过程本身。
DevOps 有着广泛的实践。具体的实践将在第五章中详细阐述,但并不是所有这些实践都能平等地应用于每个团队。例如,像 Microsoft 这样的环境需要将应用程序部署到多个服务器上,这与将一个小型应用部署到 EC2 实例的情况是不同的。需要应用的实践在用户数为 100 万的服务和用户数为 1000 的服务之间也有所不同。
让我们考虑一下敏捷,引用 Andy Hunt 博客中的内容:
敏捷方法要求从业者思考,坦白说,这很难做到。遵循给定的规则,并声称“按书面要求做”要轻松得多。这既简单又安全,不会受到嘲笑或责备;你不会因此被解雇。尽管我们可能会公开谴责一套规则的狭隘性,但在其中却有安全感和舒适感。但当然,要想敏捷或高效,并非是关于舒适感。
这个思路同样适用于 DevOps。为了能够频繁且迅速地发布以客户为中心的开发,提供可靠的服务,并最终对业务产生重大影响,你需要从比工具、人员和流程更广阔的视角来解决问题。
DevOps 是一种文化
所以,DevOps 不仅仅是开发和运维的融合,具体的角色、流程、工具、技术或产品的组合。人们常常简化它为“人员、流程和工具”。
那么,DevOps 到底是什么呢?
让我们听听 DevOps 社区的杰出人物 Patrick Debois 的话,以求更清晰的理解:
我当前对 Dev*Ops 的定义是:你所做的一切,旨在克服由孤岛效应带来的摩擦……其余的就是普通的工程工作。
在今天的云技术时代,无论是尖端公司、传统企业还是初创公司,都可以轻松访问相同的环境。甚至人工智能驱动的工具也以实惠的价格提供。
最终,阻碍团队和业务发展的正是摩擦,而这种摩擦通常出现在涵盖人、流程和工具的孤岛中。
DevOps 本质上代表了一种文化转变和一种旨在消除组织内部孤岛间摩擦的方法。它意味着思维方式、习惯和文化的转变。
DevOps 原则
那么,DevOps 文化究竟由什么构成呢?让我们深入探讨 DevOps 的核心原则,全面理解这种文化的真正内涵。
以客户为中心
每一个过程中的每一个行动都应该以为客户提供价值为目标。
在构建和维护软件时专注于客户,使得特性交付更快且更符合实际需求。短反馈循环有助于将产品微调以与用户需求对齐,从而降低风险,并以最低成本最大化价值。通过关注客户需求,最终目标变成了高效解决现实问题,这使得任务优先级更容易设定,积压任务得到更好的管理,资源分配更为高效,运营更加顺畅且具成本效益。最终,这为长期的商业成功奠定了基础,更好地满足市场需求和用户期望。
在 DevOps 中,以客户为中心不仅仅是一个口号;它是必不可少的。
从最终目标出发进行创造
理解客户需求并解决现实问题应优先于基于假设的操作。这提倡一种整体战略,团队同步进行开发和运营任务,以满足客户需求。这包括掌握产品的整个生命周期,从产品的诞生、开发到部署和持续支持,确保最终交付物真正能够造福最终用户。
从一开始就忽视客户需求可能导致一个在技术上完美无缺的产品,但在用户满意度和解决问题的能力上却未能达标。这一原则不断提醒我们,将所有技术努力与商业目标和用户期望对齐,确保最终产品不仅具备功能性,而且对用户具有价值和相关性。
跨职能自主团队
成功实施 DevOps 的核心在于形成自治、跨职能团队的不可或缺的策略。与传统的工程设置不同,开发人员、运维和**质量保证(QA)**专家在独立的隔间中操作,DevOps 倡导打破这些障碍。这里的关键点不仅在于形成多样化的团队,而是使这些团队自主——能够从产品或功能的概念阶段到交付全程负责。
为什么这么重要?答案在于敏捷性和效率。当团队具备从编码、测试到部署和设计甚至对业务有细致理解的一系列技能时,决策速度加快。这取代了层级系统中固有的迟缓和繁琐的命令链,转而形成了及时负责、行动迅速的文化。
消除这些组织瓶颈不仅加快了工作流程,还培养了拥有权和责任感的文化。团队不必等待外部部门或高层做出决策;他们拥有集体技能和自主权来直面挑战。
通过消除组织内常常引起摩擦的隔离,自治、跨职能团队成为顺利实施 DevOps 运营的关键。其结果是简化的流程,便于对变更快速响应,并促进了协作和责任文化的形成。这不仅是现代工程学的一个不可或缺的特性,更是任何企业成功实施 DevOps 所需的基础策略。
持续改进
持续改进是 DevOps 的基石,为现代软件开发和运营提供不可或缺的技术和文化优势。在技术上,通过持续分析性能指标和利用自动化工作流程,它使软件交付过程的可靠性、适应性和效率得以增强。这些特性不仅使最终产品更加健壮,还有助于资源优化和更快的功能交付。从文化上讲,持续改进促进了协作环境,确保责任制,鼓励学习文化,并最终有助于打破组织的隔离。它还战略上与改善开发人员体验相一致。
从反馈循环的角度来看,持续改进的重要性变得更加明确,反馈循环在 DevOps 生命周期中充当着神经系统的角色。这些循环使得实时监控成为可能,并提供可操作的指标,直接为持续改进计划提供数据支持。通过定期评估系统性能、用户参与度以及其他关键绩效指标(KPI),组织能够迅速调整其战略,确保长期成功。在今天快速发展的技术环境中,这种动态的数据驱动方法至关重要,使得持续改进不仅仅是最佳实践,而是竞争生存的基本要求。
自动化
在传统的开发模型中,开发和运维通常是两个独立的部门。开发人员专注于编写代码和构建应用,而运维专注于部署和维护。这种割裂的方式常常导致延误、低效,以及两队之间的摩擦。
持续集成/持续交付(CI/CD)成为连接这两个世界的关键桥梁。通过 CI,来自多个贡献者的代码更改会频繁地合并到一个共享代码库中,自动化测试会迅速运行,以检测错误和不一致之处。这促进了一个更加协作的环境,使得在开发周期的早期就能更容易地发现问题。CD 确保代码始终准备好进行部署,消除了由于发布待定而无法添加新功能的漫长冻结期。
手动流程不仅容易出错,还会大大拖慢速度和效率,这是 DevOps 旨在解决的核心问题。在自动化广泛采用之前,系统管理员需要手动配置服务器,这是一个繁琐且容易出错的过程。此外,开发人员常常发现很难复制操作环境进行测试,导致了臭名昭著的“在我的 机器上能运行”现象。
在这个背景下,自动化不是奢侈品,而是必需品。自动化脚本处理从代码测试到部署的任务,确保过程尽可能标准化,从而消除了许多与手动干预相关的错误和延误。
在 DevOps 实践中飞跃至卓越
到目前为止,DevOps 的基本概念已经被涵盖。事实上,DevOps 的概念一直在不断发展。一些概念被定位为本应最初包括的内容或最初包括但尚未讨论的内容,这些可能会被贴上不同于 DevOps 的标签。
让我们看看这个背景下特别重要的一些理念。这些是有助于将 DevOps 文化融入其中的有益概念。
DevSecOps
DevSecOps 是一种将安全元素整合到 DevOps 框架中的方法。在这种方法论中,安全被认为是开发初期的关键因素。其目标是在及时交付高效且安全的软件的同时,确保软件的安全性。
传统上,安全通常由独立的专门团队处理,通常是在开发周期的最后阶段进行处理。许多情况下,漏洞在开发后才被发现,导致软件发布的延迟。通过采用左移(shift-left)方法来处理安全问题,可以显著降低修复成本。在 DevSecOps 的背景下,**左移(shift-left)**指的是在软件开发生命周期(SDLC)的早期阶段(理想情况下是在设计和开发阶段)就将安全措施整合进去的做法。通过从一开始就关注安全问题,团队旨在更高效地识别和解决漏洞,从而减少与后期修复相关的成本和风险。左移方法强调主动的安全措施,而非反应式的安全,确保应用程序从设计阶段起就是安全的。在运营阶段出现的安全问题的修复成本可能极高。此外,这种晚期的关注会把客户数据置于风险之中,还可能损害公司的声誉。
根据国家标准与技术研究院(NIST)的数据,在生产阶段解决缺陷的成本可能是 30 倍,而处理与安全相关的缺陷时,这个成本可能会增加到 60 倍:
图 1.5 – 根据检测时间修复漏洞的相对成本图,数据来源:NIST
在 DevSecOps 中突出的一点是将安全视为一个持续状态的概念,而不是一个终点。例如,在过去,通常会在发布或技术采纳时进行静态代码分析,或在安全清单中勾选安全项。然而,随着发布周期的加速和技术的快速发展,每次手动检查所有安全事项变得不再切实可行。DevSecOps 通过使用自动化工具解决了这个问题,确保安全处于持续的状态。这使得在发现新漏洞时能够快速响应。
从这个意义上讲,DevSecOps 可以定义为将安全流程和工具整合到 DevOps 流程中,创造一种文化,在这种文化中,开发人员也能考虑安全,将安全视为一种状态,而非在特定时间点的一个产物,并且创建一个环境,使得流程、环境和数据始终得到保护,以便安全和创新能够兼容。
DevSecOps 对于使用开源软件(OSS)的公司尤其重要。如今,大多数企业都在积极使用某种形式的开源软件,这些软件可能包含未知的漏洞。通过在工作流程中每周检查这些漏洞并融入 DevSecOps 原则,有可能及早发现这些漏洞并迅速进行修正:
图 1.6 – DevSecOps 强调在 DevOps 生命周期中的安全性
总体来说,DevSecOps 的目标是将安全性贯穿于从开发到运营的整个生命周期,促进更安全、更高效的软件开发。这种集成方法使得业务与安全能够并存。
基础设施即代码
基础设施即代码(IaC)是一种通过代码管理和配置系统基础设施(包括网络配置和服务器设置)的方法。这是通过专门的配置管理软件自动化的。传统上,像服务器设置和网络配置这样的任务是由人工手动执行的,遵循程序文件。这种手动方法存在几个问题,包括任务的复杂性和耗时性、人为错误的高风险以及程序文件和实际环境之间可能存在的不一致性。尤其在大规模系统中,这些问题变得尤为严重,使得手动管理不可持续。
采用基础设施即代码(IaC)显著缓解了这些挑战。在 IaC 环境中,基础设施的状态通过代码定义,并由配置管理工具自动应用。这消除了繁琐的手动工作,确保了高度的可重复性和一致性。此外,由于这些工具负责基础设施的自动构建和操作,过程变得更加高效并且可以自动化,从而实现了更可靠、更可扩展的系统管理。
可观察性
可观察性在管理现代软件系统中起着至关重要的作用,特别是在旨在消除摩擦、促进跨组织沟通的 DevOps 框架内。可观察性通常被视为监控的演变形式,但两者各自有其独特的目的。
在传统监控中,重点是观察系统中预定义的元素。从本质上讲,监控是基于规则的。它涉及设置预定的度量标准和基准,当这些标准被突破时,会触发警报。监控关注的是已知问题——它是一个问“我的系统是否按预期工作?”的方法。然而,监控的范围通常仅限于发现问题发生的位置,而不是发生的原因。这可能只是冰山一角。要全面了解问题,可能需要很长时间:
图 1.7 – 监控中可见的区域只是冰山一角
然而,可观察性提供了一种更细致的方法。它不仅仅包含监控,还超越了监控,提供了对系统整体健康状况的深入洞察。在这个模型中,重点不再是监控预定义的问题,而是深入了解系统内部发生了什么。可观察性让你不仅仅停留在表面,帮助你问自己:“系统当前的状态是什么,为什么它会处于 这个状态?”
相反,可观察性依赖于指标、日志和追踪,通常称为遥测数据,提供系统性能的全面且相互关联的图景。在 DevOps 领域,监控不仅仅是运维团队的独立任务;它是一个集体责任,涉及到运维和开发专业人员。随着系统变得更加复杂,尤其是在云原生技术和微服务的兴起下,可观察性的重要性也随之增加。它提供了一种更全面的方式来理解系统不同组件之间的交互,从而更容易发现瓶颈、调试问题和优化性能:
图 1.8 – 可观察性的三大支柱是追踪、指标和日志
传统的监控工具通常是为了满足特定组织结构和需求而创建的,导致解决方案的格局有些碎片化。然而,云原生环境需要一种更集成的方法。可观察性工具旨在提供这种集成,提供跨多个环境的系统状态的统一视图。这种全面的理解使 DevOps 团队能够简化流程、降低风险,并更有效地为组织的目标做出贡献。
下一个挑战
你现在已经理解了 DevOps 的基本概念。本节内容涵盖了它是什么以及它不是什么,同时也讨论了 DevOps 文化。你还回顾了构成 DevOps 的各个领域,例如 DevSecOps、基础设施即代码(IaC)和可观察性。
那么,假设你已经在 DevOps 方面取得了成功,或者你正在朝着成功的道路前进并为此感到自豪。那么,你的下一个挑战是什么呢?也就是说,“DevOps 本身如何在组织中继续取得成功并发展?”
可能会发生这样的情况:“我们有一个很棒的 DevOps 团队。它还不完美,但文化已经发生了很大的变化,我们的合作非常顺畅!” 就在你以为一切都会变得完美时,成长起来的工程师们却纷纷离开,他们在寻找更好的工作环境。
在许多情况下,团队中最有技能、知识面最广的工程师往往在幕后做着最复杂的操作。最有价值的工程师可能最终会去做那些他们可能并不真正想做的琐碎任务。
事实上,大多数组织并没有为开发者提供一个能够高效且愉快工作的环境。可能是工具的问题,或者是大量无关紧要的任务和过多的工具。
这些仅仅是一些例子,但为了保持 DevOps 的最佳状态并进一步发展,一个组织需要从长远来看考虑其开发人员。无论你是个体贡献者还是经理,你都有责任确保你的团队成员对他们的工作感到满意,这样你才能拥有最优秀的团队。如果你体现了良好的开发体验,你的团队成员也会给你带来良好的体验。
回到这里的第一个问题:“DevOps 本身如何才能在组织内持续成功并发展?”
一个答案是让开发者感到快乐——换句话说,创造一个开发者能够做出最佳工作的环境。这被称为开发者体验。
开发者体验——推动开发者卓越的战略
开发者体验 这个术语超越了单纯的用户体验,涵盖了 DevOps 团队中的开发者如何在工作中保持高效并获得满意感。它也作为一种组织策略,帮助 DevOps 成功。如果工程师们感到愉快,组织内没有孤岛,沟通流畅,你的 DevOps 就会成功。在这方面,GitHub 基本上是一个最大化开发者体验的平台,而 Git 则是实现这一目标的工具。
图 1.9 – 开发者体验是成功 DevOps 的关键策略和基础
当你从 DevOps 领域中的各个工具来看,它们可能显得很小。然而,从维持和实现 DevOps 成功的角度来看,开发者体验与如何使用 Git 和 GitHub 之间的联系会变得显著强烈。因为正是在那里,开发者之间的核心沟通正在发生。
开发者体验是一种策略
开发者体验本质上是创造一个开发者能够做出最佳工作的环境。如果从 DevOps 的角度来定义,开发者体验可以被解读为一个策略,用于创造、发展和维持作为一个组织的最佳 DevOps 文化。DevOps 本质上关乎文化。如果你的开发团队不满,别指望会有繁荣的文化。没有繁荣的文化,DevOps 是不会成功的。
根据 GitHub 的说法,开发者体验的概念可以通过以下公式表示。这个公式可以在文章《开发者体验:它是什么,为什么你应该关心?》中找到 (github.blog/2023-06-08-developer-experience-what-is-it-and-why-should-you-care/
):
图 1.10 – GitHub 的开发者体验公式:GitHub 上开发者体验的表现方式
公式中的每个元素可以解释如下。
-
开发者生产力:这代表了效率和速度。换句话说,它反映了开发者完成任务的效率和速度。
-
开发者影响力:这包括影响力、实施代码更改和从想法到生产的转变。它展示了开发者的影响力有多大,以及他们能多快将一个想法转变为真实的产品或服务。
-
开发者满意度:这意味着在工作环境、工作流程和工具中实现高影响力、低摩擦。本质上,它衡量的是开发者对自己工作的满意度,他们的工作流程有多顺畅,以及他们使用的工具有多有效。
此外,这些元素会通过协作得到放大。团队内合作和沟通越好,开发者的生产力、影响力和满意度水平就越高。现在,让我们来探讨这些元素如何作用于 DevOps 基础。
开发者生产力
提升开发者生产力是一项复杂的挑战。DevOps 作为一种催化剂,有助于平滑组织内的低效现象,并提升开发团队的输出。
当今的开发者肩负着繁重的责任:编写代码、进行代码审查、架构规划以及偶尔的基础设施部署。其中一些任务发生在组织或团队层面,而另一些则是个人努力。DevOps 显然强调合作,但同样需要关注提升个人表现——这是一个需要精心监控的动态。开发者体验也涵盖了这一部分内容。
目前,许多企业由于缺乏熟练的工程师而面临困难。但请考虑一下:在一个有 100 名开发者的组织中,如果每个开发者每天能节省 10 分钟时间,那就相当于增加了将近 17 个小时。这几乎相当于增加了两个额外成员。
然而,衡量单个工程师的生产力是一项具有挑战性的工作。尽管诸如代码行数或提交次数等量化指标看似可靠,但它们往往未能提供全面的视角。有时候,一行经过深思熟虑的代码可能比 10 行匆忙写就的代码更有价值。像研究和架构设计这样的任务通常没有被衡量,这进一步加剧了评估的复杂性。单纯依赖这些狭隘的指标可能无意间培养出微观管理的文化。当这些指标成为评估标准时,就可能促使一些行为侧重于噪音而非实际价值。这些指标在第六章中有所介绍。
在开发者生产力的背景下,一个视角可能是将 AI 应用到各种开发场景中。将 AI 融入开发流程标志着一个变革性的时代。像 GitHub Copilot 这样的工具作为 AI 驱动的编程助手,支持代码编写过程,从而降低了时间和成本。自从 2022 年 11 月 ChatGPT 发布以来,AI 在开发中的影响力正在以前所未有的速度扩展。
开发者的影响
在 DevOps 领域,关注点通常集中在团队如何影响客户和整体业务。该领域的度量指标通常从产品或客户入手,比如衡量部署的频率。与此相对,开发者体验聚焦于开发者,强调他们对代码库以及最终投入生产的想法的影响。
那么,开发者生产力和开发者影响力之间的区别是什么呢?对于生产力,成功的衡量标准是用更短的时间做更多的工作。另一方面,理解如何衡量影响以及应当衡量什么是非常困难的。而且,不同的团队对影响的定义各不相同。
最容易识别的事情是能够量化为对最终产品价值的贡献。例如,如果一个团队正在开发一个新功能,那么其影响将体现在新功能带来的用户数量和收入。此外,其他例子还可以包括下载量、请求、服务级别协议(SLA)等。
我相信,当创造出良好的 DevOps 文化时,工程师会变得更加快乐。然而,有些人可能会想:“归根结底,这一切不就是为了钱吗?” 事实上,衡量开发者的影响力确实与钱有关。
如果工程师的影响没有被正确衡量,且薪酬未能与其市场价值相匹配,那么工程师对工作的满意度将会下降。接下来的情景应该不难想象。许多组织正变得越来越复杂,影响是间接的,但即使是现在,也没有很多公司能够衡量这种间接影响。
这最终与 DevOps 的原则非常契合。毕竟,如果要衡量开发者的影响力,就需要了解客户和客户在产品中的状态,并以“从目标出发,创建”的心态来创造产品。
衡量开发者影响力的指标有很多,但最容易获取的包括开发者关闭拉取请求所需的时间,以及从收到客户反馈到实际实施之间的时间跨度。
开发者满意度
最重要的是,在这个背景下,开发者生产力及其影响并不是让管理者去管理并指出个别开发者的生产力,而是帮助开发者以更加积极的方式加速其开发进程。
组织需要确保通过提供最佳的环境、工作流和工具,让工程师通过开发和为客户交付价值来感到满意。
在更广泛的组织层面上,在整体优化的背景下,有必要确保环境能够广泛地为所有开发者的生产力提升做好准备;而在更狭窄的组织层面上,则需要根据团队、角色和经验制定提高开发者生产力的策略。这包括通过 DevOps 实践减少团队瓶颈,从而最小化周期时间,同时为开发者提供必要的工具,以便他们能最大化地发挥潜力。
协作
在加强开发者体验以实现 DevOps 成功时,协作是关键。它不仅仅是将开发团队和基础设施团队整合并共享一些责任;更重要的是创造一个能够让开发者蓬勃发展、贡献和感受到拥有感和参与感的环境。理想的状态是透明的协作,在这种状态下,组织之间的壁垒不仅仅是减少,而是完全消除。这种思想、最佳实践和建设性批评的自由流动,创造了一个有利于开发者体验的工作环境。
例如,有人说 DevOps 不仅仅是工具和实践;它是一种文化、一种哲学,一种旨在实现商业和组织成功的理念。在这种背景下,Git 和 GitHub 经常被边缘化为 仅仅是工具 或 沟通手段。这种观点有时是对的,但往往目光短浅。采纳这种观点可能会限制你对 DevOps 中 Git 和 GitHub 的理解,可能仅仅局限于如何使用 GitHub Actions 实现 CI/CD 或者如何在学习 Git 和 GitHub 时使用 Git 和 Git 分支策略。
在组织或团队中工作时,团队由各个贡献者组成,例如开发者。尤其是当开发者在团队或组织中工作时,沟通变得极其复杂。成功的对话最终是提升团队或个人生产力的关键。Git 和 GitHub 不仅仅是工具,它们是共享代码、获取反馈、进行整合和成长的平台。在这种环境中良好的沟通能力对 DevOps 的成功至关重要。
促进这种协作的最强大范式之一就是 InnerSource。借鉴开源协作模型,InnerSource 赋能组织内部的开发者参与其他团队的项目,即使他们并非该团队的正式成员。就像开源开发一样,InnerSource 鼓励开放沟通、代码共享和集体问题解决,有效减少了常常困扰组织的孤岛现象。
开源协作模型本身就是打破障碍后能够实现的成果的证明。通过将代码库开放给公众,开源项目能够接触到全球开发者社区,每个开发者都为项目带来了独特的视角和专业知识。个人成长和集体进步之间存在一种互利的关系,这是 DevOps 团队应当在内部努力实现的共生关系。这个开放模型带来了透明文档、同行评审和民主化问题解决的核心理念,极大丰富了开发者体验。
透明度是这种协作的关键。当从初级开发者到团队负责人,所有人都能访问讨论、查看代码库,并参与决策时,角色和部门之间的壁垒开始崩塌。这种缺乏孤岛的状态自然会导致问题的更快识别、更高效的解决方案以及更统一的愿景。
放大 DevOps 和开发者体验的元素
有一些理念可以帮助你的 DevOps 更加成功。其中之一就是采用去中心化的贡献模型和 平台即产品(PaaP)的概念。没错,正是 InnerSource 和平台工程。这些都会提升开发者体验。
InnerSource – 去中心化贡献模型
InnerSource 从开源世界中汲取灵感,但它旨在通过在组织边界内应用开源实践来解决内部工程挑战。它允许跨团队合作与技术专长的共享,最重要的是,它旨在创造一个更透明和包容的文化。这种方法与 DevOps 的哲学紧密契合,旨在消除组织壁垒,促进合作和透明的文化。InnerSource 还旨在推动文化转型,这种转型对提升开发者体验至关重要。
定义 InnerSource
本质上,InnerSource 是一种在单一组织内部进行的软件项目协作方式。这个概念由像 PayPal 这样的公司推广,旨在解决大型组织中常见的孤岛效应和边界问题。InnerSource 鼓励一种开放的文化,任何人都可以为任何项目贡献代码,且流程和决策过程都是透明的。
InnerSource 的四大支柱
让我们深入探讨四个基础原则——开放性、透明性、优先指导和自愿代码贡献:
-
开源仓库中的
README.md
和CONTRIBUTING.md
,使得项目更加易于发现和访问。这种开放性提高了开发者的体验,通过减少摩擦,使得工程师可以轻松地切换上下文或团队。例如,即使在 DevOps 环境中有大量微服务,拥有这种文档文化也有助于促进协作。 -
透明性:当我们谈论 InnerSource 中的透明性时,指的是项目决策过程中的开放性。例如,问题讨论和拉取请求评审都是透明进行的,通常会被记录并且易于访问。这为我们提供了关于为什么做出某些决策、是谁做出的决策以及决策背景的洞察。透明性不仅提高了项目的质量,还通过创造一种归属感和参与感,显著提升了开发者的体验。
-
优先指导:在 InnerSource 中,指导并非附带任务,而是优先事项。在 InnerSource 中,仓库维护者的角色也被称为可信承诺者,这考虑到了与内部限制相关的活动差异。
可信承诺者的角色在这里至关重要。可信承诺者不仅仅是代码贡献者;他们还是项目的倡导者,专注于代码的质量和可持续性,并且指导新贡献者。他们对仓库和组织有深刻的理解,充当两者之间的桥梁。可信承诺者的心态是开放的、包容的,并致力于提升他人。这个角色对于通过确保高质量的贡献和培养持续学习的文化来维持良好的开发者体验至关重要。
-
自愿代码贡献:自愿代码贡献的原则强调贡献是自愿的,促使工程师对他们所参与的项目产生归属感和责任感。这是一种自下而上的方法,与自上而下的方法不同,能够营造一个更加自然、协作的环境。随着工程师自愿参与,文化变得自我调节,既有助于项目本身,也促进了组织整体文化的发展。这种参与型的环境显著提升了开发者的体验,为个人和职业成长提供了机会。它与 DevOps 文化中集体所有权的理念相似,贡献的目标是改善整个系统,而不仅仅是单个组件。
InnerSource 与 DevOps 的互补关系
通过将 InnerSource 与 DevOps 实践相结合,组织可以创建一个有利于客户导向和开发者导向的全面环境。InnerSource 通过专注于文化特征——开放性、透明性、指导和自愿贡献——提供工具来改善开发者体验,使工程师的日常工作更加有趣、有意义和令人满足。
平台工程
目前有许多云服务可用,尤其是来自主要公共云提供商的服务。特别是在云原生计算基金会(CNCF)的核心,围绕 Kubernetes 的生态系统发生了巨大的变化,涵盖了超过 1000 个项目。工具的激增增加了开发团队的认知负担。
平台工程是一种新兴的技术行业方法,专注于优化开发者体验和运营效率。其目标是实现可复用的工具和自助功能,自动化基础设施操作,从而提升开发者的体验和生产力。平台团队的“客户”是开发团队,特别强调满足他们的需求。
从根本上讲,平台工程围绕内部开发者平台(IDP)的构建和管理展开。这些平台整合了各种工具、服务和自动化工作流,提供自助服务选项。它们本质上为开发者提供了一个黄金路径,帮助他们从开发到部署的过程,不会因基础设施的复杂性而陷入困境。
平台团队通常会提供开发者门户网站,例如 Spotify 的开源 Backstage,进一步强调了 PaaP 的概念。最重要的心态是将平台视为一个产品,而不仅仅是工具,面向的是内部开发者——即“客户”。
平台团队的角色是多方面的,从创建 IDP 到建立内部 SLA。他们监控团队绩效指标,并监督一个安全高效的交付过程。平台团队工具箱中的一个重要元素是 IaC(基础设施即代码),以及强大的 CI/CD 管道。这些设置作为代码开发和部署的中枢神经系统,自动化了从搭建基础设施到构建、测试和推送代码到不同环境的所有操作。这使得平台团队能够专注于更有价值的任务和客户价值,而不是被繁琐且容易出错的手动操作所拖累。
从本质上讲,平台工程旨在加强 DevOps 范式,改善开发者体验,并优化软件交付的运营方面。该方法旨在消除大型组织中常见的障碍,加速从代码到客户的旅程。在此过程中,平台工程填补了现代 DevOps 领域中的一个关键空白,既确保了运营效率,又促进了协作文化和共享责任。
Git —— 代码协作的起点
管理代码变更是一个复杂的挑战。不同的团队成员不断地为统一的代码库做出贡献,而这个代码库必须始终保持功能正常。
Git 改变了软件开发团队的协作方式。通过分布式架构并提供创建分支的能力,Git 已经成为 DevOps 工具箱中的一项重要资产。分支本质上是一个独立的开发线路,像一个平行宇宙,在这里你可以在不影响主项目的情况下工作于新特性或修复问题。这使得团队能够同时专注于多个方面,提高了开发过程的效率,并且能够快速适应快速变化和快速交付软件的需求,这是 DevOps 环境中的关键要求。
Git 不仅仅是一个跟踪代码变化的工具——它还是组织内变革的催化剂。通过提供一种可靠的协作方式,Git 促进了团队之间更好的沟通。这与 DevOps 的核心目标高度契合,即打破组织内部的壁垒,从而消除摩擦,使得构建和部署软件的过程更加紧密和高效。
想象没有版本控制的世界
想象一下一个没有版本控制的环境。开发者将难以管理文件的更改,没有系统的方式来跟踪历史。每一次更改都必须手动记录,导致评论海量,且掩盖了代码的真正目的。在这种情况下,像 DevOps 中看到的快速交付几乎是不可能实现的。缺乏版本控制会为协作过程带来摩擦。而且,文件冲突会成为常见的头痛问题。试想一下,正在开发一个功能时,发现别人用他们的更改覆盖了你的文件——这种冲突可能会中断开发进程。如果没有版本控制,你还不得不采用一些奇怪的命名规范,比如 v1
、v2
或 backup-foobar-20230930.py
,仅仅是为了保留旧版本的文件备份。
Git 的历史
Git 由 Linus Torvalds 于 2005 年创建,至今已成为全球最受欢迎的 分布式版本控制系统 (*DVCS)。
那么,Git 为什么会被创建呢?简单的答案是因为需求。Linus Torvalds 需要一个能够很好地完成多项任务的 版本控制系统 (*VCS)。首先,它必须快速高效,让开发者工作时不受拖慢。其次,它需要允许多个开发者在同一项目上工作而不互相干扰。第三,它必须能够毫不费力地管理大型代码库。此外,Torvalds 希望拥有一个能够保持整个项目可靠历史的系统。他还寻求一种支持非线性开发方法的灵活性,这种方法能够有效管理多个分支并合并。在响应这些需求时,Git 被设计为简单但功能强大的系统。
什么是 VCS?
那么,什么是 VCS 呢?VCS 是一个用于监控 SDLC 中代码修改的系统。在有多个贡献者的项目中,跟踪谁修改了什么以及何时修改——以及由此产生的任何 bug——是至关重要的。VCS 高效地协调了这一跟踪过程。
VCS 技术通常分为两大类——集中式和分布式:
-
集中式:在这种设置下,单个远程仓库存储项目数据,所有团队成员都可以访问。SVN 和 CVS 就是这种类型的例子。
-
分布式:在像 Git 这样的分布式模型中,每个开发者都使用本地仓库的副本,进行更改,然后再将更改同步到中央远程仓库。
下图展示了在使用 Git 时,文件是如何分发和传递的。Git 本质上允许你拥有多个远程仓库和灵活的分布式开发结构,但通常,人们使用像 GitHub 这样的开发平台创建一个远程仓库,开发者通常与远程和本地仓库进行交互:
图 1.11 – Git 中的版本控制是以分布式方式进行的,允许多人同时协作
像 Git 这样的分布式模型的突出优势在于它为开发者提供的自主性。这种去中心化的方法能够更流畅地进行工作,减少了贡献者之间代码冲突的风险。
安全性与完整性
对于 Git 来说,确保源代码的完整性是首要任务。在 Git 中,每次提交都会分配一个唯一的哈希值。该哈希值是基于提交的代码内容及其元数据生成的。因此,如果代码被修改,哈希值也会发生变化,这使得篡改历史记录变得极为困难。影响分支或标签的操作,如合并或回退,也会作为变更历史的一部分被保存。
适应性
Git 具有很强的适应性,能够适应各种开发工作流。无论是小规模项目还是企业级应用程序,Git 都能以多种方式适应,追踪变更并促进团队成员之间的协作。
图 1.12 – Git 高效地使用分支、提交和合并来管理代码
Git 在现代开发中不可或缺
在 Git 引入之前,集中式系统如 SVN 和 CVS 占据主导地位。然而,Git 的兴起促使了个体开发者和组织在版本控制系统(VCS)方面的重大转变,向分布式选项倾斜。
随着云技术的兴起,应用程序和基础设施之间的界限正在变得模糊,Git 不再仅仅是为编写代码的开发者所使用,它也被那些从事基础设施工作的人员所采用。
在 DevOps 的世界里,顺畅的协作与沟通至关重要,Git 成为开发者体验的支柱,不仅支持代码管理,还支持旨在提高协作的工作流策略。
在当代软件开发领域,版本控制不是可选的,它是必不可少的。
我们将在第二章和第三章中介绍所有关于 Git 的知识。
GitHub – AI 驱动的开发者平台
GitHub 是一个 AI 驱动的开发者平台,用于构建、扩展和交付安全软件。
GitHub 常被认为只是一个代码仓库服务,或者有时作为一个附带 CI/CD 功能的代码仓库服务。然而,GitHub 现在提供了一个涵盖整个开发生命周期的平台,从编写应用程序到构建和发布。GitHub 作为 DevOps 协作和自动化的基础平台,通过开发者体验的视角,它是 InnerSource 项目的核心,并且托管着平台工程所必需的代码。此外,这一切都得到了 AI 的支持。无论你是在编写代码还是在审查代码,GitHub 的 AI 都能在多种场景中为你提供支持。
GitHub 的功能可以分为五个主要类别:由 AI 驱动、协作、生产力、安全 和 规模。让我们逐一看一下每个类别。
由 AI 驱动
由于 生成式 AI(GenAI)由 大语言模型(LLMs)驱动的进展,世界正在发生变革。这股变化的浪潮也正在影响工程师,促使开发人员借助 AI 编写代码。AI 不仅能从现有的代码中推断,还能直接将自然语言命令转化为代码,甚至能够用自然语言提供代码解释。当 AI 革命刚刚开始时,这些功能仅限于在编辑器中进行简单的代码生成。然而,作为一个平台,GitHub 的作用远不止代码生成,它在整个开发生命周期中提供了全面的支持。
GitHub Copilot – 你的 AI 编程搭档
GitHub Copilot 是 GitHub 提供的一个 AI 支持的服务。截至 2023 年,它已经成为 Stack Overflow 开发者调查中备受喜爱的服务之一。在 AI 的背景下,生产力提升意味着什么?显而易见,开发人员能够更快地编写代码,并且准确性不断提高。然而,工程师的工作不仅限于编码;例如,研究也是一个至关重要的环节。工程师经常在编辑器和浏览器之间切换,或者在编辑器和 Slack 之间切换,常常进行多任务处理。AI 工具的强大不仅体现在代码生成上,还在研究和文档工作中大放异彩。这些工具减少了工程师的多任务需求,帮助他们保持工作流。换句话说,它们帮助维持 专注模式。
AI 的多功能性
AI 不仅帮助编写代码,还协助 CI/CD 配置,并根据特定格式实现 YAML 文件。Shell 命令的实现也是这些 AI 工具能够轻松完成的任务。它们不仅对应用程序开发人员非常有用,也对平台工程师大有裨益,能够提高个人和团队层面的生产力。
AI 服务的发展速度极快,最新的信息很快就会过时。因此,本书不会深入探讨 GitHub Copilot 的具体服务和功能,但 第八章 将提供关于使用 LLM 的编码技巧的一般性建议。
协作
GitHub 是一个强大的平台,旨在为开发团队提供卓越的协作体验。
新成员的快速融入得益于GitHub Projects、GitHub Issues 和 GitHub 代码搜索的帮助。具体来说,GitHub Projects 通过看板、路线图和表格视图,使任务及其进展的可视化组织变得更加便捷,而 GitHub Issues 则清晰地列出了具体的问题或漏洞。此外,通过给每个问题添加标签,新成员可以迅速了解从哪里开始。这种透明性和方法鼓励了自然的协作。此外,GitHub 代码搜索使得过去的项目、讨论和代码能够快速检索,从而帮助新成员迅速访问现有的知识和项目历史。
为确保代码质量,拉取请求和合并队列非常有用。代码更改通过拉取请求进行明确的审查。通过集成的评论和代码视图,这一过程变得更加清晰和高效。利用合并队列可以确保已审查的更改高效且安全地合并到生产环境中。
从企业文化的角度来看,拉取请求、GitHub Issues、GitHub Discussions 和 GitHub Enterprise 内部仓库等功能有效地在组织内实现了通过 InnerSource 消除信息孤岛。通过利用这些功能,跨团队的透明沟通得到了促进,开发文化逐渐变得更加开放和自下而上。这也能够防止重复发明轮子。采用这种 InnerSource 方法,能够减少工作中的冗余,大大提升整体的员工满意度。
生产力
GitHub 提供了多种工具和功能来提高生产力。
首先,GitHub 具有多种自动化流程和高效的 DevOps 功能。通过使用 GitHub Actions,您可以自动化 CI/CD 过程。这使得您能够在保持代码质量的同时,快速地对产品进行稳定的更改。GitHub Actions 包括自托管运行器和大型运行器,允许您适应受限环境和高规格的机器需求。这种灵活性使您能够更快地将软件更改推向市场,从而有助于提高市场响应时间(TTM)。
此外,GitHub Projects 的自动化功能通过自动化一系列任务提高了工作效率。例如,你可以通过内置的工作流自动化问题项的处理流程或问题标签的分配。当然,复杂的自动化任务也可以通过 GitHub Actions 完成。GitHub Codespaces 提供了一个在线开发环境,允许你随时随地进行编码。这使得开发团队能够高效地远程协作,并显著减少了新成员环境配置所需的时间和资源。通过使用npm
和 GitHub Packages,包管理变得更加简便。通过整理依赖关系,你可以高效地共享和重用代码,从而加快开发周期。
GitHub 还提供了 GitHub Copilot,一个 AI 配对编程助手。它具备由大型语言模型(LLM)支持的高性能代码补全功能,帮助开发人员更加高效地编写代码。这不仅提高了代码质量,还显著增加了开发人员的生产力。
这些功能减少了开发人员的工作负担,让他们能够专注于创造内在价值并促进收入增长的任务。
GitHub 可以简化开发过程,并通过加速代码质量和发布速度,更快速地将符合客户需求的产品推向市场,从而实现客户满意度的提高。
安全性
GitHub 是广泛应用于软件开发的一个平台,但它在安全领域也提供了先进的体验。随着维护应用程序高安全性的重要性日益增加,GitHub 的功能变得更加至关重要。
例如,高级安全功能会自动检测代码中的漏洞,帮助公司和开发人员通过及时解决这些问题来降低风险。
该平台还提供了无缝的安全问题管理功能。具体而言,安全概览仪表板让你能够轻松查看所有安全警告和设置,将管理过程集中化。
此外,GitHub 的秘密扫描功能会自动扫描是否有任何密钥或 API 密钥错误地上传到 GitHub,并对其发出警告。对于支持的平台,GitHub 还会进一步采取措施,自动撤销错误上传到公共仓库的密钥,并能够验证密钥的活动状态。仅仅检测到秘密往往无法满足全面的 DevSecOps 要求。这种主动的做法不仅可以早期识别潜在问题,还能显著降低风险。此外,推送保护功能作为一个保障,防止敏感信息被意外上传到 GitHub。
在 GitHub 上,安全政策的创建和执行过程也非常简单。通过设置安全策略并在仓库中强制执行 GitHub 高级安全性,开发者更容易维护一个安全的编码环境。这使得整个组织或企业能够实施更一致的安全措施。
最后,让我们谈谈供应链。如今,几乎每家公司都在某种形式上使用开源技术。许多项目有数百个开源依赖项,这些依赖项可能会引入安全风险。如果其中一个依赖项发现了漏洞,会发生什么呢?这就是 Dependabot 发挥作用的地方。它自动识别依赖项中的漏洞并提议更新,帮助减少供应链中的安全风险。
总的来说,GitHub 涵盖了多个安全方面,为开发者和公司提供了有用的工具,可以在更短的时间内创建和管理更安全的代码。
规模
要实现业务规模化,一个可扩展的平台如 GitHub 至关重要。可靠性、全球访问和持续创新是不可或缺的。拥有比任何其他平台都多的开发者——确切来说超过 1 亿人——GitHub 作为其高度可信和优质的见证,展示了其信任度和质量。
接下来,作为开源的家园,GitHub 实现了全球覆盖。开发者和团队可以公开共享他们的代码,并与全球其他开发者协作。GitHub 的开放式方法是进入多元化市场、工程师和产品的关键。
此外,使用 GitHub 不仅允许用户开发自己的软件,还提供了利用其他项目和开源代码生成新创意和解决方案的机会。这表明 GitHub 不仅仅是一个代码存储工具;它是全球范围内创新和协作的催化剂。
我们将在第四章到第八章中介绍所有你需要了解的 GitHub 知识。
总结
本章开始了一段探索之旅,了解 DevOps 如何彻底改变我们开发软件的方式。我们讨论了开发者体验的重要性,作为为 DevOps 团队提供最佳工作环境的策略。我们还深入探讨了 Git 和 GitHub,作为 DevOps 环境下协作的基础。
一切都是相互联系的,并且是有原因的。DevOps 的需求、使用 Git 和 GitHub 的原因以及文化在塑造组织的人员、流程和工具中的重要性——这些元素相互关联。它们共同塑造了开发者体验策略的形式,使 DevOps 团队中的开发者能够发挥最佳水平。你所做的一切最终都会回馈给你,丰富你作为开发者的成长。
DevOps 应该被设计为允许在快速开发过程中犯错。每一次失误都是通向精通 DevOps、提升技能并为团队和组织做出有意义贡献的步伐。从本质上讲,你在 DevOps 的旅程就是我们在这一章中讨论的所有内容的体现。
所以,让我们带着新获得的理解,准备动手实践。接下来,我们将深入探讨 Git 的基础用法,Git 是作为 DevOps 文化和协作的支柱的版本控制系统(VCS)。
进一步阅读
-
改善开发者体验的秘密公式 揭晓!: https://www.youtube.com/watch?v=mRqoVlhtVzA
-
DevEx: 实际驱动 生产力的因素:
queue.acm.org/detail.cfm?id=3595878
-
GitHub 入门指南 针对创业公司的使用:
www.youtube.com/watch?v=K5zhNxnrVW8
第二章:Git 入门
让我们深入学习 Git。本章将介绍 Git 的基本用法。然而,正如上一章所解释的,这本书不仅仅是介绍概念和方法。它强调沟通和协作。因此,尽管您无疑能够掌握 Git 的基本用法,但您还将了解背后的沟通方面,以及 Git 如何在团队开发中使用。
在本章中,我们将通过快速实践文件管理和分支的基本操作,首先让您了解 Git 的基础知识,然后我们将介绍 Git 的工作原理。接着,您将了解与 Git 合作时作为工程师需要注意的协作原则。
本章将涵盖以下主要主题:
-
Git 入门
-
Git 的基本构造——适合初学者的 Git 工作原理解释
-
成为 Git 通信的高手
技术要求
继续进行此部分的配置说明可以在以下 GitHub 仓库链接中找到。请确保已安装 Git 和 ssh 工具。对于 Windows 用户,建议使用 PowerShell。我还建议您获取有关不同命令和环境的最新信息:
github.com/PacktPublishing/DevOps-Unleashed-with-Git-and-GitHub
Git 入门
在这一部分,我们将假设您在个人环境中工作,并仅仅是在建立历史记录,来进行 Git 的使用介绍。
Git 基础——从实践开始
在深入细节之前,让我们先进行一些实践体验。通过动手操作来理解概念通常比仅仅阅读更容易。
git config——向 Git 介绍自己
现在,在开始项目之前,您需要做一件事。向 Git 介绍自己。要向 Git 介绍自己,使用git
config
命令:
$ git config --global user.name "John Doe"
$ git config --global user.email "john@example.com"
git config
是用于设置 Git 配置的命令,适用于系统、用户和仓库级别。system
级别适用于所有用户和所有仓库。global
级别适用于特定用户的所有仓库。local
级别仅适用于单个仓库。
要验证您的介绍是否已注册,可以使用git congif --``list
命令检查:
$ git config --list
user.name=Your Name
user.email=Your Email
现在,第一个任务完成了!让我们快速进入 Git 的基本操作。
git init——您的代码旅程从这里开始
就像每一段伟大的旅程都有其起点一样,在 Git 的世界里,你代码的旅程从 git init
命令开始。此命令用于初始化一个新的 Git 仓库,并开始跟踪现有的目录。当你运行此命令时,它会设置一个包含版本控制所需所有文件的 .git
目录。完成这些后,你就可以开始使用 Git 的各种命令来跟踪和更新你的项目:
# Make a directory
$ mkdir handson-repo
# Change the current working directory
$ cd handson-repo/
$ git init
重要提示
另一种方法是将目录名作为参数传递,如 git init handson-repo
;这将创建该目录,因此你不需要运行 mkdir
命令。
现在,.git/
目录已创建,文件的更改保存在该目录下,但 Git 并不会像近期的 Microsoft Office 产品那样自动保存文件。
在 Git 中,保存是通过执行 git add
命令完成的,该命令有意识地从已编辑、已添加或已删除的文件中选择要保存的文件,接着 git commit
命令将这些更改注册到历史中。
接下来,让我们往仓库中添加一些代码。
git add – 为你的代码准备亮相
git add
命令是你在工作目录中进行更改与准备将这些更改永久存储到 Git 仓库之间的桥梁。当你对文件进行修改时,Git 会识别到文件已被更改,但这些更改不会自动成为历史的一部分。这时,git add
就发挥作用了。可以把它理解为保存一个文档的过程。
首先,在你的 handson-repo
目录中创建一个新文件:
# Making a README.md file
$ echo "# README" > README.md
git status
命令显示仓库的当前状态,显示哪些文件的更改是已跟踪的,哪些是未跟踪的。当你看到 Untracked files
消息时,Git 就是在告诉你有一个文件它还没有被告知要监视。在我们的例子中,README.md
文件是 Git 新增的文件,并未被注册,因此它被标记为未跟踪:
# Checking how Git recognizes the new file
$ git status
On branch main
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
README.md
新添加的文件是项目的一部分,但 Git 尚未跟踪它们。要将它们从未跟踪状态转移到已跟踪状态,你需要使用 git add
命令:
$ git add README.md
$ git status
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: README.md
现在,Git 已经识别 README.md
为一个新文件,并且它现在已被跟踪。git add
命令所保存的状态就是通过 git add
命令将工作树中的 README.md
文件标记为已跟踪。
重要提示
git add
还有其他选项。你可以使用 git add .
来包括所有文件,或者像 git add file1.md file2.md file3.md
这样包含多个文件,或者使用通配符如 git add *.md
来添加所有 .md
扩展名的文件。
一切就绪;现在是时候将你的修改记录到历史中了。
git commit – 锁定你的开发进度
git commit
命令将你通过 git add
暂存的更改记录到仓库历史中。这使你能够随着时间跟踪更改。
想象一下您在玩一款具有挑战性的视频游戏。随着您的进展,您经常会保存游戏以锁定成就。类似地,在开发软件时,您将使用git commit
保存工作。每个提交都是一个保存点,以后如果需要可以返回到该点。
要提交更改,通常可以执行以下操作:
$ git commit -m "Initial commit with README.md"
在这里,-m
标志后面跟着一个简短的描述性消息,捕捉您所做更改的精髓。编写良好的提交消息是一门艺术,因为它有助于理解更改的历史和意图。
现在,让我们再次使用git status
命令查看当前工作目录中的所有更改是否已保存:
$ git status
On branch main
nothing to commit, working tree clean
如果出现消息 nothing to commit
,则表示您的更改已经合并。
好了,就这些;在 Git 中保存文件非常容易。让我们在这里回顾一下。编辑、暂存和提交流程始终保持不变,无论您的项目有多复杂:
- 编辑文件:对文件进行必要的更改。例如,在这个示例中,已编辑两个现有文件进行删除和修改,并添加了另一个文件:
图 2.1 – 编辑文件
- 暂存更改:决定要提交的文件或特定更改,并将它们暂存。例如,在这个示例中,三次编辑中只有删除和修改被暂存:
图 2.2 – 暂存更改
- 提交更改:在满意暂存的更改后,执行提交以注册它们。请记住,每次提交都会生成一个唯一的提交 ID:
图 2.3 – 提交更改
git log – 遍历提交树
一旦您进行了几次提交,您可能希望回顾并查看存储库中所做更改的历史记录。这就是git log
命令派上用场的地方。该命令按时间顺序反向显示存储库中进行的提交列表,即最近的提交首先显示。
要尝试此操作,请使用以下命令:
$ git log
commit a16e562c4cb1e4cc014220ec62f1182b3928935c (HEAD -> main)
Author: John Doe <john@example.com>
Date: Thu Sep 28 16:30:00 2023 +0900
Initial commit with README.md
这将显示所有提交的列表,每个提交都包含以下内容:
-
独特的 SHA-1 标识符:这充当提交的签名,并可以在各种 Git 命令中用来引用特定的提交
-
提交者的详细信息:显示执行提交的个人的姓名和电子邮件地址
-
提交的时间戳:显示提交的时间
-
提交消息:捕捉提交中改动的要点的简短和信息丰富的注释
除了基本的git log
命令外,还有许多选项可以让您根据需要定制输出:
-
git log -p
: 这个选项显示每个提交引入的差异(即补丁)。 -
git log --stat
: 这提供了每个提交的简略统计信息 -
git log --oneline
: 这提供更紧凑的输出,将每个提交显示为单行 -
git log --graph
:该命令以 ASCII 图形布局可视化分支和合并历史。 -
git log --author="John Doe"
:此命令筛选提交,仅显示由特定人员(在本例中为“John Doe”)进行的提交。
例如,它还可以改善外观,如下所示:
$ git log --graph --pretty=format:'%x09 %h %ar ("%an") %s'
输出结果如下所示:
图 2.4 – 美化后的 git log
现在让我们尝试使用git log
命令。首先,更新README.md
文件,并创建一个新的CONTRIBUTING.md
文件:
$ echo "# CONTRIBUTING" > CONTRIBUTING.md
$ echo "# README\n\nWelcome to the project" > README.md
$ git add .
$ git commit -m "Set up the repository base documentation"
完成后,添加一个示例 Python 代码:
$ echo "print('Hello World')" > main.py
$ git add .
$ git commit –m "Add main.py"
当在日志中确认已正确记录时,任务完成:
$ git log --oneline
344a02a (HEAD -> main) Add main.py
b641640 Set up the repository base documentation
a16e562 Initial commit with README.md
从本质上讲,git log
命令是任何开发者都必备的重要工具。它帮助你轻松浏览代码的历史,无论是查找特定的更改,还是仅仅回顾之前的工作。
现在我们已经回顾了到目前为止关于git log
功能的内容。
使用分支 – 协作的基石
虽然前面的部分已经为你提供了如何初始化和管理 Git 仓库的深入理解,但分支的概念将这一过程提升到了一个新层次。虽然仅使用git commit
会创建一个线性历史,git branch
可以用来创建一个并行环境的历史。然后,你可以将这些多个环境合并为一个,这使得多人可以共同开发,同时为你提供了灵活性,能够在不影响主代码库的情况下尝试新功能、修复 bug,甚至是完全的前卫创意。
git 分支 – 理解 Git 分支的基础
当你初始化一个 Git 仓库时,它会自动创建一个默认分支,通常叫做main
(之前称为 master)。当你运行git branch
命令时,它会显示仓库中所有分支的列表,并高亮当前分支:
$ git branch
* main
直观地可以将线性主分支视作如下图所示:
图 2.5 – git 分支
你可以使用git branch <分支名称>
命令创建一个新分支。此命令从当前分支创建一个新分支:
# Create a new branch named 'feature/new-feature'
$ git branch feature/new-feature
如果你创建一个新分支,你可以建立一条具有不同历史的分支,并向该分支添加提交,示例如下:
图 2.6 – 创建新分支
分支命名规范对于沟通非常重要。常用的标准是将分支名称前缀为feature/
、bugfix/
或hotfix/
,后跟简短描述。这使得任何人都能一目了然地理解分支的目的。
你还可以从一个特定的分支或提交创建分支,与你当前所在的分支不同。这在你需要创建一个特性分支或修复 bug 的分支时特别有用,这些分支应该从指定的开发或暂存分支而不是从当前工作分支创建:
# Create a branch from a specific branch
$ git branch <new-branch-name> <base-branch-name>
# Create a branch from a specific commit
$ git branch <new-branch-name> <commit-hash>
git checkout/git switch – 在分支之间切换
在日常工作流中,你将经常需要从一个分支切换到另一个分支,特别是在同时处理多个特性或修复 bug 时。当你已经在多个分支上开始工作时,了解你当前所处的分支变得尤为重要。在 Git 中,HEAD是指你当前正在处理的分支的最新提交。
更改当前工作分支被称为切换分支。git checkout
命令可以实现这一操作:
# Switch to 'feature/new-feature' branch
$ git checkout feature/new-feature
这个操作将 HEAD 位置,即分支的最新提交,切换到名为feature/new-feature
的分支:
图 2.7 – 切换分支
git checkout
命令将当前的位置切换到feature/new-feature
分支的最新提交,即 HEAD。
Git 的最新版本还提供了git switch
命令,它提供了一种更直观的方式来切换分支:
# Switch to 'feature/new-feature' branch
$ git switch feature/new-feature
有时,你可能会发现创建一个新分支并立即切换到它更为高效。Git 提供了一个简化命令,结合了git branch
和git checkout
或git switch
的功能。
若要在一步操作中创建并切换到一个新分支,可以使用git checkout -b
命令:
# Create and switch to a new branch
$ git checkout -b feature/another-new-feature
这相当于执行以下命令:
$ git branch feature/another-new-feature
$ git checkout feature/another-new-feature
在 Git 的最新版本中,你也可以通过使用-c
选项和git switch
实现相同的操作:
# Create and switch to a new branch
$ git switch -c feature/another-new-feature
现在,你不仅可以通过git commit
线性保存更改,还可以通过git branch
创建平行世界,并通过git checkout
在平行世界之间自由切换。现在,是时候合并这两个世界了。
git merge <分支名称> – 合并分支
一旦你在某个分支上做出了更改并彻底测试过,你可能希望将这些更改合并回主分支或其他分支。这个操作被称为合并:
# First, switch to the branch you want to merge into
$ git checkout main
# Now, merge your feature branch
$ git merge feature/new-feature
合并允许你将有不同历史的代码行合并在一起,如下图所示:
图 2.8 – 合并分支
合并操作可能很简单,但如果分支之间存在冲突,操作会变得复杂。在这种情况下,Git 需要手动干预以解决冲突。冲突解决将在下一章讨论。
git branch –d – 删除一个分支
一旦一个分支成功合并并且不再需要,它可以被删除,以保持仓库的整洁:
# Delete a local branch
$ git branch -d feature/new-feature
因此,到目前为止,在实践教程中,你应该已经掌握了 Git 的基础,并对其有了一定的了解。你现在应该清楚你的项目中发生了什么,并学会了如何使用基本命令。
另一方面,你仍然需要学习如何灵活地在团队中协作。未来,你将学习这些方法,但同样有必要理解 Git 的实际工作原理,才能从根本上理解这些操作。
了解 Git 的机制可以加深你对命令本质作用的理解,不仅能提升你在 Git 操作中的熟练度,还能改善你在 Git 和 GitHub 中的沟通,从而促进 DevOps 的发展。
让我们来看看 Git 是如何工作的。
Git 的结构 – 一个对初学者友好的解释,讲解 Git 是如何工作的
Git 非常强大,尤其是在项目变得更加复杂时。到目前为止,我们的重点是直观的历史记录。然而,Git 真正的优势在于其处理大型项目的能力,尤其是当有大量贡献者参与并且能够无缝地管理团队中动态发展的代码时。我们通过直观的方式,像使用命令一样操作 Git。现在是时候深入探讨了。虽然对 Git 有直观的了解是有帮助的,但通过理解 Git 在幕后是如何运作的,我们可以充分发挥它的潜力。
Git 中的文件生命周期
在 Git 中,我们在上一节中学到,保存更改是一个两步操作,即暂存和提交,但 Git 实际上将文件处理为四种状态。
你项目中的每个文件都可以处于四种状态之一:
-
未跟踪:这些是存在于你的目录中的文件,但尚未被 Git 控制。它们是新文件,或是 Git 被明确告知忽略的文件。
-
未修改:这些是之前已经被添加到 Git 中,并且自上次提交以来没有任何变化的文件。它们安静地存在,被 Git 监控,但不需要立即采取任何行动。换句话说,它们是已提交的。
-
已修改:一旦你对已跟踪的文件进行更改,Git 就会将其标记为已修改。此时,该文件自上次提交以来已经被更改,但尚未为下一次提交做好准备(或暂存)。
-
git add
命令会暂存你的修改。虽然这些更改已被标记,但在你提交之前,它们并未被保存到仓库中。
下图显示了这些状态的转换。文件在这些状态之间来回变化:
图 2.9 – Git 中文件状态的四种类型
当你创建或引入新文件到项目中时,使用 git add
命令后,它们会过渡到未修改状态,表示它们现在在 Git 的监控之下。
对这些已跟踪文件的后续更改会将它们置于已修改状态。在它们被提交之前,这些更改需要被暂存,从而将文件移动到已暂存状态。暂存让你预览即将提交的更改。可以把它想象成将物品放入箱子(暂存区)并准备运送(提交)。你决定哪些物品(或更改)将放入那个箱子。
在暂存之后,你可以使用 git commit
命令提交这些更改。当你通过 Visual Studio Code 等界面进行暂存更改时,通常只需点击一个按钮。一旦你对暂存的更改感到满意,就可以提交它们,永久地将它们保存到你的项目历史中。
提交后,这些文件将恢复为 未修改 状态,等待将来的编辑或更改。换句话说,在此阶段,可以说其状态已经变为 已提交。在 Git 中,你所做的每个提交都记录了项目在特定时间点的当前状态。这个记录机制是 Git 能力的基础,确保每个更改都被文档化。这使得开发者可以回到任何特定的提交,提供了回顾或恢复到以前版本的灵活性。
此外,如果一个被跟踪的文件从目录中被删除,Git 会将其视为 未跟踪,直到它被明确地从仓库中删除。
这个生命周期为开发者提供了对项目更改的精确控制,允许进行战略性的提交,并确保清晰、有序的版本历史。
幕后 – Git 的架构
首先,Git 到底是什么?从本质上讲,Git 就是一个 .git
目录。这个隐藏目录包含了你的代码历史记录——提交、分支、配置文件等。你可能记得在 Git 学习初期,在执行 git init
命令时,会创建一个 .git
目录。
深入探索 – 探索 .git 目录
通过执行 ls
命令,你可以看到多个子目录和配置文件。其中,objects
目录与我们当前的讨论最为相关。它是 Git 键值存储的核心,存放着 blob(实际文件内容)、树对象(目录结构)和提交记录:
$ ls .git
COMMIT_EDITMSG hooks objects
HEAD index refs
config info
description logs
现在,让我们来看看 objects
文件夹。这就是键值存储所在的地方。文件夹的名称由两个字母数字字符组成,代表提交 ID 的前两个字符:
$ ls .git/objects
2f 7e b1 e3 info
34 a1 b6 e6 pack
4b af df ea
在 Git 中,每个提交或数据块都通过一个键(SHA-1 哈希)来唯一标识。这个哈希值是一个由 40 个字母数字字符组成的字符串,类似于 b641640413035d84b272600d3419cad3b0352d70
。这个每个提交的唯一标识符是 Git 根据提交内容生成的。你在执行 git log
命令时看到的这些 ID 对应着你迄今为止所做的更改:
$ git log
commit 344a02a99ce836b696c4eee0ee747c1055ab846b (HEAD -> main)
Author: John Doe <john@example.com>
Date: Thu Sep 28 18:41:41 2023 +0900
Add main.py
commit b641640413035d84b272600d3419cad3b0352d70
Author: John Doe <john@example.com>
Date: Thu Sep 28 18:41:18 2023 +0900
Set up the repository base documentation
commit a16e562c4cb1e4cc014220ec62f1182b3928935c
Author: John Doe <john@example.com>
Date: Thu Sep 28 16:35:31 2023 +0900
Initial commit with README.md
如果我们打开 b6
目录,我们会认出键值存储的结构,其中提交 ID 作为文件名或键。但这些文件里到底有什么呢?为了弄清楚,我们接下来可以使用 git cat-file
命令来查看其中的内容。
重要提示
在本书中,Hash 的前两个字符是 b6,但在你的环境中会显示不同的列表。让我们选择一个合适的哈希值并执行 ls 命令:
$ ls .git/objects/b6/
41640413035d84b272600d3419cad3b0352d70
git cat-file – 解剖 Git 的内部工作原理
要检查键值存储中值的内容,可以使用 git cat-file
命令。当传递提交 ID 的前七个字符作为参数时,我们得到的结果展示了树对象和父对象,它们分别引用了父提交的 ID:
# Passing the first seven letters of the Commit Id as an argument
$ git cat-file -p b641640
tree af4fca92a8fbe20ab911b8c0339ed6610b089e73
parent a16e562c4cb1e4cc014220ec62f1182b3928935c
author John Doe <john@example.com> 1695894078 +0900
committer John Doe <john@example.com> 1695894078 +0900
Set up the repository base documentation
重要提示
在 Git 命令中处理哈希时,不需要传递完整的 40 个字符;可以省略。这个例子中传递了前七个字符作为参数,但最少要求是四个字符。虽然具体情况取决于项目的大小,但为了避免键冲突,建议至少指定七个字符。
在 Git 中,主要管理和使用四种对象:
-
commit
对象:引用了 tree 对象 -
tree
对象:包含对 blob 和/或其他 tree 对象的引用 -
blob
对象:包含数据(如文件内容) -
tag
对象:包含有关注释标签的信息
提交引用结构中嵌入了 parent
提交的 ID。但实际的提交文件去了哪里呢?在输出中,我们看到标有 tree
的 ID 和 parent
。看起来这个 tree 对象也有一个 SHA-1 哈希值,因此让我们使用 git
的 cat-file
命令来检查其值:
# Passing the first seven letters of the Tree Id as an argument
$ git cat-file -p af4fca9
100644 blob b1b003a2...a277 CONTRIBUTING.md
100644 blob ea90ab4d...79ca README.md
100644 blob e69de29b...5391 main.py
在调用 git cat-file
命令查看与 tree
标签关联的 ID 时,我们得到的结果展示了一种名为 blob
的文件类型。让我们通过 git cat-file
命令引用 README.md
的 blob ID。这样可以显示文件内容,表明存储在键值存储中的 blob 类型数据实际上是文件本身。这些观察结果让我们对 Git 的架构有了更清晰的了解:
$ git cat-file -p ea90ab4
# README
Welcome to the project
现在你已经知道 Git 如何在键值存储中存储值。你应该理解,Git 并不是一个黑匣子;它是一个通过 SHA-1 哈希键来管理历史记录的系统。
git show – 在日常使用中更易于操作
之前我们使用了 git cat-file
命令来了解 Git 的工作原理,但有一个类似的命令是 git show
。这两个命令都是强大的 Git 工具,但它们的作用有所不同,输出也不同。git cat-file
是一个低级工具,主要用于检查 Git 对象,如 blobs、trees、commits 和 tags。它可以显示对象的类型、大小,甚至是原始内容。而 git show
更加用户友好;该命令以可读的方式展示各种 Git 对象的内容。默认情况下,它展示提交的日志信息和文本差异。然而,它也足够灵活,可以以易于阅读的格式展示其他对象类型,如 blobs、trees 和 tags:
$ git show b641640
commit b641640413035d84b272600d3419cad3b0352d70
Author: John Doe <john@example.com>
Date: Thu Sep 28 18:41:18 2023 +0900
Set up the repository base documentation
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..b1b003a
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1 @@
+"# CONTRIBUTING"
diff --git a/README.md b/README.md
index 7e59600..ea90ab4 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,3 @@
# README
+
+Welcome to the project
diff --git a/main.py b/main.py
new file mode 100644
如果你是开发人员或 Git 用户,想查看某个提交引入的更改或查看特定版本的文件内容,git show
是一个更直观的选择。相比之下,git cat-file
更深入地探讨了 Git 的内部结构,允许用户直接与原始 Git 对象进行交互和检查。对于那些深入参与 Git 内部工作或开发与 Git 核心系统接口工具的人来说,git cat-file
提供了一个详细的层次。然而,对于大多数日常任务,以及那些刚刚开始接触 Git 和 GitHub 的用户,git show
提供了一种更友好的方式来查看更改和内容,无需深入了解 Git 对象数据库的复杂性。
Git 树结构
现在,我们知道 Git 本质上是一个键值存储。接下来,让我们看看每个对象是如何连接和管理的,以便作为历史数据的一致性存储。在上一节中,我们看到过关键字 tree
和 parent
,但它们到底是什么呢?现在,我们将探索提交与这些关键字所链接的对象之间的关系。
提交、树和数据块
在 Git 中,树结构的概念在维护仓库状态方面起着至关重要的作用。每个提交不仅仅是一组更改。
每个关键字的解释:
-
提交:Git 中的每个提交都有一个唯一的 SHA-1 哈希值。它通过引用一个树对象携带着仓库状态的快照。
-
树:Git 中的树像目录一样工作。它们可以引用其他树(子目录)和数据块(文件)。每个树都有其独特的 SHA-1 哈希值。表示仓库顶级目录的主树被称为根树。
-
数据块:数据块表示仓库中文件的内容。像提交和树一样,每个数据块都有其独特的 SHA-1 哈希值。
父与子
仓库历史的传承和进展通过提交之间的父子关系得以记录。
大多数 Git 提交都引用单个父提交,表示仓库时间线中的直接前驱。
如前一页所解释的,提交持有其父提交的 ID,建立了引用关系。在许多提交的可视化表示中,箭头通常表示这种关系。值得注意的是,这些箭头的方向通常与提交的顺序相反。每个提交都具有图中所示的关系。
图 2.10 – Git 提交关系
重要提示
提交有时会有多个父级,特别是在两个分支合并时。这种双重父级标志着两个独立开发线的合并。
Git 如何存储树和数据块?
Git 高效性的奇妙之处在于它如何存储其树和数据块。
让我们通过以下方式说明这些关系:
图 2.11 – Git 树结构
Git 中的每个提交都对应一个树,表示在特定时刻仓库中文件和目录的状态。要更深入地了解,可以参考所提供的图表。标签为 fb36640
的提交包含了对树 d6f50a2
的引用。这个树反映了该提交时仓库的根目录状态。
当我们遍历这棵树(d6f50a2
)时,我们会遇到各种指针。有些指向 blobs,有些指向 trees。像 2d69956
这样的 blob 对应一个文件——在这种情况下是 LICENSE
。而像 1d0f85d
这样的 tree 则代表一个名为 contents
的子目录。这个子目录树可以进一步指向它自己的一组 blobs 和 trees。
这种复杂的关联构建了一个类似传统文件系统的层级结构。这个层级的每一层代表着你仓库中不同的文件和目录。Git 设计哲学的核心是高效。通过以这种层级结构存储数据,Git 能够快速跟踪文件和目录的变化,而无需冗余存储。例如,不变的文件在不同的提交中会指向相同的 blob,从而优化了存储和检索:
图 2.12 – Git 中的高效文件管理
理解 Git 的树结构以及它与 blobs 和 commits 之间的关系是每个开发者的基础知识。这不仅仅是使用 Git 命令;更重要的是理解其背后的巧妙架构,确保代码的历史得到高效且准确的保存。当你在 Git 的旅程中不断前进时,这些知识将帮助你充分利用 Git 的功能。
到目前为止,你已经学会了 Git 如何识别每个文件的状态,并将其记录在一个键值存储中。你还了解到,这个键值存储采用树形结构,具有父子关系。如果你深入 .git/
目录,你会发现它有一个非常简单的结构,正是因为这个简单,Git 才能高效管理复杂的项目。
如果你已经理解了这些,那么你已经为掌握 Git 之旅做好了充分准备。尽管本书无法覆盖每个 Git 命令,但理解这些基础将确保你在未来遇到不熟悉的 Git 命令时也能应对自如。你已经具备了理解它们的技术基础。
现在,让我们学习另一个内容,以结束你在本章中对 Git 之旅的准备工作。那就是 Git 之旅的思维方式。这个思维方式不仅适用于使用 Git,还直接与 DevOps 中的协作相关。
成为 Git 通信的专家
让我们稍作停顿。尽管大多数资源可能会催促你快速学习 Git 的基本命令、冲突解决、合并类型和 Git 工作流,但我们选择暂时休息一下,专注于问题的核心。
那么,为什么 Git 一开始会被开发出来呢?从本质上来说,Git 是为了简化复杂开发项目中的沟通而创建的。由于本书的目的是提升你在 DevOps 团队中的角色,理解 Git 在沟通方面的力量至关重要。毕竟,DevOps 不仅仅是技术问题;它关乎于改善协作、打破壁垒和促进更顺畅的工作流程。
当你在 Git 命令和仓库中导航时,要记住,你不仅仅是在分享代码;你还在与团队进行沟通。你的提交、拉取请求和合并应该被视为在一个更广泛对话中的部分,旨在共同创造出某个宏伟的作品。
因此,在学习 Git 的过程中,专注于磨练你的思维方式。一个经过精心调整的 Git 方法不仅仅是掌握命令,它还能让你成为一名无价的团队成员,与 DevOps 环境中的整体目标保持一致。
记住:欲速则不达。花时间深入理解 Git 的基础知识,你不仅能成为一名熟练的编码员,还能成为你 DevOps 团队中的杰出合作者。
git commit – 重新审视最重要的命令
如果你问我,Git 中最重要的命令是 git commit
。如果这个命令做得完美,其他的一切都是次要的。这个命令界定了你所有编码活动的范围,并巩固了你的输出,决定了你工作的质量。一次提交就像是一个沟通单元。如果这个沟通单元出错,所有后续的沟通都会变得混乱。
你有没有用乐高积木做过什么东西?乐高的精髓不仅仅在于按照说明书操作,更在于激发创造力并打造独特的作品。有时,你还需要和朋友一起合作,像一起建造一个城堡。每个人可能都有一个角色:有人负责做城门,有人负责做地基,还有人负责做塔楼。你们的成功在于作为一个团队共同打造出一个宏伟的城堡。但考虑一下:即使你个人做了一部分很棒的作品,如果它不符合其他人设想的效果,或者和他们做的部分不匹配,它可能不会得到认可。确保各个部分的尺寸匹配并经常检查其兼容性是至关重要的。换句话说,你需要在正确的时机进行沟通,并不断调整建造的方式和各个部件。小时候,你可能做过一些奇怪的作品,虽然缺乏完美的沟通,但由于富有创意还是会受到赞扬。然而现在情况不同了。如果你正在阅读这本书,你很可能在一个组织中工作,创造某种产品,可能有工资收入,并且承担责任。这意味着,这些考虑因素不仅仅是可有可无的,它们是必须要有的。
让我们回到编程。当你在团队中编程时,不仅你自己,所有团队成员(包括过去和现在的)都会审查你在 Git 中的代码和操作。即使你一个人编程,你的未来自我和过去自我也是你的合作伙伴。我曾多次因为不理解自己以前写的代码或忘记自己当时想要实现什么,通过运行 git log
命令后还是毫无头绪。Git 操作管理得如何,将直接反映在代码管理和生产流程中。最好有一个你可以记住的原则来使用 Git。
控制质量和数量,成为一个优秀的沟通者
关于如何使用 Git,存在很多良好的实践。然而,许多内容可以归类于下图的四个框中的某个位置:
图 2.13 – Git 提交的卓越实践
早期和频繁提交 —— 采用 DevOps 成功的核心原则
过去几十年,现代软件开发的格局发生了巨大的变化。传统的瀑布式开发方法已被敏捷方法取代,进一步演变为如今许多组织所采用的 DevOps 文化。这个演变的核心概念就是 持续集成和持续交付 (CI/CD)。支持这一方法论的基础实践之一就是早期和频繁提交代码的理念。本节将深入探讨这一实践的重要性,尤其是在 DevOps 的 Git 和 GitHub 上的应用。
传统范式
在过去,开发人员通常会花费几天甚至几周的时间来开发一个功能或修复,然后在最后将其合并到主分支。这通常会导致合并冲突、错误和大量手动干预以修复问题。这种模式不可扩展,与今天快速发展的、以客户为中心的技术环境需求背道而驰。
向早期提交的转变
尽早提交意味着,当你完成一部分逻辑性的工作时,你就应该提交。这并不一定意味着整个功能已经完成,可能只是某个函数或类。为什么这样做有益?
-
更小的更改:较小的更改更容易审查。它们更易于理解,使得代码审查过程更高效、更有效。
-
减少合并冲突:通过尽早提交和推送你的更改,你可以减少遇到合并冲突的机会,因为你会频繁地将分支与主分支同步。
-
更快的反馈循环:你越早提交和推送更改,自动化测试就能越早运行,你就能越早获得关于代码的反馈。这有助于更快地迭代和更快地交付功能和修复。
经常提交的好处
经常提交与尽早提交是相辅相成的。你提交的频率越高,以下几点就越容易做到:
-
更容易找到问题:如果出现 bug,通过筛选一个小的提交要比筛选一个大的提交要简单得多。
-
更容易回滚:如果一个提交引起了意外问题,回滚到之前的稳定状态非常简单。这种安全网在生产环境中可能是一个救命稻草。
了不起的提交信息 - 为您的代码打造清晰、信息丰富和简洁的叙述
在软件开发领域,我们编写的代码不仅仅是机器的一组指令;它也是与同行交流的一种形式。然而,代码展示了如何做到,而提交信息则阐明了为什么这样做。一个精心制作的提交信息是同行开发者的灯塔,提供上下文、清晰度和代码变更的历史记录。
提交信息的目的
在其核心,提交信息具有几个关键功能:
-
历史记录:它提供了变更的按时间排序的账户,使开发人员能够理解代码库的演变。
-
上下文:它提供了变更的理由,提供了单单代码本身无法传达的洞察力。
-
文档:除了内联注释和外部文档之外,提交信息还充当一种文档形式,可以解释决策和权衡。
优秀提交信息的特征
一个好的提交信息与一个了不起的提交信息有何不同?以下是出色提交信息的标志:
-
简洁的主题行:以简洁直接的主题行开头,理想情况下不超过 50 个字符,捕捉提交的核心内容。
-
git commit -m "subject-line" -m "``description-body"
命令。 -
使用主动语态:像“添加功能”或“修复错误”这样的短语比“添加了功能”或“修复了错误”更清晰、更直接。
-
引用问题:如果提交与跟踪系统中的特定问题或任务相关,请引用其 ID 或链接,有助于可追溯性。
DevOps 的联系
对于现代的 DevOps 工程师,提交信息不仅仅是一个附带的想法;它们是 DevOps 哲学的核心组成部分。
在当今的软件领域中,应用程序开发和基础设施之间的界线变得日益模糊。精通基础设施的工程师可能会发现自己阅读应用程序代码,反之亦然。更重要的是,现在两个领域的工具都与 Git 集成,使专业人士能够在各种平台上查看提交信息以及 Git 哈希值。
鉴于这种集成,提交信息不再局限于其仓库或团队。它开始跨越不同的组织边界和平台。这一过渡突显了精心编写的提交信息作为一种通用语言的价值,它可以被软件开发和交付流程中的不同利益相关者理解和欣赏。在打破组织孤岛并改善开发者体验的过程中,你的优秀提交信息将助力你的旅程。
以下是一些优秀提交信息的建议:
-
透明性至关重要:清晰的提交信息使得从开发到运营的团队能够理解代码变更,减少摩擦并促进协作。
-
持续交付依赖历史:随着组织推动更频繁的发布,快速理解和验证变更的能力变得至关重要。信息丰富的提交信息是这一过程中的重要工具。
提交信息示例
这是一个提交信息的示例。它是基础性的,你可以在此基础上添加你自己的上下文,但不应过长:
类别 | 示例 提交信息 |
---|---|
简单变更 | 添加README.md |
更新许可证到期日期 | |
移除已废弃的方法 XYZ | |
功能添加/更新 | 实现用户认证流程 |
为主页添加搜索功能 | |
扩展 API 以支持版本控制 | |
错误修复 | 修复导致会话超时的登录错误 |
解决数据处理模块中的内存泄漏 | |
修正用户注册表单中的拼写错误 | |
重构和代码 质量改进 | 重构数据库连接逻辑以支持连接池 |
优化图片加载以加速页面渲染 | |
改进支付网关中的错误处理 | |
文档 和注释 | 记录 XYZ 模块中的主要算法 |
更新 X 函数中的注释以提高清晰度 | |
修订 API 文档以包含新端点 | |
恢复变更 | 恢复“添加实验性功能 X” |
回滚到缓存层更新之前的稳定状态 | |
依赖项和 外部集成 | 升级到 ABC 库的 v2.1.3 版本 |
集成 XYZ 框架的最新安全补丁 | |
带有问题/任务 跟踪引用 | 修复#1234:处理订单结账中的边缘情况 |
功能#5678:添加多语言支持 | |
合并操作 | 合并分支‘feature/user-profiles’ |
解决 main.css 中的合并冲突 | |
与测试相关的变更 | 为工具函数添加单元测试 |
重构集成测试以使用模拟数据 | |
修复用户注册流程中的不稳定测试 |
表 2.1 – 提交信息示例
单一功能代码
在不断发展的软件开发领域中,敏捷和 DevOps 实践倡导快速迭代和强有力的协作,单一功能代码的原则变得愈发重要。它的影响力不仅限于代码结构的领域,甚至渗透到开发者体验和 Git 通信的核心。让我们深入探讨这些概念之间深刻的联系。
与敏捷原则的契合
敏捷方法论的核心理念是促进适应性、持续改进,并以小而可管理的增量交付价值。单一功能代码与这些原则无缝契合:
-
增量开发:就像敏捷将特性拆解为更小的用户故事或任务一样,单一功能代码鼓励将软件组件拆分成专注的、可管理的模块。
-
适应性:单一功能组件更易于修改或替换,符合敏捷方法论对变化的拥抱。
提升 DevOps 流水线
DevOps 强调软件的持续集成和交付,架起了开发与运维之间的桥梁。此时,单一功能代码的优势便显现出来:
-
精简的 CI/CD:由于代码组件专注且独立,集成过程中一个模块意外影响另一个模块的几率降低,从而使得 CI/CD 流水线更加顺畅。
-
更好的监控和日志记录:当组件具有单一责任时,监控其行为和记录相关事件变得更加容易。任何异常都可以直接追溯到特定功能。
提升开发者体验
开发者体验(DX)的概念围绕着让开发者的工作更轻松、提高生产力和减少摩擦展开。单一功能代码在这一过程中扮演了至关重要的角色:
-
直观的入职体验:当代码库由清晰且专注的组件组成时,新团队成员能够更快理解并做出贡献。
-
高效的调试:由于每个组件只做一件事,识别和解决问题变得更加高效。
与 Git 通信的深厚联系
如前所述,单一功能代码与 Git 之间有着深刻的协同作用:
-
清晰的提交信息:编写单一功能代码能生成精确的提交信息。对单一功能或模块的更改可以在 Git 中简洁地描述,从而促进透明度和沟通清晰度。
-
简化的代码审查:在 GitHub 等平台上,若 Pull 请求围绕着专注的变更进行,审查过程将变得更加直接。审阅者更容易理解意图并验证实现,从而带来更有意义的反馈和协作。
完整的代码:在精确性和进度之间找到平衡
在软件开发领域,完美与进步之间的长期矛盾始终存在。开发人员常常会遇到这样的问题:我的代码什么时候才算准备好了?在本文中,我们旨在阐明“完整代码”这一概念,这一理念强调在不陷入对完美的难以实现追求中,提供稳健、完全实现的解决方案。
完整代码的本质
完整代码背后的理念既简单又深刻:任何编写的代码都应在意图和执行上是完整的。这意味着:
-
没有半途而废:无论是在实现功能还是修复 bug 时,代码都应该完全达成目标,而不是部分地或表面地实现。
-
准备审查:代码应该达到足够的质量,准备接受同伴审查。这不仅意味着功能正常,还意味着符合团队或组织的编码标准和最佳实践。
-
附带测试:在适用的情况下,代码应附带测试,确保其不仅现在能按预期工作,而且随着软件的演进,依旧能够继续保持这种效果。
完成比完美更好
虽然重点放在完整性上,但必须认识到,追求完美可能是一个适得其反的行为。软件本质上是迭代的,等待完美的解决方案可能会拖慢进展。口号“完成比完美更好”提醒我们以下几点:
-
迭代改进是关键:即使一开始解决方案不是最优的,也没有关系。它需要能工作,之后可以随着时间不断改进。
-
反馈推动完美:通常,发布代码并收集反馈比无休止的内部迭代能得出更好的解决方案。
测试在完整代码中的角色
测试是完整代码哲学中的关键:
-
验证:测试验证代码是否按预期执行,提供防止回归的安全网。
-
文档:精心编写的测试也充当文档的角色,提供关于代码预期行为的洞察。
-
自信:测试能增强信心。当开发人员编写有测试保障的完整代码时,他们可以在修改或添加功能时确信,如果代码出现问题,自己能及时发现。
现代开发实践的重要性
在敏捷和 DevOps 主导的时代,CI/CD 管道自动化软件交付,完整代码的重要性愈加凸显:
-
简化的管道:CI/CD 管道的执行考虑到集成的代码可能是不完整的。但这并不意味着代码随时都可以不完整,若失败过多,你团队的测试统计数据将变为红色。不完整的代码会破坏管道,造成瓶颈和低效,并且是你团队代码质量的体现。
-
协作效率:在团队合作中,当开发者提交完整且可以审查的代码时,能够促进更顺畅的协作。审查者花费更少的时间指出基础问题,更多的时间可以用来深入讨论架构或逻辑问题。
现在你已经理解了如何在 Git 中保留历史记录。编写提交信息是件让人头疼的事。你需要在仅仅几十个字符内描述你的工作。然而,在 Git 协作中,关注这些细节是非常重要的。
因为你花了几个小时来进行提交(可能是几十个小时),如果因为一些小细节的疏忽而破坏了合作的质量,那真是太可惜了。只需要多花几秒钟或几分钟的时间关注细节,就能让协作变得更好。
卓越藏于细节。让我们练习并成为真正的大师!
总结
在结束本章时,你应该对 Git 的基本功能、底层机制以及最重要的协作哲学更加熟悉了。理解这些基础知识通常比死记 Git 命令或存储快捷代码片段更为重要。
如果你已经内化了本章的洞见,放心,你已经牢牢掌握了 Git 的基础知识。你不仅准备好提交代码,还准备好在团队中成为一个协作的力量。
当你第一次加入一个由 Git 管理的项目时,最初的挑战通常不是解决复杂的冲突或处理混乱的分支;而是如何编写有意义的提交,并证明自己是一个不可或缺的团队成员。每一次经过深思熟虑的提交,不仅有助于项目的发展,还能巩固你在团队中的地位。
现在,让我们继续深入学习更高级的 Git 使用方法,帮助你在团队中大放异彩。
第三章:高级 Git 用法用于团队协作
现在,我们进入实际协作的部分。在本章中,我们将深入探讨你希望采用的各种协作实践。你将学习如何组织提交历史、管理复杂的分支,并在合并时解决冲突。本章的目标是帮助你能够很好地控制这种分支流的团队协作。
在这里,重点不仅仅是完成工作,而是以一种能增强团队合作的方式来完成。要认识到,管理整个代码库背后有一套深思熟虑的策略,旨在最大化团队的生产力和影响力。根据产品或项目的类型、团队的规模及其成熟度,会采用不同的策略。在我们深入 Git 命令之前,理解这个基础策略——分支策略,是至关重要的。
那么,让我们开始让你的协作变得更加顺畅高效吧。
本章将涵盖以下主题:
-
团队协作的分支策略
-
在分支上应用变更的方法
-
处理冲突
-
掌握更好的协作
技术要求
继续本部分的配置说明可以在以下 GitHub 仓库链接中找到。请确保已安装 Git 和 SSH 工具。对于 Windows 用户,建议使用 PowerShell。我也鼓励你获取关于不同命令和环境的最新信息。
github.com/PacktPublishing/DevOps-Unleashed-with-Git-and-GitHub
团队协作的分支策略
在团队协作的领域中,提交是至关重要的构建块。这些提交链接在一起,形成一个按时间顺序排列的历史记录,记录了你的项目的演变。这一历史记录通过分支进行组织和维护。
那么,工程师和团队如何将这些历史记录编织成一个连贯有意义的叙事呢?分支策略就是这个问题的答案。分支策略是一个有效管理 Git 中分支的开发策略,它能够促进顺畅的协作和服务交付。
为什么分支策略很重要
一个精心设计的分支策略不仅仅是锦上添花;它在团队开发环境中至关重要。你的分支策略会对你的 DevOps 流程产生连锁反应,影响部署单元和工作流效率。顺利协作的能力不仅依赖于团队内部的良好沟通,还会影响你的产品发展速度。这与我们在前几章中覆盖的所有元素相关,例如 CI/CD 测试的频率和方法论。本质上,你的分支策略在消除组织摩擦方面至关重要,而这正是 DevOps 的最终目标。
以下是为什么深思熟虑的分支策略是不可妥协的原因:
-
变更隔离:它允许单独的团队成员在不干扰彼此工作的情况下,处理不同的功能或缺陷。
-
风险缓解:分支策略可以保护主分支(通常称为 master 分支)不被未经测试或不稳定的代码破坏。
-
促进协作:通过良好的分支策略,多个团队成员可以并行在不同的分支上工作,从而提高团队的整体效率。
在 DevOps 的背景下,分支策略还具有以下功能:
-
自动化测试集成:分支策略可以设计为在不同阶段触发自动化测试。这样可以确保只有经过充分测试的代码才能合并回主分支,从而支持持续集成。
-
简化部署:一个组织良好的分支结构可以简化部署过程,使得代码更容易从开发环境移至暂存环境,最终到生产环境。
-
增强开发者体验:通过提高协作的透明度和效率,它改善了整体的开发者体验(DX),这是成功 DevOps 的关键策略。
-
特定环境的分支:为特定环境(开发、暂存、生产等)设置分支,有助于实现更加平滑和可控的部署。
-
增强安全性:通过在分支之间建立清晰的边界,分支策略可以通过控制对敏感代码的访问,并确保在合并到关键环境之前,所有更改都经过适当的审查和批准,从而增强安全性。
分支策略与分支政策
在软件开发和 DevOps 领域,分支策略和分支政策这两个术语可能看起来是同义的。虽然它们经常被混淆,但必须认识到,分支策略是一个更广泛的概念,涵盖了分支政策。
分支策略是一个全面的计划,概述了如何在开发工作流中管理、创建和集成分支。它不仅仅涉及处理分支的技术方面,还包括组织的规模、团队文化以及项目或产品的具体需求等背景变量。
分支策略,另一方面,是一组更为具体的规则或指南,用于分支管理。这些规则通常构成分支策略的骨架,作为模板,根据具体需求进行定制。有时,像Git Flow或GitHub Flow这样的策略名称(我们将在本章中介绍)被用作开发策略的名称。它们应该被视为分支策略的一种类型。著名的软件思想领袖马丁·福勒在他的文章《源代码分支模式管理》(Patterns for Managing Source Code Branches)中并未将这些视为策略本身,而是在观察一些分支策略部分进行讨论,具体内容请参见他的文章(martinfowler.com/articles/branching-patterns.html
)。
因此,在建立分支方法时,选择一个作为基础的分支策略至关重要。这个策略应根据组织的独特需求和目标进行定制,以有效减少开发过程中的摩擦并加速软件发布。这种定制化的方法确保了分支策略不仅优化了工作流程,还与团队的文化和组织方面无缝融合。
较小且频繁的修改 vs 较大且不频繁的修改
目前存在许多分支策略。公司通常会创造特定的分支策略名称,并将其发布为最佳实践,如GitHub Flow。从根本上讲,所有的分支策略都可以映射到两个原则之一:频繁地进行较小的修改,或者偶尔进行较大的修改。
对于小型团队来说,集成变更和快速发布的摩擦自然较小。但在大型组织中,尤其是涉及大型产品或冗长审批过程时,摩擦不可避免地增加。更多的冲突出现,需要更多的检查来防止这些冲突。
然而,允许这些挑战拖慢开发过程可能会对产品或项目发布的及时性产生不利影响,最终影响业务的成功。
随着时间的推移,许多公司设计了各种分支策略来缓解这些问题。它们中的大多数都是现有实践的扩展,旨在减少组织或产品限制内的摩擦,目标是加速发布。
需要理解的是,没有任何一种分支策略是适用于所有情况的“万能解”。基础策略通常是根据团队的组成和文化来选择的,然后根据具体需求进行定制。
本节介绍了基于主干的开发、Git Flow 和 GitHub Flow 这三种分支策略,它们的映射如下:
图 3.1 – 分支策略映射
基于主干的开发以将较小、频繁的更改直接集成到主分支中而闻名。与此相对,Git Flow 因其较少频繁地集成较大更改的策略而著名。两者各有优缺点,选择其中之一通常取决于团队的具体需求和工作流程。
对于那些希望更频繁地发布更多更改并减少摩擦的大型组织,GitHub Flow 是一个很好的代表。这些策略基本上是将频繁小规模更改的核心原则适应大型企业的复杂性和规模。
像 GitHub Flow 这样的策略是在 Git Flow 的影响下构建的,但现在许多公司使用 GitHub Flow 作为模板,在此基础上构建他们自己的定制化分支策略。
分支策略的类型
现在,让我们来看一个典型的分支策略。
本节介绍三种典型的分支策略:
-
基于主干的开发
-
Git Flow
-
GitHub Flow
考虑根据您的团队或组织需要发布的频率以及您的产品和项目的规模来分析每种情况。
基于主干的开发
基于主干的开发(TBD)是一种软件开发方法,在这种方法中,开发人员在短生命周期的分支上工作,通常不超过一天,或者直接从一个名为主干或主线的单一分支上工作。其关键原则是尽量缩短分支的生命周期,以促进频繁的集成,并避免长期存在的特性分支所带来的问题,如合并冲突和代码库分歧。
在 TBD 中,主干始终保持工作状态并且健康,应该始终处于可部署状态。开发人员会选择一个小的功能或任务块进行开发,并尽快将其合并回主干。如果某个功能尚未准备好发布,可以使用功能标志来隐藏这些功能,直到它们完成为止,这样代码就可以合并而不影响最终用户。像功能标志这样的发布实践将在第五章中讨论。
如图所示,在 TBD 中,创建了许多短生命周期的分支,并将它们合并到主线中。
图 3.2 – 基于主干的开发
由于集成发生得很频繁,因此必须拥有一个强大的自动化测试套件,在每次代码合并到主干时进行运行。这确保了代码库始终保持稳定和可部署。持续集成(CI)工具通常与 TBD 一起使用,以自动化测试和构建过程,确保主干始终保持良好状态。
为了处理提供紧急修复的 hotfix,开发者可能会创建短生命周期的分支,这些分支在完成后立即合并回主干。这确保了能够迅速解决关键问题,而不会危及主干的稳定性。
TBD 的主要优点之一是它的简单性和专注于生成清晰、可部署的代码库。鼓励频繁的合并减少了合并冲突的可能性,并使所有开发者保持与最新版本代码的一致性。这在优先考虑快速迭代和快速交付的 DevOps 文化中尤为重要。
以下是基于主干开发的优缺点。
优点:
-
频繁集成:由于代码频繁合并,合并冲突的可能性较小,且更容易解决
-
快速反馈循环:频繁集成变更有助于在开发过程中尽早发现问题
-
简化的工作流程:没有大量长生命周期的功能分支,开发工作流程得以简化,管理起来更容易
缺点:
-
不稳定的风险:如果没有经过适当的测试,频繁的合并可能会导致不稳定的代码进入主干
-
不适合大型功能:对于非常大的或破坏性更强的变更,这种方法可能会导致问题,因为这些变更可能会使主干在较长时间内不稳定
总结来说,TBD(基于主干开发)注重快速集成,始终保持主干可部署,并通过自动化测试保持代码质量。它与敏捷开发和 DevOps 方法论非常契合,旨在通过简化开发过程来消除摩擦并改善开发者体验。
Git Flow
Git Flow 是一种主要针对健壮的项目版本控制的分支策略,特别适合有定期发布周期的项目。它引入了一种结构化的方法,涉及多种类型的分支,包括 feature
、release
、develop
和 hotfix
,以及 main
(或 master
)分支。
图 3.3 – Git Flow
在 Git Flow 中,开发工作从 main
分支创建一个 develop
分支开始。develop
分支作为功能集成的分支,是所有开发者的分支合并的地方。当你准备开发一个新功能或修复一个 bug 时,你会从 develop
分支创建一个 feature
分支。这个隔离的环境使你能够在不影响整个代码库的情况下进行工作。
随着功能的进展,增量更改会提交到这个 feature
分支。一旦功能完成并经过测试,它会被合并回 develop
分支。为了准备发布,会从 develop
分支创建一个 release
分支。这个分支用于进行最后的小错误修复或文档更新。一切准备就绪后,release
分支将合并到 main
并标记版本号。同时,它也应该被合并回 develop
,以确保未来的发布也包含这些更改。对于紧急的修复,可以直接从主分支创建一个 hotfix
分支。
Git Flow 提供了一个严格的结构,对于需要平衡稳定性和新特性的多个开发者的大型项目非常有益。它确保了开发过程是分开的但平行进行,使项目历史更易于理解和回退。
下面是 Git Flow 的优缺点。
优点:
-
结构化工作流程:适用于有计划发布周期的项目
-
隔离:功能分支允许开发者在隔离的环境中工作,使得管理复杂功能变得更加容易
-
热修复支持:专用的热修复分支使得快速修复生产版本变得更加容易
缺点:
-
复杂性:对于较小的团队或项目,Git Flow 可能引入不必要的复杂性
-
延迟集成:由于功能分支生命周期较长,这可能导致合并冲突或在开发过程中发现的错误
总结来说,Git Flow 为更复杂的项目提供了一种模型,确保代码库保持有序,并且发布管理得当。它特别适用于大型团队,其中协调和发布规划至关重要。
GitHub Flow
GitHub Flow 是一种简化的工作流程,鼓励持续交付实践。它只包括主线和短期存在的功能分支。其主要原则是分支、开发新功能、提交拉取请求、审核代码后再进行部署。拉取请求是 GitHub 的发明,是开发者通知团队成员他们已完成一个功能或修复的方式,随后该功能或修复会被审核和讨论,合并到代码库的主分支中。详细内容请参考 第四章。
GitHub Flow 很简单,但其背景不仅仅限于 Git。注意,图中包含了 GitHub 流程,如拉取请求和批准:
图 3.4 – GitHub Flow
它从创建一个新的描述性分支开始,该分支从默认的代码库分支派生,为修改提供一个安全的环境,不会影响主代码库。所有修改都提交并推送到这个远程分支。当准备好时,创建一个详细的拉取请求供团队审查,通常会与相关问题链接,以提供上下文。审查可能包括问题、建议或特定行的评论。针对反馈的后续提交会自动添加到拉取请求中。批准后,拉取请求会被合并到默认分支,将你的贡献并入主代码库。根据设置,可能需要解决合并冲突或满足批准标准。合并后,工作分支会被删除,但提交历史和讨论仍然可以访问,供日后参考。
本质上,GitHub Flow 促进了协作、透明性和增量开发,为团队项目提供了一种灵活而结构化的方式。
以下是 GitHub Flow 的优缺点。
优点:
-
简洁性:GitHub Flow 提供了一种简单的方式,只有一个主分支和短生命周期的特性分支,即使是新手也容易上手和管理。这种简洁性简化了开发过程,让开发者可以专注于提高生产力,而不是复杂的分支策略。
-
更快的部署:通过鼓励持续集成和交付,GitHub Flow 使团队能够更频繁地发布更新。这种快速的部署周期能让团队及时获得反馈并进行更快的迭代,减少从开发到市场的时间。
-
增强的协作性:GitHub Flow 中心的拉取请求机制促进了透明的代码审查和协作。它允许每个团队成员参与讨论,确保代码质量并共同拥有项目。
缺点:
-
平台兼容性考虑:采用 GitHub Flow 可增强协作与效率,特别是在 GitHub 上完全支持的情况下。然而,在与不同平台集成时,可能需要额外的工具或调整,以充分利用其潜力,确保在多种环境下无缝的项目管理。
-
适应复杂项目的能力:尽管 GitHub Flow 提供了一种简化、直接的方式,适合快速部署和持续交付,但在多团队协作、处理复杂项目时可能会遇到挑战。这种工作流注重简洁性和单一主分支开发,有时会限制对多个同时进行的开发任务或不同项目时间表的精细控制。例如,对于需要广泛集成测试或多个子团队之间协调的项目,可能需要额外的分支管理策略或更强大的发布规划机制。
分支命名规范 – 了解 Git 中命名分支的最佳实践
在 Git 和 DevOps 的领域中,命名是有效团队协作和代码管理的关键因素。当你在一堆分支中浏览时,一个清晰且具有描述性的名称能带来天壤之别,帮助你理解它们的具体功能、归属以及生命周期状态。建立一致的分支命名规范是有效分支策略的一个重要组成部分。
让我们深入探讨在 Git 中命名分支的最佳实践,旨在减少组织内的摩擦并加速发布。一个明确定义的命名规范使得工程师可以立刻理解一个分支的目的,无论它是为了某个功能、修复漏洞、热修复,还是一个实验性的尝试。在团队需要筛选数十个甚至数百个分支时,这种清晰度至关重要。命名规范为更透明、高效和精简的工作流程奠定了基础,使 DevOps 团队的每个人都能更轻松地进行协作。
分支命名规范和示例
以下是为每个主题命名的主要规范和示例。请注意,这些只是示例,每个团队的命名规范可能会有所不同。
这里有一些通用的指导原则:
-
使用破折号 (
-
)、下划线 (_
) 或斜杠 (/
) 来分隔单词。斜杠尤其常用于分隔热修复和功能等主题。 -
小写名称:虽然 Git 区分大小写,但坚持使用小写字母有助于保持一致性并避免混淆。
-
简洁但富有描述性:分支名称应该能够立刻让人理解该分支的内容,同时尽可能简洁。
以下是每个分支名称的示例:
-
feature/
,后跟简短描述。一个例子是feature/user authentication
。 -
bugfix/
,后跟简短描述。例如,bugfix/login-error
。 -
hotfix/
。例如,hotfix/xyz-security-vulnerability
。 -
release/
作为前缀。一个例子是release/v1.2
。
上下文命名
尽管这些分类提供了一个很好的起点,你也可以考虑在分支名称中加入更多的上下文信息。例如,你可以在末尾附加问题编号(如feature/123-user-authentication
),或者包括负责该分支的人员姓名(如feature/teamxyz-authentication
)。
在本节中,我们认识到,一个稳固的分支策略是任何协作开发项目的支柱。我们已经探讨了分支策略在维持稳定的代码库同时促进持续集成和交付方面的重要性。无论是采纳 TBD 的小而频繁提交、Git Flow 的结构化角色,还是 GitHub Flow 的简易性,正确的策略对于团队的成功至关重要。
记住,这些约定应该与您更广泛的分支策略和政策保持一致,并根据您的团队规模、项目以及组织的独特约束和目标进行调整。一个精心选择的命名约定将有助于强化分支政策的有效性,帮助减少摩擦并加速发布。通过遵循明确的命名约定,您能够让团队更加高效地工作,并促进清晰和责任感的文化。
在接下来的章节中,您将学习如何将不同的开发线合并在一起,同时保持代码的完整性和历史记录。让我们带着信心继续前进,将分支的知识与 Git 合并的实际技能相结合。
如何在分支上应用更改
现在,您已经深入了解了 DevOps 中分支管理和工作流的复杂性,可能已经开始看到全局。您已理解您的个人提交如何为整体开发流做出贡献。接下来的步骤是连接各个点——更具体地说,考虑您编写的代码如何被合并到主干中。
代码库是一个充满活力的协作环境,记录了各种团队成员的贡献历史。在快速发展的环境中,我们可能会因为赶进度而急于提交或一次性推送大量的更改。然而,在合并时,必须考虑到这些更改如何有助于维持一致、可理解且稳定的共享环境。特别是在 DevOps 文化中,这一点尤为重要,因为目标不仅仅是快速部署,还包括无摩擦的协作。
在接下来的章节中,我们将探讨在 Git 中执行成功合并的各种策略和最佳实践,特别是针对 DevOps 环境中出现的需求和挑战。
合并与重基(Merging vs rebasing)
Git 提供了两种主要的技术来集成这些变化:合并(merging)和重基(rebasing)。虽然它们的最终目的是相同的——将不同的代码分支合并在一起——但它们在操作上有明显的差异。在深入了解实际命令之前,让我们先区分合并和重基。
合并(Merging)
合并会将源分支的内容与目标分支集成在一起。这个新提交将拥有两个父提交,保留被合并分支的独立历史。合并可能会显得繁琐,因为它保留了各个分支的历史,但在多人同时参与项目时,它在集成过程中提供了灵活性。主要有两种合并方式:非快进合并(non-fast-forward merge),这种方式会创建一个新的合并提交来记录合并过程;以及快进合并(fast-forward merge),这种方式则不创建合并提交。还有一个叫做 squash 的功能——它可以将多个提交压缩成一个提交进行合并。
在 GitHub 等平台上,默认设置是像下面的图示一样进行合并提交:
图 3.5 – 合并示例(非快进)
以下是合并的优缺点。
优点:
-
历史保存:合并保留了两个分支的历史,提供了详细的日志
-
简单性:通常初学者更容易理解
-
分支隔离:各个分支可以继续进行独立的更改,而不影响合并
缺点:
-
复杂的日志:虽然保留了历史记录,但合并可能导致日志历史复杂且杂乱
-
缺乏线性:项目历史变得非线性,难以浏览提交历史
变基
变基是将一系列提交移动或合并到新的基础提交的过程。它本质上是将功能分支中所做的更改重新播放到另一个分支之上。与合并不同,变基不会创建新的提交,而是重写提交历史,生成一个线性的提交顺序。变基的优点是其历史的线性。回顾历史时,很容易跟随主分支发生的变化,这对于修复 bug 非常有利。
一旦掌握变基,你可以为历史记录做出无噪音的贡献:
图 3.6 – 变基
以下是变基的优缺点。
优点:
-
更清晰的历史:变基会生成一个更清晰、线性的项目历史
-
消除噪音:它移除了在执行 git merge 时出现的不必要的合并提交
-
更简便的调试:有了更清晰的历史记录,调试变得更容易
缺点:
-
共享分支风险:变基可能会破坏历史,特别是在共享分支上,因为它会重写提交历史
-
复杂性:变基可能更难理解和正确执行
两种技术都有其优缺点。合并保留了原始分支历史,但可能导致日志复杂。变基提供了更清晰、更线性的项目历史,但存在风险,尤其是在共享分支上工作时。
探索 Git 中不同的合并方式
现在让我们深入实际步骤。我们将探索合并两个分支的过程,提供实践经验帮助你更好地理解每种方法。在本节中,我们将介绍一些最常用的分支合并方法,帮助你根据项目需求做出明智的决策。
git merge --ff – 保持直线
现在让我们深入探讨实际应用。本节将聚焦于 Git 合并中的一个常见默认行为,称为 --ff
命令选项。我们将逐步讲解,确保你理解它的工作原理以及何时使用它。
快速前进合并是 Git 中集成分支的最简单方法之一。本质上,快速前进合并将目标分支的末端移动到源分支的最新提交。
使用快速前进合并时,在查看历史记录时,实际上什么也没有改变,如下图所示。这就是快速前进合并的好处。HEAD 持续移动,合并可以顺利进行:
图 3.7 – 快速前进合并
在 Git 中,当基础(或目标)分支在特性分支创建后没有新的提交时,就可以进行快速前进合并。本质上,它消除了需要新提交来连接分支的需求,从而保持项目历史的线性。
快速前进合并的实际步骤
在本指南的上下文中,假设你有一个 main
分支和一个 add-description
分支。add-description
分支是从 main
分支派生出来的,你打算将特性合并回 master
:
# Initialize a new repository
$ mkdir try-fast-forward-merge
$ cd try-fast-forward-merge
$ git init
# Add and commit initial README.md to master
$ echo "# My Project" > README.md
$ git add README.md
$ git commit -m "Initial commit on master"
# Create and switch to a new branch 'add-description'
$ git checkout -b add-description
# Make changes to add a description, add and commit changes
$ echo "This project is an example of how to use Git." >> README.md
$ git add README.md
$ git commit -m "Add project description to README.md"
此时,你的仓库结构应该类似于下图:
图 3.8 – git merge –ff (1)
现在,让我们通过切换回 main
分支来合并这些分支:
# Switch back to 'main' and perform a Fast-Forward merge
$ git checkout main
$ git merge add-description
# View the linear history
$ git log --graph --oneline
* 26d90bf (HEAD -> main, add-description) Add project description to README.md
* 37ecd54 Add project description to README.md
* a1164b9 Initial commit on master
现在,你的仓库历史应该如下所示:
图 3.9 – git merge –ff (2)
为什么在 DevOps 和团队协作中更倾向于使用快速前进合并
在后台,快速前进合并只是将 HEAD(指针)移动到最新的提交。而且,快速前进合并不会创建新的合并提交,保持 Git 历史的清晰和线性。这使得它成为一种简单且高效的操作。
快速前进合并在团队协作中通常更受欢迎,原因有几个:
-
简洁性:它们保持 Git 历史的线性,使得更容易跟踪
-
透明性:通过清晰的历史记录,更容易追踪变更、调试问题,并理解功能集成的顺序
-
效率:快速前进合并消除了额外合并提交的需求,从而简化了代码审查
然而,请记住,快速前进合并并非总是可行的。当你单独开发或进行简单开发时,可以使用这种方式,但在大多数开发中,通常有很多事情并行进行。当主分支和特性分支同时发生变更时,可能需要非快速前进合并,有时也称为三方合并。
git merge --no-ff – 保留分支历史
非快进合并,通常通过--no-ff
标志来调用,提供了一种与之前讨论的快进合并不同的合并策略。与快进合并不同,快进合并将目标分支的指针移动到源分支的最新提交,而非快进合并会生成一个新的合并提交。这个新的提交有两个父提交:一个来自源分支,一个来自目标分支。
非快进合并可以将上下文嵌入到合并提交中,以便以后回顾时查看为什么进行此合并。
图 3.10 – 非快进合并
这种方法可以追踪特性分支合并到主分支的事实,保留了过去提交时的上下文。
非快进合并的实际步骤
假设你正在处理main
分支和add-feature
分支。以下是执行非快进合并的步骤:
# Initialize a new repository
$ mkdir try-no-fast-forward-merge
$ cd try-no-fast-forward-merge
$ git init
# Add and commit initial README.md to master
$ echo "# My Project" > README.md
$ git add README.md
$ git commit -m "Initial commit on main"
# Create and switch to a new branch 'add-feature'
$ git checkout -b add-feature
# Make changes, add and commit them
$ echo "Adding a new feature..." >> README.md
$ git add README.md
$ git commit -m "Implement a new feature"
现在commit
日志如图 3.11所示。到目前为止,你所做的与在git merge --ff
部分所做的相同。
图 3.11 – git merge --no-ff (1)
然后,让我们切换回main
并执行非快进合并:
$ git checkout main
$ git merge --no-ff add-feature
以下编辑消息将出现在终端中。编辑提交信息并保存:
git merge branch 'add-feature'
编辑完成后,我们来看看当前的日志:
# View the history
$ git log --graph --oneline
* f58977f (HEAD -> main) Merge branch 'add-feature'
|\
| * a48c0a9 (add-feature) Implement new feature
|/
* fe93feb Initial commit on main
你的仓库历史将显示一个新的合并提交,指示add-feature
分支是如何集成到main
分支中的。
图 3.12 – git merge --no-ff (2)
为什么在 DevOps 和团队协作中使用非快进合并?
非快进合并提供的好处在各种 DevOps 和团队协作场景中都可能非常有价值:
-
上下文保留:在合并过程中生成一个新的提交,不仅保留了代码,还保留了历史和上下文。这种清晰的集成记录使得理解不同分支的更改何时以及如何被合并变得更加容易。
-
--no-ff
提供了无价的透明性,清晰地记录了谁在何时做了什么更改,以及为什么做这些更改。这在较大的团队和复杂项目中尤为重要,能够帮助理解贡献的流动。
虽然合并提交可以提供丰富的上下文和历史记录,但如果过度使用或文档记录不充分,它们也可能会使 Git 历史记录变得杂乱。团队在选择合并策略时必须谨慎,并努力保持清洁的共享仓库。
git merge --squash – 压缩复杂性
git merge --squash
选项提供了一种不同的合并技术,它兼具清晰性和整洁性。虽然快进和非快进合并非常适合跟踪分支历史,但可能会有一些情况需要在合并之前将 feature
分支的更改浓缩为一个提交。这时,git merge --squash
就发挥了作用。
在压缩合并中,源(或功能)分支上的所有更改都会合并为目标(或主)分支上的一个单一提交。此操作有效地将功能分支的历史浓缩为一个提交,同时进行合并,提供了一个干净、易于跟随的 Git 历史。这使得更改处于未提交状态,允许你在最终提交之前修改差异。
尽管团队致力于保持干净的共享代码库,个别开发分支通常会因为各种尝试和错误而变得杂乱无章。压缩合并有助于通过防止这些杂乱的实验性日志进入生产历史,从而保持主代码库的整洁。
如下图所示,压缩合并在某些方面是最干净的合并方式,并且有很多好处。然而,请记住,它是一种合并更改的方式,丢失了之前的更改历史以及其他公司的提交历史。稍后将在本节中提到这一点。
图 3.13 – 压缩合并
压缩合并的实际步骤
假设你有一个main
分支和一个add-multiple-features
分支。要执行压缩合并,请执行以下操作:
# Initialize a new repository
$ mkdir try-squash-merge
$ cd try-squash-merge
$ git init
# Add and commit initial README.md to main
$ echo "# My Project" > README.md
$ git add README.md
$ git commit -m "Initial commit on main"
# Create and switch to a new branch 'add-multiple-files
$ git checkout -b add-basic-files
# Make some changes, add and commit them
$ echo "# HOW TO CONTRIBUTE" >> CONTRIBUTING.md
$ git add CONTRIBUTING.md
$ git commit -m "Add CONTRIBUTING.md"
$ echo "# LICENSE" >> LICENSE.txt
$ git add LICENSE.txt
$ git commit -m "Add LICENSE.txt"
现在,分支应该像以下图所示:
图 3.14 – git merge --squash (1)
让我们切换回主线并执行一个 squash
合并:
# Switch back to 'main' and perform a squash merge
$ git checkout main
$ git merge --squash add-basic-files
然后,这些提交将被压缩并成为一个单一的提交:
图 3.15 – git merge --squash (2)
然后,Git 会将未提交的更改添加到 main
分支。此时是完成合并过程的时候了。要完成合并,你需要提交这些未提交的更改:
$ git add .
$ git commit -m "Add repository standard docs"
$ git log --graph --oneline
* 6eb6df3 (HEAD -> main) Add repository standard docs
* ffc2ed5 Add CONTRIBUTING.md
* 2c5ad11 Initial commit on main
这将把 add-multiple-features
分支的所有更改合并为 main
分支上的一个新提交:
图 3.16 – git merge --squash (3)
为什么在 DevOps 和团队协作中使用压缩合并?
压缩合并为 DevOps 和协作开发提供了一套独特的好处:
-
原子性更改:压缩合并会创建一个包含所有功能更改的单一提交,这样在需要回滚时会更加方便。
-
减少噪音:压缩合并消除了来自主分支的许多小的、可能是实验性的提交的杂乱无章。这使得日志历史更加清晰,便于阅读和理解。
-
战略性提交信息:squash 使你能够创建一条综合性的提交信息,比一系列小的提交信息更有效地概括功能的目的和影响。
然而,值得注意的是,虽然 squash 合并可以简化历史,但它也可能会使历史变得模糊。来自功能分支的单独提交会在主分支中丢失,这使得理解每个单独更改的开发背景变得困难。在使用这种合并策略时,要谨慎并了解它对开发历史的影响。
squash 合并的伦理问题与陷阱
squash 其他人的提交有时可能会带来问题。
git merge --squash
命令是一个强大的工具,可以将多个提交合并成一个。虽然此功能能保持提交历史的清晰和可管理性,但当用于其他人提交时,它会引发伦理和实际问题。
在团队协作中,了解以下问题非常重要:
-
作者归属错误:默认情况下,执行 squash 的人将成为合并提交的作者,从而有效地抹去原始贡献者的历史。这可能会使团队成员感到沮丧,因为他们的贡献没有得到认可。
-
历史篡改:该命令会改变提交历史,这可能会被认为是不尊重原作者工作的行为。
如果保持单独提交的完整性很重要,考虑使用标准合并。这可能会导致历史更为复杂,但它能保留所有贡献者的工作和认可。
此外,git rebase
命令提供了对提交历史的更多控制,这对于清理或重新安排自己的工作而不影响他人非常有用。
接下来让我们探索这个命令。
git rebase – 以便清晰的方式重写
Rebase 是 Git 中另一种强大的技巧,它与合并有显著的不同。合并和 rebase 之间的主要区别在于它们整合更改的方式。git merge
将一个分支的更改合并到另一个分支,而 git rebase
则是将一系列提交移动或合并到新的基础提交上。
在团队协作的背景下,rebase 用于保持线性的项目历史,这可以简化调试并使代码审查变得更容易。然而,rebase 也有其复杂性和陷阱,通常最好在特定情况下使用。
在深入实践教程之前,让我们先大致了解一下 git rebase
的工作原理。rebase 的主要用途是将 feature
分支的更改放到另一个分支的顶部。
例如,考虑以下分支:
图 3.17 – Rebase (1)
在将 feature
分支 rebase 到 main
后,你的分支可能看起来像这样:
图 3.18 – Rebase (2)
最后,你可以合并回主分支,此时可以进行快速合并:
图 3.19 – Rebasing (3)
git rebase 的实际步骤
让我们以一个main
分支和new-feature
分支为例,来看一下如何进行 rebase:
# Initialize a new repository
$ mkdir try-git-rebase
$ cd try-git-rebase
$ git init
# Add and commit initial README.md to main
$ echo "# My Rebase Project" > README.md
$ git add README.md
$ git commit -m "Initial commit on main"
# Create and switch to a new branch 'new-feature'
$ git checkout -b new-feature
# Make some changes, add and commit them
$ echo "This is a new feature." >> NewFeature.md
$ git add NewFeature.md
$ git commit -m "Add new feature"
到此为止,你的分支历史可能会像这样:
图 3.20 – git rebase (1)
现在,假设在你工作在new-feature
时,main
上添加了新的提交:
# Switch back to 'main' and add new commits
$ git checkout main
$ echo "Updates to the project." >> Updates.md
$ git add Updates.md
$ git commit -m "Update main"
你的提交图现在已经分叉:
图 3.21 – git rebase (2)
现在,将new-feature
分支 rebase 到main
分支:
# Switch to 'new-feature' and rebase onto main
git checkout new-feature
git rebase main
让我们看看现在的情况:
$ git log --graph --online
* 43ea59e (HEAD -> new-feature) Add new feature
* 16e1878 (main) Update main
* 3021494 Initial commit on main
之后,你的分支将如下所示:
图 3.22 – git rebase (3)
现在是时候合并并完成git
rebase
过程了:
# Switch to 'main' and perform fast-forward-merge
$ git checkout main
$ git merge new-feature
当进行快进合并时,main
和new-feature
分支的 HEAD 将是相应的提交,如下所示:
图 3.23 – git rebase (4)
为什么在 DevOps 和团队协作中,rebase 是强大的
在 DevOps 文化中,rebase 的主要优势是它比合并产生更干净的项目历史。更清晰的历史记录更容易调试,更易理解,并且对于后期加入项目的开发者来说更具逻辑性。
这里有一些好处:
-
线性历史:比 git merge 创建的非线性历史更容易理解。
-
简化调试:历史记录更干净后,追踪某个特定 bug 何时被引入变得更容易。
-
代码整洁:rebase 鼓励你合并修复提交或拆分较大的提交,使得你的修改比其他开发者的更容易理解。
git rebase 的注意事项和陷阱
有一些黄金规则需要遵循:不要 rebase 公共(团队)分支。其中一条基本规则是避免对公共分支进行 rebase。rebase 对于清理功能分支是一个非常好的工具,但在公共分支上使用时,它可能会变成灾难。
使用git rebase
时需要考虑的事项:
-
团队协作中的冲突:假设你已经 rebase 了一个公共分支并推送了更改。其他已经拉取了该分支旧版本的开发者,现在会有一个分叉的历史。当他们尝试推送他们的更改时,Git 会拒绝推送,迫使他们合并历史。这会增加额外的工作量并增加合并冲突的可能性。
-
复杂的合并:在公共分支已经 rebase 并且历史被修改后,将其与其他分支合并可能会变得非常具有挑战性。因为 Git 使用提交历史来确定如何整合更改,修改历史可能会使合并变得比需要的复杂得多。
-
上下文丢失:Rebase 可能会将提交合并在一起或更改它们的顺序,这可能导致这些更改的上下文丢失。这会使调试变得更加困难,并且可能使理解导致当前代码库的开发过程变得复杂。
Rebase 可能会很复杂且有风险,特别是对于缺乏经验的开发者来说。在最坏的情况下,你可能需要解决许多冲突,如果不小心处理,可能会导致错误和 bug。
通过理解并使用 git merge
和 git rebase
,你可以处理几乎所有需要合并不同开发线的情况。每个命令在 Git 中都有其适用的场合,理解何时使用哪个命令是保持代码库干净且易于理解的关键——这在 DevOps 的世界中非常宝贵。
git cherry-pick – 选择特定的提交
Git 命令中最灵活的工具之一就是 git cherry-pick
命令。虽然之前的合并方法主要用于集成整个分支,git cherry-pick
允许你选择一个分支中的特定提交并将其应用到另一个分支。此方法在你只需要应用几个特定更改而不是将另一个分支的所有修改都带入时非常有用。
假设你有两个分支,main
和 feature
。你意识到 feature
分支中的一个或两个提交应该被移动到 main
,但你还不准备合并整个分支。git cherry-pick
命令正好允许你做到这一点:
图 3.24 – Cherry-picking(1)
你可以将 feature
分支中某个特定提交的更改 cherry-pick 到 main
分支上。此操作将在 main
分支上创建一个新的提交:
图 3.25 – Cherry-picking(2)
Cherry-picking 的实际步骤
现在让我们来看看 cherry-pick 更改并将它们合并到分支中的实际步骤。每个提交都会向每个分支添加一个文件。我们将这些提交中的一部分合并到 main
。首先,我们添加这些文件:
# Initialize a new repository
$ mkdir try-cherry-pick
$ cd try-cherry-pick
$ git init
# Add and commit initial README.md to main
$ echo "# My Project" > README.md
$ git add README.md
$ git commit -m "Initial commit"
# Create and switch to a new branch 'add-base-documents'
$ git checkout -b add-base-documents
# Make changes and commit
# Add CONTRIBUTING.md
$ echo "# CONTRIBUTING" >> CONTRIBUTING.md
$ git add CONTRIBUTING.md
$ git commit -m "Add CONTRIBUTING.md"
# Add LICENSE.txt
$ echo "LICENSE" >> LICENSE.txt
$ git add LICENSE.txt
$ git commit -m "Add LICENSE.txt"
# Take a look at the 'add-base-documents' branch log
$ git log add-base-documents --graph --oneline
* 02ee2b4 (HEAD -> add-base-documents) Add LICENSE.txt
* a80e8ad Add CONTRIBUTING.md
* cfb060a (main) Initial commit
现在这些分支看起来像下图所示:
图 3.26 – git cherry-pick(1)
现在,让我们只选择 a80e8ad
提交并将其放到 main
分支上。请在你的环境中替换哈希值:
# Now switch back to the 'main' branch and cherry-pick the commit
$ git checkout main
$ git cherry-pick a80e8ad
[main 9a36741] Add CONTRIBUTING.md
Date: Sun Oct 29 16:04:56 2023 +0900
1 file changed, 1 insertion(+)
create mode 100644 CONTRIBUTING.md
# Let's check the 'main' branch log
$ git log --graph --oneline
* 9a36741 (HEAD -> main) Add CONTRIBUTING.md
* cfb060a Initial commit
在你成功 cherry-pick 该提交后,一个新的提交将被添加到你当前的分支(在此示例中为 main
),并且该提交将包含 cherry-pick 提交中的更改。请注意,新的提交包含相同的更改,但有不同的提交哈希值:
图 3.27 – git cherry-pick(2)
为什么 cherry-pick 在 DevOps 和团队协作中很有用
团队开发需要灵活的开发方式。git cherry-pick
是一个在协作编码环境中非常有用的命令,允许团队选择性地整合变更并保持代码的完整性。
让我们看看 cherry-pick
可以提供什么价值:
-
选择性集成:它允许特定的 bug 修复或功能被移入生产环境,而不需要将所有开发分支的变更一起移动。
-
轻松回退:如果出现问题,你只需要回退一个小的变更,而不是整个分支合并。
-
清理历史:通过仅包含相关的提交,保持 Git 历史的整洁,使其更易于阅读和理解。
git cherry-pick
提供了高精度的分支间变更集成方式。它允许你精确选择要包含的提交,提供对项目历史的细粒度控制。这使得它成为任何 DevOps 工程师在追求灵活且高效的版本控制工作流时的宝贵工具。
在探索了 Git 中的各种合并策略之后,你可能会问自己:“我应该使用哪种方法?” 答案就像大多数工程问题一样,“这取决于情况。” 有多个因素在决定最适合你需求的 Git 合并策略时起作用。此外,值得注意的是,你不必坚持使用单一方法;你可以根据情况调整你的方法。理解这些因素可以帮助你做出明智的决定。
让我们检查以下因素,看看你应该如何选择:
-
项目复杂性:具有多个贡献者和并行开发线的复杂项目可能需要更一致的合并方式,以减少冲突。像 GitHub 这样的平台允许你为项目设置合并策略。
-
git merge
可以减少错误发生的几率。然而,如果大多数团队成员都有经验,你可能会想利用rebase
的优势,以保持更清晰的 Git 历史,并促进更顺畅的沟通。 -
Git 历史的期望清晰度:如果你非常重视清晰、线性的 Git 历史,那么选择快进合并或 rebase 可能是最好的方法。另一方面,如果你更看重项目开发过程的详细记录,那么非快进合并会更适合,能更好地保留变更的全面记录。
随着你前进,让这一节中的知识帮助你选择适合你项目的合并方法。通过实际场景练习来增强信心。记住,在 Git 中没有“一刀切”的答案——灵活性和适应性是你最好的盟友。
解决冲突
在一个协作开发环境中,冲突不仅仅是可能的——它们是不可避免的。当多个开发者在相同的代码库上工作,甚至是在相同的文件上进行修改时,很有可能会发生变化重叠,导致冲突。处理和解决这些冲突对于保持顺畅和高效的 DevOps 工作流程至关重要。
为什么会发生冲突
冲突通常发生在两个分支对文件的同一行或部分进行更改,然后尝试进行合并时。尽管 Git 非常强大,但它无法判断哪个更改应该优先。高效解决这些冲突的关键是理解冲突发生的原因,并尽可能主动预防它们。
如何解决 Git 中的合并冲突
让我们从基础开始。Git 中的冲突解决通常需要手动干预。以下是如何进行处理的步骤:
-
识别冲突。使用
git status
查看哪些文件发生了冲突。 -
检查发生冲突的文件。打开文件并查找冲突标记(
<<<<<<<
、=======
和>>>>>>>
)。这些标记区分了来自不同分支的更改:<<<<<<< HEAD Someone's change is here ======= Your change is here >>>>>>> branch-you-want-to-merge
-
解决冲突。选择保留哪些更改。你可以保留某个分支的更改,混合两者的更改,甚至添加全新的内容。
-
提交已解决的文件。解决冲突后,需要将文件添加到暂存区并提交。
如何解决合并冲突
在协作开发环境中,合并冲突是不可避免的。关键在于知道如何高效地解决它们。根据冲突的性质,你可以遵循几种不同的解决模式。
当有一个明确的版本需要保留时
如果你已经合并了两个分支,并希望完全接受其中一个版本而忽略另一个版本,可以选择使用 git checkout --ours
或 git checkout --theirs
:
-
checkout --ours
:当发生合并冲突时,使用此命令保留你分支的文件:checkout --theirs: This command will retain the files from the merged branch, discarding the ones in your current branch:
$ git checkout --theirs –
在运行这些命令之一后,你需要将更新后的文件添加到暂存区并提交。
当你需要评估两个版本时
如果不清楚应该优先采用哪个版本,则需要更细致的处理方法。
-
审查代码:在文本编辑器中打开冲突文件,手动检查冲突的代码行。决定保留哪些部分并相应地编辑文件。
-
沟通:如果有必要,与你的团队成员协商,决定保留哪些更改。可以通过面对面的讨论、虚拟会议或代码审查工具进行此操作。
-
运行测试:一旦你手动解决了冲突,运行测试是非常重要的,以确保代码库仍然稳定。
-
提交更改:在成功测试后,将已解决的文件暂存并提交到你的代码库。
通过小心处理合并冲突,你可以帮助保持干净的代码库,并促进团队内部更好的沟通。
支持合并活动的有用命令
合并是一个充满挑战的活动。拥有合适的工具和命令可以让这个过程更加顺利,减少出错的机会。在这一节中,我们将介绍一些有用的 Git 命令,帮助你有效管理合并。
git diff – 查找差异
git diff
命令是一个用于识别两组代码差异的关键工具。它帮助你查看两者之间究竟发生了什么更改,使得在冲突出现时更容易解决问题。
你可以通过以下方式将当前分支与目标分支进行比较:
$ git diff feature-branch..main
该命令逐行显示 feature-branch
和 main
之间的更改对比。你也可以专注于特定的文件,甚至是特定的代码行,这使它成为一个灵活的工具,可以在不同的粒度下发现差异。
尽管 Git 没有内置 git merge
的 dry-run 选项,你可以模拟一次合并以查看会发生什么:
$ git merge --no-commit --no-ff feature-branch
$ git diff --cached
该命令序列尝试将 develop
的更改合并到当前分支,但会在提交之前停止。你可以使用 git diff --cached
查看已暂存的更改。如果合并结果不符合预期,你可以简单地中止:
$ git merge --abort
git mergetool – 简化的工具指南,用于可视化冲突解决
当你遇到一个难以手动解决的合并冲突,或者你更喜欢图形界面时,Git 内置的 git mergetool
可以帮助你解决问题。
可以设置以下项:
-
kdiff3
,meld
,和vimdiff
。 -
全局配置:使用以下命令将你选择的工具设置为所有 Git 仓库的默认工具:
# For example, if you chose vimdiff, you would run: $ git config --global merge.tool vimdiff $ git mergetool
该命令打开你选择的图形工具,并并排显示冲突的更改。这个界面简化了理解冲突的过程,并帮助你决定保留哪些更改。这个设置允许你指定不仅是命令行工具,还可以指定像 Visual Studio Code 这样的现代工具,以图形化方式解决合并。
通过遵循这些简单的步骤,你可以以更直观的方式解决复杂的合并冲突,使这个过程对于所有技能水平的团队成员都变得更加可访问。
总的来说,冲突解决是任何工程师必备的技能。尽管冲突可能复杂且具有挑战性,但知道如何高效地解决冲突,可以让你的开发工作流程更加顺畅。这不仅仅是关于解决冲突本身,而是理解导致冲突的根本问题。这种细致的方法对促进团队之间更好的沟通和合作至关重要。
随着团队开发的推进,项目变得越来越复杂。冲突是不可避免的。冲突是一个很好的机会,帮助团队提升协作能力。你不应该害怕冲突,应该学会如何通过逐一解决它们来帮助团队进行更好的沟通。
精通更好的协作
到目前为止,我们已经讨论了如何在 Git 中处理合并并解决冲突。我们已经看到,你可以使用 git merge --squash
清理本地更改,或者使用 git rebase
调整提交历史。虽然保持工作区整洁是很好的,但理想的情况是保持代码库的干净,尤其是在推送到共享环境时。接下来,让我们讨论一下可以帮助你成为一个优秀协作者的命令,无论你是个人贡献者还是管理主共享分支的团队领导。
回滚时间
在任何协作项目中,错误是不可避免的。即使不是错误,很多时候你也会想要回退并回到过去。无论是一个破坏构建的错误提交,还是一个未能按预期实现的功能,回滚更改的能力至关重要。两个不可或缺的命令就是 git reset
和 git revert
。
git reset – 回滚更改
git reset
命令允许你回退 Git 历史,本质上是将 HEAD 和可选的工作目录移动到某个特定的提交。这是一个非常强大的功能,但需要谨慎使用。有几种不同的方式可以使用 git reset
。了解它们,以便你能有效地组织你的环境:
-
软重置:这会保持工作目录和索引不变,但会移动 HEAD。此命令用于当你只想撤销提交,而不影响索引工作树时:
git reset --soft command:
图 3.28 – git reset --soft
-
git add
和git commit
。文件更改将保持不变;这是在git reset
未指定任何选项时的默认行为:git reset --mixed command:
图 3.29 – git reset --mixed
-
硬重置:这会重置索引和工作目录,永久删除未提交的更改。它会删除所有提交、更改和文件,因此所有更改将不再保留。如果你想删除所有内容,这就是方法:
git reset --hard command:
图 3.30 – git reset --hard
git revert – 在不重写历史的情况下撤销更改
与 git reset
不同,后者会更改提交历史,git revert
创建一个新的提交来撤销之前提交所做的更改。这在共享环境中非常有用,因为重写历史是不被鼓励的。
git revert
会创建一个相反的提交,如下所示:
图 3.31 – git-revert 创建一个提交来取消该提交
此命令将撤销指定哈希的提交所做的更改,并创建一个新的提交来记录此操作:
$ git revert <commit_hash>
以下是可以使用 git revert
的情况:
-
git revert
不会重写历史,因此在共享分支上使用是安全的 -
git revert
是一种干净的删除方法 -
git revert
操作作为回滚策略的一部分
让我们了解一些额外的高级git revert
选项:
-
回退多个提交:你可以通过指定提交范围来回退一系列提交:
-n or --no-commit flags:
git revert -n <commit_hash>
This will apply the revert changes to your working directory but will not commit them. You can then make further changes and commit manually.
注意
注意插入符号(^
)。这意味着回退范围内的最旧提交也会被包括在内。
掌握git revert
对任何开发者或 DevOps 专业人员来说都至关重要。它提供了一种安全的机制来撤销更改,促进更好的协作和更可靠的代码。
git checkout – 不只是切换分支
在之前的讨论中,我们主要在切换分支的上下文中提到了git checkout
命令。虽然这是它的主要功能之一,但理解git checkout
是一个多用途工具也至关重要,它不仅可以操作分支,还能在单个文件或目录的粒度上进行操作。在本节中,让我们扩展对git checkout
的理解,看看它如何在高效协作和错误修正中发挥重要作用。
这里是切换分支的基本命令回顾:
$ git checkout <branch_name>
那如果你只需要恢复一个文件到先前的状态呢?git checkout
同样能帮你解决。如果你对一个文件进行了更改,但还没有提交,并且决定想要撤销这些更改,你可以这样操作:
$ git checkout -- <file_name>
这将丢弃你工作目录中的更改,并将文件恢复到最后一次提交的状态。有时候,你可能只想将另一个分支的部分更改应用到当前工作分支。git checkout
也能做到这一点:
$ git checkout <branch_name> -- <file_name>
这个命令会将另一个分支中的特定文件检出到你当前的工作分支,让你根据需要混合和匹配代码。
它在团队协作中提供了多种灵活性:
-
快速回滚:如果生产环境出现问题,你可以从稳定的分支快速检出特定的文件。
-
选择性功能测试:在合并新功能之前,你可以只检出与该功能相关的文件进行测试。
-
轻松错误修正:错误是常有的事。能够单独检出文件使得更正错误变得更加容易,而不会影响到代码库中的其他部分。
注意
在文件上使用git checkout
将会丢弃更改。确保这是你打算执行的操作。如果你正在进行实验,建议经常提交更改。这样,如果需要,你可以轻松回退到某个特定的提交。
理解git checkout
的完整功能可以显著提升你的工作流程和协作效率。无论你是单独工作还是作为团队的一员,能够操作的不仅仅是分支,还有单个文件,这赋予了你更高的控制力和适应性。
组织你的工作环境
在团队开发中,你的个人工作区就像是一个实验室——一个可以自由创新、调试和测试的空间,而不会影响到整个项目。有效管理这个空间至关重要,Git 提供了一组强大的命令,帮助你高效管理。在本节中,我们将探索三种基本的 Git 命令——git clean
、git stash
和.gitignore
——这些命令可以轻松保持你的工作区干净。
git clean – 清爽的开始
git clean
命令提供了一种快速清理工作目录中未跟踪文件和目录的方法,基本上提供了一个全新的起点。在执行合并之前或之后,或者当你想清除不需要版本控制的杂物时,这个命令特别有用。
# Remove untracked files and directories
$ git clean -fd
git stash – 轻松暂停和恢复工作
git stash
是一个非常有价值的工具,用于暂时保存你已做的更改,而这些更改还没有准备好提交。
作为开发人员,多任务处理往往是日常工作的一部分。无论是被紧急的 bug 修复打断,还是需要暂时切换上下文,git stash
都能派上用场。此命令让你保存当前的更改,而无需进行正式提交,从而让你有自由切换任务,之后可以轻松回到之前的工作状态:
# Stash changes with a description
$ git stash save "WIP: New Feature"
# Reapply the stashed changes
$ git stash apply stash@{0}
此外,这里列出了一些常用的git stash
命令,这些命令可以显著提升你的工作区管理:
-
git stash
:此命令将你的更改保存到 stash,使你的工作目录保持干净。未跟踪的文件不会被 stash。 -
git stash save "Your Comment"
:此命令将你的更改保存到 stash,并允许你附加评论。这对于稍后使用git stash list
识别 stash 非常有用。 -
git stash list
:此命令显示你所有已保存的更改的列表。如果你使用了git stash save
,你将在此看到你的评论,这使得识别每个 stash 变得更加容易。 -
git stash apply
:此命令将最近保存的更改恢复到你的工作目录。stash 仍会保留在git stash list
中。 -
git stash apply [stash@{n}]
:此命令根据索引号恢复特定的 stash,你可以通过git stash list
找到该索引号。 -
git stash drop
:此命令删除 stash 列表中的特定 stash。 -
git stash drop [stash@{n}]
:此命令根据索引号删除特定的 stash。 -
git stash clear
:此命令删除所有 stash,清空你的stash list
。
.gitignore – 自定义共享内容
在处理复杂项目时,你的本地环境可能会生成日志文件或包含个人配置设置——这些内容你不希望与团队共享。.gitignore
文件允许你指定在git add
期间应该忽略哪些文件和文件夹,从而确保它们只存在于你的本地环境中。
这是一个.gitignore
文件的示例:
# Ignore all log files
*.log
# Ignore personal config files
config/personal/
谁做了什么?帮助你调试的绝妙方法
Git 有几种典型的方法来分析过去的情况。git blame
和 git bisect
是非常实用的工具,记住它们很重要,因为它们易于使用,可以帮助追溯并调试是谁做了什么修改。
git blame – 谁做了什么?
在共享代码库中工作时,可能会有需要了解特定代码行历史的情况。git blame
命令提供了一个文件的分解,注释每一行,显示最后修改它的人员以及该行属于哪个提交。这有助于识别负责特定更改的人员,这对调试、重构或简单理解某段代码存在的原因非常有用。
$ git blame file.txt
-L
选项允许你指定输出的行数:
$ git blame -L 5,10 README.md
$ git blame -L 5,+5 README.md
必须强调的是,git blame
的目的是不是为了将错误或可疑决策归咎于个人。在任何协作环境中——尤其是在团队合作至关重要的 DevOps 中——必须记住,错误是集体的责任。每个人都可能犯错;重要的是团队如何合作解决这些错误。从心理安全的角度来看,使用 git blame
应该视为增强团队沟通和识别改进领域的一种方式,而不是用来归咎他人的机制。
git bisect – 在提交范围内高效地寻找 bug
调试常常感觉像是在大海捞针,尤其是在拥有大量提交历史的大型代码库中。在 DevOps 领域,快速部署周期是常态,而在无数变更中引入的 bug 可能会造成严重后果。此时,git bisect
就发挥了作用,成为定位引入 bug 的特定提交的强大工具。
git bisect
命令通过在提交历史中执行二分查找来找到引入 bug 的提交。该过程从标记一个已知良好的提交和一个已知有问题的提交开始:
$ git bisect start
$ git bisect bad # If the current version is bad
$ git bisect good <Last known good version commit hash>
Git 然后会自动检查良好提交和有问题提交之间的一个中间提交。你需要测试这个提交,然后将其标记为良好或有问题:
$ git bisect good # or
$ git bisect bad
Git 将继续这一过程,逐步缩小提交范围,直到识别出罪魁祸首。一旦找到有问题的提交,就能更容易地理解出了什么问题并制定修复方案:
# Exit bisect mode and return to your branch
$ git bisect reset
在 DevOps 中,识别和解决问题的速度对保持卓越的运营至关重要,git bisect
成为了一种无价的工具。它能够很好地融入 DevOps 工具链,支持自动化测试,并促进更快速的回滚和修补。通过高效地定位错误,它增强了团队合作解决方案的能力,强调了快速反馈和持续改进这两个 DevOps 的基本原则。
版本控制的卓越性
在 DevOps 生态系统中,CI/CD使一切都变得增量化。然而,拥有具体版本的软件作为里程碑仍然至关重要。这些版本不仅是时间的标记;它们表明了代码的稳定性、新功能以及整体健康状态。它们还促进了更顺畅的回滚,并使隔离问题变得更加容易。
什么是语义化版本控制
语义化版本控制是一种版本控制方案,旨在传达每次新发布所涉及的具体变化含义。其格式由三个数字组成,数字之间用点分隔(例如:2.4.4)。每个数字具有特定的意义,与向后兼容性和引入的更改类型相关。
Git 中的标记
在 DevOps 环境中,标记成为了有效版本控制的基石。通过标记代码库历史中的特定点,您可以创建作为稳定发布点或重要里程碑的锚点。这些标记版本为您的 CI/CD 流水线奠定了基础,确保了持续一致和可靠的部署。
为了使标签更具信息性,可以考虑为其添加有用的元数据和上下文:
$ git tag -a v1.0 -m "Initial stable release"
您标记的提交通常应该代表稳定的发布点或关键的里程碑。这些是所有自动化测试通过、功能完成并经过同行评审的提交。从本质上讲,它们是您的生产就绪提交。
要标记特定的提交,可以使用以下命令:
$ git tag v2.4.4 32de0b2
Git 标记和语义化版本控制不仅是技术操作,还是战略性措施。它们确保每个人都能理解当前部署的内容以及它能执行的功能。
在本节中,我们已经掌握了如何在代码库中回溯时间的知识,了解何时以及如何使用git reset
、git revert
和git checkout
。这些强大的命令确保我们能够优雅地撤销更改、纠正方向并保持清晰的项目历史记录——这些对于协作环境至关重要。通过git clean
、git stash
和正确的.gitignore
设置来组织工作环境,为您提供一个干净的工作区,在这里,专注和清晰取代了杂乱和混乱。这个干净的工作区不仅仅是个人偏好的体现;它是为团队设定标准,确保只有有意的更改才能进入共享的代码库。
将这些技巧融入到您的日常工作中,不仅能提升个人表现,还能增强整个团队的协作能力。
概述
在本章中,我们深入探讨了分支策略的细节、合并和变基的复杂性以及如何处理冲突的艺术,目标是掌握团队内部更好的协作。我们从理解为什么分支策略至关重要,到将分支命名与组织目标对齐,为您奠定了构建稳健工作流程的基础。
我们探索了各种分支模型,如基于主干的开发、Git Flow 和 GitHub Flow,强调了它们各自独特的优势以及适合的应用场景。关于合并方法的讨论,提供了关于维护干净且富有信息的项目历史的工具,并解决了有时令人头疼的冲突解决问题。
当你完成本章内容时,请记住,掌握 Git 的道路是不断前行的。鼓励自己去探索更多,并通过自信地在开发工作中练习这些技能。拥抱挑战及其带来的学习机会,继续成长为一名开发者。Git 不仅仅是一个工具,它是你在 DevOps 旅程中的伙伴,通过消除摩擦并促进协作文化,提升开发者的体验。
第二部分:GitHub 卓越性与 CI/CD 基础
本部分强调了 GitHub 在 DevOps 中的作用,扩展了其用途,不仅仅是代码托管。它讨论了 GitHub 的功能,这些功能增强了团队协作,并促进了向现代 DevOps 实践的过渡。此外,还深入探讨了 GitHub Actions,重点关注工作流优化、先进的部署方法,如蓝绿部署和金丝雀发布,以及功能发布策略。这为深入理解 GitHub 在持续集成和部署中的能力提供了帮助。
本部分包含以下章节:
-
第四章,通过 GitHub 提升团队协作
-
第五章,通过 GitHub 驱动 CI/CD