作为开发人员,我们都使用某种形式的版本控制系统,最流行的是 Git。
我们很多人都使用 Git,但很少有人知道它的内部工作原理。我们大多只是通过 GUI 客户端或内置于 IDE 和代码编辑器中的客户端来使用它。我们中的一些人(包括我自己)甚至不知道如何使用终端来导航它。
这可能会对您的项目造成不利影响,因为您可能会遇到一些问题,如果您不完全了解 Git 在后端的工作原理,您将不知道如何解决这些问题。
在本文中,我将分享一些 Git 幕后的工作原理以及使用它时应该了解的内容。
本文的灵感来自我参加的一门名为“使用 Git 进行协作编码”的课程,该课程教您如何使用 Git 与合作伙伴和团队成员协作完成项目。
好啦,开始吧。
注意:本文不是使用 Git 的详细指南。相反,它旨在让您了解使用 Git 时幕后发生的情况。
如果你想要了解 Git 和 GitHub 的基本介绍, 这里有一个适合初学者的指南 .
Git 是什么?它如何工作?
Git 是一个分布式版本控制系统,可帮助开发人员跟踪项目中的更改。可以将其视为项目从开始到结束的整个时间线。
当您创建新项目并使用 初始化 Git 存储库时 git init
,它会创建一个名为 的隐藏文件夹 .git
,其中包含一堆其他文件和文件夹。这些文件和文件夹包含 Git 引用该特定目录所需的所有内容。
初始化存储库后,Git 开始跟踪该目录内的所有文件和文件夹,但文件中您明确告诉它不要跟踪的 .gitignore
。该 .gitignore
文件是隐藏的,可让您列出您不希望 Git 跟踪的所有文件和文件夹。这些文件和文件夹永远不会被 Git 在您的项目中引用。
当你使用 git 时,你的文件将以 Git 的三种状态之一保存:
- 已修改状态 – 这是您在文件中添加、删除或更改某些内容时的状态。Git 已注意到此更改,但您尚未事先通知 Git 此更改。
- 暂存状态 – 这是您通知 Git 更改并让 Git 记录该更改的时间。通常称为“暂存区域”,处于此阶段的文件称为暂存文件。即使在暂存文件后,您也可以修改文件,这样您就可以同时看到修改状态和暂存状态的文件。暂存后所做的修改不会在提交后显示。
- 已提交状态——这是 Git 保存您修改的更改的状态。
您可以使用该命令 git status
查看您的文件当前处于哪些状态。
当您提交一些代码时,Git 会对该特定版本的代码进行快照,以供您稍后参考。
因此,你可以说 Git 是项目引用的数据库。该数据库使用三种类型的对象构建:
- 包含每个提交的元数据(例如消息和作者)的提交,
- 包含对文件名、文件内容以及有时其他树的引用的变化树,
- 以及代表文件中保存的实际数据的 blob。
注意:在某些情况下,.git 文件夹在 macOS 中是隐藏的,因此您必须在系统设置中启用隐藏文件才能看到它。
Git 如何跟踪你的文件
使用 Git 时,有许多移动部件,有时我们会处理几个不同的文件。
为了让 Git 跟踪所有这些文件,它创建了一种称为安全哈希算法 (SHA) 的东西,它是一系列加密哈希函数,并将其分配给每个提交。这使得识别重复文件并为其提供与原始文件相同的标识符变得更加容易,从而节省了存储空间。
使用 SHA,我们可以引用 Git 中的任何内容,例如查看提交并恢复到它,或者只是标记它。
除了初始提交之外的每个提交都有一个父提交,当您启动一个项目时,您的 HEAD 指向主分支上的最新提交。
所有这些都使用分配给提交的 SHA 编号进行维护。当您创建分支时,它会创建文件的单独副本。当您切换分支时,HEAD 会将其指向您切换到的分支上的最新提交。
删除的分支被称为处于分离 HEAD 或无头状态。如果我们有唯一的哈希,我们可以访问它们,但没有指向它们的 HEAD —— 所以如果我们没有它们的哈希,就无法访问它们。
我们还可以将它们作为单独的分支来检查,以保持对它们的访问权限。
使用 Git 时,我们经常会为不同的任务(例如开发新功能或修复错误)创建单独的分支。您可以使用命令创建分支 git branch
。
git branch [branch name]
当我们从一个分支切换到另一个分支时,Git 不再有权访问前一个分支中的任何提交。因此,如果我们想恢复到另一个分支中的提交,我们必须切换到该分支或将当前分支重新定位到我们想要使用的另一个分支上。
我将在稍后讨论变基问题,但首先,我想讨论一下合并和合并冲突。
什么是 Git 合并以及它如何工作?
当您为项目中正在处理的内容创建分支时,您通常不希望因为弄乱了某项工作而影响其余工作。因此,完成该任务后,您会希望将其添加到主项目中。
最常见的方法是将 合并 到项目的主分支或主分支中。
合并有 3 种类型:快速合并、三向合并和上面我简要提到过的变基。
顾名思义,快速合并是一种快速简便的方法来合并您的分支并继续处理您的项目。它不需要任何额外的工作,而且您很少会遇到合并冲突。
这是因为它所做的只是将其 HEAD 从主分支的当前提交移至正在合并的分支的最新提交。这就像将您的项目向前推进一样。
另一方面,三向合并需要更多的工作,因为您需要处理三次提交。
当您尝试合并的分支位于尝试合并的分支之前时,就会发生三方合并。例如,您创建了一个分支来修复错误,同时,一位同事正在处理主分支。在您完成修复错误之前,主分支中已经添加了新的提交。
Git 无法将您的分支顺利地集成到主分支中,因为主分支现在位于您的分支指向的提交之前。
因此,它必须为其创建一个单独的提交,并将您的提交、主分支上的新提交以及它们都指向的提交(因为它们都有相同的祖先提交)合并为一个提交,以便将它们合并为一个新的提交。
最后一种方法是重新定基,将整个功能分支转移到主分支上。
重新定基(合并没有)的一个缺点是会丢失项目历史记录。当您将一个分支合并到另一个分支时,您仍然可以访问该分支,直到您删除它为止。但是当您重新定基一个分支时,您将无法访问该分支的所有历史记录。这可能是好事也可能是坏事,具体取决于您(您的团队)和您的项目。
随着项目过程中发生的所有合并和变基,您必然会遇到合并冲突。
当您在不同分支上对文件的同一位置进行更改时,就会发生合并冲突。例如,您对正在处理的功能分支的第 10 行进行了更改,而另一个人也在主分支上对同一行进行了更改。
此时,Git 不知道要处理哪些更改,因此它会引发合并冲突并且拒绝合并或变基,直到您手动解决冲突。
当发生合并冲突时,Git 会暂停合并并等待您解决冲突并继续合并。您可以运行命令 git status
来查看哪些文件已合并,哪些文件未合并。
您可以使用 Git 的冲突解决标记来解决冲突,当您打开发生冲突的文件时,它看起来像这样:
上面的版本 ===
是您当前签出的版本,这就是为什么 HEAD 指向它。下面的版本是您要合并的分支。
为了解决冲突,您必须决定要保留哪个版本、删除哪个版本,或者您必须自己手动合并内容。选择后,您就可以继续合并。
如何在 Git 中保存未提交的更改
前面讲了Git的3个状态,分别是modified,staged,committed。
有时我们想快速切换分支而不保存对当前分支所做的更改。这可能是为了快速修复或只是签出内容,而我们还没有完成当前分支上的工作。我们可以通过 存储来实现这一点。
Git 中存储的工作原理
通过存储,您可以保存所有未提交的工作,并将其保存为未完成的更改,即使您在单独的分支上,您也可以稍后重新应用它们。
要存储文件,您可以使用命令 git stash
。这会将所有未提交的更改保存在堆栈中,并为您留下一个干净的分支。要查看存储,您可以使用命令 git stash list
。但它不会保存未跟踪的文件。
当您准备好应用存储的更改时,只需运行命令 git stash apply
,它就会应用这些更改。
如果您有多个存储,您可以通过指定要应用的存储来指定要应用哪个存储(例如 stash@{0}
)。如果您不指定要应用的存储,Git 会自动应用最近的存储更改。
如何撤消 Git 中的更改
无论我们多么小心谨慎,我们的系统多么高效,在开展任何类型的项目时,我们难免会犯错。这可能是人为错误,也可能是计算机错误。
通常,在使用传统应用程序和文字处理器时,我们只需点击撤销按钮或快捷键 cntrl/cmd + z,即可返回到原来的位置。但是,在使用 Git 时,我们没有这种奢侈,因此我们必须执行更多步骤才能修复/撤销这些错误。
我们可以使用几种方法来修复/撤消这些错误,让我们逐一看一下。
查看
Checkout 主要用于切换分支,但也可用于将 HEAD 指向特定提交。您可以在 checkout 命令后指定提交的 SHA 编号来执行此操作。
git checkout [commit SHA]
这会使您处于分离的 HEAD 状态,您可以对其进行临时或实验性的更改,而不会影响任何分支。
成功检出您的提交后,您可以将其转变为单独的分支。
git checkout -b [name of your new branch]
or
git switch -c [name of your new branch]
但请记住,使用 checkout 可能会导致丢失之前的分支,因为它处于分离的 HEAD 状态。
恢复
这是修复存储库中错误的最直接方法。顾名思义,恢复是通过在有错误的提交之前添加一个提交来恢复错误(但没有错误)。可以将其想象成同时在时间上来回移动。
举个例子:您有 2 个提交,分别名为 \'initial commit\' 和 \'setting up dev environment\'。在工作时,您注意到在第二次提交中犯了一个错误,并想返回到 \'initial commit\'。
当您调用该 git revert
关键字时,它会创建第三次提交,这与将您的仓库恢复到第二次提交之前完全相同。使用 Git revert,您需要传递提交引用,并且与三向合并类似,您需要为正在创建的新提交传递提交消息。
重置
这是 Git 中撤消更改的最复杂、最通用且最危险的方法。通常建议不要在共享存储库中使用 reset,因为它可能导致项目遭受巨大损失。好了,说完了这些,让我们看看 reset 的作用是什么,以及它与其他方法有何不同。
Git reset 有三个选项。根据你使用的不同,它可能会改变提交历史、提交历史和暂存索引,或者提交历史、暂存索引和工作目录。
第一个只会 git reset --soft
更改提交历史记录。使用这个,您需要使用其 ID 指定要返回哪个提交。
git reset --soft [commit ID]
这将删除您要返回的提交之后的所有提交。
第二个 git reset --mixed
是调用的默认行为 git reset
。它会同时更改提交历史记录和暂存索引。因此,如果暂存区域中有任何文件,它会取消暂存这些文件,并且如果指定了提交 ID,它会返回到该提交,删除其后的所有提交。
//Both do the same thing
git reset [commit ID]
git reset --mixed [commit ID]
最后一个选项是最危险的,只能作为最后的手段使用 git reset --hard
。这会改变提交历史、暂存索引和工作目录。
因此,它首先返回到提交 ID 指定的提交,取消暂存所有暂存文件,并删除工作目录中所做的所有更改。这意味着,如果您修改了任何文件或添加了更多文件,它会从工作目录中删除所有这些修改和添加。
您可以看到为什么除非别无选择,否则不建议使用此命令。
修复或撤消错误的最安全方法是使用 git revert
命令。这是因为它不会改变您的提交历史记录,而是在出现错误的提交之前创建一个新的提交。这样,您仍然可以在需要时返回到出现错误的提交。
包起来
我希望本文能让您清楚地了解使用 Git 时底层发生的情况。希望您现在已经熟悉使用 Git 时应该了解的最重要的部分。
如果您想深入了解使用 Git,我的博客中有一篇 文章 ,其中介绍了一些学习 Git 的最佳资源。
如果您已经读到这里,感谢您的阅读,我是 Faisal 。您可以在 Twitter、Instagram 和 YouTube 上关注我。欢迎在任何平台上与我联系。
发表评论 取消回复