Git-和-Github-入门指南-全-
Git 和 Github 入门指南(全)
一、版本控制系统
这是我们进入版本控制系统(VCSs)的第一步。在本章结束时,你应该知道版本控制、Git 及其历史。主要目的是了解在什么情况下需要版本控制,以及为什么 Git 是一个安全的选择。
什么是版本控制?
顾名思义,版本控制是关于一个项目的多个版本的管理。要管理版本,必须跟踪项目中文件的每次更改(添加、编辑或删除)。版本控制记录对一个文件(或一组文件)所做的每次更改,并提供一种撤消或回滚每次更改的方法。
为了有效的版本控制,你必须使用被称为版本控制系统的工具。它们帮助您在更改之间导航,并在出现问题时让您快速返回到以前的版本。
使用版本控制的一个最重要的优势是团队合作。当不止一个人对一个项目做出贡献时,跟踪变更就变成了一场噩梦,并且它大大增加了覆盖另一个人的变更的可能性。有了版本控制,多人可以在他们的项目副本上工作(称为分支),并且只有当他们(或者其他团队成员)对工作满意时,才将那些变更合并到主项目中。
注意
这本书是从开发人员的角度写的,但书中的一切都适用于任何文本文件,而不仅仅是代码。版本控制系统甚至可以跟踪对图像或 Photoshop 文件等许多非文本文件的更改。
你为什么需要一个?
你曾经做过一个文本项目或者一个需要你回忆对每个文件所做的具体修改的代码吗?如果是,您是如何管理和控制每个版本的?也许你试图复制这些文件,并用“审阅”、“修复”或“最终”这样的后缀来重命名它们?图 1-1 显示了那种版本控制。
图 1-1
后缀为“最终版”、“最终版(副本)”和“已审核”的 Gimp 文件
图中显示了许多人处理文件更改的方式。如你所见,这有可能很快失控。很容易忘记哪个文件是哪个文件,以及它们之间发生了什么变化。
要跟踪版本,一个想法是压缩文件并在文件名后添加时间戳,以便版本按照创建日期排列。图 1-2 显示了那种版本跟踪。
图 1-2
按日期排序的压缩版本文件
图 1-2 所示的解决方案似乎是完美的系统,直到你意识到即使版本被跟踪,也没有办法知道每个版本的内容和描述是什么。
为了补救这种情况,一些开发人员使用如图 1-3 所示的解决方案,即将每个版本的变更摘要放在一个单独的文件中。
图 1-3
跟踪每个版本的独立文件
如图 1-3 所示,项目文件夹附带了一个单独的文件,其中简要描述了所做的更改。还要注意许多包含项目早期版本的压缩文件。
应该可以了,对吧?不完全是,你仍然需要一种方法来比较每个版本和每个文件的变化。在那个系统中没有办法做到这一点;你只需要记住你所做的一切。如果项目越来越大,每个版本的文件夹都会越来越大。
当另一个开发者或作者加入你的团队时会发生什么?你会用电子邮件互相发送你编辑过的文件或版本吗?还是在同一个远程文件夹上工作?在最后一种情况下,您如何知道谁在处理哪个文件以及什么发生了变化?
最后,你有没有觉得有必要在不破坏过程中的一切的情况下,撤销几年前做出的改变?一个无限全能的 ctrl-z?
所有这些问题都可以通过使用版本控制系统或 VCS 来解决。VCS 跟踪您对项目的每个文件所做的每个更改,并提供一种简单的方法来比较和回滚这些更改。项目的每个版本还附有对所做更改的描述,以及新文件或编辑过的文件的列表。当更多的人加入这个项目时,VCS 可以准确地显示谁在特定的时间编辑了特定的文件。所有这些都让你为你的项目赢得了宝贵的时间,因为你可以专注于写作,而不是花时间跟踪每个变化。图 1-4 显示了一个由 Git 管理的版本化项目。
图 1-4
Git 版本化的项目
如图 1-4 所示,一个版本化的项目结合了我们在本章尝试的所有解决方案。有变更描述、团队合作和编辑日期。
让我们了解更多关于版本控制系统的信息。
有哪些选择?
版本控制系统有很多种,每种都有自己的优点和缺点。VCS 可以是本地的、集中式的或分布式的。
本地版本控制系统
这些是为管理源代码而创建的第一批 VCS。他们通过跟踪对本地存储的单个数据库中的文件所做的更改来工作。这意味着所有的更改都保存在一台计算机中,如果出现问题,所有的工作都会丢失。这也意味着与团队合作是不可能的。
最受欢迎的本地 VCS 之一是源代码控制系统或 SCCS,它是免费的,但不公开源代码。由美国电话电报公司开发,在 20 世纪 70 年代被广泛使用,直到修订版控制系统(RCS)发布。RCS 比 SCCS 更受欢迎,因为它是开源的、跨平台的,而且更有效。RCS 于 1982 年发布,目前由 GNU 项目维护。这两个本地 VCS 的缺点之一是它们一次只能处理一个文件;他们无法跟踪整个项目。
为了帮助你想象它是如何工作的,这里的图 1-5 展示了一个简单的本地 VCS。
图 1-5
本地 VCS 是如何工作的
如图 1-5 所示,所有东西都在用户的电脑上,只有一个文件被跟踪。版本控制存储在由本地 VCS 管理的数据库中。
集中式版本控制系统
集中式 VCS (CVCS)的工作方式是将变更历史存储在客户机(作者)可以连接的单一服务器上。这提供了一种与团队合作的方式,也提供了一种监控项目总体进度的方式。它们仍然很受欢迎,因为这个概念非常简单,很容易安装。
主要的问题是,就像当地的 VCS 一样,一个服务器错误会让团队失去所有的工作。还需要网络连接,因为主项目存储在远程服务器上。
你可以在图 1-6 中看到它是如何工作的。
图 1-6
中央集权的 VCS 是如何运作的
图 1-6 显示了集中式 VCS 的工作方式类似于本地 VCS,但是数据库存储在远程服务器上。
使用集中式 VCS 的团队面临的主要问题是,一旦某个文件被某人使用,该文件就会被锁定,其他团队成员就无法对其进行操作。因此,他们不得不相互协调来修改单个文件。这造成了开发中的许多延迟,并且通常是贡献者的许多挫折的来源。团队成员越多,问题就越多。
为了解决本地 VCS 的问题,并行版本系统(CVS)被开发出来。它是开源的,可以跟踪多组文件,而不是单个文件。许多用户也可以同时处理同一个文件,因此名称中有“并发”一词。所有的历史都存储在远程存储库中,用户可以通过签出服务器来跟上变化,这意味着将远程数据库的内容复制到他们的本地计算机上。
Apache Subversion 或 SVN 是在 2000 年开发的,可以做 CVS 能做的一切,还有一个好处:它可以跟踪非文本文件。SVN 的主要优势之一是,它不是像以前的 VCS 那样跟踪一组文件,而是跟踪整个项目。所以,它本质上是跟踪目录而不是文件。这意味着重命名、添加和删除也会被跟踪。这使得 SVN,连同它的开源,成为一个非常受欢迎的 very 至今仍被广泛使用。
分布式版本控制系统
分布式 VCS 的工作方式与集中式 VCS 几乎相同,但有一个很大的区别:没有保存所有历史的主服务器。每个客户端都有一个存储库的副本(以及更改历史),而不是签出单个服务器。
这大大降低了丢失一切的可能性,因为每个客户都有一个项目的克隆。对于分布式 VCS,拥有“主服务器”的概念变得模糊,因为每个客户端本质上都拥有自己存储库中的所有权力。这极大地鼓励了开源社区中“分叉”的概念。分叉是克隆一个存储库的行为,以进行您自己的更改,并对项目进行不同的处理。分叉的主要好处是,如果您认为合适的话,您还可以从其他存储库中提取变更(其他人也可以对您的变更做同样的事情)。
分布式版本控制系统通常比其他类型的 VCS 更快,因为它不需要通过网络访问远程服务器。几乎所有的事情都在本地完成。它的工作方式也略有不同:它不是跟踪版本之间的变化,而是将所有变化作为“补丁”来跟踪这意味着这些补丁可以在存储库之间自由交换,因此没有“主”存储库需要跟上。
图 1-7 显示了分布式 VCS 的工作原理。
图 1-7
分布式 VCS 如何工作
注意
通过查看图 1-7 ,很容易得出这样的结论:有一个主服务器是用户一直关注的。但分布式 VCS 并非如此,它只是许多开发人员为了获得更好的工作流而遵循的惯例。
BitKeeper SCM 是 2000 年发布的专有分布式 VCS,像 20 世纪 70 年代的 SCCS 一样,是闭源的。它有一个免费的“社区版本”,缺少 BitKeeper SCM 的许多大功能,但因为它是第一个分布式 VCS 之一,所以即使在开源社区中也非常受欢迎。BitKeeper 的这种受欢迎程度对 Git 的创建起到了很大的作用。它现在是一个开源软件,其源代码于 2016 年在 Apache 许可下发布。可以在 www.bitkeeper.org/
上找到当前的 BitKeeper 项目;发展慢下来了,但还是有社群在贡献。
Git 是什么?
还记得上一节提到的专有分布式版本控制系统 BitKeeper SCM 吗?嗯,Linux 内核开发人员使用它进行开发。使用它的决定被广泛认为是一个糟糕的举动,使许多人不高兴。他们的担心在 2005 年得到了证实,当时 BitKeeper SCM 不再免费。由于它是闭源的,开发者失去了他们最喜欢的版本控制系统。这个社区(由 Linus Torvalds 领导)不得不寻找另一个 VCS,由于没有替代方案,他们决定自己创造一个。于是,Git 诞生了。
因为 Git 是用来取代 BitKeeper SCM 的,所以它的工作原理基本相同,只是做了一些调整。像 BitKeeper SCM 一样,Git 是一个分布式版本控制系统,但是它速度更快,并且更适合大型项目。Git 社区非常活跃,有很多贡献者参与了它的开发;你可以在 https://git-scm.com/
上找到更多关于 Git 的信息。Git 的特性及其工作原理将在本节稍后解释。
Git 能做什么?
还记得我们在本章开始时试图解决的那些问题吗?嗯,Git 可以全部解决。它甚至可以解决你以前不知道的问题!
首先,它非常适合跟踪更改。你可以
-
在不同版本之间来回切换
-
查看这些版本之间的差异
-
检查文件的更改历史
-
标记特定版本以便快速参考
Git 也是团队合作的一个很好的工具。你可以
-
在存储库之间交换“变更集”
-
查看其他人所做的更改
Git 的主要特征之一是它的分支系统。一个分支是一个项目的拷贝,你可以在不弄乱存储库的情况下工作。这个概念已经存在一段时间了,但是有了 Git,它会更快更有效。分支还伴随着合并,合并是将分支中完成的变更集复制回源的行为。通常,您创建一个分支来创建或测试一个新的特性,当您对工作满意时,将该分支合并回来。
还有一个你可能会经常用到的简单概念:藏东西。隐藏是安全地把你当前的编辑放在一边的行为,这样你就有一个干净的环境去做一些完全不同的东西。当你尝试或测试一个特性,但需要优先开发一个新特性时,你可能想使用 stashing。所以,你把你的修改藏起来,开始写那个特性。完成后,您可以取回所做的更改,并将其应用到当前的工作环境中。
作为开胃菜,下面是您将在本书中学习的一些 Git 命令:
$ git init # Initialize a new git database
$ git clone # Copy an existing database
$ git status # Check the status of the local project
$ git diff # Review the changes done to the project
$ git add # Tell Git to track a changed file
$ git commit # Save the current state of the project to database
$ git push # Copy the local database to a remote server
$ git pull # Copy a remote database to a local machine
$ git log # Check the history of the project
$ git branch # List, create or delete branches
$ git merge # Merge the history of two branches together
$ git stash # Keep the current changes stashed away to be used later
如您所见,这些命令非常简单明了。不要担心把它们都记在心里;当我们适当地开始学习时,你将一个一个地记住它们。你不会一直使用它们,你会经常使用 git add 和 git commit。您将了解每个命令,但我们将重点关注您可能在专业设置中使用的命令。但在此之前,我们先来看看 Git 的内部工作原理。
Git 是如何工作的?
与许多版本控制系统不同,Git 处理快照,而不是差异。这意味着它不跟踪文件的两个版本之间的差异,而是拍摄项目的当前状态。
这就是为什么 Git 与其他分布式 VCS 相比非常快;这也是为什么在版本和分支之间切换如此快速和容易。
还记得集中式版本控制系统是如何工作的吗?嗯,Git 是完全相反的。你不需要与中央服务器通信来完成工作。由于 Git 是一个分布式 VCS,每个用户都有自己完整的存储库,有自己的历史和变更集。因此,除了共享补丁或变更集之外,一切都在本地完成。如前所述,不需要中央服务器;但是许多开发人员使用一个作为惯例,因为这样更容易工作。
说到补丁共享,Git 怎么知道哪些变更集是谁的?当 Git 拍摄快照时,它会对快照执行校验和检查;因此,通过比较校验和,它知道哪些文件发生了变化。这就是为什么 Git 可以很容易地跟踪文件和目录之间的变化,并且它还检查任何文件损坏。
Git 的主要特点是它的“三态”系统。这些状态是工作目录、临时区域和 git 目录:
-
工作目录就是您正在处理的当前快照。
-
临时区域是修改后的文件在其当前版本中被标记的地方,准备存储在数据库中。
-
git 目录是存储历史的数据库。
因此,基本上 Git 的工作方式如下:修改文件,将想要包含在快照中的每个文件添加到暂存区(git add),然后获取快照并将它们添加到数据库(git commit)。对于术语,我们将添加到暂存区域的修改文件称为“暂存”,将添加到数据库的文件称为“提交”因此,文件从“修改”到“暂存”再到“提交”
典型的 Git 工作流程是怎样的?
为了帮助您形象化我们在本节中讨论的所有内容,这里有一个使用 Git 的典型工作流的小演示。如果你现在还不明白所有的事情,不要担心;接下来的几章将会帮助你设置好。
这是你第一天工作。您的任务是将您的名字添加到现有的项目描述文件中。因为这是你的第一天,一个高级开发人员会在那里检查你的代码。
你应该做的第一件事是获得项目的源代码。向您的经理询问存储代码的服务器。对于这个演示,服务器是 GitHub,这意味着 Git 数据库存储在 GitHub 托管的远程服务器上,您可以通过 URL 或直接在 GitHub 网站上访问它。这里,我们将使用 clone 命令来获取数据库,但是您也可以从 GitHub 网站下载项目。您将获得一个 zip 文件,其中包含和项目文件及其所有历史。
因此,您可以使用“clone”命令克隆存储库以获得源代码。
git clone https://github.com/mariot/thebestwebsite.git
Git 然后在您工作的当前目录中下载一个存储库的副本。之后,您可以进入新目录并检查其内容,如图 1-8 所示。
图 1-8
将显示存储库的内容
如果你想检查最近对项目所做的修改,你可以使用“日志”命令来显示历史。图 1-9 显示了一个例子。
图 1-9
典型的 Git 历史日志
很好!现在你应该创建一个新的分支来工作,这样你就不会弄乱这个项目。您可以使用“branch”命令创建一个新的分支,并使用“checkout”命令将其签出。
git branch add-new-dev-name-to-readme
git checkout add-new-dev-name-to-readme
现在新的分支已经创建,您可以开始修改文件了。你可以使用任何你想要的编辑器;Git 将通过校验和跟踪所有的变化。既然您已经做出了必要的更改,那么是时候将它们放到准备区域了。提醒一下,登台区是您放置准备拍摄的修改代码的地方。如果我们修改了“README.md”文件,我们可以使用“add”命令将其添加到临时区域。
git add README.md
您不必将修改过的每个文件都添加到暂存区,只需添加那些您希望包含在快照中的文件。既然文件已经暂存,是时候“提交”它或将它的更改放入数据库中了。我们通过使用命令“commit”并附加一些描述来实现这一点。
git commit -m "Add Mariot to the list of developers"
就这样!您所做的更改现在已保存在数据库中,并被安全地存储起来。但只能在你的电脑上!其他人看不到您的工作,因为您在自己的存储库和不同的分支上工作。为了向其他人展示您的工作,您必须将您的提交推送到远程服务器。但你必须先给高级开发人员看代码,然后再进行推送。如果他们同意,您可以将您的分支与项目的主快照合并(称为主分支)。因此,首先您必须使用“checkout”命令导航回主分支。
git checkout master
您现在在主分支上,团队的所有工作都存储在这里。但是,当您进行修复时,项目可能已经改变,这意味着团队成员可能已经改变了一些文件。您应该在将自己的更改提交给 master 之前检索这些更改。这将限制“冲突”的风险,当两个或更多的贡献者改变同一个文件时,可能发生“冲突”。要获得更改,您必须从远程服务器(也称为源服务器)中提取项目。
git pull origin master
即使另一个同事和您一样修改了同一个文件,冲突的风险也很低,因为您只修改了一行。只有当同一行被多人修改时,才会产生冲突。如果您和您的同事更改了文件的不同部分,一切正常。
现在我们已经跟上了项目的当前状态,是时候将我们的版本提交给 master 了。你可以用“合并”命令合并你的分支。
git merge add-new-dev-name-to-readme
既然提交已经合并回主服务器,那么是时候将更改推送到主服务器了。我们通过使用“推送”命令来做到这一点。
git push
图 1-10 显示了我们使用的命令和结果。
图 1-10
一个简单的 Git 工作流
就这么简单!再说一遍,如果你还没有完全理解,也不要担心。这只是 Git 通常如何使用的一个小演示。这也不太现实:没有哪个经理会像那样给新员工一张进入他们主资料库的通行证。
摘要
这只是对 Git 的一个瞥视;它有许多更强大的功能,您将在这个过程中了解到。但是在其他事情之前,在进入下一步之前,你应该问自己一些问题:“Git 将如何在我的项目中帮助我?”、“哪些功能最重要?”,以及“Git 会改善我的工作流程吗?”
本章的主要内容是分布式和集中式 VCS 之间的区别。使用 CVCS 的团队的工作流程不太有组织性,让太多的开发人员没有成就感。因此,您需要了解更多关于分布式 VCS 的知识,以跟上时代的步伐。
在本章中,我们已经看到了团队使用 Git 的典型工作流程;这是大多数团队在专业环境中甚至在开源社区中使用的工作流。即使你计划独自工作,使用工作流程也会提高你的工作效率。
现在不要担心理解 Git 的所有内容;专注于它能为你做什么。读完几章后,你就会熟悉它了。但是现在,让我们思考一下如何在您的系统上安装 Git。
二、安装和设置
现在你知道了什么是版本控制以及 Git 是如何工作的,我们将学习如何安装和设置它。这一章比其他章节要短,因为设置 Git 非常容易。
装置
安装 Git 所需的文件在所有系统的 https://git-scm.com/downloads
上。只需点击链接并选择您的操作系统。
你还可以在图 2-1 中看到,那里也有 Git 的 GUI 客户端。在完成本书的第三部分《与 Git 的团队合作》之前,不要离开。在使用 GUI 客户端之前,您需要熟悉 Git 命令;否则,您将会浪费大量时间来解决一个简单的问题,而使用简单的 Git 命令只需几秒钟。
图 2-1
截至 2019 年 5 月的 git-scm.com 下载部分
在您熟悉 Git 命令之后,您可以检查一个 GUI 客户机并亲自查看。在这本书的最后一部分有一章是关于 GUI 客户端的。但是在那之前请不要使用任何 GUI 客户端;会大大拉长你的学习时间。
注意
Git 捆绑了两个 GUI 工具:用于查看历史的 gitk 和用于基本命令的 git-gui。你将在本书的最后一部分学习使用它们,所以前面的建议仍然适用。
Windows 操作系统
在 Windows 系统上安装 Git 非常容易。打开链接( https://git-scm.com/download/win
)后,下载应该会自动开始,你会到达图 2-2 所示的确认页面。如果没有,只需下载符合您的 Windows 风格的版本。
图 2-2
用于 Windows 的 Git 下载屏幕
执行下载 exe 文件开始安装。第一个屏幕是概述条款和条件的许可声明;你应该一直读到最后(对,没错)。点击下一步,您将进入类似于图 2-3 所示的组件选择屏幕。这里会提示您选择要安装的组件。我建议保留默认选项。
图 2-3
选择要安装的组件
你可以在图 2-3 中看到,你只需要检查组件来安装它们。保持选中 Windows 资源管理器集成是一个好主意;这样,您只需右键单击一个文件夹,在默认 GUI 或上下文菜单中的 Bash(命令窗口)中找到启动 Git 的选项。所有其他组件都是不言自明的,所以决定权在你。
小费
如果您没有安装 Windows 资源管理器集成,并且想要打开文件夹中的命令窗口,您必须使用 Shift +右键单击来打开扩展的上下文菜单。
做出选择后点击 next,您将看到默认的编辑器选择,如图 2-4 所示。Git 需要您定义一个默认编辑器,因为您需要一个编辑器来写出提交描述和注释。
图 2-4
默认编辑器选择
正如你在图 2-4 中看到的,由于历史原因,Vim 是 Git 的默认编辑器。只需从下拉列表中选择您最喜欢的文本编辑器。前两个,Nano 和 Vim,在控制台或命令窗口中工作,所以你不必打开另一个程序。在列表中,你可以找到许多流行的编辑器,如 Notepad++,Sublime Text,Atom 和 Visual Studio (VS) Code。如果您的编辑器没有列出,您可以选择最后一个选项,一个新的输入将会出现(如图 2-5 所示),这样您就可以提供一个到编辑器主可执行文件的链接。
图 2-5
设置自定义编辑器
在图 2-5 中,您可以看到这样一个屏幕,如果它没有在下拉列表中列出,您可以在这里设置您的自定义编辑器。
对于这本书,我决定保留默认选项,使用 Vim。如果你决定使用其他编辑器,这不会改变这本书的任何内容。但是如果你想学习 Vim(需要一点时间),你可以看看“vimtutor”,这是 Vim 附带的一个辅导程序,或者通过 https://vim-adventures.com/
上一个有趣的视频游戏来学习。还有 www.vi-improved.org/vimusermanual.pdf
更全但是 300 多页!
不要担心,这个选择不是决定性的,你仍然可以随时改变。你将在本章的最后一节了解如何操作。
警告
在网上,永远不要开始或参与编辑大战。选择你喜欢的文本编辑器,不要和任何人谈论它。我仍然带着过去在“Emacs vs. Vim”战争中留下的伤疤。
一旦你选择了你喜欢的编辑器,你就可以进入下一个屏幕,这是路径环境调整,如图 2-6 所示。PATH 环境是一个变量,它包含可执行程序在其值中所在的目录列表。这是必需的,这样当你想在控制台中执行一个可执行文件时,你就不必键入它的完整路径;你只需要输入它的名字。例如,要从控制台启动 Visual Studio 代码,我应该键入 C:\ Program Files(x86)\ Microsoft VS Code \ bin \ Code。但是由于我的路径中有 C:\ Program Files(x86)\ Microsoft VS Code \ bin,所以我只需键入“Code”即可启动它。
图 2-6
选择是否将 Git 添加到路径
如果你愿意,这同样适用于 Git。如果您不希望这样,而只想将 Git 与它自己的独立控制台“Git Bash”一起使用,请选择第一个选项。因此,要使用 Git,您必须从应用列表或文件夹的上下文菜单中启动它(如果您选择安装 Windows 资源管理器集成)。
如果您希望能够在任何地方使用 Git,请保留默认选项,将其添加到您的 PATH 环境中。这样,其他工具也可以使用 Git,您可以从任何命令窗口工作。我强烈建议这个选项。
最后一个选项有点侵入性。它将向您的路径添加许多 Git 命令,并将覆盖一些 Windows 的默认工具。只有在您也有正当理由的情况下才选择此项;一般你没有这样的理由。
如图 2-6 所示选择一个选项,进行下一步。您将到达一个关于 HTTPS 连接的屏幕,如图 2-7 所示。通过 HTTPS 发送数据时,您必须选择使用哪个库。在本书的后面,您将不得不连接到一个远程服务器(因为 Git 是一个分布式 VCS)来与其他人共享您的提交,因此所有这些连接都必须加密,以进一步保护您的数据,确保它们不被窃取。
图 2-7
选择 HTTPS 交通工具
除非有理由(公司政策或您自己的小安全设置),否则请使用默认选项。
在这之后,进入下一步,这是关于行尾。这又是一个选择屏幕,所以你的屏幕应该如图 2-8 所示。不同的操作系统对文本文件的操作是不同的,尤其是在处理行尾的时候。您将要合作的团队可能会使用不同的操作系统。因此,在共享提交之前,Git 需要将行尾和每种结尾风格相互转换。
图 2-8
行尾转换
因为您将使用 Windows,所以应该选中默认选项。如果您不注意行尾,其他两个选项会对您的提交造成很大的损害。选择默认选项后,您可以进入下一步。
警告
这一步很重要,因为 Windows 和 MacOS 使用\r\n 而不是 Linux 的\n 来结束行,如果您不转换,您的文件将变得非常难以阅读,Git 将检测到许多更改,即使没有进行那么多更改。
下一步是选择默认的终端(或控制台)模拟器。这是一个简单的选择屏幕,如图 2-9 所示。Git Bash 需要一个控制台模拟器才能工作,所以您需要选择一个。默认模拟器是 MinTTY,另一个选项是 Windows 的默认控制台。
图 2-9
选择终端模拟器
我建议保留默认选项,因为 MinTTY 可以做 Windows 控制台窗口可以做的一切,但在各方面都更好。单击“下一步”进入最后一步。
我们现在处于最后阶段!这个安装就要结束了。只是在额外选项屏幕上做了一些调整。这个屏幕(如图 2-10 所示)允许您启用一些额外的特性,这些特性将会很好地配合您的 Git 安装。例如,Git 凭证管理器将帮助您安全地连接到远程服务器,并与其他 Git 工具配合良好。
图 2-10
配置额外选项
只要保留默认选项,除非你有理由不这样做。之后,只需启动安装并让它完成。就这样!Git 安装在您的 Windows 系统上。但是在使用它之前,请跳到下一节来正确设置它!
苹果个人计算机
如果你已经用 Mac OS X 做过一些软件开发,你可能已经有 Git 了,因为它是和 XCode ( https://developer.apple.com/xcode/
)一起安装的。您可以通过从控制台运行以下命令来检查您是否拥有 Git:
$ git --version
它应该给你当前安装的 Git 版本,或者如果没有安装,提示你安装 XCode 的命令行工具。如果在提示符下选择 install,将会安装 Git,您可以跳过本节的其余部分。
要在 Mac 上安装 Git,只需进入下载链接 https://git-scm.com/download/mac
,下载应该会自动开始,如图 2-11 所示。执行下载的文件,安装将开始;这很简单。
图 2-11
用于 Mac 的下载屏幕
也可以用家酿( https://brew.sh/
)安装。只需运行命令:
$ brew install git
这将安装大约一半的宇宙,但它最终会停止,Git 将被安装。
就是这样!对于 Mac OS X 来说,安装 Git 要简单得多,你可能已经有了。
Linux 操作系统
如果你经常使用 Linux,你可能比我更了解你的发行版。因此,用您的包管理器安装 Git 对您来说可能是小菜一碟。
对于 Ubuntu 和 Debian 风格的发行版,您使用 APT 来安装 Git。
$ sudo apt-get install git
或者
$ sudo apt install git (for newer systems)
对于 Fedora,您可以使用 YUM 或 DNF。
$ sudo yum install git
或者
$ sudo dnf install git (for newer systems)
如果你有一个不同的发行版,你可以检查 https://git-scm.com/download/linux
来获得一个关于如何为每个流行的发行版安装 Git 的命令列表。这个列表应该类似于图 2-12 所示的列表,越来越多的 Linux 版本将会出现。
图 2-12
如何在 Linux 上安装 Git
使用图 2-12 中列出的与您的发行版相对应的命令后,Git 就安装好了!
警告
就像编辑战一样,发行战在网上是一大禁忌。
设置 Git
在开始使用 Git 之前,您需要先做一些设置。您可能只需要这样做一次,因为所有的设置都存储在一个外部全局文件中,这意味着所有的项目都将共享相同的配置。还有一种方法可以逐个配置项目,但我们将在后面看到这一点。
由于 Git 是一个分布式版本控制系统,总有一天你会需要连接到其他远程存储库。为了避免犯任何身份错误,有必要告诉 Git 一些关于你自己的情况。不用担心;它不会问你一个有趣的事实!
要设置 Git,请打开 Git Bash(对于 Windows 系统)或默认控制台窗口(对于修改了路径环境的 Linux/MacOS 或 Windows 系统)。在命令提示符下,只需告诉 Git 您的姓名和电子邮件地址:
$ git config --global user.name "Mariot Tsitoara"
$ git config --global user.email "mariot.tsitoara@gmail.com"
请注意“全局”参数;这意味着该设置适用于所有将来的 Git 存储库,因此您不必在将来再次设置它。
使用 config 命令,您还可以更改您的默认编辑器。如果您因为找到了新的编辑器或者卸载了自己的编辑器而想要更改编辑器,config 命令可以帮助您。例如,要将默认编辑器更改为 Nano,您可以键入
$ git config --global core.editor="nano"
您可以在您的主文件夹中找到记录 Git 配置的文件。对于 Windows,可以在 C:\Users\YourName.gitconfig 中找到,对于 Linux 和 Mac OS,可以在/home/yourname/中找到。gitconfig 如图 2-13 所示。
图 2-13
我的 .gitconfig 文件
紧挨着。gitconfig 文件,您可能会找到另一个名为。bash_history 记录您在控制台上输入的所有命令。如果你想重新检查你忘记的命令,你可以检查这个文档。
摘要
让我们复习一下到目前为止所学的内容!首先,现在您应该已经在系统上安装了 Git。安装过程在 Windows 上非常容易,在 Mac 和 Linux 上更容易。如果你不确定你需要什么,我建议你保留所有的默认选项(即使在前面的截图中没有显示)。
接下来是设置。在安装 Git 的每个系统中,您只需要这样做一次。Git 将使用你的名字和电子邮件来签署你的每一个动作,所以在你使用它之前有必要设置它们。
就这样!您现在已经准备好使用 Git 的所有优点了。阅读下一章,从 Git 开始。
三、入门指南
您终于准备好开始使用 Git 了!在这一章中,你将学习一些任何项目都需要的 Git 术语和概念。然后,您的任务是建立一个项目,对其进行更改,检查更改,最后在不同版本之间导航。走吧!
仓库
存储库是保存所有项目和对项目所做的所有更改的存储器。你可以把它想象成一个“变化数据库”但是不用担心;它只是你系统中的一个普通文件夹,所以很容易操作。
对于您想用 Git 管理的每个项目,您必须为它建立一个存储库。建立一个存储库非常容易。只需导航到您想要跟踪的文件夹,并告诉 Git 在那里启动一个存储库。
所以对于每个你想开始的项目,你应该
-
创建包含您的项目的目录
-
导航到目录
-
初始化 Git 存储库
看到了吗?很简单。让我们将这些语句转换成命令。但是首先,让我们打开一个控制台来输入我们的命令。对于 Linux 用户来说,你只需要启动你喜欢的终端(对于 Debian 之类的发行版,Ctrl-Alt-T)。对于 MacOS,你只需要使用 Cmd-Space 来调出 Spotlight,在那里你可以搜索终端应用。Windows 用户可以打开两个控制台:cmd 和 powershell。Powershell 更现代,有类似 UNIX 的命令。要打开其中一个,请使用 Windows-R 并键入名称(cmd 或 powershell)。注意,如果您打开了这些控制台,那么在第一次安装 Git 时,您需要重新启动所有这些控制台。Git for Windows 还附带了一个名为 Git Bash 的控制台模拟器,它提供了一个类似于 Linux 和 Mac 控制台的环境。如果你使用 Windows,我强烈建议使用 Git Bash,这样你就可以和其他使用不同操作系统的人有相同的体验。
打开 Git Bash(从应用列表或上下文菜单中),并键入以下命令:
$ mkdir mynewproject
$ cd mynewproject/
$ git init
mkdir 是用于创建目录的命令;它是“制作目录”的缩写 cd 是用于在目录间导航的命令;它是“更改目录”的缩写最后, git init 是“Git initialize”的缩写
初始化存储库后,Git 会告诉你数据库是在哪里创建的,如图 3-1 所示。
图 3-1
新储存库的初始化
注意
mkdir 和 cd 是系统命令;它们由操作系统管理,而 init 是一个 Git 命令。每个 git 命令都以“Git”开头。
Git 将创建一个名为。git ”,它将包含您所有的变更集和快照。如果你想签出它,你必须从你的文件资源管理器的设置中显示隐藏文件。存储库看起来如图 3-2 所示的目录。
图 3-2
一个空仓库
如果你打开。git 目录中,您会发现更多属于 Git 数据库的项目。查看图 3-3 中的示例。
图 3-3
在里面。git 目录
还记得第一章说 Git 不是跟踪版本之间的变化,而是拍摄快照吗?嗯,所有这些快照都存储在”。git”目录。每个快照被称为“提交”,我们将在本节结束后不久对此进行研究。
头文件在此”。git”目录指向您正在工作的项目的当前“分支”或子版本。默认的分支被称为“主”,但是它就像任何其他分支一样;这个名字只是一个老约定。
您还应该知道初始化是获得存储库的唯一方法。您可以复制整个存储库及其所有历史和快照。它被称为“克隆”,我们将在另一章中看到。
练习:创建一个空存储库
我们的第一次演习并不完全是火箭手术。只需在系统中的某个地方创建一个空的存储库。您可以使用默认控制台或 Git Bash。
工作目录
“外面的空地呢。git”目录?它被称为工作目录,您将要处理的文件将存储在那里。通常,您的最新版本会在工作目录中。
您处理的每个文件都在工作目录中。这个地方没有什么特别的地方,除了你只能直接操作这里的文件。永远不要修改里面的文件。git”目录!
Git 将检测你放入工作目录的任何新文件。您可以使用 Git 命令“status”来检查目录的状态
$ git status
例如,如果我们在工作目录中创建了一个名为 README.md 的新文件,我们会看到 Git 会知道项目已经更改。确保将新文件放在。git 目录如图 3-4 所示,不要放入其中!
图 3-4
在工作目录中创建新文件
如果我们检查工作目录的状态,我们将得到如图 3-5 所示的结果。
如图 3-5 所示,我们还没有任何提交;这是因为我们仍在工作目录中,还没有拍摄任何快照。它还说我们在“主”分支上;这是在存储库初始化时创建的唯一分支的默认名称。然后我们得到未被追踪的文件。这些是我们修改过的文件(在本例中是创建的)。
图 3-5
工作目录的状态
本质上,这就是工作目录:您直接与项目文件交互的地方。
练习:为项目创建一些文件
这个练习也很简单。只需在项目目录(存储库)中创建一些文件,并检查工作目录状态。
部队从一个战场转往另一个战场的集结地
暂存区是拍摄快照前文件存放的地方。在拍摄项目当前状态的快照时,并不是您在工作目录中修改的每个文件都应该被考虑在内。只有放置在临时区域中的文件才会被拍摄快照。
因此,在拍摄项目快照之前,您需要选择要考虑哪些已更改的文件。文件中的更改可以是创建、删除或编辑。
把它想象成指定哪些文件要出现在全家福照片中。为了将文件添加到临时区域,我们使用 Git 命令“add”
$ git add nameofthefile
就这么简单。如果我们想要存放我们之前创建的 README.md,我们将使用“git add README.md”。或者,如果您创建了多个文件,您可以像“git add file1 file2 file3”那样依次或一起添加它们
让我们使用以下命令来存放我们的新文件:
$ git add README.md
然后让我们用 git status 命令检查状态。
$ git status
将文件添加到暂存区不会产生任何可见的结果,但是检查状态会得到类似于图 3-6 的结果。
图 3-6
暂存文件
如果您查看图 3-6 ,您会注意到在暂存文件之后,工作目录又变得干净了。这是因为“git status”只跟踪“未暂存”的文件(未标记为快照的已编辑文件)。
正如你在图 3-6 中看到的,你也可以使用 Git 命令“git rm”和选项“- cached”来卸载一个文件
$ git rm --cached README.md
警告
在卸载文件时,不要忘记选项“- cached”。如果你忘记了,你可能会丢失你的文件!
在转移了所有希望更改生效的文件后,现在就可以拍摄第一个快照了!
练习:暂存和取消暂存文件
获取您在上一个练习中创建的文件并转移它们。取消转移一个文件并重新转移它。在每个阶段/取消阶段后检查工作目录状态。
承诺
就像我们在这一部分之前讨论过的,提交只是整个项目在某一时刻的快照。Git 不记录对文件所做的单独更改;它拍下了整个项目的照片。
除了快照之外,提交还包含有关内容的“作者”和“提交者”的信息,或者是谁将变更集放入了存储库。
注意
“作者”和“提交者”通常是同一个人,除非提交者从另一个团队成员那里获得变更集。请记住,Git 提交是可交换的,因为它是分布式 VCS。
因为提交是项目状态的快照,所以项目的前一个状态是另一个被称为“父”的提交第一次提交是在创建存储库时由 Git 创建的,并且是没有父提交的提交。所有将来的提交都通过父子关系相互链接。这些互为父级的提交的集合称为“分支”
注意
如果一个提交有两个父级,这意味着它是通过合并两个分支创建的。
提交由其名称标识,名称是通过散列提交获得的 40 个字符的字符串。这是一个简单的 SHA1 散列,因此具有相同信息的多个提交将具有相同的名称。
对特定提交的引用称为“head”,它也有一个名称。而你目前正在做的头部叫做“头”(见上一节)。
我们现在可以提交之前暂存的文件。在每次提交之前,您应该检查工作目录和临时区域的状态。如果您想要提交的所有文件都在临时区域中(在短语“要提交的更改”下),您可以提交。否则,您必须使用“git add”来存放它们
为了提交我们所做的所有更改,我们使用“git commit”这将对项目的当前状态进行快照。
$ git commit
如果我们执行这个命令,它将打开我们的默认编辑器(如果您想修改您的编辑器,请查看第二章),并要求我们提交消息。提交消息是对与前一次提交相比,提交中发生了什么变化的简短描述。
我的默认编辑器是 Vim,所以如果我执行 commit 命令,我会看到如图 3-7 所示的屏幕。
图 3-7
Git 打开默认编辑器,这样您就可以编辑提交消息
在图 3-7 中可以看到文件的第一行是空的;这是您必须编写提交消息的地方。提交消息应该写在一行上,但是您可以添加多行注释。注释以“#”开头,被 Git 忽略;它们仅用于完成提交消息,使其更加清晰。还要注意,Git 会自动将已更改文件的列表放入提交注释中(与您在“git status”中看到的文件相同)。
在后面的章节中,你会学到正确编写提交消息的正确方法。但是现在,只需在第一个空行输入一条简单的消息,如图 3-8 所示,“将 README.md 添加到项目中”。
图 3-8
写在文件顶部的提交消息
如图 3-8 所示编写提交消息后,您可以关闭编辑器(保存后!).然后你会得到一个提交的概要,如图 3-9 所示。
图 3-9
提交摘要
提交摘要将包含大量信息:
-
当前分支:主
-
前一次提交的名称:root-commit,因为这是我们的第一次提交
-
提交的名称:提交散列的前七个字母
-
提交消息
-
更改的文件数量:一个文件
-
对每个文件所做的操作:创建
我们拍了第一张快照!如果您检查存储库的状态,您可以看到它又是干净的,除非您留下了一些未转移的文件。
练习:提交您的更改
从上一个练习中取出您的暂存文件并提交它们。然后修改一个被跟踪的文件,再次登台,并进行新的提交。比较每次提交的摘要。有什么不同?这些提交是如何联系在一起的?
Git 快速入门
现在,您已经熟悉了 Git 的基本概念,我们将在一个真实的项目中应用它们。让我们假设您想要创建一个文件夹来保存您的待办事项列表,并希望对其进行版本控制,以便您可以检查每个项目何时完成。
为了让您更熟悉 Git,您将在没有任何帮助的情况下进行下一个练习。如果你卡住了,只需查看前面的部分的方向。
只要记住 Git 的基本原则:
-
您可以修改工作目录中的文件。
-
您将想要记录当前状态的文件放在暂存区。
-
您通过提交来获取项目的快照。
不要忘记在提交之前将您修改的文件放在暂存区,否则它们将不会成为快照的一部分。您没有放在暂存区的修改过的文件将只保留在工作目录中,直到您决定放弃它们或者在将来的提交中包含它们。
让我们开始练习吧!请完成它直到结束,不要进入下一章,直到你明白 Git 是如何工作的。
练习:版本化的 TODO 应用
-
创建新的存储库。
-
在目录中创建一个名为 TODO.txt 的文件,并输入一些文本。
-
Stage TODO.txt
-
提交项目并放入一条简短的提交消息。
-
创建两个名为 DONE.txt 和 WORKING.txt 的新文件。
-
暂存并提交这些文件。
-
将 WORKING.txt 重命名为 IN PROGRESS.txt。
-
向 DONE.txt 添加一些文本。
-
检查目录状态。
-
阶段进行中. txt 和完成. txt。
-
Unstage DONE.txt。
-
提交项目。
-
检查目录状态。
完成这个练习后,合上书,试着用自己的话解释这些事情:
-
工作目录
-
部队从一个战场转往另一个战场的集结地
-
犯罪
如果您在理解这些概念方面没有太多问题,那么您已经准备好学习更多的 Git 命令和概念了。
摘要
这一章对你理解 Git 非常重要。主要要点是文件的三种状态:
-
修改:您修改了工作目录中的一个文件。
-
暂存:您将文件添加到暂存区域,以便可以对其进行快照。
-
已提交:您拍摄了整个项目的快照(所有未修改的和暂存的文件)。
如果文件是上一次提交的一部分,而您没有修改它们,它们将自动成为下一次提交的一部分。已修改但未转移的文件被视为未修改。您必须要求 Git 通过暂存这些文件来跟踪它们。
我们还学习了一些关于提交和提交消息的知识。一开始,打开外部编辑器来编写提交消息可能会有点笨拙,但是过一段时间后,您最终会掌握它的窍门。
在下一章,我们将学习如何检查项目历史和在版本之间导航。我们还将了解如何忽略某些文件,并显示自上次提交以来对项目所做的当前更改。
四、深入 Git
现在您已经熟悉了 Git 的基本命令,我们将深入研究它的其他特性。你将在本章中发现我在第一章中承诺给你的特性。
忽略文件
Git 不应该跟踪工作目录中的所有内容。有些文件(配置、密码、错误代码)通常不会被作者或开发人员跟踪。
那些文件(或目录)被列在一个名为“. gitignore”的简单文件中,注意“gitignore”之前的句点;这很重要。要忽略文件,请创建一个名为。gitignore 并在其中列出要忽略的文件或文件夹。
让我们回到上一章的知识库,待办事项列表。假设您想要包含一个名为 PRIVATE.txt 的私有的、未被跟踪的文件。使用您最喜欢的文本编辑器忽略文件,然后在其中写入 PRIVATE.txt,如图 4-1 所示。
图 4-1
的。git 忽略包含 PRIVATE.txt 的文件
如果您随后创建并修改 PRIVATE.txt 文件(如图 4-2 所示),如果您检查状态,Git 不会考虑它。
图 4-2
添加 PRIVATE.txt
让我们试着检查一下状态。
$ git status
你会得到如图 4-3 所示的类似结果。
图 4-3
工作目录的状态
在图 4-3 所示的状态上可以看到,PRIVATE.txt 没有被跟踪。您还可以看到。gitignore 文件被跟踪;因此,您必须在修改后添加并提交它。
$ git add .gitignore
$ git commit
像往常一样,暂存一个文件,然后提交项目,将会产生一条确认消息,总结所做的更改(如图 4-4 所示)。
图 4-4
承诺。被增加
请记住。gitignore 全局文件应该放在你的库的根目录下。如果将它放在一个目录中,只有该目录中的匹配文件会被忽略。一般来说,有多个。gitignore 多个目录中的文件被认为是一个糟糕的举动,除非你的项目非常庞大。更喜欢把它们列成一个。gitignore 文件放在你的库的根目录下。
您可能会问自己在使用 Git 时应该忽略哪种文件。经验法则是忽略项目生成的所有文件。例如,如果您的项目是一个软件源代码,您应该忽略编译后的输出(可执行文件或翻译文件)。临时文件和日志,以及大型库(node_modules)也应该被排除在外。不要忘记排除所有的个人配置和文本编辑器的临时文件。
的。gitignore file 不仅忽略按名称列出的文件;您也可以忽略与描述匹配的目录和文件。您将在表 4-1 中找到您可以使用的所有模板的便捷提示。
表 4-1
。git 忽略行和它们的作用
|。吉塞尔线
|
什么被忽略了
|
例子
|
| --- | --- | --- |
| 配置文件 | 任何目录中的 config.txt | 配置文件本地/配置. txt |
| 构建/ | 任何构建目录和其中的所有文件。但不是名为 build 的文件 | 构建/目标. bin 构建/输出. exe 不输出/构建 |
| 建设 | 任何构建目录、其中的所有文件以及任何名为 build 的文件 | 构建/目标. bin 输出/构建 |
| * exe | 所有带有扩展名的文件。可执行程序的扩展名 | target.exe 输出/res.exe |
| bin/* . exe | 所有带有扩展名的文件。bin/目录中的 exe 文件 | bin/output.exe |
| 临时议程* | 名称以 temp 开头的所有文件 | 临时雇员温度箱临时输出 |
| □配置 | 任何名为 configs 的目录 | config/prod . py 本地/config/prep rod . py |
| □config/local . py | 任何名为 configs 的目录中任何名为 local.py 的文件 | config/local . pyserver/configs/local.py 不是 configs/fr/local.py |
| output/∫∫/result . exe | 输出中任何目录下的任何名为 result.exe 的文件 | 输出/结果输出/最新/结果. exe 输出/1991/12/16/result.exe |
这些是与. gitignore 一起使用的最常见的行。还有其他的行,但它们只在非常特殊的情况下使用,在普通项目中几乎从不使用。如果你使用的是计算机语言或框架,你可以去 https://github.com/github/gitignore
获取模板。git 忽略您应该使用的文件。
但是如果您想忽略除了一个文件之外的所有匹配描述的文件呢?嗯,您可以告诉 Git 忽略所有文件,然后立即进行例外处理。要从忽略列表中排除某个文件,请使用“!."例如,如果您想忽略除 output.exe 之外的所有 exe 文件,您将编写您的。gitignore 如图 4-5 所示。
图 4-5
如何破例
注意各行的顺序。规则之后是例外!
不过,这个异常标记只对描述文件名的行有效。你不能用它来忽略目录。如图 4-6 所示的. gitignore 文件不起作用。
图 4-6
异常不适用于目录匹配忽略的文件
练习:忽略文件和目录
从上一个练习中取出您的存储库,并创建多个文件和目录。检查表 4-1 并尝试忽略您使用每一行创建的文件。根据需要创建尽可能多的文件,在理解每个模式之前不要停止。不需要记住所有的东西,但是你至少应该知道什么时候应该使用它们。
练习:这些行忽略了什么
检查图 4-7 。不看上一节,每行忽略了什么?
图 4-7
猜猜每行忽略了什么
你就是这样忽略文件的!这几乎和忽视你的责任一样简单!但是请记住。gitignore 文件被跟踪和版本化,所以不要忘记在提交之前暂存它!
检查日志和历史记录
如果您遵循了练习(正如您应该做的),或者开始在您自己的项目中使用 Git,那么您现在会遇到一个小问题,我曾保证使用 Git 可以很容易地解决这个问题:如何查阅历史日志。
这是 Git 最常用的特性之一,也是最简单的 Git 命令之一:git log
$ git log
试试看!打开您的存储库并运行命令。您应该会看到如图 4-8 所示的视图。
图 4-8
提交日志
提交日志将列出您或其他人提交的所有快照(从最新的到最早的)。对于每次提交,它还包括
-
名称(唯一的,通过哈希获得)
-
作者
-
日期
-
描述
由于提交名称太长,我们将只使用前五个字母作为名称。这对下一节很重要。
如果您的提交历史非常长,您可以使用键盘开始
-
向前或向后一行:上下键或 j 和 k 键
-
向前或向后一个窗口:f 和 b
-
在日志的末尾:G
-
在日志的开头:g
-
获取帮助:h
-
退出日志:问
git log 有许多参数可以使用;表 4-2 正在向您展示。
表 4-2
最常见的 git 日志参数
|命令
|
使用
|
例子
|
| --- | --- | --- |
| git log --reverse
| 颠倒提交的顺序 | |
| git log -n <number>
| 限制显示的提交数量 | git log -n 10
|
| git log --since=<date>``git log –after=<date>
| 仅显示特定日期后的提交 | git log --since=2018/11/11
|
| git log --until=<date>``git log --before=<date>
| 仅显示特定日期之前的提交 | |
| git log --author=<name>
| 显示来自特定作者的所有提交 | git log --author=Mariot
|
| git log --stat
| 显示变更统计 | |
| git log --graph
| 在简单的图形中显示提交 | |
练习:显示历史记录
这个练习很简单。只需重新打开上次练习中的存储库,并检查历史日志:
-
以相反的顺序
-
从昨天开始
-
对于最后两次提交
查看以前的版本
既然您已经知道了如何检查历史和提交日志,那么是时候检查文件了,首先看看哪些文件发生了更改。
还记得每次提交时创建的那些长名字吗?我们将使用它们在提交或快照之间导航。要检查特定快照上的文件,只需知道它的名称。知道每个提交名称的最好方法是查看历史日志,如图 4-9 所示。
图 4-9
待办事项列表的历史记录
要显示和了解对项目做了哪些更改,只需使用“git show”命令,后跟提交的名称。你甚至不需要写全名,只需要前七个字母。
$ git show <name>
尝试使用您的存储库!您应该会得到如图 4-10 所示的结果。
图 4-10
git 显示的结果
如您所见,提交以非常详细的方式显示。您将看到所选提交和前一个提交之间的差异。增加的用绿色显示,删除的用红色显示。您可以使用“git show”命令显示任何提交的细节。
练习:检查您对项目所做的更改
列出您对项目所做的提交,并检查每个提交的更改。
查看当前的更改
检查以前的版本是很好的,但是如果您只想检查您刚才所做的更改呢?检查最后一次提交和当前工作目录之间的差异是 Git 的一个基本特性。你会经常用到它!检查差异的命令很简单:git diff。
$ git diff
修改目录中的一个或多个文件,然后执行命令。您将得到如图 4-11 所示的结果,这与上一节中 git show 命令的结果非常相似。它们实际上是相同的视图,因为显示的信息是相同的。
图 4-11
检查工作目录中的所有更改
大多数情况下,您只需要检查对单个文件的更改,而不是对整个项目的更改。您可以将文件名作为参数传递,以查看它与上次提交时的差异。
$ git diff TODO.txt
要记住的主要事情是 git diff 检查对工作目录中的文件所做的更改;它不检查暂存文件!要检查对暂存文件所做的更改,您必须使用参数“- staged”
$ git diff --staged
在提交项目之前,您应该总是检查暂存文件中的差异,这样您就可以做最后的检查。我知道有一天你会忘记这么做,所以去下一章学习如何撤销或修改你的提交。
本章到此结束,我们学到了很多东西。在进入下一章之前,请确保您熟悉这些功能:
-
忽略文件
-
检查历史日志
-
审查本地和阶段性变更
如果你是,并且你完成了练习,祝贺你!但是我们还没有完成提交!
摘要
这一章是关于项目历史的。我们学习了使用 git log 和 git show 检查日志,还学习了使用 git diff 检查当前的更改。Git log 和 git diff 在未来会特别有用,所以一定要好好理解它们。Git diff 是将当前修改的文件与上次提交的文件进行比较,而 git log 只是所有以前提交的列表。
忽略文件的能力。gitignore 也是一项很好的技能,这样您的 git 状态就不会被您不想提交的修改过的文件饱和。这也是确保特定文件(可能包含密钥)不会被意外提交的好方法。
在下一章,我们还有很多关于提交的内容要学。我们将首先回顾 Git 文件的三种状态,然后我们将看到如何将以前的版本带回工作目录。您至少将学习如何撤销和修改提交。抓紧了!
五、提交
前一章教了你一些关于 Git 的基本特性。您应该知道如何检查历史日志并查看对当前版本所做的更改。但是 Git 提交是一块难啃的骨头,所以我们将在本章中更多地讨论它们。首先,我们将(再次)探索 Git 的内部工作及其术语。然后,我们将学习如何查看和检查以前的版本。我们走吧!
Git 的三种状态
在详细讨论提交之前,我们必须回到基础,重新了解 Git 是如何工作的。您肯定记得文件可以找到自己的三种状态。如果没有,就不要跳过这一章;这对于您使用 Git 所做的一切都是必不可少的。如果你记得,也不要跳过,因为我花了很多时间写的。
正如你在上一章看到的,Git 并不是跟踪所有的文件;一些文件被忽略(由。gitignore 文件)。还有一些文件没有被忽略,但是还没有被 Git 跟踪。它们是新创建的文件,从未包含在快照中(提交)。
被跟踪的文件可以有三种状态:
-
修改:您更改了文件。
-
已转移:您更改了文件并准备拍摄快照。
-
已提交:您拍摄了整个项目的快照,文件就在其中。
未跟踪的文件将保持原样,直到您决定暂存并提交它们或明确忽略它们。
记住:Git 不跟踪变化,它跟踪快照。每次提交时,整个项目的状态都会被保存,而不仅仅是所做的微小更改。
书呆子的事实:Git 很快,因为你总是在项目的最后状态工作。当您想要查看以前的提交时,它只是向您显示项目在那个特定时间的状态。许多 VCS 存储对一个文件所做的每一个更改,当您想回到以前的状态时,它们会反向重放这些更改。当项目变大时,这会导致许多问题,如速度和内存。Git 的思维方式不就是创建超级大数据库吗?不会,因为当你拍快照的时候,一个文件没有变化,它不会再被存储;而是使用对文件的引用。
让我们再回到这三个状态,看看它们之间的关系:
-
你在工作目录上工作。它只是您在初始化存储库之前创建的目录。这是你阅读和编辑文件的地方。
-
临时区域是您在拍摄整个项目的快照之前放置已更改文件的地方。如果不暂存已更改的文件,则不能拍摄快照。快照中将只考虑转移的文件(和未更改的文件)。未转移的文件(跟踪或未跟踪)和忽略的文件将保持相同的状态。
-
数据库或者。git 目录存储您拍摄的每个快照。这些快照称为提交。
记住:暂存只关注你选择的变更文件,而提交关注整个项目。你暂存一个文件;然后提交项目。
在版本间导航
很多时候,您不仅想知道项目中发生了什么变化,还想知道它处于什么状态,想看看您拍摄的快照。用 Git 很简单。
当您想要将项目的先前状态带到工作目录中时,我们必须使用“git checkout”来检查提交因为这会更改工作目录中的文件,所以您必须确保那里没有任何未转移的文件。未跟踪的文件很好,因为 Git 还没有跟踪它们的状态。
为了检查项目的快照,我们使用“git checkout”命令并将提交名称作为参数传递。
$ git checkout <name>
我们试试吧!在文本编辑器中打开当前项目,记下其内容。现在检查之前的提交,如图 5-1 所示。
图 5-1
签出旧的提交
警告
如果您的工作目录不干净,您就不能检查任何其他提交!请确保在切换快照之前提交您的更改。
检查以前的提交时,注意不要更改任何内容。就像电影里一样,改变过去是一个非常糟糕的主意!
如果您检查您的文本编辑器,您会注意到项目现在就像您拍摄快照时一样。这就是 Git 的最佳之处。您拍摄的快照不会丢失任何东西!
现在让我们学习一些 Git 术语。首先是“头”“头”只是对提交的引用。当谈到提交时,我们不说“名字”,而是说“头”
当在不同的提交之间切换时,我们需要一种方法来知道我们在哪个“头”上。当前头(被检出的那个)只是被称为“头”
就这样!head 是对提交的引用(一个存储库中可以有多个 head),指向当前签出的提交的 HEAD 称为 HEAD。
练习:在你的历史中移动
使用“git checkout”从一个提交转移到另一个提交确保不要更改任何内容。
但是如何返回到正常的当前工作目录呢?因为我们没有对我们的存储库做任何大的改变,所以返回到工作目录只是检查我们拥有的唯一的分支。按照惯例,那个分支叫做“主”
$ git checkout master
试试看!记住时间旅行的两条黄金法则:
-
只有当现在是干净的时候(工作目录中没有未存储的内容),才可以回到过去。
-
不要改变过去(直到你有了更多的经验)。
在版本之间导航后,不要忘记检查当前的分支(主)。
撤消提交
总有一天,你会暂存并提交文件,但随后又改变主意。每个人都会这样。但是使用传统的方法(没有版本控制),回滚更改是非常困难的,特别是如果更改发生在很久以前。对于 Git,它只是一个命令:git revert。
为什么不直接删除提交?因为上一节的时间旅行规则:永远不要改变过去。为了历史,无论发生什么变化,都必须保持不变;改变过去发生的事情是非常危险和违反直觉的。相反,您将使用 git revert 创建一个新的提交,它包含与您试图撤消的提交完全相反的提交。
所以,撤销提交只是提交了它的反面。就这么简单!要使用它,您必须将要撤销的提交的名称作为参数传递。
$ git revert <commit name>
您可以恢复任何提交;只要确保在一个干净的工作目录下工作。因此,在恢复提交之前,不要忘记暂存和提交您的文件。我们试试吧!
首先,确保工作目录是干净的,如图 5-2 所示。
图 5-2
使用 git status 检查工作目录
太好了。既然我们知道工作目录是干净的,那么是时候检查历史以了解要撤销哪个提交了。我们应该会得到如图 5-3 所示的结果。
图 5-3
使用 git 日志检查提交历史记录
注意
如果您不喜欢提交历史的显示方式,可以传递“- oneline”参数来减少显示的信息。查看图 5-4 中的示例。
图 5-4
更漂亮的 git 日志输出
让我们恢复第三次提交!我们只是使用 git revert 后跟提交名。
$ git revert 5f57824
因为 git revert 只创建一个包含相反更改的新提交,所以过程的其余部分与任何新提交相同。如图 5-5 所示,您将被要求描述您的新提交。我建议总是保持默认的提交描述,因为它很容易识别。
图 5-5
新提交描述
保存提交描述后(像所有提交一样),您将看到快照内容的摘要。图 5-6 显示了运行命令并保存提交描述后的结果。
图 5-6
回复的摘要
如您所见,使用 Git 撤销更改非常容易。需要记住的是 git revert 只会创建一个包含相反更改的新提交。这意味着您可以恢复一个恢复!恢复一个还原只会重新应用您的原始提交,并且两个“还原”会相互取消。但是,这些提交将保留在您的历史日志中,因为您无法改变过去。
注意
其实你可以改变过去。但是永远不要这样做。这是一个非常糟糕的主意,它只会给你带来更多的问题。
修改提交
正如我在上一章中所承诺的,你将在本章中学习如何修改提交。当您忘记存放文件或想要更改提交消息时,可以使用此选项。这不应该用来修改很多文件,因为这是违反直觉的。下一章将详细讨论何时何地使用这个。我再说一遍:永远不要试图改变过去。
要修改提交,您必须使用 git commit 命令,但是要使用“- amend”作为参数。它将像普通提交一样打开您的默认文本编辑器,但是已经有了暂存文件和提交消息。
$ git commit --amend
然后,像每次提交一样,保存并关闭文本编辑器。我使用的“修改”这个词有点误导,因为你不是在修改一个提交;您正在创建新的提交并替换当前的提交。所以,从现在开始,我会用“修正”这个词
修改提交会获取暂存区域中的所有内容,并使用它进行新的提交。因此,如果您想在提交中添加一个新文件或从中删除一个文件,您可以随意存放和取消存放它们。提醒:要卸载一个文件,你必须使用 git reset HEAD
让我们再次使用我们的 TODO 应用。编辑现有文件;然后创建两个名为 filenottocommit.txt 和 fileforgotten.txt 的新文件,如图 5-7 所示。
图 5-7
我们工作目录中的所有文件
您可以通过执行 git status 命令来检查项目的当前状态:
$ git status
根据您之前添加到项目中的文件数量,您可能会得到稍微不同的结果,但仍然类似于图 5-8 。
图 5-8
已修改和未跟踪的文件会突出显示
我们要做的下一件事是将文件转移到提交中。添加更改后的文件和 filenottocommit.txt。
$ git add TODO.txt DONE.txt filenottocommit.txt
从上一章你知道,在提交之前,你应该总是检查你用“git diff - staged”准备了什么。但是让我们假设你忘记检查并立即提交。
$ git commit
即使这样,您也会看到提交消息屏幕,该屏幕概述了要提交的更改,如图 5-9 所示。
图 5-9
提交消息屏幕是最后一个安全措施
正如您所看到的,将要提交的更改和未跟踪的文件被概括和突出显示。很难错过它们,但是让我们假装错过,写一个简单的提交消息,保存,然后关闭编辑器。您将获得如图 5-10 所示的常规摘要。
图 5-10
提交摘要。我们搞砸了
现在您已经阅读了提交摘要,您注意到您提交了错误的文件,并且忘记提交另一个文件。
首先,您应该使用 git reset 从项目中删除最后一次提交。我们将使用“- soft”选项,以便我们所做的编辑保留在工作目录中。HEAD~1 表示前一次提交是对当前提交的引用。
$ git reset --soft HEAD~1
此后,您可以通过 git reset 再次卸载该文件:
$ git reset HEAD filenottocommit.txt
通过检查项目的当前状态,检查命令是否按预期工作。
$ git status
您将得到如图 5-11 所示的结果。
图 5-11
重置后项目的状态
如您所见,filenottocommit.txt 现在未被跟踪,因为我们已将其从临时区域中移除。自然,fileforgotten.txt 也没有被跟踪,因为我们没有登台。只有 DONE.txt 保留在临时区域,因为我们在提交后还没有接触过它。
警告
使用重置命令时要小心。非常危险。确保仔细检查你写的东西。
然后上演正确的。
$ git add fileforgotten.txt
现在您已经准备了正确的文件,您可以提交项目了。
$ git commit
在提交消息中放一个语法错误,这样您就可以看到 Git 的另一个特性。
修改提交
对于像提交消息中的错误这样的简单错误,没有必要修改整个提交。你只需要修改它。让我们试试我们的项目吧!
$ git commit --amend
修改过程看起来就像普通的提交,但是提交消息已经被写入,如图 5-12 所示。
图 5-12
编辑提交消息
您可以随意更改提交消息,然后像往常一样保存并关闭编辑器。
就这么简单!看一下新提交的名称,并将其与旧名称进行比较。你会注意到它们是不同的。这是因为提交名称是快照中信息的散列。因此,项目的不同状态会产生不同的名称。
关于修改提交的临别提示:不要滥用它!是的,写代码的时候犯错误并不理想,很多时候我们都想马上改正。但是错误也帮助我们变得更好;记录我们的错误是一种很好的学习方式。
练习:干净地修改提交
回到你的待办项目。本练习的目标是彻底修改提交。
-
编辑一些文件并登台。
-
提交它们,并在提交消息中犯一个语法错误。
-
取消文件登台。
-
上演另一出。
-
用正确的消息修改提交。
摘要
这一章主要讲述了导航、撤销和修改项目的版本。你现在应该对你提交的小修改没有问题了。一定要重读本章的第一部分,因为它对你在 Git 中做的所有事情都是必不可少的。你要把 Git 三种状态的区别背下来。
下一章很小,因为我们将只讨论理论。您将学习如何编写一个好的提交消息,在提交中包括和忽略什么,以及初学者会犯的常见错误。请务必仔细阅读下一章,因为它将对你和你的团队有很大的帮助。走吧!
六、Git 最佳实践
前一章是这本书最重要的一章。每次你对提交有疑问的时候,一定要回来看看。阅读完之后,您应该能够毫无问题地制作、检查和修改项目快照。既然您已经了解了 Git 的基本特性,那么是时候学习最佳实践来使您(和您的队友)的生活更加轻松了。这些是我希望在第一次使用 Git 时就知道的东西。我们将讨论提交消息,Git 的注意事项,以及初学者最常犯的错误。然后我们将以 Git 如何工作的一个小提示来结束。
提交消息
提交消息是版本控制最重要的方面之一,也是最容易被忽视的方面之一。这些消息可以帮助您(和其他人)理解提交中做了哪些更改,最重要的是,为什么要做这些更改。干净易读的提交消息对于更好的 Git 体验至关重要。让我们从确定问题开始。
Git 面临的最常见的问题是,提交消息通常没有意义,没有传达任何有意义的信息。大多数时候,每次提交的信息越来越不清晰。这是因为对 Git 概念的误解:每次提交都必须袖手旁观自身;如果一个提交需要其他提交才有意义,它就不应该存在。你不应该承诺一个半途而废的项目。如果一个任务变得太大,把它分成几个逻辑块,每个部分都有自己的意义。在拆分任务时,知道您是否在错误的路径中的一个好方法是检查可能的提交消息:如果您考虑使用非常相似的提交消息,您可能在拆分任务时犯了错误。例如,如果您的任务是在一个大型网站中进行许多小的修改,那么将它分成更小的任务是有意义的,比如提交每个页面或提交每个页面类别。所以记住:你的提交必须是独立的、原子的和完整的。
许多初学者也有一个问题,就是在提交消息中传递了太多的信息,从而用不必要的细节堵塞了大多数屏幕。提交消息必须简明扼要。你不需要告诉所有改变了的事情,你只需要解释为什么要做这些改变。如果有人想查看发生了什么变化,他们可以使用 git show 命令,该命令显示提交中发生变化的文件的完整摘要。
请记住,您不是唯一会阅读您的代码或文本的人。你必须花一点时间来解释这些变化的背景以及为什么要这样做。对自己说“我会记住的”是谎言,永远不要去实践。对于每一次提交,你应该问自己:“如果另一个人看着我的项目,他们会仅仅通过看我的提交消息就理解项目中变更的时间线吗?”也要记住,另一个人可能就是几个月后的你;密码很容易忘记。
底线是你的 Git 消息应该告诉你为什么要做这些改变。如果有人想知道发生了什么变化,他们可以查看 Git diff。
Git 提交最佳实践
为了获得更好的提交消息并避免前面列出的问题,这里有一些提示,您应该从现在开始遵循。这些建议会帮助你的同事,而且很有可能,在将来,你会清楚地知道为什么要犯错误。随着项目的进行,我们往往会忘记之前的步骤,所以在快节奏的开发中,拥有一个好的历史日志是必不可少的。
- 提交消息应该看起来很容易。
当您使用 git log 时,当消息太长时不会形成新行;因此用户必须滚动才能查看所有内容。这并不理想,因为您应该能够轻松地搜索和检索提交。
-
你写的信息不应该超过 50 个字符。
-
以大写字母开始信息。
-
不要以句号结尾。
-
利用现在的时间,扔掉不必要的文章。
-
提交消息应该一致。
因为 Git 消息在任何项目中都是基础,所以它们应该是一致的,并且不应该受到粗暴的改变。每次提交都应该使用相同的语言,并遵循其内部逻辑。在项目中期改变写作风格会使搜索提交变得非常困难。
- 这些信息必须清晰明了,并与上下文相关联。
当许多作者在不同的部分工作时,背景是大项目的关键。例如,许多开发人员通过变更所涉及的项目的上下文或区域开始他们的提交消息;但这只涉及非常大的项目。
不清楚或模糊的信息,如“更改 CSS”、“修复测试”、“热修复”、“小修复”和“更新”,应该不惜一切代价避免。它们经常误导用户,迫使用户去寻找不同之处。一定要包括为什么要做这些改变。永远不要强迫用户看你的代码变化来理解提交。
- 不要在细节上发疯。
您可以在主体中扩展您的提交消息,但是不要犯提供太多信息的错误。你唯一需要解释的是为什么要改变,而不是改变什么。
请记住:您的提交消息应该说明如果应用它,项目将会发生什么。所以你应该总是使用清晰的、现在时的、命令式的语言。最好的提交消息通常简短、直截了当、清晰明了。
没有比例子更好的方式来让它更清楚,所以让我们这样做。表格 6-1 是一个方便的工具,可以给你指出正确的方向。
表 6-1
最佳和最差提交消息的一些示例
|最好的
|
严重的
|
最差的
|
| --- | --- | --- |
| [登录]修复数据库调用中的打字错误 | 修复了数据库调用中的打字错误 | 修复打字错误 |
| 重构登录函数以便重用 | 通过将声明移动到参数来更改登录函数 | 代码重构 |
| 为用户程序检查添加新的 API | 为用户程序检查添加新的 API | 新用户 API |
表 6-1 中给出的例子应该表明在编写提交消息时你是否处于良好的方向。
请注意,这些都是建议的行动,并不是一成不变的。如果真的有必要,你可以忽略其中的一些,如果这样能让信息更清晰的话。
做什么
让我们从列举使用 Git 时应该永远记住的良好实践开始。这对你的成功是必不可少的,因为它会为你节省一些时间。
要记住的最重要的事情是,提交是项目中独立的变更。您应该始终保持提交小且独立。提交的作用是(大部分时间)引入一个特性或修复一个 bug 它不是用来记录你所做的每一个改变。如果一个特性或错误修复需要大的独立步骤,那么将它们分成多次提交。例如,一个特性需要一个 API 端点和一个前端调用。没有必要在一次提交中完成所有这些更改,因为它们是独立的,没有任何逻辑联系。如果您在后端代码中犯了一个错误,您可以在不干扰前端代码的情况下恢复更改。通过多次提交来分隔它们还会使历史日志更易读,提交消息更清晰。
我们之前已经讨论过这个问题,但是因为它非常重要,所以让我们回头再讨论一下。每个提交消息必须回答“为什么?”为什么创建提交?它解决什么问题?请记住,在 Git 中,提交可以在许多用户之间交换。因此,提交消息必须回答这个问题:如果我选择并应用这个提交,它会做什么?这就是为什么提交时态应该是现在形式。很难摆脱用过去式写的需要,但是几个星期后,你应该会适应了。
就这样!使用 Git 要做的事情很少。只是要确保为你的小的、独立的提交写清楚信息。另一方面,不要做的事情列表如下。
不要做什么
这个列表比前一个列表长了一点。这是因为 Git 是一个非常强大的工具,它不会限制您可以做的事情。所以,犯错误是很容易的,尤其是当你认为这会节省你的时间的时候。不会的。糟糕的实践会给你带来更多的问题。最好是完全避免做那些事情。
大多数初学者容易犯的一个常见错误是一次提交解决多个问题。例如,当他们发现另一个 bug 时,他们正在修复一个 bug。他们解决了这两个问题,然后提交项目。这似乎很好,直到发现提交在代码库中引入了许多问题。因为只有一个提交,他们不知道是哪个更改导致了问题。这只是阻塞提交问题的一个方面。另一个原因是,它使得编写连贯清晰的提交消息变得困难。如果您发现自己在不同的上下文中提交了许多变更,那么可以考虑将这些提交分成更小的几个。
与前一个错误类似的另一个错误是合并没有任何共同点的提交。例如,代码重构不应该与错误修复或新特性在同一个提交中,而应该在它自己的提交中。这也是为了便于追踪 bug,并使历史日志更加清晰。
下一个错误来自对 Git 的根本性误用和一些公司的需求。就是把 Git 作为备份系统的错误。因为 Git 是一个分布式版本控制系统,所以存储库可以存储在远程服务器中。这促使一些开发人员在每天结束时提交他们的更改,不管这是否有意义。这也是因为需要显示您的日常进展,因为一些公司会查看生成的代码行数来衡量生产率。这是一种非常违反直觉的工作方式,因为它创建了许多试图解决同一问题的提交。它还会导致混乱的提交消息,随着时间的推移,这些消息越来越不清晰。不惜一切代价避免这种情况。你应该在工作准备好的时候提交,而不是因为你不得不提交。如果你需要提交,因为你的任务是做其他的事情,你将有机会借助分支或堆叠这样的概念来这样做。几章后你会学到这些。
Git 的另一个被滥用的特性是 amend 命令。避免修改提交来引入大的改变。修改应该只用于纠正错别字和添加忘记的文件或非常小的变化。如果变化太大,以至于您觉得有必要更新提交消息,只需再提交一次。但是这不是会把我的错误留在代码库中吗?是的,但是 Git 是用来跟踪版本和显示发生了什么变化的。你也需要记录你的错误,因为它们很容易忘记。不要为你的错误感到羞耻。试图抹去它们对任何人都没有帮助,当你再次面对同样的问题时,它会节省你很多时间。
这最后一个常见的错误已经在本书和无数电影中谈到过:永远不要试图改变历史。回到以前的版本并改变一些东西是非常诱人的。这是一个非常糟糕的想法,也是你能做的最危险的事情之一。如果你这样做,你的同事会讨厌你,你可能会把整个存储库搞得一团糟。改变某事的正确方法是重新承诺。过去的就过去了。放手吧。
注意
在本书的后面,你会学到如何回到过去,改变历史。我相信你永远不会这么做。
Git 如何工作(再次)
我知道,我知道。我们已经讨论过这个了。但是在我们进入这本书的第二部分之前,我想确定你已经完全适应了。
还记得 Git 的三种状态吗?它们也被称为三棵树(事实上这是文献中的官方称谓)。让我们再复习一遍。图 6-1 将帮助您快速识别树木。
图 6-1
Git 的三种状态之间的关系
如图 6-1 所示,这里没有什么新东西,只是提醒一下。要跟踪项目中的变更,您需要拍摄整个项目的快照。Git 不跟踪变化;它跟踪版本。
您将只与工作目录交互,因为在那里您的文件可以自由编辑。没有什么特别要说的:这只是你的文件的当前状态。
当您准备好拍摄项目的快照时,临时区域是您放置文件的地方。任何尚未放入临时区域(或临时索引)的已更改文件都不会成为快照的一部分。但是,这些更改仍然可以在工作目录中使用。因此,有必要在将文件添加到暂存索引之前和之后检查工作目录的状态,以确保一切正常。
存储库是 Git 架构的数据库。你会发现你所有的提交和历史记录。你可以在“中找到它。git”文件夹(除非要调整配置,否则永远不要碰它)。提交操作会获取临时区域中的所有内容,并对其进行快照。这就是为什么我们说“提交项目”,而不是“提交文件”或“提交更改”过去提交的未更改文件已经在临时区域中。这就是为什么你不需要把所有东西都放在舞台上,只需要编辑好的文件。记住也要存放新的或删除的文件!
最后,签出会将项目的状态带回到之前的状态。工作目录将会改变以反映这些变化,所以确保没有任何未提交的文件。
所以使用 Git 的基本步骤是
-
(在工作目录中)进行更改
-
转移每个已更改的文件(在转移索引中)
-
提交项目(在存储库中)
就这么简单,但是在进入下一章之前,请务必理解这些状态之间的关系。本节之后的每一节都假设您对这些内容很熟悉。
但是提交在存储库中是什么样子的呢?很简单:它们看起来像链表。提交包含许多信息:内容和元数据。内容只是项目文件(更改的文件和对未更改文件的引用)。元数据包含其他非常重要的数据:提交日期、提交者身份和 Git 消息。提交中存在的另一个元数据是父指针或引用。它只是前一次提交的名称;如果为空,则意味着提交是第一个。因此,每个提交都以父子关系链接到下一个提交。
警告
由于提交的名称是通过散列其内容和元数据获得的,因此更改其中之一将导致名称的更改。如果名称更改,下一次提交将不会指向任何父对象,因为它的元数据中有父引用。这就是为什么改变历史是非常危险的。千万不要做。
摘要
这是一个充满概念和术语的章节。它不像其他的那么专业,但是它对你使用 Git 的成功至关重要。您现在应该知道什么时候是提交的正确时间,以及如何编写有用的提交消息。记住:你的目标是让跟踪项目变更变得更容易。提交消息应该足够清楚地回答这个问题:提交带来了什么?不要忘记历史日志也可能被非开发团队成员阅读。
要记住的主要事情是提交是你项目的基础,所以,每一个都必须是稳定和独立的。您的提交消息应该总是解释提交存在的原因,而不是做了什么。
这一章也有很多关于 Git 的注意事项。试着记住这些,因为它会节省你无数的调试时间。
这也是本书第一部分的结论。我们将要学习一个非常有用的工具:GitHub。我们至少可以分享和追踪我们的项目。您可能想知道我之前向您承诺的 Git 特性。不用担心;他们将在这部分之后来。我知道你很想开始,所以我们走吧!
七、远程 Git
恭喜你完成了这本书的第一部分!现在,好戏开始了。第一部分向您介绍了 Git 的基本特性。您应该能够轻松地使用 Git 进行更改和跟踪。编写有意义的提交消息有点困难,但是如果你遵循上一章的建议,每次提交都会变得更好。您还应该能够查看以前的版本并查看历史日志;这些是所有后续章节需要的非常重要的特性。
现在,您已经准备好应对一个全新的挑战:离开您的本地存储库,使用远程存储库。在这一章中,你将学习为什么远程工作很重要,最重要的是,它是如何工作的。还将向您介绍典型的团队工作流程以及如何正确使用远程存储库。由于远程 Git 的概念有一点挑战性,您将看到一个简单的工具,它将在整个过程中对您有很大的帮助(提示:这是本书的名称)。我们上网吧!
为什么要远程工作
从本书开始,我们只在本地库上单独工作。但是 Git 是一个很好的团队合作工具;如果只在本地存储库上使用它,那就太可惜了。在这一节中,我们将看到什么是远程 Git,以及为什么有人想要使用它。
在本书的开头,我说过 Git 是一个分布式版本控制系统。这意味着存储库不是存储在单个服务器上,而是存储在许多本地存储库中。每个客户端都有自己的本地存储库,包含自己的提交和历史记录。这些提交可以自由交换,并且所有文件都可以随时编辑。这就是 Git 设法支持团队工作的方式。
因为团队工作是基于提交交换的,所以必须找到一种方法来确保所有的提交在任何时候都是可用的。在访问他们的提交之前,等待你的同事到达工作地点并启动他们的计算机是非常不方便的。显而易见的解决方案是让一个服务器托管存储库,每个人只需从它那里推和拉提交。但是这不是危险地接近 VCS 中心的工作流程吗?一点也不(嗯,有一点)。正如我们之前所讨论的,分布式 VCS 的创建是为了避免拥有中央存储库所带来的问题。每个客户都有自己的存储库,他们可以在任何需要的时候使用它;几乎所有的 Git 操作都是在本地完成的。远程服务器只是被指定为一个客户端,它有一个存储库,每个人都可以在那里提交他们的提交。这样,每个人都可以随时看到所有的更改。这种工作方式只是用来促进提交交换;它没有内置到 Git 中。对于 Git,所有的存储库都是平等的。开发人员只是认为一些存储库比其他的更平等。
注意
不需要中间服务器就可以共享提交。但这是一个如此糟糕的想法,我们甚至不会在这本书里教它。
即使您独自工作,除了本地存储库之外,拥有远程存储库仍然是一个好主意。这样,您就可以在一个安全的位置备份项目及其所有历史记录。您还可以随时访问您的项目,前提是您可以通过网络访问存储库所在的服务器。
警告
正如我们在上一章中所说的,仅仅因为 Git 可以用作备份系统并不能使它成为备份系统。将它用于这一唯一目的并不是一个好主意。
那么,你对那个远程存储库感兴趣吗?你当然是,太神奇了!让我们看看它是如何工作的。
它是如何工作的
使用远程服务器只是让一台计算机保存您的项目及其历史的副本。你不需要把你所有的提交都推给它,你只需要把你想分享的提交推给它。然后,您的同事提取他们感兴趣的提交,并将它们应用到他们自己的存储库中。基本上就是这样了!您使用远程服务器来复制存储库并推送和提取更改。让我们详细看看这一切是如何工作的。
要建立一个远程存储库,您首先需要一个能够运行 Git 软件的服务器。任何称职的计算机都可以运行 Git,因为它是一个非常小的软件。你也不需要很大的火力来正常运行它。即使像 Raspberry Pi 这样非常小的计算机对 Git 来说也绰绰有余。
现在您已经有了服务器,您必须找到一种与它通信的方法。对服务器的网络访问是必要的,以便多个客户机可以在同一个存储库之间进行推和拉。与服务器的通信应该非常安全。如果能够访问服务器的任何人都可以阅读和编辑存储库,那将是非常令人失望的。为了能够与存储库交互,用户必须在每次 Git 操作中验证自己。可以使用登录/密码 HTTPS 类型的认证,但是因为认证必须在每个操作之前,所以它会很快变得很累。对此的解决方案是使用 SSH 认证。SSH 验证的原理很简单:只有预先确定的客户机才能访问存储库。
基本上就是这样了!设置远程 Git 服务器是一项非常简单的任务。另一方面,维护和保护它…
注意
就像之前一样,Git 没有区分“服务器”和“客户端”它们只是开发者强加的社会结构。
如果您独自工作或者希望保持项目的私密性,那么使用您自己的服务器来托管您的 Git 项目是一个好主意。然而,当你和一个团队一起工作时,这就成了一种痛苦。每个团队成员必须能够通过网络访问 Git 服务器,所以如果您的团队在同一个工作空间,您需要建立一个本地网络。服务器还应该全天候运行,这样 Git 操作就不会有延迟。
如果你的一些同事在异地或不同的工作场所,会发生什么?嗯,你需要把你的服务器连上互联网。因此,你也需要提高你的安全游戏。您的同事越多,您需要管理的身份验证异常就越多。
使用你自己的 Git 服务器的另一个问题是你需要处理权限。正如在第一章中所看到的,并不是所有的开发人员都应该拥有对存储库的写权限。例如,初级成员在提交到存储库之前,需要由高级成员审查他们的提交。给他们直接访问项目是一个坏主意(因为他们贪得无厌地需要改变历史)。
这些都是维护自己的 Git 服务器所带来的问题。如果有一种工具可以帮我们处理这些事情就好了…
简单的方法
你猜怎么着?有一个工具可以帮我们处理所有这些事情!它的名字叫 GitHub!GitHub 是处理远程存储库时的首选工具;您可以将 GitHub 视为使用 Git 的项目的代码托管服务器。它就像你自己的 Git 服务器一样工作,但是没有那么麻烦。
它创建于 2008 年,用于托管 Git 项目,现在是微软的子公司,微软一直在开源社区投入大量资金。图 7-1 显示了他们在 github.com 的主页。
图 7-1
GitHub 主页
现在来说说数字。GitHub 拥有超过 3600 万用户构建的超过 1 亿个存储库。如图 7-2 所示,他们对这些数字感到非常自豪。
图 7-2
GitHub 的用户
GitHub 几乎涵盖了开发人员的所有需求,无论是希望共享软件的开源开发人员,还是希望不用自己的服务器就能私下工作的专业团队。
几乎像一个社交媒体,GitHub 也为开发者提供了一个空间来构建、分享和记录他们的项目。不再需要外部工具或网站。对于开源项目来说,GitHub 也是一个非常重要的工具,因为它旨在促进开发者关系和代码发布。用户可以查看彼此项目并提出更改建议。您甚至可以关注和贡献您最喜爱的存储库!
而且不限于开源项目!公司和开发者也可以创建私有存储库,只有他们可以访问。他们受益于 Git 的常用特性,但还不止这些。这就是 GitHub 如此受欢迎的原因:每个人都有适合自己的东西!
也有很多软件公司提供与 GitHub 非常相似的服务,最受欢迎的是 GitLab 和 BitBucket。
GitLab 的大部分功能与 GitHub 非常相似,有两个版本:社区版和企业版。GitLab Community Edition 是开源的,与 GitHub 非常相似,你可以毫无问题地阅读这本书的全部内容。GitLab 在 DevOps 圈子里也是很受推崇的,所以如果你对那个职业道路感兴趣,一定要去看看。
BitBucket 最初是为了托管 Mercurial 项目而创建的,自 2011 年以来,它增加了对 Git 项目的支持。由 Atlassian 开发,其业务模型与 Git 非常相似,并且提供相同的企业利益。
使用本地服务器有利也有弊;但是缺点的数量是如此之多,以至于我们将在本书中选择简单的方法。然而,您至少应该知道远程存储库是如何工作的,以及为什么需要它。如果你仍然想使用你自己的服务器,在这本书的一个附件里有一个关于如何做的指南。玩得开心,☺
摘要
本章只是远程存储库的一个非常简单的介绍。在本地工作很有趣,但团队合作需要分享你精心制作的承诺。您可以在自己选择的远程服务器上托管您的 Git 存储库,但是最简单的方法是使用 GitHub 这样的专门托管代码的服务。
但是 GitHub 做的远不止这些!在下一章,我们将详细讨论它的主要特性是什么,以及我们如何利用它们。我们将学习错误跟踪、访问控制、特性请求等等。让我们继续前进!
八、Github 优先
在上一章中,我们初步发现了远程存储库及其重要性。你也应该对它们的工作原理有一个基本的了解,最重要的是,使用它们有什么好处。现在,我们来谈谈最著名的代码托管平台:GitHub。
首先,我们将介绍 GitHub 的简短历史,以便更好地了解它。然后,我们再来说说使用 GitHub 的是什么样的人,他们用它来做什么。
GitHub 概述
给 GitHub 下一个定义真的很难,因为它同时做这么多事情。所以,我就用它自己的话说:“GitHub 是一个受你工作方式启发的开发平台。从开源到商业,你可以托管和审查代码,管理项目,与 3600 万开发人员一起构建软件。”
因此,GitHub 不仅是一个代码托管平台,还是一个开发平台。这是什么意思?它意味着你不仅仅使用 GitHub 来存储你的代码;你用它来计划和追踪它的进化。我们将在下一节看到它的所有特性,但是要记住的主要事情是 GitHub 可以帮助你构建和发布你的项目。
如果你使用 GitHub 只需要一个理由,那就是它提供的开发工作流程。项目经理将所有未完成的任务写在白板上,团队成员互相发送电子邮件来跟踪谁在做什么的日子已经一去不复返了。也不需要一长串来回的电子邮件来检查任务的进度。所有这些都由 GitHub 管理。
GitHub 和开源
GitHub 一直是开源项目的亲密盟友;事实上,GitHub 是世界上最大的开源社区的所在地。由于开发人员需要一个方便的地方来构建和共享他们的项目,GitHub 是一个显而易见的选择。这样,任何人都可以咨询和参与关于项目的所有决策和讨论;这就是开源的美妙之处。
有了 GitHub,对一个开源项目来说,你能做的最好的事情比以往任何时候都要简单:贡献。当你发现一个你喜欢的项目时,你可以像在社交媒体上一样关注它,并看到它的进展。如果你想开发一个新的特性或者修复一个 bug,你只需要做一个项目的克隆,然后在上面工作。这个过程被称为“分叉”,它是开源项目的支柱。当您完成了对项目副本的所有更改后,您可以向项目的维护者提交一个 Pull 请求(PR)。这意味着您正在请求您所做的变更被提取并合并到项目中。然后,其他参与者将检查您的更改,并可能要求一些其他更改。所有这些都在 GitHub 上完成,而不是通过电子邮件或即时消息进行交流。在所有各方就变更达成一致后,拉取请求被接受,您的变更现在是项目的一部分了!
当然,开源项目不仅仅是代码;他们需要文档、翻译、社区经理、维护人员等等。您可以通过编写文档和提供翻译,甚至审查其他贡献者所做的更改来为项目做出贡献。项目还需要测试人员和能够对最终产品提供见解的人。它们是有数百万贡献者的项目,所以需要社区管理者。他们对社区的福祉负责,并被期望执行社区的内部行为准则。一些贡献者的任务是欢迎和辅导初学者,这对任何项目来说都是困难但非常必要的。
GitHub 被数以百万计的开源项目所选择,因为从想法到发布的工作流程是如此的简单和容易。派生一个项目来为它做贡献的概念是任何开源项目的主要驱动力。如果你喜欢一个项目,但不喜欢它的发展方向;你可以叉起它,开始你自己风格的项目。然后,您将成为新项目的维护者,如果其他人想要做贡献,他们可以向您提交 Pull 请求。因此,任何人都是幸福的!
正如以前建立的那样,开源项目需要文档和初学者教程。对于小型项目,一个文本文件(习惯上称为 README)就足够了。自述文件应该介绍项目,并传达它解决了哪些问题。它还应该告诉用户如何安装和使用它,以及如何为它做出贡献。您可以查看图 8-1 中的自述文件示例(您也可以查看 https://github.com/git/git
)。
图 8-1
Git 的自述文件
如图 8-1 所示,自述文件可以有基本的文本格式和链接。它们还可以包括图像和代码示例。
大项目需要的不仅仅是自述文件,因为它们需要被恰当地呈现和记录。GitHub 项目有一个名为“wiki”的部分是专门为这些需求定制的。就像所有的维基一样(它是从维基百科模仿来的),GitHub 维基帮助新来者理解这个项目是如何工作的。许多 wikis 也有一个名为“常见问题”的部分,其中回答了最常见的用户查询。通常,wikis 用于文档和教程太长而不适合自述文件的项目。你可以在图 8-2 中看到一个维基页面的例子,你也可以在 https://github.com/Dash-Industry-Forum/dash.js/wiki
找到;注意所有链接都出现在侧边栏。
图 8-2
提供文档的 wiki
因为文档在开源项目中非常重要,所以编写文档并保持更新是许多贡献者的工作。请记住,wikis 也是 Git 存储库,因此对它所做的更改也会像任何存储库一样被跟踪。这样做是为了将开发工作流与文档工作流分开。
最重要的是,自述文件和维基是用一种叫做 Markdown 的标记语言编写的。这是一种非常简单的语言,可以呈现简单的格式和链接。你可以在图 8-3 中看到一个例子。但是你也可以选择用 HTML 写所有的东西,而不是转换成 Markdown。你还会在这本书的附录中找到一份减价备忘单。
图 8-3
降价示例
开源项目的繁荣还需要一件小事:市场营销。是的,README 文件和 wikis 对开发人员来说是很好的资源,但是最终用户可能不会觉得它们太有帮助。这就是为什么许多项目都有一个专门吸引用户使用他们产品的网站。网站也是一个为自己扬名并展示自己的好方法。如果一个项目没有网站或者没有被搜索引擎引用,它被最终用户发现的机会就很小。尽管如此,维护和托管一个网站并不是一件容易的事情;从金钱的角度来看,它甚至可能是昂贵的。许多开源项目没有一个好的营销活动所必需的资源;他们主要依靠搜索引擎的点击率和口碑。这就是 GitHub Pages 存在的原因。GitHub Pages 只是一个直接托管在您的存储库上的网站;你可以用它来展示你的产品,提供教程,或者任何你想要的东西,真的。它摆脱了创建网站和托管网站的麻烦。但是这不会干扰代码吗?一点也不,像维基一样,GitHub 页面存在于存储库的其他部分;所以他们可以有不同的贡献者。你可以查看图 8-4 中一个托管在 https://scd-aix-marseille-universite.github.io/latexamu/
上的 GitHub 页面的例子。如你所见,这只是一个简单的网站,但直接托管在 GitHub 上。它不仅限于简单的演示网站;你也可以建立博客和类似的网站。您将在附录中看到如何构建 GitHub 页面。☺
图 8-4
GitHub 页面示例
如你所见,GitHub 为开源社区提供了很多东西。所有这些都是免费的!但是现在,让我们来看看 GitHub 能为你个人提供什么。
个人使用
是的,开源是伟大的,但什么是不是你果酱?或者当你有一个你想保密的项目时?GitHub 也覆盖了你!
你不必公开你所有的 GitHub 库;还有一个选项是将它们设为私有。这样,只有你和少数合作者(你选择的)可以访问它。您可以在 GitHub 上创建无限数量的公共和私有存储库;唯一的限制是你的创造力和时间。然而,私有存储库的贡献者数量是有限制的:3。如果你想与更多的贡献者合作,你可以注册 GitHub Pro,这是一个付费计划。但是对于几乎所有人来说,免费计划已经足够了。
拥有一个个人 GitHub 账户来展示你的作品也是推销自己的好方法。这样,人们可以检查你贡献的开源或个人项目,甚至检查你的代码。许多开发人员也使用 GitHub 页面来呈现他们的简历或展示他们的作品集。你可以查看图 8-5 中的例子。
图 8-5
一个随机的人的 GitHub 页面
由于 GitHub 上有 3600 万开发者,你可能想和他们中的一些人联系。一种联系方式是关注一个特定的项目。当项目进行的时候,您将会收到更新,并且可以检查变更。请注意,您将自动跟踪您所贡献的存储库。另一种表达你对一个项目的欣赏的方式也是给它打“星”。这类似于喜欢社交媒体上的内容。因此,存储库的星级越多,用户就越满意。GitHub 还提供了一个新闻提要,即来自特定项目的新闻和通知。那些项目被选中是因为你为它们做出了贡献或者“主演”了它们。它们也是通过分析你最常用的语言或工具而量身定制的。您可以查看图 8-6 中的示例。这是对你周围发生的事情有一个清晰的认识的好方法。
图 8-6
GitHub 探索
在我们进入下一部分之前,有一个很酷的东西你可以用 GitHub 查看:你的贡献活动。你在 GitHub 上的每一次提交都会被注册为一个贡献,如果你启用了这个选项,甚至会被注册到你的个人或私有存储库中。这些活动以类似于图 8-7 所示的漂亮插图呈现。它们展示了你一年来的贡献,并向你个人资料的访问者展示了你的成就。
图 8-7
2018 年我的投稿历史
面向企业的 GitHub
GitHub 不只是针对个人项目或开源社区;企业在那里也有一席之地。现在许多企业都投资开源产品,还有比 GitHub 更好的地方可以找到高质量的开发者吗?
GitHub 中有一个企业计划,它包含了付费计划的所有好处,但还有许多附加功能。这些功能包括从主机选择、安全性到在线支持。所有这些功能可能对企业非常有吸引力,但对我们来说,一个简单的免费计划就足够了。
摘要
本章介绍了 GitHub 的用户和一些小功能。你现在应该对你要用它做什么有一些想法了。在下一章,GitHub 的主要特性将会和一些关于如何使用它和队友一起工作的技巧一起呈现。我们将讨论项目管理、代码审查等等。最后,我们将从 GitHub 的第一个存储库开始。你将在下一章回到行动中,所以一定要复习前面的练习以保持敏锐。开始吧!
九、GitHub 快速入门
到目前为止,我们只讨论了什么是 GitHub,谁需要它。现在,我们要看看它到底能做什么,它的主要特点是什么。GitHub 最重要的特性是它的项目管理工具;与正确的开发工作流程相结合,这是让项目向前发展的可靠方法。
对于这一节的书,没有什么比好的老式练习更好的了!我可以告诉你 GitHub 的所有优点,但是如果你自己去探索,你会理解得更好。让我们首先创建一个 GitHub 帐户并开始一个项目。
项目管理
GitHub 最令人钦佩的特性之一是能够在遵循既定路线的同时管理项目。在这一部分你要跟着我。这样做非常重要,因为您将对这些功能有更好的理解。
因为我们要用 Git 和 GitHub 管理我们的项目,所以我们的第一步是创建一个帐户。这非常简单,除了您的姓名和电子邮件之外,您不需要任何其他信息,如图 9-1 。
图 9-1
GitHub 签名页
注册后,你会在你的电子邮件客户端收到一个确认链接,按照提供的链接将结束铭文。然后你将到达 GitHub 主页面,看起来应该如图 9-2 所示。
图 9-2
GitHub 主页
你的 GitHub 主页很空,但是我们正在用很酷的项目填充它。在页面的右边,你会看到一些趋势库或新闻故事;但我们还不会去那里。
正如您在图 9-2 中看到的,您可以通过三个链接来创建新的存储库:一个在左侧,一个在中间,最后一个在导航栏中。单击其中一个,这样我们就可以创建我们的存储库。
存储库的创建形式也非常简单,如图 9-3 所示。您只需要在表单中填写项目的名称和简短描述。这个描述是可选的,但是您应该尽量使它简单,以便访问您的存储库的用户知道发生了什么。
图 9-3
创建新的存储库
如果您愿意,您可以选择将存储库设为私有;除了你,没有人能接触到它。公共知识库并不意味着任何人都可以编辑它;这只是意味着任何人都可以阅读它,并且登录的用户可以对它提出修改。您仍然是项目的维护者和存储库的所有者。
然后,您可以选择用自述文件初始化存储库。现在忽略这一点,因为我们的目标是从头创建一个存储库;我们将添加自述文件。gitignore,以后再许可文件。
完成所有工作后,单击 Submit 按钮创建您的第一个 GitHub 库!就这么简单!然后,您将被重定向到您的项目页面,这是到您的存储库的唯一链接。链接是这样的:https://github.com/your_username/your_repository
;例如,我创建的新存储库可以通过下面的链接访问: https://github.com/mtsitoara/todo-list
。因此,您不能创建两个同名的存储库。您的项目页面应该类似于图 9-4 中所示的页面。
图 9-4
你全新的仓库
正如您在图 9-4 中所看到的,无论您想要创建一个新的存储库还是推送一个现有的存储库,都有一些关于如何开始的说明。因为我们正在从头开始构建我们的存储库,所以我们将选择第一个选项。第二个选项对我们来说也是可行的,因为我们已经有了一个本地存储库,但是从现在开始我们将忽略它。
因此,我们创建了第一个存储库,并准备在其上推进我们的项目。但是让我们看看这个神奇的盒子,看看到底发生了什么。
远程存储库如何工作
还记得第七章关于远程 Git 以及我们如何决定使用 GitHub 作为远程存储库吗?这一节是那一章的逻辑延伸,因为我们将学习如何使用 GitHub 管理远程存储库。
当我们使用 GitHub 网站创建存储库时,我们向 GitHub 服务器发出指令,要求它们初始化一个空的存储库。如果你还记得第二章的话,初始化一个存储库非常简单:转到任意目录并执行 git init。这正是这里发生的事情,只不过不是在你的电脑上,而是在 GitHub 托管的服务器上。
因此,就好像我们在安装了 git 的远程服务器上执行了以下命令
$ mkdir todo-list
$ cd todo-list
$ git init
我们将使用相同的命令来创建本地存储库。所以现在,GitHub 的服务器中有一个远程存储库,我们将使用它来共享我们的项目。
使用远程存储库,因此您不必使用自己的电脑来共享您的项目。就 GitHub 而言,任何人都可以访问远程存储库,但只有所有者可以编辑它们。我们将在后面的部分讨论团队合作。
主要的好处是远程存储库是您可以发布项目的地方,以便每个人都可以使用它。任何人都可以克隆您的存储库,因此他们可以跟踪您的进步以获得最新的更改。
将您的本地存储库发布到远程存储库称为“推送”,将最新提交从远程存储库获取到本地存储库称为“拉取”Push 和 pull 可能是 Git 中最常用的命令。
但是我如何告诉 GitHub 我希望哪个远程存储库与我的本地存储库相链接呢?这就是需要到您的存储库的唯一链接的地方。您将使用该链接来推送您的本地更改或拉取您还没有的提交。
总之,GitHub 创建了一个空的远程存储库,它只能由您修改,但每个人都可以看到。我们现在需要做的是创建一个本地存储库,并将其链接到远程存储库。
链接存储库
既然 GitHub 已经为我们创建了远程存储库,那么是时候创建我们自己的本地存储库并将其链接到远程存储库了。
正如我们在前面章节中所做的,我们将使用 git init 命令创建一个存储库。本地和远程的存储库名称可以不同,但是最好使用唯一的名称,以免混淆。对于这个特定的项目,命令将是
$ mkdir todo-list
$ cd todo-list
$ git init
注意
如果您喜欢使用您在本章前面创建的存储库,而不是一个新的存储库,您可以跳过初始化部分,直接进入链接。
这里没什么新鲜的;并且你应该得到如图 9-5 所示的相同结果。
图 9-5
Git 存储库的初始化
现在我们有了本地存储库,是时候将它链接到远程存储库了!要列出、添加或删除远程,我们将使用 git remote 命令。例如,让我们使用以下命令链接当前的遥控器:
$ git remote
你不应该得到任何结果,因为这是一个全新的存储库,我们没有链接任何远程到它。现在再加一个吧。
注意
如果您在结果中看到远程,您可以使用 git remote rm [remote_name]删除它们。无论如何,如果它是一个新的存储库,您不应该看到任何远程。
您将需要到您的存储库的唯一链接,以便能够将本地存储库链接到它;所以,从上一节中拿起你的。我的是 https://github.com/mtsitoara/todo-list.git
。不要忘记。最后 git!
您还需要为您的远程存储库创建一个名称。这样,您可以在一个项目中拥有多个遥控器。在测试和生产远程互不相同的情况下,这可能是必要的。按照惯例,默认名称是“origin”。虽然您可以选择任何名称,但建议使用 origin 作为队友共享工作的遥控器的名称。
向遥控器添加链接的命令很简单。它是
git remote add [name] [link]
因此,要向新创建的存储库添加链接,您必须执行以下命令:
$ git remote add origin https://github.com/mtsitoara/todo-list.git
就这样!您可以通过执行 git remote 或 git remote -v 来获得更多信息,从而检查是否添加了远程。您应该会得到类似于图 9-6 所示屏幕的结果。
图 9-6
添加新遥控器
就这样!添加新遥控器是一项简单直接的任务。现在我们清楚了这一点,让我们把这个项目推向 GitHub 吧!
推送到远程存储库
我们最终将本地和远程存储库连接起来。是时候把我们的项目推到 GitHub 上了,这样我们就可以分享我们的工作了。
将提交推送到远程存储库非常简单;但是首先,让我们创建一些要推送的提交。在您的工作目录中,创建一个名为 README.md 的文件,并在 Markdown 中放入您的项目描述。例如,下面是我的 README.md 文件:
# TODO list
A simple app to manage your daily tasks
## Features
* List of daily tasks
现在,让我们使用 git add 将新创建的文件添加到临时区域。
$ git add README.md
现在是时候用 git commit 提交我们的项目了。对于提交消息,许多开发人员在第一次提交时选择“初始提交”。这不是一个规则,如果你想的话,你可以改变它。
$ git commit
因为我们已经做了很多次了,现在你应该对准备和提交已经很熟悉了。提交之后,您应该会得到类似于图 9-7 的结果。
图 9-7
创建、暂存新文件
所以,我们有了第一次提交!现在,我们可以将这些更改推送到远程存储库。将更改推送到远程的命令很简单;您只需要远程存储库的名称和要推送的分支。因为我们还没有创建任何分支(我们将在后面的章节中学习分支),所以我们唯一的分支叫做“master”git push 命令是
git push <remote_name> <branch_name>
因此,在我们的例子中,命令将是
$ git push origin master
有一点点运气,一切顺利;但事实并非总是如此。如果你使用一个密码管理器或者使用不同于你提供给 GitHub 的配置(名字和电子邮件),你会遇到一个认证问题。例如,我被拒绝访问我的存储库,因为我使用了密码管理器,它试图用我的旧凭证登录我。您可以查看图 9-8 中的验证错误示例。
图 9-8
认证错误
为了解决这类问题,我们必须用正确的信息重新配置 Git。您可以在图 9-9 中看到,我在全局配置中更改了我的电子邮件,也就是说,在我计算机上的每个存储库中。
图 9-9
Git 的重新配置
现在,我们必须确保删除该存储库中任何指向密码管理器的链接。对于我的情况,我在这台计算机上的其他存储库中使用凭证助手(密码管理器);所以我不会设置一个全局配置,而是设置一个本地配置。
$ git config --local credential.helper ""
这应该解决了我们的问题,我们可以继续努力。在执行 git push 命令之后,将会要求您输入用户名和密码。然后,你会得到一个类似于图 9-10 的结果。
图 9-10
成功的 git 推送
小费
因为我们使用 HTTPS 来推送和拉取更改,所以每次都需要提供我们的用户名和密码。它很快就会变得很累,所以如果你想使用密码管理器或者完全停止使用密码,请查阅本书附录中的课程。
现在,每个人都可以在 GitHub 上看到我们的项目!让我们在它的项目页面上查看一下。如果我们刷新项目页面,我们应该得到如图 9-11 所示的页面。
图 9-11
更新的项目页面
如图 9-11 所示,存储库页面现在显示了许多英特尔:
-
提交的次数
-
最后一个提交名称及其提交者
-
所有项目文件的列表
-
README.md 的预览
我们刚刚做的是代码共享的基础:推动改变。在使用远程存储库时,您将会反复使用这个命令。这是一个非常简单的特性,但是您必须完全理解它的作用。推送仅仅意味着将所有当前提交(在特定分支中)复制到远程存储库中的远程分支。所有历史记录也会被复制。
在进入下一章之前,问自己这些问题:远程存储库存储在哪里?谁对它们有只读访问权限?谁可以编辑它们?还要确保理解远程和本地存储库链接的基础以及为什么它是必要的。
摘要
在这一章中,我们第一次与远程 Git 库进行了交互。正如我们已经建立的,它们只是普通的存储库,存储在远程服务器上,而不是本地机器上。我们看到了如何创建和链接本地和远程存储库,这个特性我们会经常用到。我们学习的主要命令是 git push,它将本地存储库的状态复制到远程存储库。
在下一章,我们将深入项目管理,看看 GitHub 还能提供什么功能。我们还将学习从远程存储库中提取变更,以及解决推送和提取问题。我们走吧!
十、开始项目管理:问题
上一章,我们快速浏览了使用 GitHub 托管和共享我们的代码。但这还不足以描述 GitHub 能为您做什么;有很多特性可以帮助你的项目成熟。在这一章中,我们将开始学习如何用 GitHub 管理项目。因此,我们将从 GitHub 项目管理的基本形式开始:问题。
问题概述
要成功地管理一个项目,任何项目,你都要提前计划;仅仅对新的输入做出反应,通常做任何你想做的事情都是灾难的完美配方。GitHub 项目也不例外;你必须在考虑行动之前记录下你的行动。这就是为什么 GitHub 有一个很棒的功能叫做 Issues。我们将在本节中讨论它们,并学习如何正确管理它们。
在本书的所有章节中,你既是开发者又是项目经理;但是在一个大项目中,你可能不在计划阶段。但是现在,你暂时被提升为项目经理和首席开发人员(除了是唯一的开发人员),恭喜你!项目经理的职责之一是提前计划所有需要完成的任务。计划还不需要非常精确(在现实世界中从来都不是),但是有一个需要完成的所有任务的列表是必要的。这些任务可以是新特性、错误修复,或者只是团队讨论。在 GitHub 中,这些任务被称为问题。
问题用于跟踪团队成员提出的新功能开发、错误修复或新想法。他们是 GitHub 项目管理的基石;从理论上讲,不应该对附加的问题执行任何操作。你采取的每一个行动的目标都应该是解决一个问题。
通过无聊的团队会议来计划下一步的日子已经一去不复返了;现在你确切地知道你的下一步是什么,最重要的是知道其他人在做什么。向你的同事提出新想法比以往任何时候都容易;只需打开一个问题与您的团队讨论,无需使用电子邮件客户端的其他应用。使用 issues 的最大好处是历史可以永久保存——每个特性、每个 bug 和每个讨论。
制造问题
了解问题的最佳方式是直接与它们互动;所以,让我们回到我们的 GitHub 项目页面来处理它们。
当你打开你的 GitHub 项目页面时,你直接到达项目的“代码”部分。这是显示项目文件的部分。现在,你的项目页面应该看起来像我的,如图 10-1 所示。
图 10-1
项目页面在“代码”部分打开
就在项目名称下面,有许多选项卡显示项目的所有部分。你将主要处理“代码”、“问题”、“拉取请求”和“项目”但是现在,让我们把重点放在问题上。继续点击它开始。你应该到达一个如图 10-2 所示的空白部分,因为你的项目还没有问题。
图 10-2
问题部分
那里有许多关于制造新问题的行动呼吁。点击其中一个,你会看到一个类似于我的表单,如图 10-3 所示。
图 10-3
新问题表单
形式非常简单;只有标题是强制性的。如果你需要更多的空间来解释,在标题下面还有一个评论部分。让我们继续用基本的东西填充我们的第一期;暂时不要改变右边的值。
对于我们的第一期,我们开始讨论我们的产品将使用的技术。问题不仅仅是特性和 bug 跟踪所需要的;它们也被用来开始讨论和分享想法。继续填写你的第一期,就像我的一样,如图 10-4;我把我的标题定为“选择应用要使用的技术”,因为这是任何项目的第一步。
图 10-4
我们的第一期
既然我们已经填写了问题的基本信息,请提交它。然后,您将被重定向到新问题的详细视图。它应该类似于图 10-5 中显示的我的问题。
图 10-5
问题的详细信息
首先要注意的是,您的问题已经有了一个编号。每期都有一个唯一的编号,这些编号不会被回收,这意味着即使您删除了一期,它的编号也不会被重复使用。这个数字很重要,您将在本节中看到。
详细信息页面还包括一个评论部分,团队成员可以在这里讨论这个想法。它甚至包括数量有限的表情符号,你可以用它来代替评论。例如,如果你同意某人的观点,向他们竖起大拇指比评论或写“我也是”更好!这会阻碍交流,拖延对话。
在页面的右下方,您可以看到一个订阅按钮。如果您选择订阅某个问题,您将收到有关该问题所做更改的通知。您还会收到关于里程碑事件的新评论和新闻。
既然你是团队的唯一成员,就不多做讨论了。只需添加评论或反应图片,然后关闭问题。关闭问题不会删除它;它只会将其标记为已完成。不建议删除问题,因为需要保留项目的历史记录,而问题是跟踪变更的最佳方式。记住:如果你的知识库是公开的,任何人都可以阅读你的评论;所以,请善待和倒带任何可能出现的不愉快。
评论并关闭问题后,您将返回到问题详细信息页面,它看起来与我的类似,如图 10-6 所示。
图 10-6
结案的问题
您可以继续对已结束的问题发表评论,但不鼓励这样做,因为每个人都认为问题已经解决并继续前进。一个问题也可以被锁定,没有人可以再对它发表评论;这被认为是维持和平的最后努力。我们都有自己的观点,在互联网上讨论这些观点从来都不容易,尤其是在一个开放的论坛上。但是任何时候都要保持专业,因为你说的每一句话都可能被任何人看到。
与问题互动
我们已经成功创建并关闭了一个问题,但是我们并没有过多地参与其中。如果一个问题对项目没有任何影响,那么它有什么好处呢?在这一节中,我们将直接与 GitHub 和我们的代码中的问题进行交互。
在这一部分的第一部分,你将保持你的项目经理的帽子,因为我们将需要计划我们的项目。到目前为止,我们的待办事项应用只是一个挨着一个的多个文本文件。然后我们决定用 HTML5 来更好的呈现它们。为了编码,我们需要一个行动计划;作为一个项目经理,你的工作就是美化这个计划。
因为这是一个简单的 HTML5 应用,我们不需要一个非常大的计划,只需要一些必要的要点。因此,要创建这个应用,我们需要
-
用 HTML5 写出 app 的骨架
-
用 CSS3 添加一些样式使它更漂亮
-
在 README.md 中描述应用
-
记录代码
-
为应用创建网页
这些是我们需要完成的一些基本步骤:发布一个 to do 应用。
既然您已经知道如何创建问题,我将让您为每个要点创建一个问题。完成后,你的问题页面应该看起来像我的,如图 10-7 所示。
图 10-7
所有未完成的任务
正如您所看到的,任务是按照它们被介绍的顺序显示的。除了它们的编号之外,也没有办法区分它们,如果问题太多,就很容易迷路。因此,为了更清楚地了解我们所有的任务,我们将使用标签。
标签
标签正是你所期望的:帮助你快速过滤问题的文本。让我们直接使用它们,这样您可以熟悉这个概念。
如图 10-7 所示,在问题页面中有一个搜索栏,您可以使用它来过滤问题。但是由于我们还没有任何标签,所以无法进行任何过滤;只是基本的搜索。点按搜索栏旁边的标签按钮以显示所有可用的标签。然后,您将看到可以使用的默认标签列表;查看图 10-8 中的示例。
图 10-8
默认标签列表
这些是开发者社区中最常用的标签。但这并不意味着它们是强制性的或不可改变的;你可以根据自己的喜好和需要改变它们。只有当你在做一个开源项目时,改变它们才是不明智的,因为大多数开发者已经习惯了它们。
但是因为这是你的个人项目,而你是项目经理,你可以添加、编辑或删除任何你想要的标签。例如,如果你在私人场合独自工作,那么“招聘”这个标签就没有用了。您还可以使用标签来标记问题的严重性;如果问题很严重,许多项目会使用“紧急”或“中断”这样的标签。如果项目足够大,标签也可以用来区分问题的来源。一个大项目可以使用标签“前端”、“后端”或“数据库”来将问题分成不同的组。
对标签进行更改后(尽管我建议只添加您需要的新标签,保留默认标签),回到您的问题并打开详细信息页面。然后,通过单击“标签”按钮在每个标签上应用一个或多个标签。例如,您可以查看图 10-9 。
图 10-9
向问题添加标签
添加标签后,通知将出现在问题页面的评论部分。您可以查看图 10-10 中的示例。
图 10-10
关于新添加标签的通知
现在,仔细检查你的每一个问题,并在上面贴上一些标签。然后,完成后,返回到问题页面。看起来应该是我的,如图 10-11 所示。
图 10-11
标记的问题
完美!既然我们给问题贴上了标签,我们就可以过滤它们了。例如,要查看每个标有“增强”的问题,只需点击过滤器(如图 10-12 所示),您将得到如图 10-13 所示类似于我的结果。
图 10-13
过滤的问题
图 10-12
按标签过滤
过滤不是很好玩吗?!但你知道什么更有趣吗?将问题分配给其他人!开始吧。
代理人
既然我们的问题已经被正确标记,是时候将它们分配给开发人员了。这是一项相当简单的任务,与标签没有太大区别。
您最多可以将一个问题分配给十名小组成员。但既然现在只有你一个人,你只能指派自己。开始吧!导航到标题为“用 HTML5 编写应用的框架”和“用 CSS3 添加一些样式使其更漂亮”的问题,并将它们分配给自己。将问题分配给团队成员的工作方式与添加标签完全一样。您可以查看图 10-14 中的示例。
图 10-14
分配问题
在您将这两个问题分配给自己后,您将会得到一个与我类似的结果,如您的问题页面上的图 10-15 所示。现在,您可以按标签和受托人过滤您的问题。
图 10-15
完整的问题列表
现在问题已经分配给你了,摘下经理的帽子,戴上开发人员的帽子。是时候把手弄脏了!
将问题与提交联系起来
正如我们在本章开始时所说的,你用 Git 采取的每一个行动都应该以解决问题为目标。大多数时候,在使用 Git 的时候,你会和 commits 一起工作;因此,这些提交中的每一个都应该与一个问题相关联。在本节中,我们将学习如何将我们的提交与问题联系起来。
首先,让我们决定我们将处理哪些问题。正如我们在图 10-15 中看到的,有两个问题分配给我们:“用 HTML5 编写应用的框架”和“用 CSS3 添加一些样式使它更漂亮。”我们将首先开始写框架,因为从这开始很有意义。所以打开这一期的详细页面,记下它的编号。如图 10-16 所示,我的是第 2 期。
图 10-16
问题编号 2 详细信息页面
处理提交
现在我们有了一个要解决的问题及其编号,是时候准备提交了。因为我们决定为这个应用使用简单的 HTML5,所以我们只需要一个文件作为框架。因此,在您的工作目录中创建一个名为 index.html 的文件,并粘贴以下代码:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>TODO list</title>
</head>
<body>
<h1>TODO list</h1>
<h3>Todo</h3>
<ul>
<li>Buy a hat for the bat</li>
<li>Clear the fogs for the frogs</li>
<li>Bring a box to the fox</li>
</ul>
<h3>Done</h3>
<ul>
<li>Put the mittens on the kittens</li>
</ul>
</body>
</html>
现在,我将让您暂存新创建的文件,但不要提交它;我们必须讨论提交消息。
引用问题
我们准备在当前状态下提交项目,但是我们必须调整提交消息,以便提交可以链接到一个问题。将提交与问题相关联的最常见方式是在提交消息中提及问题编号。
到目前为止,我们只使用了非常短的提交消息,因为我们试图将它们保持在一行之内。但是因为我们需要空间来用一种更精细的方式来描述我们的提交,所以从现在开始,我们将按照这种方式来构造我们的提交消息:标题、主体和页脚用一个空行隔开。为了帮助您理解,您可以在图 10-17 中找到它的图示。
图 10-17
提交消息结构
警告
不要忘记提交消息的每个部分之间的空行。它们真的很重要。
正文和页脚是可选的;只有在必要的时候才使用它们,尤其是身体。人很懒;他们可能只会阅读标题并继续前进,所以即使没有正文也要特别清楚。
页脚是我们现在感兴趣的;这是为 GitHub 等问题跟踪者保留的部分。我们使用页脚引用问题的数字。例如,要引用我们正在处理的问题,我们只需将其编号放在页脚中,前面加“#”当 GitHub 看到这一点时,它会立即将提交与引用的问题联系起来。
注意
我们可以将对问题的引用放在提交消息中的任何地方,甚至放在标题中。但这种做法很难看,应该劝阻。
综合所有这些,让我们用一个合适的提交消息进行提交。以我的提交为例,如图 10-18 所示。
图 10-18
提交链接到问题#2 的消息
在我的提交消息中,我跳过了正文部分,因为它是不必要的。我只需要将这个提交链接到问题#2,所以我将这个数字放在页脚中。
现在,推它!如果你忘了怎么做,看一下上一章(提示:git push origin master)。
现在让我们回到本期的详细页面。您将注意到的第一件事是添加了一个新的注释:这是对我们的提交的引用。它应该看起来像图 10-19 中描绘的我的。
图 10-19
对我们上次提交的引用
这是 GitHub 的一个非常有用的特性,您肯定会经常用到:显示所有与特定问题相关的提交。这就是为什么不应该在没有绑定到某个问题的情况下进行提交;这样对项目的管理更好。
如果点击参考上显示的提交标题(见图 10-19 ,您将看到一个熟悉的屏幕。我会让你自己发现图 10-20 中描绘的是哪个屏幕。
图 10-20
提交的详细视图
没错!这是“git show”视图。没有必要迷失在 Git 命令中去查看 commit 做了什么,你可以直接在 GitHub 中看到它!
既然我们已经成功解决了该问题,请返回其详细信息页面并关闭它。让我们解决下一个问题!
使用关键字结束问题
解决一个问题并结束它感觉很好,对吧?嗯,还有更有趣的事情:通过在提交消息中使用关键字来结束一个问题!
首先,我们必须决定要解决哪个问题。我们的下一期是“用 CSS3 添加一些样式使它更漂亮”,它的数字是 3。大家一起解决吧!打开 index.html,将内容更改为:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>TODO list</title>
<style>
h1 {
text-align:center;
}
h3 {
text-transform: uppercase;
}
li {
overflow: hidden;
padding: 20px 0;
border-bottom: 1px solid #eee;
}
</style>
</head>
<body>
<h1>TODO list</h1>
<h3>Todo</h3>
<ul>
<li>Buy a hat for the bat</li>
<li>Clear the fogs for the frogs</li>
<li>Bring a box to the fox</li>
</ul>
<h3>Done</h3>
<ul>
<li>Put the mittens on the kittens</li>
</ul>
</body>
</html>
暂存文件,但不要提交。关闭问题的关键词是
-
关闭
-
关
-
关闭的
-
固定
-
修理
-
固定的;不变的
-
分解
-
分解
-
断然的
使用这些单词中的一个后跟一个问题编号,会将其标记为已解决并关闭它。我们的提交将解决问题#3,因此我们将把它放在提交消息页脚中。你的提交消息应该看起来像我的,如图 10-21 所示。
图 10-21
通过提交消息解决问题
就像提交消息一样,问题引用应该使用命令式语气;所以最好用“resolve”而不是“resolved”现在,是时候推动我们的承诺并亲眼看看了!
导航到您处理的问题(您不会在打开的问题中找到它,请使用过滤器查看关闭的问题)并打开详细信息页面。你应该会看到一个和我一样的新评论,如图 10-22 所示。
图 10-22
按关键字关闭的问题
如果您单击提交名称,您将再次看到提交的“git show”视图。
GitHub 的这个小特性很有用,但是使用时要非常小心。仅当您完全确定问题已经解决时,才关闭该问题。关闭和重新打开问题会让人们感到困惑,并生成大量通知。不要错误地关闭不同的问题!83%的工作场所暴力是由于问题解决失误造成的。我刚刚发明了这个统计数据,但这并不意味着你应该认真对待它!
摘要
哦!那一章有点长,不是吗?我们学到了很多关于问题的知识,但最重要的是,如何将它们与提交联系起来。永远记住,在采取行动之前,要把所有的行动都放到问题中去。别忘了用标签和受托人对它们进行分类。
我们关于基本项目管理的章节到此结束。现在你应该知道如何在 GitHub 中计划你的下一步行动了。但是项目管理不仅仅是预先计划任务;你还应该对过去发生了什么以及达到了哪些里程碑有一个清晰的认识。因此,我们将对项目进行“适当的”项目管理;本节还包括 GitHub 工作流的大部分形式的非常简短的总结。走吧!
十一、深入项目管理:分支
上一章,我们发现了问题并利用它们来计划我们的项目。我们还学习了如何将我们的提交与问题联系起来,这样我们就可以跟踪项目中的每个变更。我们的工作方式很简单:选择一个问题,做出可以解决它的承诺,然后推给 GitHub。该问题随后得到解决并关闭。但这种工作方式在大多数现实项目中并不太适应;搞砸的可能性太大了。
如果您需要多个提交来解决一个问题,该怎么办?如果其他团队成员提交了一个包含对您正在处理的相同文件的更改的提交,该怎么办?如何确保推送的提交真正解决了问题?所有这些都是不建议对项目进行直接变更的部分原因,即使你独自工作。
正如我们在上一章中所说的,通过提交消息中的关键字来结束一个问题是很酷的,但是你应该非常小心。只有你看过你的作品,它可能无法解决问题。或者它可能在项目中引入新的 bug。这就是为什么在接受修改之前,最好让别人来检查你的代码。
这就是我们在本章将要讨论的部分。首先,将向您介绍最常见的 GitHub 工作流(大多数团队如何在 GitHub 上工作),然后我们将了解分支的概念。
但在我们开始这一章之前,有一件小事你应该永远记住:“你会犯错误。很多时候。所以你必须确保使用尽可能多的安全措施。”走吧!
GitHub 工作流程
在本节中,我们将讨论开发人员使用 GitHub 的最常见方式。请记住,每个团队都有自己的做事方式,但是每种工作方式都受到我们将要介绍的基本工作流程的启发。
还记得关于犯错的小事实吗?这种错误无处不在的可能性就是你需要遵循 GitHub 工作流程的原因,所以即使错误发生了,你也要以可控的方式隔离它的影响。我们从上一章开始的工作方式是将一切直接提交给主项目,这是非常危险的。主项目大部分时间是“生产”线,客户看到和使用的版本。所以,这个版本必须非常干净,应该总是可利用的。如果任何错误出现在主版本中,客户将会遇到错误,并且会扰乱每个团队成员。
解决这个问题的一个方法是创建一个主项目的副本,并处理这个克隆。您对此副本所做的每个更改都不会影响主项目,因此您的任何错误都不会影响客户。当您(和其他人)完全确定所做的更改解决了问题时,您可以在主版本中重现这些更改。
主项目的那些副本被称为分支,将变更复制到另一个分支的概念被称为合并。您可以创建任意多的分支,并且可以在它们之间交换提交。当您第一次创建存储库时,Git 会为您创建一个新的分支;它叫“主人”大多数开发人员将他们的主版本或生产版本放在 master 中,只有当他们完全确定可以这样做时,才在那里重新创建更改。
就像树的分支一样,Git 分支可以有许多分支,这意味着您甚至可以从主分支之外的分支创建新的分支,即使很难维护这样的架构。大多数情况下,您会在处理问题时创建一个分支,并在问题解决后删除它。
为了正确看待这一切,我们将学习默认的或常见的 GitHub 工作流。如你所知,一切都应该从一个问题开始。我们已经讨论了最后一章,所以你已经很熟悉了。因此,我们将讨论工作流程的每个后续步骤。
当您打算通过更改代码来解决问题时,您应该首先创建项目的当前工作版本的副本:创建一个新的分支。
然后,像往常一样,您进行更改并提交项目的状态。您可以根据需要进行任意数量的提交;不会影响主枝。你也可以将你的提交推送到 GitHub,这样你的代码就可以被看到了。
然后,您将您的分支链接到主分支,这样其他人就可以比较更改并审查您的代码。这个链接称为“拉”请求:您请求将您的提交应用到主分支。
然后,其他团队成员可以在 GitHub 上审查您的代码并发表评论。然后,您会提交更多的提交来处理这些注释,直到所有问题都解决了。
如果每一方(开发人员、管理人员、测试人员或客户)都同意您的更改是可以的,并且解决了手边的问题,那么拉请求就被接受了。这意味着您在分支上进行的每个提交都将应用到主分支。然后,您可以删除您创建的分支。
就是这样!你可能想知道它与直接推入 master 有何不同。这是非常不同的,因为错误和遗漏是在将更改应用到生产版本之前发现的;这意味着生产缺陷的数量减少到了最低限度。这也使得你的团队中的不同成员能够在应用变更之前对其进行审核,这是大多数科技公司的标准工作方式。将变更捆绑到一个拉请求中也解决了多人同时提交解决不同问题的问题。它可以保持历史日志的整洁。
只有当您觉得您的工作已经完成时,您才可能想要打开“拉”请求。除非你做的工作很小很简单,否则不要等太久才打开一个 PR。通过在开发的早期进行公关,你可以在做出太多改变之前收到反馈。这对初学者来说非常有用,尤其是因为从一开始就走上错误的道路将需要很长时间来纠正,而且你会希望早点被告知正确的道路。打开一个拉请求并不意味着工作已经完成;这仅仅意味着您正在考虑将提交从一个分支应用到另一个分支。
注意
如前所述,您可以从任何分支创建分支,并打开对它的拉请求。不仅仅是留给主人的。
总结所有这些步骤,你可以在图 11-1 中找到一个小插图。
图 11-1
基本 Git 工作流程
如您所见,我们可以从项目中的任何分支创建分支。Git 在存储库初始化时为我们创建了一个名为 master 的分支。然后我们可以创建更多的分支(例如,一个 bugfix 分支或者一个 feature 分支)来引入主分支中的变更。
分支
正如我们之前所说的,分支是代码评审背后的主要特征。你要先做好自己的分支再发表作品,这样就不会被别人的改动所困扰。简单来说,一个分支只是你自己在某个时间对项目的独立拷贝。让我们看看它们是如何工作的,并创建和删除一些。
分支背后的逻辑很简单:获取项目的当前状态,并制作一个副本。在这个副本中,您可以在不影响其他人的情况下进行更改。您可以使用分支来获得不同的发布渠道,或者只是尝试项目中的新事物。
创建存储库时,默认情况下会得到一个分支:master。在做非常小的项目的时候,这个分支就够了;但是大多数项目需要更多的分支才能得到最好的结果。首先,他们需要一个生产分支,客户可以在那里获得软件的最新稳定版本;这是主分支。只有当项目确定稳定时,生产分支才会被更新,因为这是发布分支。然后是开发分支,在那里所有的进展都被记录,所有的提交都被测试。你将主要在开发分支工作,因为这是最有趣的地方。最后,在将提交合并到开发分支之前,您将创建短命的修补分支来保存您的提交。那些打补丁的分支生死与拉请求;您可以在解决问题时创建一个,然后删除它。
简单总结一下,你会(大部分时间)有三种分支:
-
生产分支,您将在这里发布项目的稳定版本
-
开发分支,您将在那里测试您的最新版本
-
修补分支,您将在那里处理您的问题
除非有非常紧急的重大问题需要立即解决,否则你永远不会直接向生产部门或开发部门提交任务。为了更新这些分支,您将使用 pull 请求,以便对变更进行检查和测试。有一些公司,每个开发人员都直接提交给开发分支,但是这是非常违反直觉的,因为如果发现了 bug,他们不知道是哪个提交引入的。此外,它迫使开发人员进行“一次完成”提交,这是一种反模式。全做提交是指尝试同时解决许多问题的提交,例如,修复一个错误并同时引入一个新功能的提交。这种做法通常是由开发人员的懒惰造成的,因为他们不想为另一个问题创建新的分支。这就产生了非常糟糕的拉请求,并且很难跟踪项目的进展。这也给测试人员带来了巨大的挑战,因为他们不知道哪个版本是稳定的。这是一个全方位的坏主意;即使是你的小项目也不要这样做。一直创建和删除分支似乎很累,但这是使用 Git 时最好的工作流程。
关于 Git 分支要记住的一件事是,它们只是提交的简单引用;这就是为什么创建和删除它们如此之快。还记得我们讨论过 Git 如何在链接中存储提交吗?嗯,分支只是对其中一个提交的引用。提交包含关于作者、日期、快照以及最重要的上一次提交的名称的信息。前一个提交的名称称为 parent,除了第一个提交之外,每个提交都至少有一个 parent。因此,每次提交都与前一次提交相关联,这样我们就可以重新创建项目的变更历史。
现在,您只有名为 master 的默认分支,它引用项目的最后一次提交。为了创建一个新的提交,Git 检查哪里是引用,并使用该提交中的信息在新提交和之前引用的提交之间建立链接。因此,每次提交时,引用都移动到新的提交,循环继续。因此,分支只是对提交的引用,而提交是下一个提交的父提交。
但是 Git 怎么知道我们在哪一个分支上呢?它使用了另一个名为 HEAD 的引用来引用当前的提交。如果在分支上,HEAD 引用该分支的最后一次提交。但是如果你正在签出一个先前的版本(就像我们使用“git checkout <commit_name>”)时所做的那样,HEAD 引用提交,你就处于一种被称为“分离 HEAD”的状态</commit_name>
警告
就像人体一样,如果可以避免,永远不要处于“脱头”的状态。发现自己处于非常危险的境地。
在大多数情况下,您可以将 HEAD 视为对当前分支的引用,并且您创建的每个提交都将使用该分支中的最后一个提交作为父提交。
当您将一个分支合并到另一个分支时,会创建一个具有两个父级的新提交:每个分支一个父级。因此,您可以通过提交类型的父类数量来识别提交类型:
-
没有父母:第一次提交
-
一个父级:分支中的正常提交
-
多个父级:由分支合并创建的提交
创建分支
现在你已经知道了很多关于分支的知识,让我们创建一个吧!这很容易;您只需要使用“git branch”命令,后跟分支名称。请记住,分行名称应该只包含字母数字值和破折号或下划线;不允许有空格。
$ git branch <name>
例如,让我们为我们的项目创建一个开发分支。我们把它命名为“开发”以下是如何做到这一点:
$ git branch develop
执行该命令后,您会注意到项目中没有任何变化。这是因为创建一个分支仅仅是创建一个对当前分支的最后一次提交的引用,除此之外别无其他。要开始使用分支,您必须切换到它。
切换到另一个分支
我们创建了我们的开发分支,现在是时候切换到它了。但问题是:我忘记了我给这个分支取的名字。现在,有人可能会建议,我们可以回头看看前面的部分,看看名字。但我有一个更好的主意:列出我们目前所有的分支机构。为此,只需执行不带任何参数的 git branch 命令。
$ git branch
这个命令将给出你当前拥有的分支列表,并在你当前所在的分支(头部)旁边放一个小星星。检查图 11-2 中的分支列表示例。
图 11-2
我们项目中的分支列表
您会注意到,我们仍然在主分支上,因为除了创建一个分支之外,我们没有做任何事情。现在我们切换到它。
您已经知道了在版本之间切换的命令。我们将使用相同的命令在分支之间导航。只需使用“git checkout ”,并将分支的名称作为参数。
$ git checkout <name>
因此,如果我们想切换到 develop 分支,我们必须执行:
$ git checkout develop
注意
就像我们在版本间导航时,如果你有未提交的变更文件,你不能切换分支。行动前提交。或者使用一种叫做“隐藏”的技术,我们将在后面的章节中看到。
在签出新的分支后,您将从 git 得到一条确认消息,您还可以检查 Git 状态的结果以确保这一点。图 11-3 显示了这些命令的结果。
图 11-3
切换分支
练习:创建测试分支
在我们开始下一场战斗之前做一个简单的练习。这很简单,因为所有的答案都在这一部分。这个练习将创建一个名为“testing”的分支,在将所有提交合并到主分支之前,我们将在这个分支中测试我们的项目。你必须这么做
-
回到主分支
-
创建一个名为“testing”的新分支
-
切换到新的分支
小费
要在创建分支后立即切换到新分支,可以在 git checkout 命令中使用选项“-b”。例如,“git 检验-b 测试”与“git 分支测试”和“git 检验测试”是一样的
删除分支
你喜欢创建测试分支吗?很好。是时候删除了,因为我们已经有了一个测试分支:开发。我们将在那里合并我们的修补分支,所有的测试都将在那里进行。
您可以删除推式分支,即远程存储库中存在的分支,方法是在创建拉取请求时选中“PR 合并后删除分支”。这将删除远程分支,但您的本地分支将保持不变。您必须手动删除本地分支。
要删除一个分支,只需使用与创建分支相同的命令,但要使用选项“-d”
$ git branch -d <name>
因此,要删除我们的测试分支,我们将使用
$ git branch -d testing
就像一个真正的树枝一样,你不会切断你现在站着的 Git 树枝。请在删除分支之前签出另一个分支;由于这个原因,一个项目中不能少于一个分支。如果您仍然尝试,您将得到如图 11-4 所示的错误。
图 11-4
删除当前分支
因此,您必须在删除测试分支之前检出主分支或开发分支。如果你做的正确,你应该得到一个如图 11-5 所示的结果。
图 11-5
删除一个分支(我们几乎不知道叶)
记下确认消息,它给出了您刚刚删除的分支的 SHA-1 名称。因为我们创建和删除的分支不包含提交,所以它只引用当前分支的最后一次提交。让我们检查历史日志来确认这一点。执行 git log 命令来获取最新提交的列表,如图 11-6 所示。
图 11-6
提交名称检查
您将看到最后一个提交名称和分支名称是相同的;这是因为我们还没有在我们的分支中进行任何提交。您还会在历史日志中看到分支的起始位置。在本例中,开发分支源自 80f145c 提交;是分支的母公司。
合并分支
我们在这一章中谈了很多关于合并分支的内容,但是我们还没有进行一次合并。让我们改变这一点。
假设您想通过添加一些信息来改进项目的自述文件。这项任务已经在我们的 GitHub 问题中列出,所以没有问题。下一步是从开发分支创建一个新的分支,这样我们可以在以后合并它们。您必须从 develop 分支而不是主分支创建一个新分支,因为我们不会触及主分支,直到一切都被正确测试。如果一切都清晰明了,我们将把开发分支合并到主分支中。
很明显,让我们创建新的分支,我们将在那里工作。我们把它命名为“改进-自述-描述”在从 develop 分支创建新的分支之前,不要忘记签出 develop 分支。因此,我们必须执行
$ git checkout develop
$ git branch improve-readme-description
现在已经创建了分支,切换到它,这样我们就可以开始工作了。要切换到新的分支,只需使用 checkout 命令。
$ git checkout improve-readme-description
完美!现在我们有了一个名为“改进-自述-描述”的分支,它源自于 develop 分支。我们如此喜欢树枝,以至于我们用树枝创造了树枝!
现在我们开始工作吧。打开 README.md 文件,将其内容更改为
# TODO list
A simple app to manage your daily tasks.
It uses HTML5 and CSS3.
## Features
* List of daily tasks
现在,存放文件并准备提交。我将让您选择提交消息,但是不要忘记引用您试图解决的问题!因此,接下来的步骤是
$ git add README.md
$ git commit
这里没有什么新的,因为每个命令在任何分支中都是一样的。唯一的细微变化是提交描述中的分支名称不同。你可以在图 11-7 显示的我的结果上看到。
图 11-7
提交到另一个分支
提交之后,检查 Git 历史记录,以便正确看待我们所做的一切。执行 git log 命令来查看我们的项目历史。
$ git log
小费
使用 git log 时使用选项“- oneline”可以获得更好的结果。
在你提交后,你的项目历史日志应该和我的一样,如图 11-8 所示。
图 11-8
提交分支后的历史日志
如图所示,HEAD 现在指向我们新分支的最后一次提交;这意味着我们将创建的每个提交都将有它作为父提交。您还会注意到主分支和开发分支没有改变;那是因为我们只在新成立的分公司工作。
既然我们对修复感到满意,让我们将分支合并到开发分支,这样我们就可以测试它了。要将我们的分支合并到 develop 中,我们首先必须检查它。因此,使用 git checkout 命令导航到那里。
$ git checkout develop
现在让我们试着将分支合并到开发分支。合并只是意味着将一个分支上的所有提交复制到另一个分支上。为此,我们将使用 git merge 命令,后跟要合并的分支的名称。
$ git merge <name>
因为我们希望将“改进-自述-描述”合并到“开发”中,所以我们要在开发分支上执行的命令是
$ git merge improve-readme-description
该命令将把您的提交从“改进-自述文件-描述”重新创建为“开发”因此,您将得到与提交确认类似的结果。查看图 11-9 中的示例。
图 11-9
合并结果
让我们重新检查一下 git 日志,以便更清楚地了解发生了什么。在执行如图 11-10 所示的“git log - oneline”后,您将得到与我类似的结果。
如您所见,HEAD 现在指向 develop,因为它是签出的分支。您还可以注意到 develop 和 improve-readme-description 现在指向相同提交;那是因为合并。
图 11-10
合并后的历史日志
恭喜你第一次合并!下一次就没那么容易了(提示:合并冲突,当同一行代码在不同的提交中被修改时就会出现)
将分支推到远程
分支不仅仅是为了在本地工作而创建的,您还可以通过将它们推送到远程存储库来将它们发布给全世界。例如,让我们将我们的开发分支推送到 GitHub,这样每个人都可以看到我们的进展。
将分支推到远程的命令是(你猜对了!)git 推送,就像我们上一章学的一样。命令是
$ git push <remote_name> <branch_name>
远程名称没有改变;还是“起源”这次不同的是分店名称。代替 master,我们将推动 develop 分支。因此,命令将是
$ git push origin develop
因为您之前已经推至远程,所以图 11-11 中所示的结果对您来说很熟悉。
图 11-11
推送到远程分支机构
正如您所看到的,结果有一点不同:它给了我们一个创建 pull 请求的链接,即请求允许在 develop to master 上复制提交。请注意这个链接,因为我们将在下一章学习拉请求。☺
如果您返回 GitHub 查看您的项目页面,您还会看到关于创建 pull 请求的行动号召按钮。现在忽略它们,转而在主分支和开发分支之间导航。您可以查看图 11-12 中新分支被推送后的项目页面示例。
图 11-12
我们的新项目页面
现在都是关于分支的。您现在知道如何创建、合并和删除它们。最重要的是,您对 GitHub 工作流有一个基本的了解:创建一个分支,处理这个分支,并创建一个 pull 请求。
现在,你可能会问自己:“但是你不是答应我们代码审查和拉请求吗?我们使用工作流程了吗?”你完全正确。我们没有使用工作流,因为我们使用了直接的方法:直接处理分支。在现实世界的项目中,你不会像我们之前所做的那样直接提交并推送到主项目或开发分支。相反,您将使用 Pull Request 将分支合并在一起。这样,在将您的工作合并到开发或主分支之前,您的同事可以审阅您的工作。
摘要
本章讨论了是什么让 Git 成为项目管理的强大工具:分支。在快节奏的开发中,分支是必要的,因为您可能会同时处理许多问题。将所有这些变化保持在同一个地方是一种灾难。比如,你需要在一个干净的环境中开始修复一个 bug 或者引入一个特性;试图同时做这两件事会严重增加引入更多 bug 的风险。
本章的主要内容是使用 Git 开发时使用工作流的重要性。这些工作流都使用分支来分离干净的问题解决所必需的不同类型的工作。
我们已经看到了如何创建、检出和删除一个分支。现在,让我们学习更多关于拉请求和代码审查的知识,这样我们就可以在我们的主分支中提出变更!
十二、更好的项目管理:拉取请求
在最后一章,我们了解了典型的 GitHub 工作流程;大多数公司在日常工作中使用这种工作流程的变体。我们也学到了很多关于树枝和如何使用它们的知识。但是有一点我们没有机会复习:如何将那两个概念结合起来。答案很简单:拉请求和代码审查。
前一章提供了许多使用传统的代码管理方法(每个人都提交到同一个分支)是个坏主意的原因。但是由于我们在当前的项目中单独工作,我们还没有看到不便之处。但是他们在这里,他们需要很多时间来解决;所以,相信我,还是跟着工作流程走比较好。
本章将向您展示如何实现前一章中介绍的工作流。我们将使用新创建的分支向旧分支引入变更。我们还将学习代码审查以及如何管理它们。
为什么使用拉取请求?
许多不遵循特定工作流程的开发人员说这是浪费时间,因为这会占用宝贵的开发时间。这句话是有道理的,因为遵循工作流意味着等待其他人来审查你的代码。但是你必须记住,你不必在等待审查的时候无所事事,你可以直接去解决另一个问题!这就是为什么分支在版本控制系统中如此强大;你可以同时处理多个问题。有了这个工作流程,你可以开始处理一个问题,向你的同事寻求一些想法或指导,然后在等待回复的时候处理另一个问题。收到必要的反馈后,您可以继续处理第一个问题,并重复此过程,直到所有问题都得到解决。使用工作流还可以让您开始处理某个问题,即使关于该做什么的信息尚未完成;你可以处理一个问题,中途停下来获取更多信息。最后一点:让别人重读你的代码是减少错误的最好方法;不到处追 bug 所获得的时间,比直接致力于 master 所获得的时间更大。
GitHub 工作流也是开源贡献者的首选工作方式。如果任何人都可以不经过任何审查就直接将提交推送到分支,那将是非常糟糕的。取而代之的是,每个贡献者都有一个项目的工作克隆,并且可以提出其他贡献者将审阅和讨论的变更。
因此,总之,使用 GitHub 工作流是最好的工作方式,使用它将大大减少您的错误。正如我们在上一章中看到的,使用分支只是第一步,所以您必须使用拉请求来完成工作流。让我们进一步了解他们吧!
拉取请求概述
拉请求虽然有用,但却是一个相当容易理解的概念。提交一个拉请求,或者 PR,只是请求将一个分支中的所有提交应用到另一个分支的许可。但是我们进展太快了。在了解拉请求之前,我们必须了解什么是“拉”。
拉
在 Git 术语中,“拉”正好是“推”的反义词(如果你猜对了,给自己一个击掌吧!).Push 获取您的分支,并将其所有提交复制到远程分支,如果服务器上还不存在该分支,则创建该分支。拉就是这样,但是向后:它查看一个远程分支,并将提交复制到您的本地存储库。这只是一个提交的交换:如果是从本地到远程,就推;如果是从远程到本地,就拉。
语法也非常相似:
$ git pull <remote_name> <branch_name>
因此,例如,如果您想从 GitHub 上的主分支获得提交,您必须在检出主分支时执行该命令:
$ git pull origin master
在运行任何命令之前,请确保始终位于与您正在拉动的分支相对应的分支上。因此,在这种情况下,您必须在运行 git pull 之前检查 master。执行该命令后,你会得到一个如图 12-1 所示的结果。
图 12-1
从原点拉主控形状
因为您在本地存储库和 GitHub 上有相同的提交,所以什么也没发生。但是一旦你开始和其他人一起工作,你将不得不在你的本地机器上拉动他们的分支来审查他们的变更或者仅仅是在 GitHub 上审查变更。
基本就是它了!拉只是将提交从远程分支复制到本地分支。别担心,你很快会有更多的机会玩 git pull。
公关是做什么的
现在我们对拉取有了更多的了解,我们对什么是拉取请求有了更清晰的概念。PR 只是请求在远程存储库上执行“拉”操作的权限。但是拉动一个分支还不足以完成动作:你还必须将分支合并在一起。
还记得我们将补丁分支合并到开发分支的时候吗?公关只是在请求允许这样做。您可以对您的本地分支做任何您想做的事情,但是当您与上游分支(远程存储库中的分支)打交道时,您必须使用一点礼貌并首先请求许可。这确保了主分支中提交的每个修复都经过了适当的测试和评审。
所以,把它放在一起,拉请求就是你让 GitHub 执行那些动作的请求:拉你的补丁分支,并把它与另一个分支合并。例如,在我们的项目中,我们目前有三个本地分支(master、develop 和 improve-readme-description)和两个远程分支(master 和 develop)。如果我们作出任何新的承诺,以改善-自述-描述,我们想把它与开发,我们会打开一个公关。在 PR 被接受后,GitHub 将执行以下操作:拉出 improve-readme-description 分支,然后将其与 develop 分支合并。
您可能会问自己:“如果拉请求的最终目的是合并分支,为什么不称之为合并请求呢?”嗯,很多人(包括 GitLab 等其他 Git 托管服务)称之为 Merge Request。意思是一样的。在本书中,我们将交替使用这两个术语。
创建拉取请求
言归正传!创建一个新的公关是非常容易的;你只需要两个分支:一个工作,另一个合并。开始吧!
首先,让我们创建一个要处理的问题。所以去 GitHub,创建一个名为“改善应用风格”的问题是的,我们以前也遇到过类似的问题,但是既然我们已经解决了那个问题,我们将打开一个新的问题。回收问题不是一个好主意,因为这将使你更难跟踪你的进展。
创建问题后,是时候回到终端了,因为每个 PR 都是以分支开始的。我们将从最新的开发分支 develop 中创建一个名为“improve-app-style”的分支。正如我们在上一章中看到的,从另一个分支创建新分支的方法是检出源分支并执行分支创建命令。因此,我们必须一个接一个地执行这些命令:
$ git checkout develop
$ git branch improve-app-style
$ git checkout improve-app-style
执行完这三个命令后,你会发现新的分支被检出,如图 12-2 所示。
图 12-2
创建新的分支机构
在我们新成立的分支机构中,让我们来解决这个问题。打开 index.html 并将其内容替换为
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>TODO list</title>
<style>
h1 {
text-align:center;
}
h3 {
text-transform: uppercase;
}
ul {
margin: 0;
padding: 0;
}
ul li {
cursor: pointer;
position: relative;
padding: 12px 8px 12px 40px;
background: #eee;
font-size: 18px;
transition: 0.2s;
}
ul li:nth-child(odd) {
background: #f9f9f9;
}
ul li:hover {
background: #ddd;
}
</style>
</head>
<body>
<h1>TODO list</h1>
<h3>Todo</h3>
<ul>
<li>Buy a hat for the bat</li>
<li>Clear the fogs for the frogs</li>
<li>Bring a box to the fox</li>
</ul>
<h3>Done</h3>
<ul>
<li>Put the mittens on the kittens</li>
</ul>
</body>
</html>
然后,存放文件并准备提交。把非常简单的东西作为提交消息,不需要引用问题;我们以后再做这个。作为一个提交消息,你可以简单地说:“在项目行上添加基本的颜色变化。”像往常一样,在提交之后,您将得到如图 12-3 所示的确认消息。
图 12-3
提交确认
现在该推到 GitHub 了。正如我们之前看到的,我们将不得不使用 git push 命令,后跟远程名称和分支名称。因此,命令将是
$ git push origin improve-app-style
在你把你的分支推送到 GitHub 之后,你会得到另一个熟悉的确认消息。你可以查看图 12-4 中的例子。
图 12-4
将分支推向 GitHub
正如您在确认消息中看到的,Git 直接向您显示一个链接,以便您可以创建一个 Pull 请求。但是让我们用另一种方式来创建一个 PR:直接在 GitHub 上。
转到您的项目页面,在演示文稿中寻找一些不同的东西。在最近推送到一个新的分支后,你的项目页面应该如图 12-5 所示。
图 12-5
最近推送后的项目页面
如您所见,页面上有一个新的行动号召,就在分支列表的上方。它显示了您刚刚创建的分支机构的名称和一个用于创建 PR 的大按钮。单击按钮继续;您应该会看到拉取请求创建表单,如图 12-6 所示。
图 12-6
拉取请求创建表单
您可以注意到,PR 创建表单与 issue 创建表单非常相似。在右边,你可以找到关于受托人和标签的相同信息;它们的工作原理完全一样。在页面的底部,您可以看到 Pull 请求所应用的提交;如果你向下滚动,你会发现不同版本之间的差异。查看图 12-7 中的示例。
图 12-7
版本之间的差异
但是您可能会问自己为什么要应用两个提交。是因为目标分支。如果您仔细查看图 12-6 ,您会发现 PR 的基本分支是 master。这不是我们想要的,因为我们的目标是开发分支。去吧,换个基地分支去发展。更改后,页面会重新加载,你会得到不同的结果,如图 12-8 所示。
图 12-8
开发时拉取请求
当您更改它时,请注意 PR 名称也已更改;这是因为 PR 名称将最后一条提交消息作为默认名称。但是如果您愿意,您可以更改它,尤其是如果您在一个 PR 中有多个提交。记住关于 PR 名称的一件事:它应该像提交消息一样清晰、直截了当。你的 PR 名要回应这个问题:“如果我合并了这个 PR 会怎么样?”所以要好好保管你的 PR 名和描述,这样评审人员不用看你的代码就能知道你在试图解决哪个问题。
您可以在“描述”文本框中扩展您的 PR 说明,并毫不犹豫地提供有关变更的更多信息。你应该把结束问题的关键词放在那里。查看图 12-9 中的示例。
图 12-9
已完成的拉取请求
准备好后,点击“创建拉取请求”即可完成;您将到达一个类似于图 12-10 所示的页面。
图 12-10
您的新拉动请求
同样,这个视图与它的 Issues 对应物非常相似,甚至 PR 号跟在 Issues 号后面。唯一的区别是合并拉请求的按钮。如果您点击此按钮,PR 将被接受,并且分支将被合并。但是先别这么做!在合并之前,让我们先玩玩我们的公关。
现在我们的 PR 提交了,是时候审核了!放下你的开发人员帽子,戴上你的技术领导帽子,是时候做代码审查了!
代码审查
代码审查是 GitHub 最好的特性之一。你不得不与你的技术主管安排一次一对一的会议,以便他们能检查你的代码,这样的日子已经一去不复返了。对于代码中的每个变更请求,不需要互相发送长长的电子邮件链(抄送列表上有一长串讨厌的人)。现在,一切都在 GitHub 中完成。让我们看看!
进行代码评审
在图 12-9 中,你可以看到代码评审过程。您看到了与当前版本相比对文件所做的所有更改,但是您还不能与它们进行交互。在本节中,您将学习如何评审您的共同贡献者的代码。
你可以在图 12-10 中看到,公关页面有许多部分,就像问题页面一样。您必须单击“文件已更改”来开始代码审查。然后你将到达一个类似于图 12-11 所示的页面。
图 12-11
代码审查部分
这个视图应该提醒您 git diff 结果,因为本质上是一样的。它向您详细展示了版本之间的差异,这意味着您将看到添加、删除或替换了什么。
留下评论
现在,让我们假装复习这段代码。在代码审查期间,您可以对整个更改或特定的一段代码进行注释。例如,让我们在第 17 行对“ul Li”CSS 定义进行注释。当您在代码评审变更上移动光标时,一个小的“加号”图标会跟随其后。这意味着你可以在那里发表评论。就这么办吧。将光标放在第 17 行,当“加号”图标出现时,单击它。它将打开一个小的评论区,如图 12-12 所示。
图 12-12
一行上的代码审查
和往常一样,您可以借助 Markdown 语法对这一部分进行各种各样的注释。对于这个例子,我们要加上这个注释:“为了一个更干净的 UX,使列表项不可选择。请使用“用户选择:无”你应该在离开评论前检查预览,就像图 12-13 一样。
图 12-13
评论预览
如果您对自己的评论感到满意,请点击“开始评论”进入下一步。评论会显示在评论页面,评论上也会有回复按钮,就像图 12-14 所示的结果。
图 12-14
发布的评论
使用此按钮,开发人员可以在开始修改 PR 之前与审阅者讨论注释。如果您愿意,您可以添加更多的注释,因为注释是构成代码评审的基本要素。如果您满意,请点击页面顶部的“完成您的评估”按钮。您将再次看到一个小部分,如图 12-15 所示。
图 12-15
完成审查
完成审核后,您将有三个选择:评论、批准或请求更改。因为这是我们自己的拉取请求,我们不能批准或请求对它进行更改,所以我们将只选择默认选项,这是对更改的一般反馈。让我们把“不要忘记考虑不同的浏览器”作为评论并提交评论。您将再次返回到 PR 详细信息页面,如图 12-16 所示。
图 12-16
您完成的代码审查
PR 详细信息页面将向您显示审阅者留下的不同评论,以及整个 PR 的一般评论。让我们来解决这些评论。
更新拉取请求
评论者留下的评论建议我们应该在我们的 PR 被接受之前改变一些代码。所以,让我们这样做吧!让我们通过将新的提交推送到修补分支来更新我们的 PR。
注意
修补分支也称为主题分支,因为每个分支都应该有自己的主题要解决。
再次打开 index.html,将其内容更改为:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>TODO list</title>
<style>
h1 {
text-align:center;
}
h3 {
text-transform: uppercase;
}
ul {
margin: 0;
padding: 0;
}
ul li {
cursor: pointer;
position: relative;
padding: 12px 8px 12px 40px;
background: #eee;
font-size: 18px;
transition: 0.2s;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
ul li:nth-child(odd) {
background: #f9f9f9;
}
ul li:hover {
background: #ddd;
}
</style>
</head>
<body>
<h1>TODO list</h1>
<h3>Todo</h3>
<ul>
<li>Buy a hat for the bat</li>
<li>Clear the fogs for the frogs</li>
<li>Bring a box to the fox</li>
</ul>
<h3>Done</h3>
<ul>
<li>Put the mittens on the kittens</li>
</ul>
</body>
</html>
再次暂存文件,并提交项目,同时显示消息:“使列表项不可选择。”然后,再次将分支推送到 GitHub。如果您在这个练习中迷路了,请查看前面的部分。提示:git push origin improve-app 风格。
推完分支后,再回到 PR 页面。您将在详细信息页面上看到一条新的评论。查看图 12-17 中的示例。
图 12-17
GitHub 检测到的新变化
每次提交后,GitHub 会更新 PR 来反映应用到分支的变更;单击“查看更改”以查看新的更改。您将再次到达代码审查页面,但是有一点小小的变化:您将只注意到新的变化,意味着您还没有看到的变化。这使得审阅者更容易跟踪 PR 的进展。
由于我们没有任何其他意见,请点击“完成审核”,然后给出一般性意见。在工作环境中,您不会审查您自己的代码,因此 Approve 选项是可用的。但是既然我们是单独工作,就给出一个一般性的评价,比如“干得好!”因为开发者真的很努力。与图 12-18 一样,一般意见将出现在 PR 详情页面上。
图 12-18
最后的评论已经完成
现在,我们可以安全地将我们的分支合并到基础分支,因为我们的代码已经过了适当的审查。单击绿色大按钮接受并合并 PR。在分支机构合并之前,您将被要求确认。确认后,分支机构将被合并,采购申请将被关闭。你甚至可以删除源分支,如图 12-19 所示。
图 12-19
接受拉取请求
是否要删除该分支由您决定。有时,团队不会删除分支,直到测试人员确认一切正常。
“但为什么我的问题没有自动关闭?”你问。这是因为开发分支中的修复,而不是默认分支。只有在默认分支(主分支)中合并的修复才会自动关闭问题。但是既然你担心这个问题,让我们在进入下一章之前做一个小练习。
练习:将开发合并到主控形状中
让我们假设一个测试人员测试了我们的新特性,并说可以发布。所以,我们必须把发展融合成大师。这个练习是
-
返回到项目页面
-
打开一个 PR 以合并开发
-
接受 PR 并合并
摘要
祝贺您的第一份 PRs 被接受!(但如果你自己不接受他们,印象会更深刻)。这一章已经很长了,但是你需要完全理解它才能从 GitHub 的强大功能中获益。对于您的下一个问题,打开一个公关,而不是直接提交给主人。请记住,在大多数专业设置中,GitHub 不仅不鼓励在 master 上提交,还默认拒绝提交。每个变更必须来自拉取请求。
现在你应该已经习惯使用公关了;如果没有,重读本章的第一节。要记住的一件事是,拉请求只是请求允许在分支上应用提交的一种奇特方式。
你现在可能会有一些问题:“如果在我完成我的 PR 之前,有人在基础分支中推动了一些更改,该怎么办?”,“如果别人修改了和我一样的文件怎么办?”,或者“如果我在处理一份公关工作时被要求解决另一个问题,该怎么办?”这些问题确实非常中肯;这就是为什么我们将在下一章讨论它们。我们将处理合并冲突以及如何解决它们。但是在学习如何解决它们之前,我们将学习如何完全避免它们!走吧!
十三、冲突
最后一章向我们介绍了合并请求的奇妙世界。你应该知道它们的用途,以及为什么使用它们是个好主意。即使它们是一个很容易掌握的概念,它们也有一些难以忽视的缺点。
你的旅程已经基本结束;你已经走了很长的路。但是在独自继续你的道路之前,你仍然需要学习一些东西。你需要知道在这个过程中你会遇到什么问题。我们将在本章中讨论这些问题。首先,我们将回顾分支机构合并是如何工作的,然后我们将介绍你在职业生涯中最有可能遇到的问题。最后,我们将看到这些问题的通用解决方案。不要害怕冲突,因为它们很容易解决;他们只是很烦人。
合并是如何工作的
让我们倒回去一点,回到基础:合并做什么?合并将分支中的每个提交应用到另一个分支。简单吧?好吧,一个精心策划的合并在大多数情况下进展顺利。但是,即使你计划了每一个细节,也有你无法控制的事情:其他人做什么。
不要忘记 Git 是一个分布式版本控制系统,这意味着每个贡献者都有他们自己的项目副本,并且可以对他们的本地存储库做任何事情。每个人都可以更改每个文件,因为没有像某些 VCS 上那样的“文件锁”。这意味着存在多人对同一个文件进行更改的情况。将所有这些变化结合在一起需要一个合并。
在进入下一节之前,您必须记住一件事:只有当您确定一个分支中的提交是最终提交时,才合并该分支。合并包含未完成工作的分支违背了分支的目的——拥有清晰的历史。即使您不打算实际合并分支,也可以打开一个拉请求;但实际上合并它们并不完整。
如前所述,合并是从分支开始的。大多数时候,它是一个本地存储库中还没有的分支。因此,您必须从 origin(远程存储库的默认名称)中提取它。
拉
让我们第二次修改拉动命令。拉意味着将远程分支复制到本地存储库。例如,我们已经将一个分支合并到 develop 和 master 中,但是没有对我们的本地分支做任何事情。这意味着我们在历史时间线中“落后”,因为在远程存储库中有我们没有的提交。
事实上,“背后”这个词有点用词不当,因为正如我们所建立的,每个存储库都是独立的,Git 中没有中央存储库。我们选择有一个主远程存储库,因为它使团队工作更容易。但是,具体地说,你可以随意交换提交;“落后”这个概念的发明只是为了让开发者的生活更轻松。
让我们试着将 master 拉入我们的本地存储库。请记住,在进行本章的后续步骤之前,您需要完成上一章的练习(将 develop 合并到 master 中)。首先,检查你当地的总分行,确保它是干净的。
$ git checkout master
$ git status
如果你没有在你的工作目录上做任何有趣的事情,你应该得到如图 13-1 所示的结果:一个干净的目录。
图 13-1
在拉入之前,需要一个干净的目录
现在,让我们在进行任何更改之前检查一下历史日志。
$ git log --online
这将导致主分支机构历史记录的输出。它不会有我们最近所做的更改,因为那些更改现在只在远程存储库中。主历史日志应该如图 13-2 所示。
图 13-2
拉入前的历史日志
正如你在图 13-2 中所看到的,头部现在指向分支的最后一次提交(大部分时间都是这样)。根据这个结果,我们的本地主分支和远程主分支处于相同的级别,这意味着它们包含相同的提交。我们知道这不是真的,因为我们已经在远程主机上做了更改。我们的本地 Git 存储库不知道这一点,因为我们还没有从服务器获取任何提交。就这么办吧。
正如我们在上一章所看到的,pull 和 push 命令的工作方式是一样的:您只需要将远程存储库名称和远程分支名称作为参数传递。因此,命令将是
$ git pull origin master
在干净的工作目录上执行这段代码后,您将得到如图 13-3 所示的结果。
图 13-3
从原点拉主控形状
快进合并
当你把师父从原点拉出来后,你会得到一个操作摘要。您将看到已更改的文件数量和已完成的合并类型。这里的类型是“快进”,这是最简单的合并类型。快进意味着远程分支上的提交与本地分支在同一时间线上,所以 Git 只需移动到源分支的最后一次提交。还记得我们讨论过通过父子关系将提交链接到另一个提交吗?如果 Git 发现第一个分支上的提交和要合并的分支之间有链接,就会进行快速合并,这意味着只需要移动指针,这使得 Git 非常快。您应该始终努力使用快进作为合并的方法,因为它更容易,最重要的是,对历史日志来说最干净。
谈到历史日志,让我们检查一下它,看看我们从服务器获取的更改。再次使用“- oneline”选项来获得更好的结果。
$ git log --oneline
这将给出如图 13-4 所示的结果。
图 13-4
从原点提取后的历史日志
你有额外的承诺!来自远程分支的提交被合并到本地分支中。现在,您的本地主分支指向与原始分支相同的提交。
让我们把这些都打开。首先说一下树枝颜色。绿色分支是您的本地分支,而红色分支是远程分支。Remote 也有两个名称,因为它们的名称与远程存储库名称结合在一起。
可以看到,improve-readme-description,developer,origin/developer 在同一个层次上。我们知道这是不正确的,因为我们从 GitHub 改变了 develop。Git 不会知道发生了变化,直到您从原点拉出 develop 分支。
您会注意到在这个历史中有一些您没有提交的请求,即“来自 mtsitoara/improve-app-style 的合并请求#8”和“来自 mtsitoara/develop 的合并请求#9”它们被称为合并提交,由 Git 在合并两个或更多提交时创建。在我们的项目中,我们将 improve-app-style 合并为 develop,然后 develop 成为 master。这些合并中的每一个都会产生一个合并提交。
就像普通的提交一样,您可以使用 git show 命令显示关于它的更多信息。让我们展示第一个合并提交。
$ git show 33753ec
这将产生一个我们熟悉的视图:提交英特尔视图。您应该会得到如图 13-5 所示的相同结果。
图 13-5
合并提交的详细视图
这个视图并不特别有趣,因为它只显示了执行合并的提交父对象和用户。但是,有一点要记住,提交者和合并者可能是不同的人。您应该将解决问题的关键字放在合并提交消息中,而不是提交消息中。大多数时候,提交不足以解决问题;因此,将这些关键字放入“拉”请求消息中,这样只有在分支机构合并后,问题才会关闭。
图 13-4 中显示的历史日志很漂亮,但是它并没有真正显示分支和合并的概念。图形可能更合适,git log 命令中有一个参数。参数是"- graph ",您应该将它与"- oneline "一起使用,以获得最佳结果。
$ git log --oneline --graph
该命令将生成如图 13-6 所示的简单图形。
图 13-6
我们项目的历史图表
如您所见,日志图提供了我们项目的更详细的历史。一如既往,每个星号代表一次提交。但是在这张图上出现了一种新的元素类型:分支。你可以看到,我们从主分支中分支出来,创建了开发分支,开发分支又分成了改进应用风格的分支。我们对那个分支进行了两次提交,然后将它合并回去进行开发。之后,我们把 develop 合并成 master。
当您处理一个使用许多分支并经常合并的项目时(正如您应该做的),最好使用图表视图,因为它比传统视图更清晰。还有,颜色很好看。
为了获得更清晰的历史日志,我建议您删除本地的 improve-readme-description 分支。
$ git branch -D improve-readme-description
删除已经合并的分支几乎没有风险;但是许多开发人员并不经常这样做,以防以后需要返工。大多数时候,这种情况不会发生。一个很好的经验法则是,只有当你确定不需要再次签出来测试某个东西时,才删除分支。
我们在这里做的是最简单的合并形式:快进。但是请记住,在您从一个分支分叉之后(就像我们在 master 和 develop 上所做的那样),您就处于一个完全独立的区域。除非你向其他分支机构询问,否则你不会从他们那里得到任何更新。这也意味着其他分支将独立于您的分支进行评估。当您对分支发出拉请求时,它可能已经改变了。例如,多个贡献者可以开发新的分支,并致力于他们自己的问题。他们不会在同一时间完成,所以每个公关将被一个接一个地接受。这就是麻烦开始的地方:当你处理你的问题时,你的目标分支会在你的影响之外改变。当你完成你的改变时,你正在处理的现实可能会改变。可能多个人在各自的分支机构中更改了相同的文件。这在你的职业生涯中会经常发生,很多时候,一个公关不会像我们在本章中做的那样好。这些问题被称为“冲突”,解决这些问题对您的 Git 之旅至关重要。开始吧!
合并冲突
理解合并冲突的最好方法是创建一个合并冲突。所以,让我们搞砸我们的项目吧!首先,看看我们当地的发展分支。因为我们没有碰过这个树枝,所以它现在应该还是干净的。
$ git checkout develop
我们要做的第一件事是检查历史日志。
$ git log --oneline --graph
你会得到和以前一样的结果,因为我们还没有从原点出发。该结果如图 13-7 所示。
图 13-7
在拉取之前开发历史日志
这里没什么特别的,只是一根没有任何问题的旧木头。因为我们删除了 improve-readme-description 分支,所以开发历史日志中没有留下任何分支。
日志显示 develop 和 origin/develop 处于相同的状态;但这不是真的,因为我们是从 GitHub 改过来的。但是,我们不是从原点出发,而是首先在我们的分支中进行变更,这些变更会导致与原点的变更发生冲突。
打开 index.html 并用以下代码替换其内容:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>TODO list</title>
<style>
h1 {
text-align: left;
}
h3 {
text-transform: capitalize;
}
li {
overflow: hidden;
padding: 22px 0;
border-bottom: 2px solid #eee;
}
</style>
</head>
<body>
<h1>TODO list</h1>
<h3>Todo</h3>
<ul>
<li>Buy a hat for the bat</li>
<li>Clear the fogs for the frogs</li>
<li>Bring a box to the fox</li>
</ul>
<h3>Done</h3>
<ul>
<li>Put the mittens on the kittens</li>
</ul>
</body>
</html>
运行 git diff 来检查您的更改。我们只是做了些小改动,所以没什么大不了的,对吗?
$ git diff
这个结果对我们来说非常熟悉,因为我们在 GitHub 和 git show 上经常看到它。你的结果应该和我的一样,如图 13-8 所示。
图 13-8
开发目录和工作目录的区别
这里没什么新鲜的。让我们将更改后的文件添加到临时区域,然后提交当前项目。
$ git add index.html
小费
为每次提交打开文本编辑器是否令人厌烦?嗯,如果你赶时间的话可以跳过。要在跳过提交消息编辑阶段的同时提交项目,可以将提交消息作为参数传递:
$ git commit -m "<commit_message>"
Don't forget the `-m`!
$ git commit -m "Change CSS to introduce conflicts"
警告
使用 git commit 命令的简写形式可能会节省您几秒钟的时间,但是它更容易出错,因为您在提交之前没有机会检查您的更改。我强烈建议只在只有一个更改过的文件时使用它。另外,您不能用它来编写多行提交消息。
这不会产生任何我们没有见过的结果。正如你在图 13-9 中看到的,我们得到一个标准结果,因为还没有冲突。
图 13-9
将引入冲突的提交
为了产生冲突,我们需要在将分支合并到开发中时得到我们推进的提交。
从原点提取提交
我们已经看到了运行中的 pull 命令,但是,在这个场景中,我们会遇到一个小问题:我们在不同的提交中更改了同一个文件。这将产生冲突,我们必须在您完成拉动之前解决这些冲突。请记住,拉仅仅意味着将远程提交复制到您的本地存储库中。
先从原点直接拉 develop 开始吧。同样,该命令非常类似于 push 命令。您只需要远程存储库和分支名称。
$ git pull origin develop
我们得到的结果与我们之前看到的完全不同。我们得到的不是一个完整动作的结果,而是一个冲突,我们被困在两个状态之间。您可以查看图 13-10 中的示例。
图 13-10
执行 pull 命令时发生合并冲突
让我们一个一个的解开这个结果。首先,我们有用于拉取的 URL,所以这里没什么特别的。
接下来,Git 执行第一个动作。这个动作叫做“fetch”,它的作用是将选择的分支从远程复制到本地存储库中。然后,这个分支被存储到一个名为 FETCH_HEAD 的临时存储器中。就像 HEAD 是对我们正在处理的最后一个提交的引用一样,FETCH_HEAD 引用我们刚刚从 origin 获取的分支的顶端。
下一个动作是基本的合并,就像我们之前看到的一样。我们获取了远程分支,现在是时候将它与当前分支合并了。该操作详述了要执行的合并:分支 develop 和 origin/develop。它甚至指定了将要使用的提交。您的提交名称会有所不同,但是要验证第一次提交,您只需检查提交日志:
$ git log --oneline
您将在倒数第二个提交上找到提交名称,如图 13-11 所示。
图 13-11
倒数第二个提交将用于合并
请注意,合并不会使用最后一次提交,因为它是我们正在处理的提交,是引入了更改的提交。
图 13-10 也引用了合并的另一个提交,你可以在 origin/develop 上找到那个提交。转到 GitHub 上的项目页面,选择 develop 分支,查看远程分支的历史日志。你也可以通过 GitHub 链接直接访问,比如 https://github.com/mtsitoara/todo-list/commits/develop
。您将看到最后一次提交,如图 13-12 所示。
图 13-12
关于起源/开发的提交
如你所见,图 13-10 中引用的第二次提交是远程分支的最后一次提交,它是由我们之前在 GitHub 上的合并创建的。要获得更多信息,您可以单击它并获得提交的详细信息。查看图 13-13 中的示例。
图 13-13
有关合并提交的更多信息
在图 13-13 中可以看到,这个提交有两个父级;这是因为它是由两个分支的合并创建的提交。你还可以看到,图 13-10 中也引用了其中一个父节点,因为这是我们在 GitHub 上合并分支之前最后一次提交的。
让我们回到图 13-10 。在结果的下一部分,Git 试图“自动合并”分支,这意味着它试图自动合并分支。当不同的文件或文件的不同部分被分支改变为合并时,这可以顺利进行。但是由于发现了冲突,合并失败了。这要靠我们来解决。
Git 试图将我们的本地 develop 分支与 FETCH_HEAD 合并,但是由于两个分支都包含对 index.html 相同部分的更改,您必须决定保留哪些更改。我们将在下一节中看到如何做到这一点。
从图 13-10 中应该注意的最后一个信息是我们的本地存储库所处的状态。如果你观察控制台的左边部分,你会发现存储库处于“develop|merging”状态,而不是标准的“develop”分支。这意味着项目中仍然有未解决的冲突,合并(以及,通过扩展,拉)还没有完成。您可以检查状态,以了解关于存储库当前状态的更多信息。
$ git status
这会让你得到一个我们之前没有见过的新结果,如图 13-14 所示。
图 13-14
合并的状态
这个结果非常容易阅读,并为接下来的步骤提供了很好的建议。首先,它告诉我们接下来应该做的事情:解决冲突并提交项目。然后,它告诉我们如果我们决定退出冲突,中止当前合并的方法。在许多情况下,这是一个好主意,因为我们可以在本地分支工作,以解决我们知道会出现的冲突。例如,我们可以中止这个合并,恢复引入冲突的提交,然后再拉一次。然后我们将有一个没有任何冲突的自动合并。但是这对我们来说太简单太合理了,所以,让我们来点硬的吧!
接下来,我们有一个合并所涉及的文件列表。这里只涉及 index.html,并且在两个分支中都进行了修改。让我们打开它来看看冲突。如图 13-15 和图 13-16 所示。
图 13-16
维姆的 index.html
图 13-15
Visual Studio 代码中的 index.html
你会注意到文件中有三大行分隔你的代码。在每个代码冲突中,这些行总是相同的,但是不同的文本编辑器可能会呈现不同的内容。例如,像 Visual Studio 代码这样的 IDE 会用不同的颜色渲染代码,甚至会添加一些按钮来与代码进行交互(如图 13-15 )。相比之下,一个非常简单的文本编辑器会将代码行显示为普通的代码行,可能会打乱您的配色方案。在图 13-16 中,我使用了 Vim,没有任何额外的工具,所以渲染有点平淡无奇;但是很多插件可以用来修复这个。
解决合并冲突
让我们从解释这三行是什么意思开始。“<<<<<<> > > > > >”线划定了有冲突的区域。请记住,一个文件可以有多个冲突区域。
这些区域由“=======”行分隔,显示了两个分支的代码。第一部分是您当前分支上的代码;第二部分是您试图合并的分支上的代码。
所以,我们的文件里有两个相互冲突的代码。第一个是关于开发的代码,第二个是关于起源/开发的代码。要解决合并冲突,我们必须编辑文件,使其只有一个变更集。这并不意味着你必须在两个变更集中做出选择,只是意味着最后只能剩下一个;如果需要,您可以合并它们。
在我们的情况下,最好保留第二部分的大部分内容,因为我们已经审查并接受了这些更改。但也有一些东西我们可以从第一部分保留下来。因此,最好的做法是从第一部分复制我们需要的代码,并将其复制到第二部分。代码将变成
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>TODO list</title>
<style>
h1 {
text-align: left;
}
h3 {
text-transform: capitalize;
}
<<<<<<< HEAD
li {
overflow: hidden;
padding: 22px 0;
border-bottom: 2px solid #eee;
=======
ul {
margin: 0;
padding: 0;
}
ul li {
cursor: pointer;
position: relative;
padding: 12px 8px 12px 40px;
background: #eee;
font-size: 18px;
transition: 0.2s;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
overflow: hidden;
}
ul li:nth-child(odd) {
background: #f9f9f9;
}
ul li:hover {
background: #ddd;
>>>>>>> 33753ecaebae2ba1c3ffdc1e543d372385884c78
}
</style>
</head>
<body>
<h1>TODO list</h1>
<h3>Todo</h3>
<ul>
<li>Buy a hat for the bat</li>
<li>Clear the fogs for the frogs</li>
<li>Bring a box to the fox</li>
</ul>
<h3>Done</h3>
<ul>
<li>Put the mittens on the kittens</li>
</ul>
</body>
</html>
如你所见,我们只复制了第一部分的一行,因为第二部分已经快完成了。现在是清理文件中不必要部分的时候了。首先,我们可以删除代码冲突的第一部分(在<<<<<<< and =======) because we don’t need them anymore. Then we can just remove the remaining line (>> > > > > >之间),因为它不再有意义了。该文件将变成
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>TODO list</title>
<style>
h1 {
text-align: left;
}
h3 {
text-transform: capitalize;
}
ul {
margin: 0;
padding: 0;
}
ul li {
cursor: pointer;
position: relative;
padding: 12px 8px 12px 40px;
background: #eee;
font-size: 18px;
transition: 0.2s;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
overflow: hidden;
}
ul li:nth-child(odd) {
background: #f9f9f9;
}
ul li:hover {
background: #ddd;
}
</style>
</head>
<body>
<h1>TODO list</h1>
<h3>Todo</h3>
<ul>
<li>Buy a hat for the bat</li>
<li>Clear the fogs for the frogs</li>
<li>Bring a box to the fox</li>
</ul>
<h3>Done</h3>
<ul>
<li>Put the mittens on the kittens</li>
</ul>
</body>
</html>
文件恢复正常!合并冲突代码,不再有那三条大线。现在,您可以继续合并过程。如果您忘记了下一步,您可以再次运行 git status(或者检查图 13-14 )。
现在,文件已经准备好了,我们必须转移它。
$ git add index.html
之后,您必须像往常一样提交项目。
$ git commit
您将会看到熟悉的提交消息视图,但是有一点小小的变化:提交消息已经编写好了。查看图 13-17 中的示例。
图 13-17
默认提交消息
当然,您可以随时修改提交消息,但是我建议保留默认消息,除非您遵循个人或公司的指导原则。您可以保存提交消息并继续。
如果你查看命令结果(如图 13-18 所示),你会看到你回到了开发分支,不再处于“合并”状态。
图 13-18
回到正常状态
您还可以通过检查历史日志来检查合并是否已经完成。确保添加图形选项以获得漂亮的结果。
$ git log --oneline --graph
这将产生如图 13-19 所示的惊人视觉效果。
图 13-19
我们项目的近期历史
您可以在图中看到,当我们合并 origin/developer 分支时,我们导入了它的所有历史。所以,看起来我们有一个分支的分支。在大型 Git 项目中,这种情况经常发生。
摘要
这是这本书最大的一章。祝贺你到达那里!我们看到了如何从远程服务器获取代码,以及当相同的代码区域被两个不同的分支修改时如何解决冲突。
关于拉取的主要要点是,它实际上是两个相继执行的命令:
-
获取,将远程分支复制到临时分支
-
合并,将临时分支合并到当前分支
但是当两个分支包含相同代码的编辑时,合并有时会引发冲突。要解决这些冲突,您必须重新打开相关的文件,并决定保留哪个代码。然后,剩下的就是基本的了:准备和提交。
合并冲突是令人讨厌的事情之一,但不幸的是,在你的职业生涯中会发生很多,所以了解它们很重要。既然他们很讨厌,我们将在下一章学习如何减少他们的出现。坚持住!
十四、关于冲突的更多信息
最后一章很紧张,不是吗?我们讨论了什么是合并冲突以及它们何时会发生。我们还了解了如何手动解决这些问题。别担心,这一章会容易消化很多。我们将讨论如何在合并冲突后将分支推到远程。此外,我们还将看到一些可以用来减少可能发生的冲突的策略。走吧!
推动冲突的解决
正如我们在前面章节中看到的,推送意味着将本地提交复制到远程分支。这意味着我们在本地的每一次提交都将被应用到远程存储库。
我们在上一节中看到,拉操作只是一个接一个执行的两个操作:将远程分支复制到临时位置的获取操作和将临时分支合并到本地分支的合并操作。因为拉和推的动作是一样的,但是方向不同,所以把本地分支推到原点的方式是一样的。
因此,推送操作也分为两个部分:将本地分支复制到远程分支,以及分支的合并。push 和 pull 动作之间的唯一区别只是哪个参与者执行动作的问题:您还是服务器。
在正常情况下,推送会顺利进行,因为合并是使用“快进”自动执行的当本地分支上的提交可以与远程分支上的提交直接链接时,快进是可能的。例如,简单地在我们的主分支上一个接一个地添加提交(就像我们到目前为止所做的那样),然后推送它们会产生一个快速前进的合并,不需要创建一个合并提交。
在我们的情况下,这也会发生,因为我们只在我们的开发分支上添加了新的提交。我们不会有任何问题,除非我们或其他人回到过去,改变历史。千万不要尝试这样做。
也就是说,让我们使用通常的命令来推进我们的开发分支。
$ git push origin develop
正如所料,我们将得到如图 14-1 所示的通常结果。
图 14-1
推动我们的发展分支
总之,在提取和合并变更之后将分支推回到原点不应该导致意外的行为。除非有人改变了历史。
合并前检查更改
在尝试任何合并之前,您要做的最重要的事情是检查您的分支将引入的所有更改。这是一个不应该被忽略的关键步骤,因为它将节省您与 Git 斗争的无数时间。
检查分支位置
你首先要确定的是你的位置。若要将两个分支合并在一起,您必须签出目标分支。例如,如果您打算将 develop 合并到 master 中,您将需要首先检查后者。因此,代码应该是(现在不要实际执行第二个命令):
$ git checkout master
$ git merge develop
审查分支差异
审查差异不仅仅是提交的专利!您还可以使用它来检查两个分支之间的差异,这在合并这样的微妙情况下非常方便。这个命令相当简单:
$ git diff branch1..branch2
注意两个分支名称之间的两个点。这将在一个熟悉的 diff 视图中显示两个分支之间的差异。让我们比较一下开发人员和硕士:
$ git diff master..develop
比较提交时,结果与我们的 diff 结果非常相似。查看图 14-2 中的示例。
图 14-2
分支之间的差异
如果你做了很多修改,不想滚动太多,你也可以在 GitHub 上查看这些修改。就推分支,开一个拉请求!
了解合并
我们已经看到了许多关于 Git 合并的概念,但是让我们回顾一下,以便更清楚地了解这个特性。正如我们前面看到的,合并是将两个分支合并的行为,或者更准确地说,是将一个分支注入另一个分支。
分支可以由任何其他分支形成,当一个分支被创建后,它将独立于其父分支。在合并之前,对任何一个分支所做的更改都不会影响到另一个分支。
让我们想象这样一种情况,您创建了一个子分支,并在这个新分支上提交。当合并的时候,会出现几种情况。
如果父分支没有改变(没有提交)并且您试图合并,将会发生“快进”合并。“快进”合并在技术上不是合并,而只是 Git 中的引用更改。请记住,Git 提交的行为类似于链表,这意味着一个提交包含对前一个提交的引用。事实上,如果父分支没有改变,Git 只是将对父分支的引用向前移动(遵循链表),子分支中的最后一次提交成为父分支的最后一次提交。简单地说,Git 只是将子分支中的提交追加到父分支中。这是最简单的“合并”类型,但也是最不常见的,除非你独自工作。
相反,如果父分支已被更改(接收到提交),则不能进行快速合并。将要发生的被称为“真正的合并”或“三方合并”这就是我们上一章看到的合并类型。这种类型的合并将创建一个新的提交,它包含子分支中的所有更改,并将该提交附加到父分支。这个提交被称为“合并提交”,它有两个父分支:父分支和子分支。如果来自父分支和子分支的不同提交修改了同一行代码,就会出现冲突,开发人员必须手动选择保留哪些更改。
因此,合并只是创建包含子分支中所有更改的提交并将其附加到父分支的一种奇特方式。清楚地了解这一点非常重要,这样我们就可以减少合并冲突的频率。
减少冲突
我们在上一章中看到,解决冲突可能会很痛苦,而且根据冲突的大小,可能会花费很多时间。因此,将它们的出现减少到最低限度对我们是有益的。在这一节中,我们将看到限制冲突的策略。
拥有良好的工作流程
如果你使用一个好的工作流程,你在 Git 和 GitHub 中会遇到的大多数问题都是可以避免的。在前面的章节中我们已经看到了最常见的 Git 工作流,但是让我们再回顾一下。
要记住的第一件事是不要直接承诺你的主要分支。简单来说:你打算引入到你的主或者开发分支的每一个改变都应该通过合并来完成。并且每个合并必须由一个拉请求引入。这样,你可以在工作的时候收到反馈。它也给测试人员一个更好的方法来跟踪项目变更。即使你独自工作,你也应该总是使用 PRs 来引入主要分支的变化。这将提供一个比简单的提交消息更清晰的项目历史日志。
每个拉取请求都应该以解决问题为目标。因此,一个公关应该只做一件事,无论是一个 bug 修复,一个特性提案,还是文档变更。不要试图用一个公关来解决几个问题。无所不能的拉请求是合并冲突的完美配方。
开发人员经常忽略的一点是行尾和文件格式。正如我们在第二章看到的,不同的操作系统使用不同的行尾。你的团队有必要讨论每个项目用哪些;大多数团队使用 Unix 风格的行尾,所以 Windows 用户应该相应地配置他们的 Git 客户端。至于格式,这取决于您的团队,但唯一的规则是,您必须对缩进和换行符使用相同的格式。
警告
当讨论制表符和空格时,事情可能会变得激烈。提前准备好你的论点。
中止合并
许多合并冲突不是来自代码冲突;许多将来自格式和空白差异。例如,即使代码没有更改,尾随的回车空格或缩进空格的数量也会引起冲突。
当遇到这种冲突时,最好的办法就是中止合并,恢复格式差异,然后再次尝试合并。正如您在前面看到的,中止合并的命令是
$ git merge --abort
这不会破坏你的任何提交,它只会取消合并,你会停留在你当前的状态。
使用可视化 Git 工具
当使用简单的文本编辑器时,可能很难解决冲突,因为大多数时候,它会打乱代码的配色方案。一个简单的解决方案是使用专门的 Git 工具。它们可以是专门为 Git 开发的 IDE 扩展或工具。让我们在下一章发现它们!
摘要
这一章简单地提醒了我们什么是合并以及如何使用它们。我们看到了各种类型的 Git 合并以及它们可能出现的情况。我们还回顾了合并是如何工作的,目标是什么:将提交从一个分支转移到另一个分支。
要记住的主要事情是减少合并冲突的各种方法。你可能永远也摆脱不了它们,但是遵循这些建议会让它们的出现降到最低。
在我们的 Git 之旅中,我们已经取得了很大的进步,但是我们是使用我们简单而乏味的控制台完成的。是时候在我们的 Git 项目中加入更多的色彩了,所以让我们来学习一下 Git GUIs 吧!
十五、Git 图形用户界面工具
在前面的章节中,我们已经看到了很多最重要的 Git 特性和概念。我们已经学习了提交、分支、拉请求和合并。使用这些概念,您已经可以在 Git 中完成几乎任何事情。不过,只有一个小问题:我们只使用了终端或控制台窗口。在这一章中,你不会学到任何新概念或新特性;你将学习如何运用你已经知道的☺风格
首先,我们将研究 Git 附带的默认工具,然后学习更多关于集成 Git 的 ide,最后看一些专门为 Git 制作的工具。
默认工具
如果您已经按照第二章中的安装步骤进行了操作,那么您的计算机上已经安装了这些工具。如果没有,你可以很容易地在我们常用的软件商店里找到它们。Git 附带了这些默认工具,为用户提供了非常简单的 GUI 来浏览他们的存储库和准备提交。它们几乎适用于任何操作系统,所以不要担心,它们对您都适用。它们在本书中出现是出于历史原因,也因为它们内置于 Git 中。
提交:git-gui
我们将要看到的第一个工具叫做 git-gui,它是 git 的图形化提交界面。您将使用它来提交您的项目并审查提议的更改。你可以在 https://git-scm.com/docs/git-gui
上找到更多关于它的信息。
您可以像打开 Git Bash 一样打开它:通过命令行、上下文菜单或起始页。选择最适合你的选项。在基于 Windows 和 Debian 的操作系统上,您可以通过导航到存储库的目录并右键单击空白区域来打开 Git GUI。这样做会给你一个类似于图 15-1 的结果。
图 15-1
Windows 上下文菜单
如您所见,您可以在那里打开 Git GUI 和 Git Bash。继续并选择 Git GUI。你会看到一个小程序窗口,详细说明你当前的工作目录状态。该窗口如图 15-2 所示。
图 15-2
Git GUI 界面
如果您不想使用上下文菜单或者不能使用,您可以通过在 Git 存储库的位置打开一个终端并执行以下命令来打开它:
$ git gui
Git GUI 界面非常轻量级和直观;每个操作系统都一样,所以每个人都有宾至如归的感觉。它分为四个部分:
-
左上角是尚未暂存的已编辑文件列表。
-
左下角是已转移的文件列表。
-
右上方是一个不同的视图。
-
右下角是提交消息文本区域。
由于我们没有改变项目中的任何内容,所以所有内容都是空的。所以,让我们用额外的提交来打乱我们的项目。
首先,让我们确保我们在主分支中,然后从它创建一个新分支。转到“分支”菜单,选择“结帐…”;这将打开如图 15-3 所示的选择窗口。
图 15-3
选择要签出的分支
您会注意到,当您的光标悬停在某个分支上时,会出现关于其上次提交的信息。它将帮助您找到正确的分支机构,但如果您有一个好的分支机构名称,就没有必要了。签出主分支,然后通过选择“分支”菜单上的“创建…”来创建一个新分支。你将得到如图 15-4 所示的分支创建窗口。
图 15-4
创建新的分支机构
第一个输入区域是最重要的:新分行的名称。将该分支命名为“分离代码和样式”
第二个输入是一个选择输入,您必须选择从哪里创建分支。在我们的情况下,我们将从本地主分支创建一个新分支;所以选择“本地分支”,选择“主”
第三部分是选项,我建议保留默认选项。使用默认选项,Git 将获取远程(跟踪)分支上的最新提交,然后签出新分支。
现在,您可以单击“创建”来查看结果。您将会看到左上角的小消息框现在将“单独的代码和样式”列为当前分支。为了让您看得更清楚,下面是我们刚才所做的命令等价物:
$ git checkout master
$ git branch -b separate-code-and-styles
现在我们进入了正确的分支,我们可以继续我们的提交了。还记得我们讨论 Git 工作流时的黄金法则吗?每次提交都必须以解决某个问题为目标。我会让你创造这个问题。
练习:创建一个问题
转到 GitHub 问题。
创建一个名为“分离代码和样式”的问题
记下发行号。
现在我们准备好提交了!在存储库中创建一个名为“style.css”的新文件,并粘贴以下代码:
h1 {
text-align:center;
}
h3 {
text-transform: uppercase;
}
ul {
margin: 0;
padding: 0;
}
ul li {
cursor: pointer;
position: relative;
padding: 12px 8px 12px 40px;
background: #eee;
font-size: 18px;
transition: 0.2s;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
ul li:nth-child(odd) {
background: #f9f9f9;
}
ul li:hover {
background: #ddd;
}
然后,打开“index.html ”,将其内容更改为
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>TODO list</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>TODO list</h1>
<h3>Todo</h3>
<ul>
<li>Buy a hat for the bat</li>
<li>Clear the fogs for the frogs</li>
<li>Bring a box to the fox</li>
</ul>
<h3>Done</h3>
<ul>
<li>Put the mittens on the kittens</li>
</ul>
</body>
</html>
保存这两个文件,让我们跳到 Git GUI 来查看结果。你会看到…没什么新鲜的!因为 Git GUI 还不知道我们的更改。单击提交消息框附近的“重新扫描”以查看更改;您将得到如图 15-5 所示的结果。
图 15-5
Git GUI 中显示的更改
现在我们有了我们的改变!您可以在 Git GUI 的左上方看到修改过的文件列表,这里是未登台的文件。您会注意到这些文件有不同的图标:
-
新文件的空文件图标(从未提交)
-
已修改文件的文件图标(以前是提交的一部分)
-
一个"?"已删除文件的图标(以前是提交的一部分)
那景色没有让你想起什么吗?嗯,当然是状态视图了!单击“重新扫描”等同于在终端上执行以下命令:
$ git status
在这里,我们修改了“index.html”并创建了“style.css .”如果您单击文件名(不是图标;先不要点击图标),您将会看到 diff 视图的变化。查看图 15-6 中点击 style.css 后的结果示例。
图 15-6
新创建的 style.css 文件的差异
这肯定比执行“git diff”要快!此外,如果你有很多修改过的文件,看起来更容易。因此,单击文件名相当于执行以下命令:
$ git diff index.html
$ git diff style.css
现在是准备提交文件的时候了。暂存和取消暂存文件非常简单:你只需点击它的图标。或者,您也可以选择要暂存的文件(通过单击其名称),然后在“提交”菜单中选择“暂存以提交”。单击文件图标等同于执行以下命令:
$ git add index.html style.css
$ git reset HEAD index.html
$ git reset HEAD style.css
看到了吗?比输入命令要快得多!
我们终于可以提交我们的项目了!但是首先,确保您创建或修改的所有文件都是暂存的,这意味着它们位于左下部分。然后,您可以在 Git GUI 的右下部分编写提交消息,如图 15-7 所示。
图 15-7
提交消息的写入
现在,我们的文件已暂存,提交消息已写入,我们准备好提交了。只需单击提交消息框旁边的“提交”按钮。这样做之后,Git GUI 回到正常的空状态。我们已经通过图形工具提交了!
因此,单击“提交”按钮会产生与此命令相同的结果:
$ git commit -m "Move style code to external file"
既然你是我最好的学生(不要告诉其他人),我就让你在我们支部再干一次。
练习:再次提交
打开 README.md。
在文件末尾添加这一行:“许可证:麻省理工学院。”
创建名为 LICENSE 的新文件。
将 https://choosealicense.com/licenses/mit/
中的许可文本复制到许可文件中。
暂存这两个文件。
提交并显示消息“添加 mit 许可证”
哦!现在您的新分支上有两个提交,是时候将它们推送到远程存储库了。您肯定已经猜到了要单击哪个按钮;是“推”点击它将得到图 15-8 中的结果。
图 15-8
推树枝
这是一个简单的界面;你只需要选择你要推的分支和你要推的位置。
默认选择当前分支,所以我们不需要做任何改变。第二部分是目的地选择下拉列表;同样,我们不需要做任何更改,因为我们只有一个远程存储库。暂时忽略这些选项;我们将在后面的章节中看到它们。
点击推送推送!如果你使用 HTTPS 认证来连接 GitHub,你将被要求输入你的 GitHub 用户名和密码,然后得到如图 15-9 所示的结果。
图 15-9
推送结果
小费
如果不想每次推送都写密码,可以缓存或者使用 SSL 认证;所有这些将在后面的章节中解释。
这里没有什么新东西,我们得到了与这个命令相同的结果:
$ git push origin separate-code-and-styles
练习:创建拉取请求
跟着推送后得到的链接走。
使用以下描述创建一个拉取请求:“修复#10”(用您之前创建的问题编号替换该编号)。
合并公关。
欢欣鼓舞。
这就是使用 Git GUI 提交的方式!简单吧?而且非常快。这是一个很棒的工具,可以在审查提交时节省您很多时间。谈到提交,让我们看看另一个默认工具!
浏览:gitk
在上一节中,我们讨论了很多关于创建和推送提交的内容。现在,我们将这些提交在它们的自然环境中可视化:存储库。gitk 是一个简单的工具,可以简单地查看您的项目历史。你可以把它想象成一个强大的“git log”命令。更多关于 gitk 的文档可以在 https://git-scm.com/docs/gitk
上找到。
既然已经打开了 git-gui,那就用它来打开 gitk 吧。只需从“存储库”菜单中选择“可视化所有分支历史”。这样做会打开 gitk,你会看到如图 15-10 所示的窗口。
图 15-10
gitk 介面
在窗口的顶部,您会发现来自所有分支的所有项目提交的列表。它呈现在一个漂亮的图形视图中,您可以使用以下命令在控制台上再现它:
$ git log --oneline --graph
您可以单击提交以获得有关它们的更多信息。选择提交将更新窗口底部的视图。左下角也是一个不同的视图,但是有一点不同:你也可以选择查看文件的旧版本或新版本。右下部分是提交中更改的所有文件的列表。您可以单击它们来查看 diff 视图上的更改。单击提交相当于执行以下代码:
$ git show <commit_name>
Git 的默认浏览工具 gitk 就是这样!既然您现在可以使用默认的图形工具提交和浏览,那么是时候向您展示其他工具了。
IDE 工具
正如我们在上一节中看到的,与在控制台中输入相比,使用图形工具提交非常快。但是仍然有一个问题:您必须离开您的集成开发环境才能使用它们。如果您可以直接从编辑器中使用图形工具,那不是很好吗?
对很多现代编辑来说这是可能的。我将向您介绍两个集成了 Git 的流行 ide,以便您可以在未来的开发中使用它们。如果你不想使用它们,或者你已经爱上了你当前的编辑器,如果足够现代的话,你的 IDE 也可以集成 Git 工具或插件。每个 IDE 都有自己的界面和用户体验,所以在这一节我就不赘述了。我只是想展示一下有哪些功能可用。
Visual Studio 代码
一个非常流行的编辑器,Visual Studio Code,是微软支持的轻量级 IDE 你可以在 https://code.visualstudio.com/
上找到它。它是新的,所以它集成了所有闪亮的新玩具;Git 是其中的核心。你可以在图 15-11 中看到 VS 代码的观感。
图 15-11
Visual Studio 代码
它与任何其他 IDE 具有相同的接口,但是有一点额外的好处:你可以到处看到 Git 的痕迹。第一,如果你更改了一个被跟踪的文件(此处为 README.md),被编辑的部分会高亮显示;再也不需要执行 git diff 了!
并且在窗口的左下方,你有当前的分支名称;如果单击它,可以选择要导航到的分支或创建新分支。如果您有未分级的文件,在您的分支名称旁边会有一个小小的“⊙”符号,在相关的文件名旁边会有一个“M”图标。如果您已暂存未提交的推送文件,您会看到一个“+”号。
点击源代码控制图标进入 Git 选项卡,如图 15-12 所示。
图 15-12
源代码管理视图
这个视图的外观和工作方式非常像 git-gui,所以我将让您自己去发现它!
原子
Atom 是 GitHub 推出的 IDE,也是开发人员非常喜欢的选择。你可以在 https://atom.io/
上查看。你可以在图 15-13 中看到它的界面。
图 15-13
原子界面
它具有与 Visual Studio 代码相同的 Git 特性,但稍有改动:您可以将您的 GitHub 帐户链接到它,并直接从编辑器中创建 PR!同样,我会让你发现。
专用工具
我们看到了默认的 Git 工具和一些集成了 Git 的 ide。现在,我们来看一些专门为 Git 开发的工具。
GitHub 台式机
如果你喜欢默认的 gitk 和 git-gui 工具,但讨厌它们的界面,GitHub Desktop 是你的完美工具。让我们面对它,默认的工具是伟大的,但他们的外观在现代感觉很奇怪。GitHub 桌面(在 https://desktop.github.com/
上找到)已经被创造出来取代那些工具;它把它们所有的功能都集成在一个软件中。GitHub 桌面界面可以查看图 15-14 。
图 15-14
GitHub 台式机
基特克拉肯
GitKraken 是 Axolosoft 创建的 Git 客户端,越来越受欢迎。你可以在它的网站 www.gitkraken.com/
上得到它。它比所有其他工具都更先进,因为它的目标是提高开发人员的生产力。它甚至有一个集成的代码编辑器!你可以在图 15-15 中看到它的界面。
图 15-15
gitkraken 概览
同样,界面和其他的一样,但是让 GitKraken 与众不同的是它的美丽:美得令人疯狂!
摘要
这一章很有趣,不是吗?我们学到了很多关于如何使用图形工具来提交和浏览它们的知识。我们还发现了大量可供我们使用的新工具,无论是集成到 IDE 中的工具还是专用工具。我们怎么能忘记我们的旧默认工具呢?!
你可能会问自己为什么不从一开始就使用图形工具?这是因为在不了解工具背后的概念的情况下使用工具会适得其反,而且是浪费时间。相信我,学习使用终端是值得的!谈到终端,让我们回到更高级的 Git 命令!
十六、高级 Git
上一章,我们学习了如何在图形环境中实现基本的 Git 特性。现在,让我们来看看更多的 Git 命令,这些命令您不会像其他命令一样经常使用,但是对于更好的工作效率来说是强大而必要的。这些都是非常容易学习的命令,如果您在使用 Git 时犯了错误,这些命令将对您非常有用。
我们将会看到一些你在使用 Git 几次后肯定会遇到的常见问题。然后我们将看到解决它们的最简单的方法。这是一个非常简单的章节,但是我们将学习一些强大的 Git 特性。
恢复
在前面的章节中,我们已经看到了如何恢复提交。但是大多数时候,您想要做的只是将单个文件恢复到以前的状态。这通常发生在当你编码了一段时间后才意识到你的整个策略是错误的时候。与其敲 Cmd-Z 几百次,不如还原文件。
您可能已经知道如何做到这一点,因为 git 会在您检查 Git 状态后告诉您如何做到这一点。首先,让我们打开 README.md,然后在其中添加一些文本。
# TODO list
A simple app to manage your daily tasks.
It uses HTML5 and CSS3.
## Features
* List of daily tasks
* Pretty colors
License: MIT
现在,让我们看看状态。
$ git status
像往常一样,您将看到您的存储库的状态(如图 16-1 所示)。
图 16-1
更改文件后的 Git 状态
这里没有什么新内容,但是请注意修改后的文件上面显示的指令。如您所见,将文件恢复到以前的状态只是意味着将其签出。命令是这样的
$ git checkout -- <file>
此命令将放弃您对特定文件所做的任何更改。使用时要小心,不要擦除有价值的代码。使用 GUI 可能会更好,这样您就可以在丢弃它们之前快速获得当前更改的详细视图。让我们用下面的命令尝试放弃对 README.md 的更改。
$ git checkout –- README.md
您不会从这个命令得到任何响应,但是如果您再次检查 git 状态,您会看到 README.md 回到了它以前的状态。
隐藏
很多时候,您想要在分支之间导航,但是因为您的工作目录是脏的而不能。在这种情况下,脏意味着您有未提交的已更改文件,无论它们处于已修改或暂存状态。改变分支的唯一方法是首先提交它们。但是大多数时候,你还没有准备好做出承诺,因为手头的问题还没有解决。
对此的一个解决方案是进行临时提交,改变分支,处理它,然后返回并修改临时提交。这种方法有很多问题:首先,当您提交时,您的工作目录将是干净的,这意味着您不再知道哪些文件被更改了。第二,这是一个简单的肮脏和丑陋的方法。这并不是创建 amend 命令的原因。
理想的解决方案是使用一种叫做“隐藏”的技术 Stashing 意味着将任何修改过的跟踪文件放在工作目录中,并放在一边以备后用。这意味着您将拥有一个干净的目录,并且可以在您的存储库中导航,而不必提交您的更改。这些变化存储在一个叫做“stash”的小数据库中。您可以将 stash 看作是未完成提交的临时存储库。它被设计成一个后进先出的数据库,这意味着你最后保存的更改将首先呈现给你。理解它的最好方法是尝试。所以,让我们再次更改我们的 README.md 文件。
# TODO list
A simple app to manage your daily tasks.
It uses HTML5 and CSS3.
## Features
* List of daily tasks
* Pretty colors
License: MIT
如果检查状态,您会看到 README.md 已被修改,但未转移。你会得到与先前相同的结果(图 16-1 )。
现在让我们假设当你在处理这个问题的时候,有一个紧急的问题需要你的关注。显然,您现在不能检查主分支,因为您的工作目录是脏的,并且您不能恢复您当前的更改,因为您还没有完全完成。解决方案是把你当前的修改藏在某个地方,这样你就可以有一个干净的目录来工作。为此,您必须使用 stash 命令,这非常简单:
$ git stash push
注意
仅仅使用命令“git stash”和使用“git stash push”是一样的。建议使用完整命令,因为它更直观,更容易理解。
这个命令将获取您修改过的文件,暂存它们,并在存储中创建一个临时提交,保持您的工作目录干净。试试看,你会得到如图 16-2 所示的结果。
图 16-2
粉碎当前的变化
如您所见,您隐藏的更改被赋予了名称和描述,就像常规提交一样。这很正常,因为 stash 只是一个只有一个分支的临时存储库。如果您检查存储库状态,您将得到一个干净的工作目录(如图 16-3 所示);你最终可以导航到其他分支。
图 16-3
存储推送产生一个干净的工作目录
因此,将改变放入存储库可以给你更多的行动自由,而不会丢失你当前的工作。在快节奏的开发中非常有用。
警告
即使这不是一本关于生产力的书,这里有一个小提示:如果你发现自己在问题之间来回跳跃,当然你的问题是你的优先事项,同时解决两个问题会花费你宝贵的时间。
由于 stash 只是一个小型存储库,因此您可以在其上执行大多数 Git 特性,比如检查历史日志或获得详细的变更视图。让我们探索一下藏毒之处,以便更好地了解它。首先,让我们使用 stash list 命令显示历史日志。
$ git stash list
这将为您提供一个熟悉但简化的历史日志视图,如图 16-4 所示。
图 16-4
隐藏的更改列表
正如我们前面所说的,这个数据库是基于后进先出的,所以如果我们对我们的工作目录做了其他的修改并保存了它们,它们将会出现在我们当前保存的目录之上。
你会注意到在图 16-4 中,每个贮藏都有一个数字。这种方式更容易与它们交互,不像提交时必须用名字来称呼它们。让我们通过使用命令 stash show 来查看我们隐藏的变更的详细视图。
$ git stash show
这个简单的命令将向您显示在存储区顶端更改的文件,这意味着最后的更改被推送到存储区。查看图 16-5 中的示例。
图 16-5
藏毒点的详细视图
stash show 命令只会向您显示包含在 stash 中的变更的描述,而不会显示其他内容。要查看更改,您必须应用存储。应用存储非常简单:只需执行下面的命令。
$ git stash pop
该命令将获取存储中的最新更改,并将其应用到当前分支。顾名思义,弹出更改会将它们从存储中取出。所以,如果你的储物袋里只有一套零钱,那么在你掏出小费后,它就会是空的。如果您执行前面的命令,您得到的结果将与您重新创建更改然后检查状态的结果相同(如图 16-6 所示)。
图 16-6
弹出最后一组更改
我们又回到了起点!但是,如果我们愿意,我们可以改变分支,提交,或推到原点,而不会丢失我们宝贵的变化。当您想将当前的更改放在一边,以便在其他地方进行快速更改时,隐藏特别有用。根据经验,如果您需要使用不止一组隐藏的变更,那么您的工作流就有问题。
重复定位
我希望你不要经常使用这个功能,因为它的破坏性很大!有时,你想放弃你所做的一切,重新开始工作,即使你已经提交了你的项目。为了更好地理解它,让我们创建一个提交,然后丢弃它。
在 README.md 上做一些修改,进行 stage,然后提交项目,如图 16-7 所示。
图 16-7
向项目添加错误的提交
为了正确理解这一点,让我们使用 git log 命令在提交之后检查当前的历史日志。
$ git log --oneline
该命令将显示该分支上的最新提交,如图 16-8 所示。
图 16-8
当前分支的历史日志
如您所见,我们的最新提交位于日志的顶部。注意,HEAD 引用指向它;这意味着我们的下一个提交(或分支)将该提交作为父提交。您还会注意到,远程分支来源/分离代码和样式没有改变;那是因为我们的项目还没推。
但是让我们想象一下,你对上一次提交完全不满意,想重做一次。然后,您唯一的选择是将分支重置回之前的状态。为了重置项目,我们使用 git reset 命令,后跟要重置到的项目状态。您必须使用选项"- hard "来实现这一点,因为这是一个非常危险的命令。例如,返回到与远程分支相同的状态将需要以下命令:
$ git reset --hard origin/separate-code-and-styles
此命令将删除所有内容,以便项目可以恢复到以前的状态。见图 16-9 其结果。
图 16-9
重置后项目的状态
由于“- hard”选项会覆盖其路径上的所有内容,因此在目标状态之后所做的提交、当前更改和暂存文件都将被删除。这是 Git 中最危险的命令,在使用之前应该仔细考虑。
复位只能在万不得已的情况下进行。如果可能的话,最好恢复提交,或者直接继续在新的分支上工作。如果使用不慎,重置可能会破坏您的数据。
摘要
这一章讨论了 Git 的一些高级概念,当你面对某些情况时,这些概念会对你有用。您需要使用 reset 来轻松地将文件恢复到以前的状态;当然,您也可以使用 GUI 恢复这些更改。如果你需要快速改变环境,藏起来也会非常有用。最后,硬复位是一个非常强大的功能,非常具有破坏性;不要,除非你没有其他选择。
我们关于高级 Git 命令的课程到此结束。现在让我们回到 GitHub,来发现更多可以帮助我们进行项目管理的特性。
十七、更多关于 GitHub 的信息
在前几章中,我们已经看到了几乎每一个你每天都会用到的 Git 特性。现在,让我们把目光转向 GitHub,它直到现在还只是一个代码托管站点。但是我们已经确定 GitHub 远不止于此。您可以使用它来托管项目文档和托管软件版本。您还将主要使用它作为项目管理工具和与合作者联系的方式。让我们来了解一下这些特性。
维基百科
你的项目可能是同类项目中最好的,但是如果其他人不知道如何使用它或如何工作,你将一事无成。这就是为什么文档很重要,尤其是在软件开发中。GitHub 提供了一个很好的方法来记录你的项目:wikis。
GitHub wikis 的工作方式与世界上最流行的 wiki: Wikipedia 基本相同。它的目标是提供关于你的项目的深入信息:它是做什么的,它是如何工作的,有人如何贡献…
让我们为我们的项目创建一个 wiki 页面,这样我们可以更好地理解它。只需进入你的项目主页,点击“wiki”;您将到达图 17-1 所示的页面。
图 17-1
Wiki 主页
你会在 wiki 主页上看到一个大的行动按钮,点击它创建你的第一个 wiki 页面。您将到达页面创建页面,如图 17-2 所示。
图 17-2
页面的创建
如您所见,这是一个非常简单的视图,分为三个部分:标题、内容和编辑消息。把标题想象成一个网页标题,所以它必须遵循同样的标准:它必须是清晰的、吸引人的。内容应该以 Markdown 格式编写,就像 README.md 一样。你可以选择以其他格式编写维基,但 Markdown 是推荐的选择,因为许多编辑已经在使用它,它更容易阅读。编辑消息就像提交消息一样,是对您提议的更改的简单描述。
更改您的维基中的内容;这里有一个例子:
# What is this
This is a simple app to track your daily goals
# Why another TODO app
Because that is never enough TODO apps in the world
# How does it work
Open `index.html` and update the goals as you wish
# How can I contribute to the project
You can contribute by forking the project and proposing Pull Requests. Check [Issues](https://github.com/mtsitoara/issues) to see the current areas that need help
保存更改,您将被重定向到 wiki 主页,如图 17-3 所示。
图 17-3
显示新创建的维基的维基主页
如您所见,您刚刚创建的 wiki 在您的项目页面上自动可见,并且您创建的每个页面将出现在右侧的侧边栏上。
你可以想做多少维基页面就做多少,但要确保它们是可理解的和有用的;别忘了添加图片和相关链接!
github pages-github 页面
简而言之,GitHub Pages 是一个在 GitHub 上为你托管的网站。你可以用它来展示一个项目,管理你的作品集,或者只是把它作为你简历的在线版本。
GitHub 页面可以是你的个人账户(作品集和简历),也可以是你的项目(展示)。如果你决定使用它作为你的帐户,你只能创建一个页面;但是如果是为了您的项目展示,您可以为其中任何一个项目创建一个页面。你可以查看 https://pages.github.com/
以获得更好的解释。
让我们假设您想要创建一个页面来展示您的待办事项列表项目。首先,你需要回到你的项目页面,点击“设置”;您将进入图 17-4 所示的页面。
图 17-4
设置页面
向下滚动到页面设置,如图 17-5 所示。
图 17-5
GitHub 页面设置
第一个选项是包含页面源位置的下拉列表。您必须在主分支上承载您的页面,但是您有两个位置来存放源文件。一个是直接上 master 另一个在 master 上一个名为“docs”的目录下。我推荐第二种选择,因为它对任何访问者来说都更清晰。然后,我们必须首先创建该目录。
使用 GitHub 或 Git 工具,在 docs 目录下创建一个名为 index.html 的文件。在文件中,只需编写一些基本的 HTML:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Docs</title>
</head>
<body>
<h1>Docs</h1>
<p>Example of documentation</p>
</body>
</html>
这将是你的文件。因此你的主树枝必须看起来像我的,如图 17-6 所示。
图 17-6
文档文件夹和 index.html
然后,我们可以返回到设置页面,并选择文档的来源。选择 docs 文件夹作为源,页面将重新加载并显示如图 17-7 所示的链接。
图 17-7
页面已发布
如果你点击显示给你的链接,你会看到你的 GitHub 项目页面的壮丽景色!可能性是无限的,因为你可以像设计任何其他静态网站页面一样设计你的页面!如果想要更好的风格,勾选https://jekyllrb.com/
;它可以帮助您立即生成 GitHub 页面!
小费
由于你的项目是一个静态的 HTML 页面,你可以指向它作为你的页面的位置;你会得到它的实时版本!
放
您的项目不会无限期地处于开发阶段;它迟早会被释放。还有什么平台比 GitHub 更适合发布你的应用呢?这很容易。
再次返回到您的项目页面,然后单击“发布”;您将看到如图 17-8 所示的主页面。
图 17-8
发布页面
让我们创建我们的第一个版本!点击行动号召按钮,您将看到发布创建视图,如图 17-9 所示。
图 17-9
发布创建表单
这是一个非常容易填写的表格,因为各个部分简单明了。要做的主要事情是通过将发布的二进制文件放到前面的表单上来上传它们。因为我们的应用是 HTML 格式的,所以让我们附上 master 分支的压缩版本。对于可安装的应用,它将是一个要执行的二进制文件;对于我们来说,它将是 zip 和 7z 文件。如果需要的话,不要忘记改变发布的目标。默认选项是主分支,但是您可以指向另一个分支或特定的提交!该表单将与图 17-10 所示的表单相同。
图 17-10
用二进制文件填充的发布表单
单击“发布”查看结果。您将被重定向回发布列表,并在那里看到您的新版本!查看图 17-11 中的示例。
图 17-11
所有版本的列表
如您所见,GitHub 也自动将源代码捆绑到您的发行版中!创建发布时要小心;务必正确测试和重新测试一切!
项目板
项目板是 GitHub 的一个非常有用的特性,因为它提供了一种跟踪和组织项目的方法。例如,您可以为您的任何新想法创建卡片,以便稍后与您的团队讨论。但是项目板的主要用途是跟踪项目的进展。它超越了问题,因为问题仅仅描述了一个特性或者一个需要解决的 bug 但是 Project Board 可以告诉你是否有人正在做,或者只是一个要执行的计划。
理解项目板的最好方法是直接用它们做实验。所以回到你的项目页面,选择“项目”您将得到如图 17-12 所示的空项目。
图 17-12
项目主页
项目主页仍然是空的,因为我们还没有创建任何项目。它还向您展示了您想要使用项目板的不同情况。单击“创建项目”继续;您将获得如图 17-13 所示的视图。
图 17-13
项目的创建
同样,这是一个非常简单的形式。但是请注意模板:它非常重要。作为初学者,你应该使用基本的看板模板,因为它是一个预填充的模板。您可以选择自己创建板,但现在,让我们坚持基础。创建项目,您将会看到如图 17-14 所示的半空板。
图 17-14
新项目已创建
如你所见,创建了三块板:“待办事项”、“进行中”和“完成”就像我们的 app 一样!在屏幕右侧,您可以看到我们未解决问题的列表。将这些问题拖放到各自的板上。在“待办事项”板上,你有一个你可以用你的板子做什么的小例子;它不仅适用于问题,也适用于拉取请求或简单的注释。在你把你的问题放在想要的板上后,你会得到如图 17-15 所示的结果。
图 17-15
我们的第一个项目板
一点小奖励:当你在棋盘上移动问题时,项目名称附近的彩色条会改变。这是一个跟踪你进步的好方法!
但是项目板不仅仅是一个项目进度追踪器!您可以为许多情况创建项目板:发布跟踪、会议记录、开发人员想法记录、用户反馈……您可以在图 17-16 中找到本书的项目板,您也可以在 https://github.com/mariot/boky/projects/1
上找到。
图 17-16
本书的当前项目板
我建议你在未来的项目中使用项目板,因为对自己的进展有一个清晰的认识是通往成功的必由之路。如果你感到无畏,你也可以检查自动看板,它会自动为你移动卡片!例如,每一个新问题将被填入“待办事项”下,而每一个已关闭的问题将被移至“已完成”
摘要
这一章让我们暂时离开 Git,把注意力放在 GitHub 上。我们已经看到 GitHub 不仅仅是一个代码存储库,而是一个管理和发布项目的完整工具。读完这一章后,你应该能够设计一个迷你网站,并拥有一些文档。你也应该有你的应用的第一个版本。
前面展示的最重要的特性是项目板。用它们来清楚地了解你做了什么,你要去哪里。它们看似简单,但在项目管理中非常有用。
您现在已经掌握了 Git 和 GitHub 的基础知识。但是在你的道路上仍然有障碍:你仍然不确定在真实世界的环境中等待着你的是什么。在下一章,我们将探讨你在与他人合作时肯定会面临的问题以及如何解决它们。敬请关注!
十八、常见的 Git 问题
自从我们的第一个 Git 命令以来,我们已经取得了很大的进步!我们已经了解了很多关于基本和高级 Git 特性以及何时使用它们的知识。但是因为我们只是人类,所以在我们的 Git 之旅中我们会面临很多问题。大多数这些问题都是由于疏忽造成的,所以意识到它们的存在就是朝着避免它们前进了一大步。但是如果你仍然碰到他们,这里有最好的解决方案!
贮藏室ˌ仓库
存储库是您 Git 体验的支柱;一切都在那里开始和结束。很难把事情搞砸,但是万一有不好的事情发生,这里有一些建议。
重新开始
这是本章中最激进的“解决方案”,我希望你永远不要使用它。这个解决方案基本上是删除一切,重新开始!只有当您有一个远程存储库,并且出于某种原因想要删除您的本地存储库时,这才应该是一个选项。这样做的原因包括
-
更换工作电脑
-
硬盘中不可读的扇区
-
中不可恢复的错误。git”目录
要重新开始,您只需要用 git clone 命令克隆远程存储库:
$ git clone <repository_location>
存储库位置是到您的远程存储库的 HTTPS 或 SSH 链接;您可以在您的 GitHub 项目页面上找到它。
克隆与初始化存储库的效果相同,但是有一个很大的好处:所有的历史和提交都将被复制到新的本地存储库中。你不再需要精确的原始链接。
改变原点
在正常情况下,您会希望在整个开发过程中保持远程存储库的 URL 不变。但是在某些情况下有必要改变它:
-
在 HTTPS 和 SSH 链接之间切换
-
将存储库转移到另一台主机
-
添加用于发布或测试的专用存储库
首先,让我们获得更多关于我们当前遥控器的信息。为此,使用带有“-v”选项的 git remote 命令。
$ git remote -v
这会给你一个当前遥控器的列表,如图 18-1 所示。
图 18-1
当前遥控器列表
这里,我们只有一个指向 GitHub HTTPS 链接的远程“起点”。要修改此链接,您需要使用 set-url 子命令:
$ git remote set-url <remote_name> <remote_url>
例如,如果我想切换到 SSH 而不是 HTTPS 来访问 GitHub,我会执行
$ git remote set-url origin git@github.com:mtsitoara/todo-list.git
这样做将允许我在不提供用户名和密码的情况下在 GitHub 之间进行推/拉操作。认证将通过两组密钥来完成:一个我保存在本地计算机上的私钥和一个我必须上传到 GitHub 的公钥。如果您对使用 SSH 进行身份验证感兴趣,请根据您的操作系统( https://help.github.com/en/articles/which-remote-url-should-i-use
)前往 GitHub 帮助获取更多信息。如果您决定继续使用 HTTPS,但要缓存什么作为密码,这样您就不必总是键入密码,您可以使用凭据帮助器。同样,GitHub 帮助上有更多关于这个的信息,这取决于你的操作系统( https://help.github.com/en/articles/caching-your-github-password-in-git
)。
警告
如果您更改了您的远程名称,不要忘记为每个推拉动作使用新名称。
工作目录
您将花费大部分时间在工作目录上,这里也没有太多东西可以破坏。
Git diff 为空
这种情况经常出现,但并不危险。有时,您做了很多更改,并希望检查这些更改。但是当运行 git diff 时,结果是空的。别慌!Git diff 只显示修改过的文件,所以如果你的文件是暂存的,你不会在那里看到它。要查看对暂存文件所做的更改,您必须运行:
$ git diff --staged
小费
在审查变更时,使用 GUI 工具会给你很大的帮助。
撤消对文件的更改
当您使用 Git 时,这将会经常出现。有时,您只想将文件恢复到其以前的状态,而不必签出整个提交,然后复制粘贴代码。我们已经在前面看到过这个命令:
$ git checkout <commit_name> -- <file_name>
该命令将按照提交时的样子签出文件,因此将更改您的工作目录。小心不要丢失任何未提交的更改!
承诺
当你试图提交你当前的项目时,大多数问题都会出现。但是不要担心,对于这类问题总有一个简单的解决方案。要考虑的最重要的事情是:你正在使用的命令是破坏性的吗?像 reset 或 check out 这样的命令会改变你的工作目录,所以在执行它们之前,请确保你知道你在做什么。
提交时出错
这是 Git 中的一个基本错误。在您提交您的努力工作之后,您有时会注意到一个小小的语法错误出现在您的提交消息中,或者您忘记上传一个文件。这些问题的解决方案是修改提交,这意味着您将取消立即提交并进行新的提交。命令很简单:
$ git commit --amend
提交名称将会改变,因为您基本上是在改变它的内容。这就是为什么您不应该修改已经推送到远程分支的提交,尤其是如果有人在那个分支上工作。这是在改写历史,你绝对不应该这么做。
也就是说,如果您已经提交了提交,并且单独在分支上,您可以修改提交并尝试再次提交。但是由于 commit 的名字改变了,Git 不会允许你不战而降地改变历史。您必须清除远程分支上的所有历史记录,并用您的历史记录替换,这意味着您将覆盖远程分支上的所有内容。这就是为什么如果你不是单独在一个分支上,就不应该修改提交。要使用修改的提交来推送分支,您需要强制它。
$ git push <remote_name> <branch_name> -f
“-f”选项强制 Git 覆盖远程分支上的所有内容,并用您当前的分支历史替换它。
警告
在别人工作的地方重写历史是非常粗鲁和自私的。别这么做。
只有当您想要修改提交消息或添加/删除文件时,才应该使用修改提交。不要修改提交更改代码。
撤消提交
如果您提交了一个分支,但随后意识到它是错误的,您可以撤销它,但只有当您没有推送到远程分支时。
这个命令很简单但是很危险:它是 reset 命令。但是与清除所有内容的“硬”重置相反,“软”重置是撤销提交但保留更改所必需的。
$ git reset HEAD~ --soft
然后提交将会消失,留给您一些选项来隐藏更改并将它们应用到另一个分支。
同样,这是在重写历史,如果您已经推送到一个远程分支,就不应该使用它。
分支
您将需要与分支机构合作很多,以优化工作流程。当处理一个新的特性或者 bug 时,你的第一反应应该是创建一个分支。但是你对分支越熟悉,你就越有可能忘记一个可能导致问题的小细节。以下是您在使用 Git 时会遇到的最常见的问题。
分离头
HEAD 是对当前签出提交的引用,意味着您将创建的任何未来提交的父提交。通常,HEAD 指向当前分支的最后一次提交;并且所有将来的分支和提交都将它作为父级。
当您签出分支时,头将在分支的最后一次提交之间来回移动。但是当你检查一个特定的提交时,你进入了一个被称为“分离头”的状态,这意味着你将创建的任何东西都不会被附加到任何东西上。在那种状态下尝试提交是没有用的,因为任何改变都会丢失。
Git 会告诉你什么时候你处于那种状态(如图 18-2 ),这样你就不会不知不觉地处于那种状态。
图 18-2
检查提交
因此,只有在软件上测试某些东西时,才需要检查提交。但是,如果您希望保留您打算进行的提交,您可以从该特定提交创建一个分支。该命令与从另一个分支创建分支相同:
$ git checkout -b <branch_name>
在错误的分支工作
这种情况经常发生。情况通常是这样的:你接到一个任务,你是如此渴望完成它,以至于你立即开始编码。当你注意到你一直在主分支工作时,你已经工作了一个小时了!别担心,解决这个问题非常简单。
如果您在错误的分支上修改了一些文件,您可以直接创建一个新的分支(并将其签出)以将当前的更改带到那里。又是同一个命令:
$ git checkout -b <branch_name>
这将使用您当前的更改创建一个新的分支,并将其签出。然后,您可以暂存修改后的文件并提交项目。
然而,如果您已经将分支推送到远程存储库,这将不起作用;历史就是历史,不要改变。解决这个问题的唯一方法是改变你所做的承诺,并且带着这种耻辱生活一辈子。
赶上母分公司
当您从另一个分支(通常是主分支)创建一个分支时,它们的历史不再链接,因此在一个分支中发生的事情不会影响到另一个分支。这意味着当您在您的分支上工作时,其他人可以在基础分支上提交;并且这些提交对您的分支不可用。
如果您仍然在您的分支上工作,但是对基础分支上的那些新提交感兴趣,您必须首先有一个干净的盘子,这意味着您必须提交您的项目(或者隐藏您当前的变更)。
然后,您必须签出父分支,提取新的提交,然后回到您的分支。
$ git checkout master
$ git pull origin master
$ git checkout <branch_name>
安全地放在您的本地分支上,然后您可以赶上父分支。概念很简单:Git 将取出您当前的提交,并从父分支的顶端创建新的分支;您的提交将应用到您的新分支。这就像您从主分支的最新提交中创建一个分支。该命令称为 rebase。
$ git rebase master
master 上的提交可能会在您的分支中引入冲突,所以要做好动手的准备。这些合并冲突的解决与我们之前看到的一样:打开每个冲突的文件,选择您想要保留的代码;然后你就可以上演它们并提交。
你可以在图 18-3 中找到一个 rebase 冲突的例子,其中 master 和 test_branch 上的提交都修改了 README.md。
图 18-3
重设基础期间合并冲突
如您所见,这几乎与任何合并冲突完全一样;分辨率是一样的:
$ git add <conflicted_files>
$ git rebase --continue
同样,如果你对冲突没有足够的勇气,你可以中止重置基础并回到初始状态。
$ git rebase --abort
如果你在一个分支上工作了很长时间,不时地调整基础是个好主意,这样你就不会离母分支太远。当然,您可能会面临合并冲突,但是您的变更越大,这些冲突就越有可能出现。如果你因为害怕冲突而延迟重定基础,你只会让自己失败,因为当你试图合并分支时,这些冲突会再次出现。不时地用 rebase 处理小的冲突比在合并时不得不同时合并许多冲突的文件要好。
分支已经分叉
如果您使用的是糟糕的 Git 工作流,就会发生这种情况。正如我们前面所说的,您应该在您自己的分支上工作来解决问题,因为多个人在同一个分支上工作是导致灾难的最佳方法。
我们说,当您由于历史更改而无法再推进到远程分支时,两个分支就会分叉。当您在本地分支上提交时,会发生这种情况,但是其他人已经在您之前在远程分支上提交了他们的提交。到了推的时候,Git 不会让你这么做,因为远程分支的最后一次提交不属于本地历史。您将得到如图 18-4 所示的错误。
图 18-4
拒绝的更改
下面是最明智的解决方案:提取远程分支的提交并合并您的更改。然后,您将在您的历史中记录他们的更改(在解决最终的合并冲突之后),并可以在之后进行推送。
$ git pull origin <branch_name>
$ git push origin <branch_name>
这会给你一个难看的历史日志,但是至少所有的提交都被保存了。这方面的一个例子如图 18-5 所示。
图 18-5
合并本地和远程分支
另一个解决方案更加残酷:覆盖远程分支上的所有内容,并用您的历史替换它的历史。为此,您必须使用“强制”选项进行推送。
$ git push origin <branch_name> -f
这导致提交失败和打斗;永远不要这样做。
同样,如果你使用一个好的 Git 和 GitHub 工作流,这不应该发生。
摘要
这一章是为了在面对常见的 Git 问题时给你指出正确的解决方案。当然,你会发现新的、更难的问题,但这是一个好的开始。要记住的主要事情是,在做任何事情之前,尤其是承诺之前,总是要检查你在哪里。
但是如果你使用通用的 Git 和 GitHub 工作流,这些问题根本不应该出现。所以,让我们在下一章重新发现这一点。我们已经在前面的章节中谈到了这一点,但是在您看到了所有最常用的 Git 和 GitHub 特性之后,是时候回顾一下了。
十九、Git 和 GitHub 工作流程
在前几章中,我们学到了很多,尤其是关于 Git 的技术方面。您现在知道如何正确地版本化您的项目,以及如何处理最终的问题。我们还深入研究了 GitHub 项目管理的基础知识。
现在是时候把所有这些放在一起,为你的项目准备一个完美的游戏计划。在这一章中,你将看到一个精心设计的工作流程,一个成功的项目应该遵循这个流程。您可以将其视为“最佳实践”部分或“操作”指南。
如何使用此工作流程
本章介绍的工作流是为初学者和有经验的用户设计的。它在开源项目中也经常被使用,所以很多开发人员已经在使用它了。请记住,这个工作流程不是一成不变的,它可以在合理的范围内进行调整以满足您的需求。
我的建议是,当你还是一个初学者时,就虔诚地遵循这个工作流程,这样你就可以了解它是如何工作的,以及需要经历哪些仪式。当你感觉有一点经验时,你可以稍微修改一下工作流程,如果这能让你更有效率的话;但永远不要为了时间而牺牲安全。绕过一些惯例可能会为您赢得一些时间,但是如果它导致更多的错误和合并冲突,它将会适得其反。在你使用 Git 和 GitHub 几年后,你会成为它的主人,可以创建自己的工作流,前提是你带来的东西会让你的团队更有效率。
GitHub 工作流程
在使用 GitHub 时,你可能犯的最基本的错误是只把它看作一个代码托管服务,也就是说,仅仅为了在你的合作者之间共享代码或者仅仅为了向用户发布你的产品而使用它。GitHub 是一个如此强大的工具,如果不充分利用它的潜力将是一个巨大的浪费。
将 GitHub 视为您的主要项目管理工具。你打算在项目中做的每一个动作都应该在 GitHub 中被跟踪,这样你就可以回去了解历史。你不能在没有正确记录你为什么要做这些改变的情况下就去做一些改变。以下是 GitHub 的黄金法则。
每个项目都是从一个项目开始的
当您开始一个新项目时,您应该在创建存储库之后立即创建一个 GitHub 项目。你需要尽快这样做,因为使用项目板是追踪你的发展的最好方式。你至少应该有一块看板来跟踪项目的“待办事项”。你还可以使用其他的留言板来跟踪用户的反馈或者整理你的随机想法。最主要的是要把你脑海中的想法写下来,因为你很可能会忘记大部分内容。
每个行动都始于一个问题
问题是记录项目需要做什么的好方法。当您注意到程序中的一个 bug 时,您的第一反应不应该是打开 IDE 来修复它,而是创建一个问题来跟踪它。同样的事情也发生在一个特性的想法上,即使你不确定你是否会在将来从事这项工作。创建一个问题来记录你的意图,如果你决定不执行它,你可以关闭它。
这个仪式意味着您在本地 Git 上所做的一切都应该以解决问题为目标。因此,当您在 IDE 上工作时,您应该总是问自己:“这解决了什么问题?”如果答案是“没有”,你应该为它制造一个问题,不管这个任务有多小。
没有直接推送到 master
这是一个主要的仪式,很难遵循,但对参与项目的每个人来说,这让生活变得容易多了。想法很简单:任何人都不应该直接将提交推送到主分支。向 master 引入更改的唯一方法是将其他分支合并到其中。
这直接意味着您创建的每一个变更都应该包含在它自己的分支中,然后才能合并到 master 中。因此,任何新特性或错误修复都应该从一个分支开始,然后在准备好的时候合并到主目录中。“就绪”意味着经过适当的审查和测试。
任何与 master 的合并都需要公关
既然不能直接推入 master,那么唯一的选择就是把分支合并进去。但是你也不应该盲目的把任何一个分支机构合并成 master。您必须创建拉取请求来提出更改。这样,另一个团队成员可以调查您的代码,以验证是否一切正常。
您应该在 PR 描述中引用 PR 解决的问题编号,这样当 PR 被接受时,问题会自动关闭。
使用维基来记录你的代码
这可能看起来很麻烦,但是这是记录代码的最好方法。README 文件对于完整的代码文档来说是不够的,所以需要 wiki。这似乎是一个巨大的任务,但是最好的方法是在编写代码的同时编写文档。所以,你只需要时不时的写点小改动。如果你等了很长时间才决定写文档,你会不知所措,很可能会忘记关键信息。
Git 工作流
现在让我们来谈谈 Git。到目前为止,您肯定知道 Git 所有最常用的特性;但是在正确的时候使用它们是避免错误(和冲突)的最好方法。
永远知道你在哪里
这是非常基本的,因此很容易忘记。在进行任何更改或执行任何命令之前,您应该始终知道您在哪个分支上。如果您使用的是现代 IDE,您当前的分支应该出现在屏幕的底部。如果没有,没有什么比旧的可靠的 git 状态更好的了!
在任何操作之前提取远程更改
在从远程主分支创建分支之前,先提取它。这将允许你与你的同事保持同步,并且你将避免大多数合并冲突。
当您在本地分支上工作时,您还应该不时地调整基础以接收最新的更新,从而减少将来合并冲突的数量。作为奖励,你的 git 日志图会更漂亮。☺
注意你的提交信息
请参考关于提交的第五章,回顾如何编写一个好的提交消息。不要低估这个过程,因为它将是你的历史日志的支柱。一开始,编写一个错误的提交消息可能会节省您几分钟的时间,但是到了错误修复的时候(相信我,它会来的),您将会浪费无数的时间来搜索引入错误的提交。
不要改写历史
就是不要。这是在团队中使用 Git 时最糟糕的事情之一。如果您更改了一个提交并将其强制推送到一个远程分支,那么对该分支所做的一切都将被您的更改所覆盖。这意味着,如果有人在那个分支上工作,他们将不得不放弃他们所做的一切,并重置他们的本地分支。如果你真的必须做,确保你是唯一一个在那个分支上工作的人。
摘要
这么短的一章!但这是一个成功项目的最佳方式。要记住的主要一点是,GitHub 不仅仅是一个代码托管服务。您应该使用它来正确地跟踪您的项目进展,并跟踪您或您的客户可能有的任何想法。通过遵循这个工作流程,您将会成功,因为您将会避免 Git 和 GitHub 的大多数问题。
您现在拥有了使用 Git 和 GitHub 取得成功的所有工具!现在一切都取决于你的想象力和勇气。正确使用这些工具,你将引导你的项目进入最佳路径。祝你好运!