Skip to content
This page has been auto-translated and may contain errors.View in English

Git 中的分支

docs.scrimba.com

你正在开发 weather-app 的过程中,想尝试添加一个五天天气预报功能。可能需要多个提交才能完善它,在完成之前,你不希望半成品代码与那个已经可用的应用混在一起。分支就是 Git 解决这个问题的方式:一条独立的工作线,实验代码在这里进行,而 main 则保持原来的状态。

分支实际上是什么

这是它的实际运用:

bash
$ git branch forecast
$ git switch forecast
Switched to branch 'forecast'

两个命令:第一个创建了一个叫 forecast 的新分支,第二个将你移到这个分支上。从此以后,你在 forecast 上的每一个提交都会在这个分支上,而 main 始终保持原样。

一个**分支**是指向一个提交的可移动标签。现在,mainforecast 都指向同一个提交,即你运行 git branch forecast 时所在的那个提交。当你在 forecast 上再次提交时,这个标签会移向新提交。main 会保持原位,直到你切换到它并在那里提交。

你可以用 git branch 看到两个标签:

bash
$ git branch
  forecast
* main

星号标记的是你当前所在的分支。一个分支只是指向一个提交的名称。这就是整个机制。本章其他内容都是围绕如何移动这些标签展开的。

在实际项目中,分支名称包含信息。常见的约定是一个短前缀描述更改的类型,后跟描述:feature/five-day-forecastfix/broken-date-pickerchore/upgrade-eslint。有些团队会添加工单号(feature/wa-142-forecast)。无论你选择哪种约定,保持整个项目的一致性,这样任何人都能看出分支是用来做什么的,而无需打开它。

这些名称支持的工作流是短生命周期的功能分支:为一项工作创建分支,在其上提交,获得审查和合并,然后删除分支。存在数周的分支会与 main 产生很大偏差,最终的合并会因为偏差越来越大而变得更困难。最健康的分支会在几天内完成。

分支基本上只是一个小文件,包含一个提交哈希,藏在 Git 自己的簿记中。创建一个分支意味着写入这一行,这就是为什么即使在有多年历史的仓库中,git branch 也能瞬间完成。正因为这种廉价性,命名约定和短生命周期分支在实践中很重要:删除一个陈旧的分支对 Git 来说成本为零,所以只有团队自身的纪律能阻止被遗弃的分支堆积。

Juno分支实际上是什么git branch forecast 创建一个新标签指向你的当前提交,git switch forecast 将你移到它上面。直到你真正在那里提交,你的文件不会有任何变化。我喜欢把分支想象成贴在提交上的便签:便宜到可以随意添加,便宜到可以随意移动,便宜到完成后可以随意撕掉。
Juno分支实际上是什么 分支名称是一个指针,所以创建它的成本为零,在它们之间切换是即时的。给分支起能描述工作的名称,如 feature/five-day-forecast,并保持它们的短生命周期:创建、提交、合并、删除。长生命周期的分支会把常规的合并变成真正的麻烦。
Juno分支实际上是什么 分支真的只是一个包含提交哈希的小文件,这就是为什么创建它是 Git 中成本最低的操作之一。命名约定和短生命周期分支是团队保持其并行工作清晰的方式;Git 本身不强制任何东西。我清理过足够多的六个月前遗弃的分支,对此有强烈的看法。

创建和切换分支

单独使用 git branch forecast 会创建标签,但不会将你移到那里。大多数时候你想同时做这两件事,所以 git switch 有一个快捷方式:

bash
$ git switch -c forecast
Switched to a new branch 'forecast'

-c 代表"create"(创建)。git switch -c 既创建分支又将你移到那里。这个单一命令是你几乎每次开始新工作时都会用到的。

切换分支会改变你的文件所显示的快照。试试看:下面的 echo 行是创建一个新文件 src/forecast.js 的一步方法,其中只有一行,其他的都是你已经知道的命令:

bash
$ git switch forecast
$ echo "// five-day forecast logic" > src/forecast.js
$ git add src/forecast.js
$ git commit -m "Start forecast module"
$ ls src
forecast.js  index.js
$ git switch main
Switched to branch 'main'
$ ls src
index.js

这个文件并没有消失。它存在于 forecast 分支上,而 main 分支的快照从未包含过它。切换回 forecast,它会精确地按照你留下的样子重新出现。

你会在现有代码和教程中看到一个更旧的动词:git checkout forecast 做的工作与 git switch forecast 一样。checkout 更旧且功能更多:取决于你给它什么,它可以切换分支或将文件恢复到早期版本,仅从命令名称你无法判断哪一个即将发生。switchrestore 将这两个任务分开为独立命令,所以名称能告诉你什么将要发生:switch 在分支之间移动,restore 把文件恢复到给定提交时的样子。使用更新的动词;当你在野外遇到 checkout 时要能识别它。

checkout 根据你给它的内容决定做什么:分支名称会切换分支,文件路径会恢复该文件,如果一个名称同时匹配两者,Git 不得不猜测你的意思。这种猜测正是命令被分成两个的原因。switch 只改变分支,restore 只改变文件,所以打错的路径不会再意外地把你切换到一个分支。在旧脚本和教程中仍然会看到 checkout 很多年;在任何新的东西中写 switchrestore

Juno创建和切换分支git switch -c forecast 在一步中创建分支并将你移到它上面,这是你几乎每次都会用到的。切换分支会交换你看到的文件版本,所以在一个分支上添加的文件在另一个分支上是不存在的,直到你切换回来。没有什么丢失,它只是停在另一个分支上。
Juno创建和切换分支git switch -c name 在一步中创建并移动。你会在旧教程和代码库中遇到 git checkout name 做同样的工作;它是同一个想法在更旧、更过载的命令下。日常使用 switch,当你看到 checkout 时毫不犹豫地理解它。
Juno创建和切换分支checkout 根据其参数处理分支切换和文件恢复,这正是 switchrestore 被分出来要消除的歧义。在旧脚本和教程中仍会看到 checkout 很多年;它仍然有效,但不再是默认要写的命令。

HEAD 和分离的 HEAD

Git 用一个叫做 HEAD 的标记来跟踪你当前的位置。通常 HEAD 指向你的当前分支,而该分支指向一个提交,所以每次你切换或提交时 HEAD 会自动向前移动。如果你通过提交的哈希值检出一个具体的提交,或者检出一个标签(固定在一个提交上的标签,通常标记发布版本),而不是分支名称,HEAD 会直接附加到那个提交上,而不是附加到分支标签。这个状态叫做分离 HEAD。

你可以在那里自由地查看,甚至可以提交,但没有名称指向那些新提交。如果你没有先创建分支就切换到一个分支,它们会变得很难找到。

bash
$ git switch a1b2c3d
HEAD is now at a1b2c3d Add password strength meter

那条消息"HEAD is now at"告诉你已经离开了分支领地,并将 HEAD 附加到一个单一的提交。

在日常工作中,你很少会意外进入这里。它最常发生在你检出一个旧标签或特定提交来查看代码在那个点是如何表现的,或者当构建工具检出一个确切的提交来工作时。如果你只是想看看,那没问题:浏览文件,运行应用,然后在完成时切换回分支。如果你在分离状态下做了值得保留的更改,在切换离开前创建一个分支:从这个分离状态运行 git switch -c fix/old-bug 给提交一个真实的标签,这样它在切换时就能保存下来。

分离 HEAD 留下的提交不会在你切换离开的那一刻立即被删除。它变成不可达的:仍然存储着,但没有分支、标签或 HEAD 指向它。Git 也不会立即清理那些。它保持一个 reflog,一个 HEAD 指向过的任何地方的记录,一个不可达的提交通常会在 Git 实际永久删除它之前存活数周。

这给了你真正的时间来恢复,虽然它不会永远持续。在切换离开前创建分支会保持分离工作的安全。从 reflog 中挖出一个提交今天可行,但那个条目最终会过期,提交哈希会早在那发生之前就从你的脑海中溜走,所以养成分支的习惯,在罕见的忘记时再依靠 reflog,这在撤销事物中有涉及。

JunoHEAD 和分离的 HEAD HEAD 标记你当前所在的提交,它通常随着你所在的任何分支而移动。如果你检出一个具体的提交而不是分支,你会得到一个分离的 HEAD,任何你在那里做的新提交都没有分支名称指向它。如果这曾经发生,你想保留工作,在切换到其他任何地方之前立即在当前位置创建分支。
JunoHEAD 和分离的 HEAD 你最有可能在检出标签或旧提交来四处看看时遇到分离的 HEAD,只要你只是在读取,它就是无害的。当你提交了值得保留的东西时,立即运行 git switch -c 在那里给它分配一个分支,然后再去其他地方。
JunoHEAD 和分离的 HEAD 通常 HEAD 指向分支,分支指向提交。分离时,HEAD 直接指向提交,所以你在那里添加的任何东西一旦你切换离开就没有分支名称来保持它。因为 reflog 保持了一条踪迹,它很少会彻底消失,但不要指望在三周后记住提交哈希。在切换前创建分支,总是这样。

这通向何处

你现在已经创建了分支,在它们之间移动,并看到了当你在其他地方进行实验时 main 如何保持不动。下一个问题是分支上的工作如何回到 main,这正是合并和冲突要涵盖的内容。如果你想在继续之前复习一下阅读提交历史,历史讲解了 git loggit showgit diff