Git 分支没有显示所有分支

Git branch is not displaying all branches

我刚开始使用 Git,我从 GitHub 克隆了一个分支,当我输入 git branch 时所有分支都会显示。完成我的工作后,我将它成功地推到了一个新的分支。之后我把文件夹复制到另一个目录下(因为想有个备份避免冲突),进去,输入git branch。只显示 3 个分支,知道我在 Github 上有 4 个分支。

我试图通过将分支克隆到一个新文件夹(键入 git clone -b <branch-name> <repo-link-https>)来解决问题,但现在只有我克隆的分支出现了..

有什么建议吗?

备注:

  • 你可以clone a specific branch with git clone --branch <branch> --single-branch
  • 您可以使用 git worktree
  • multiple worktrees(无需复制整个文件夹)

对于分支,使用 git branch -avv 获取所有本地和远程分支的列表。
然后再试一次你的副本,并在新复制的文件夹中完成时比较 git branch -avv:如果缺少远程分支,一个简单的 git fetch 就足够了。

为了确保您拥有来自 Github(您的遥控器)的分支机构的所有最新信息,您可以执行 git fetch:

git fetch --all

其中 --all 标志从所有远程获取分支。如果您只想 查看 所有分支(在您的机器和 GitHub 上),您可以执行 git branch:

git branch -av

其中 -a 显示来自本地和远程的分支,-v 给出更详细的输出。

当您克隆一个现有的存储库时,您的 Git 会创建一个新的不同的存储库,并复制到这个新的存储库 all1来自原始存储库的分支 的提交 和 none。 最后git clone是创建一个分支。这个分支名称是你的,不是他们的;只是拼写相同作为他们的名字之一。

当您使用克隆(一个不同的存储库)时,您可以向其中添加越来越多的分支。如果您将原始存储库中的所有 same 分支添加到它,您现在拥有他们所有的提交 and 他们所有的分支名称(请注意,作为您自己的分支机构)。但在那之前,您只有他们所有的 提交 。没关系,因为 Git 与分支无关。 Git 大约 提交 .


1精确的描述比这复杂得多,但是把它想象成“复制他们所有的提交和none他们的分支”会让你开始了。


I tried to solve the issue by cloning the branch inside a new folder (typed git clone -b) and now only the branch that I cloned is appearing..

当你创建一个新的克隆时——同样,它是一个 new 存储库,你可以在其中获得以前存储库的所有提交,但它的分支 none — git clone 命令的 最后 步骤是 运行 一个 git checkoutgit switch 命令2 构成一个分支。 -b 标志存在,因此您可以告诉您 Git 哪个 要复制的分支名称,作为最后一步。如果您省略 -b 标志,您的 Git 会询问他们的 Git 存储库(您正在克隆的存储库)他们 推荐哪个分支。但无论哪种方式,你都只能得到一个分支。

您实际上不需要任何 分支名称来在Git 中工作。不过,您确实需要 一些 类名称,而分支名称是此处最好的名称。这就是为什么您的 Git 在 git clone 流程结束时名声大噪的原因。你起的每一个名字都会让你多做一件事情。

要了解发生了什么,请继续阅读。如果您对您的直接问题已得到解答感到满意,您可以到此为止。


2git switch命令最早是在Git2.23版本中加入的,用来拆分overly-complicatedgit checkout命令分为两个单独的命令,git switchgit restore。现有的 git checkout 仍然存在;您可以使用它来代替这两个新的、更简单的命令。不过,新的简化命令在某种意义上更安全:git switch 命令试图非常安全,它复制的 git checkout 的一半也是如此。然而,git restore 命令是故意不安全的,因为它会不可逆转地破坏工作;它复制了 git checkout 的另一半。因此,如果您使用 git checkout,当您 认为 正在调用“安全地做事”时,您可能会不小心调用“破坏我的工作”。


Git 都是关于提交的

要了解 Git 在这里做什么以及为什么这样做,首先要了解 Git 本身就是关于提交的事实。这与分支无关,尽管分支名称可以帮助您(和 Git)find 提交。尽管提交 contain 文件,但它与文件无关。这实际上是关于提交:Git 所做的一切都是为了保留和添加提交。提交是事情的开始,也是其他一切的目的。

这意味着了解什么是提交 、如何 命名特定提交 以及如何创建 提交。让我们从名字开始吧。

提交的真实名称是其哈希 ID

您可能会认为分支名称会命名提交 — 确实如此,但是是间接的。事实上,每个提交都以其编号命名。每个提交都有一个唯一的编号。没有其他提交可以拥有该编号:一旦提交,该编号将分配给 that 提交。因为那个提交永远占据了那个数字,所以这个数字必须非常大,而且确实如此。目前,每个 Git 提交都会从 2160 个可能的数字中取一个。3 这个数字以十六进制表示为一个丑陋的大字符串像 e31aba42fb12bdeb0f850829e008e1e3f43af500(这是 Git 存储库中 Git 本身的实际提交)。

这个数字总是有效的:如果你有这个提交,那就是它的数字,例如 git show e31aba42fb12bdeb0f850829e008e1e3f43af500 会显示它。你通常可以缩写数字,只要前四个字符是明确的,所以如果你有 Git 存储库的克隆 Git,git show e31aba42fb12bdeb0f850829e008 几乎可以保证工作.但是 git show e31a 不是因为它可能是这次提交的缩写,或者是提交 e31a17f741... 的缩写。虽然 e31ab 今天可以工作,但随着更多提交的添加,它可能会停止工作。

这些数字看起来是随机的,但事实并非如此。事实上,每个都是提交的完整内容的加密校验和。4 Git 在提取其任何内部对象(包括提交)时执行 double-check ,校验和仍然匹配,以便检测存储故障:您告诉 Git 通过哈希 ID 查找提交(或其他对象),它检查哈希 ID 是否仍然匹配。所以这反过来意味着任何提交的任何部分——或任何Git的其他内部对象——都不能改变。您可以创建 new 个,每个都有一个新的和不同的 ID,但这不会影响现有的,它们保留在存储库中。


3计划重做编号系统以使用 2256 个数字,并进行一些丑陋的过渡。

4事实上,Git的所有内部对象都使用这个方案。这意味着 所有 保存的对象将一直冻结。例如,这就是 Git 冻结和 de-duplicates 文件内容的方式。


提交内容

既然我们知道了一种——可以说是最深入的——查找提交的方法,即通过其哈希 ID,是时候查看每个提交中的内容了。每个提交有两个部分:

  • 一次提交包含所有文件的完整快照。这是大多数提交的主要数据(通常也是存储库的大部分)。每个文件都存储为内部 blob 对象,使用相同的 hash-name-encoding 技巧。这会自动 de-duplicates 文件,因此如果您连续提交一百次,其中大部分 re-use 他们的文件,他们实际上不会占用任何额外的 space.

  • 每个提交还包含一些 元数据,或有关提交本身的信息:例如,谁、何时以及为什么。 “为什么”部分是您的日志消息:稍后您对自己 and/or 其他人的解释。为什么 this commit 比上一个好?或者至少,如果它不一定更好,为什么它会有所不同。这个特定提交的目标可能是修复一些错误,或者添加一些新功能,或者为添加新功能做准备,等等。提交本身有更新的源代码,但不一定是更新应该修复的 bug 的任何内容。这是你解释的机会。

有一段元数据是 Git 为您生成的,稍后会用到,您很少直接看到,那就是:每个提交都保存其前一个提交的原始哈希 ID。这个字符串一起提交,向后,进入以最新提交结束的提交链。

我们可以画这个。想象一下,我们有一个只有三个提交的存储库。我们将使用单个大写字母代替真正的哈希 ID 来代表提交。第一个提交将是 A,下一个将是 B,第三个提交将是 C:

A <-B <-C

由于提交 C 是最后一个,因此它的元数据中有较早提交 B 的哈希 ID。我们说C指向B。同理,提交 B 指向 A。由于 A 是有史以来第一次提交,因此它缺少 backwards-pointing 箭头:它不指向任何地方。 Git 称其为(或)root commit。这是我们停止向后工作的地方。

我刚才提到每次提交都有每个文件的完整快照。但是,如果您有 Git show 提交,Git 会显示 更改的内容 。 Git 如何以及为什么这样做?

为什么 也许是最容易解释的。如果您想查看 in 提交中的所有文件,您可以 check out 提交。 Git 将从提交中复制所有这些文件——请记住,它们以特殊的冻结 Git 格式存储,de-duplicated(并且也被压缩)——到普通的普通计算机文件.您可能有一堆比 Git 更能干的文件查看器:它们可以向您显示图像 图像,在文本编辑器中打开文本文档,用PDF 查看器等。但是您的 file-viewer 可能 无法 将整个快照与之前的整个快照进行比较。 Git 可以.

Git 可以很容易地将快照 C 与快照 B 进行比较,因为提交 C 包含提交 B 的哈希 ID。所以 Git 可以只提取 both 提交。此外,由于 Git de-duplicates 文件的方式,Git 可以立即知道 - 甚至 bother 提取 - 重复的文件。 Git只需要提取比较不同的个文件即可。 Git 将执行此操作,并将构造一组更改,将旧文件转换为新文件。这就是 Git 将向您展示的内容:这组说明。

(请注意 Git 按需创建 指令集。在您要求 Git 比较任意两个提交之前,所有 Git 有两个快照。您可以根据传递给比较命令的选项获得不同的指令集。例如,Git 可以根据单词执行 difference-checking,或者忽略某些种类white-space 变化。Git 这里的能力并不总是像我们想要的那样好,但我们可以使用一些技巧。不过,它们超出了这个特定答案的范围。)

按分支名称查找提交

我们已经知道,如果我们记住大丑陋的哈希 ID(或将它们写下来),我们就可以使用它们来查找提交。但这是荒谬的。我们有一台 计算机 。为什么我们不让 计算机 为我们记下哈希 ID?

这就是分支名称的作用。但这有点偷偷摸摸。分支名称真正做的是仅存储 last 提交的哈希 ID。让我们再次绘制那个 three-commit 存储库,并添加一个名称 main,用于标识 last 提交:

A--B--C   <-- main

在这里,我们不需要记住C的哈希ID,我们只知道名字main会帮我们做到这一点。因此 git checkout main(2.23 之前的 Git)或 git switch main(2.23 及更高版本)为我们提供了最新的提交——目前是 C——不管它有什么哈希 ID。

我们现在可以添加一个 新名称,它也指向提交 C:

A--B--C   <-- main, dev

现在我们还需要一件事:我们正在使用这些名称中的哪些?现在,这并不重要,因为两个名字 select 提交 C。但是让我们将特殊名称 HEAD 附加到两个分支名称之一,如下所示:

A--B--C   <-- main (HEAD), dev

如果我们git switch dev,我们就是re-attaching特殊名字HEAD到名字dev,像这样:

A--B--C   <-- main, dev (HEAD)

现在让我们进行 new 提交。无需担心 如何 我们进行新的提交,让我们假设这一切都已完成。这个新的提交 D 必然会指向现有的提交 C,因为我们从 C 中创建了 D 。所以看起来像这样:

A--B--C
       \
        D

但是 D 现在是 最新的 提交,所以 Git 必须更新 name。它应该更新哪个名称?答案很明确:它应该更新 HEAD 是 attached-to:

A--B--C   <-- main
       \
        D   <-- dev (HEAD)

我们现在有两个分支名称,这两个名称指定了两个不同“最新”提交。 main 上的最新提交是 Cdev 上的最新提交是 D。提交 D 指向提交 C,后者指向 B,后者指向 A;所以所有四个提交都在 分支 dev,而其中三个在 main.

如果我们切换回名称 main 并在那里进行新的提交,我们得到:

        E   <-- main (HEAD)
       /
A--B--C
       \
        D   <-- dev

这意味着我们现在有三个提交在两个分支上共享,一个提交仅在 main 上,一个提交仅在 dev 上。现在我们需要两个名称来找到所有五个提交;一个名字会找到一个提交,这会找到三个 shared 提交,但是我们需要另一个名字来找到最后一个剩余的提交。

注意分支名称move。事实上,当我们进行新的提交时,它们会自动移动:附加了 HEAD 的分支名称会自动移动以包含新的提交。所有其他分支名称此时都保持不变,但因为它们是 我们的 分支名称,所以我们处于控制之中。我们可以让 Git 随时移动这些名称。唯一的限制是我们必须有一个 commit 才能将名称移动到。

克隆创建 remote-tracking 个名称

当我们克隆其他人的存储库时,我们会得到他们所有的提交和他们分支的 none。这是如何运作的?好吧,假设我们有上面的内容,有两个实际的分支名称 maindev select 分别提交 ED。我们现在创建一个 new 存储库,我们在其中复制所有五个提交,给我们:

        E
       /
A--B--C
       \
        D

我们确实需要两个名字来找到所有提交。但是我们不需要 branch 名称。另一个 Git,与另一个存储库一起工作,有分支名称,因为这些是 his 分支,他将在他进行新提交时四处移动。所以我们的Git所做的是复制他们的名字但是改变他们。我们让我们的 Git 获取他们的 分支 名称并创建我们的 remote-tracking 名称,通过添加一些东西——通常是 origin/—到名字。5 所以我们得到:

        E   <-- origin/main
       /
A--B--C
       \
        D   <-- origin/dev

Git 将拒绝将特殊名称 HEAD 附加到这些 remote-tracking 名称之一。 HEAD 只允许附加到 分支 姓名。所以我们 git clone 的最后一步是使用 -b 选项,或者他们的建议,从这两个名称中选择一个,并从中创建一个分支名称,如下所示:

        E   <-- main (HEAD), origin/main
       /
A--B--C
       \
        D   <-- origin/dev

请注意,我们的分支名称 select是 与我们的 remote-tracking 名称相同的提交 =23=] 由 他们的 分支名称制成。但是我们现在只有一个分支名称,而不是两个。如果我们 运行:

git switch dev

这使用了 Git 提供的一项特殊功能,即找到他们的 origin/dev 并创建我们自己的新名称 dev:

        E   <-- main, origin/main
       /
A--B--C
       \
        D   <-- dev (HEAD), origin/dev

现在我们有两个分支名称。但我们最初没有。请注意,我们现在还检查了提交 D,而不是提交 E,因为 git switch(或 git checkout,如果我们使用它)不仅切换分支,而且selects 分支名称标识的提交,作为将要 checked-out 的提交,因此可供我们使用。


5从技术上讲,remote-tracking 名称位于单独的 namespace 中。我们的 Git 不只是在前面加上 origin/,它用 refs/remotes/origin/ 替换了 refs/heads/。名称 origin 实际上是一个 remote,我们可以在 Git 存储库中拥有任意数量的遥控器。但这是另一个问题的主题。