Git 分支未显示所有分支

Gee*_*Dev 6 git github git-branch

我是使用 Git 的新手,我从 GitHub 克隆了一个分支,当我输入git branch. 完成我的工作后,我成功地将其推送到了一个新的分支。之后,我将该文件夹复制到另一个目录(因为我想有一个备份以避免冲突),输入它,然后键入git branch. 只显示了 3 个分支,知道我在 Github 上有 4 个。

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

请问有什么建议吗?

tor*_*rek 6

当您克隆现有存储库时,您的 Git 会创建一个新的不同的存储库,并将所有1 次提交复制到这个新存储库中,不是原始存储库中的任何分支。在最后的步骤git clone是创建一个分支。这个分支名称是你的,不是他们的;它只是拼写与他们的名字之一相同

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


1确切的描述比这复杂得多,但将其视为“复制所有提交而不复制任何分支”将使您入门。


我试图通过将分支克隆到一个新文件夹(输入git clone -b)中来解决这个问题,现在只有我克隆的分支出现了..

当你创建一个新的克隆时——这也是一个新的存储库,你可以在其中获得以前存储库的所有提交,但还没有它的分支——命令的最后一步git clone是运行创建一个分支的git checkoutorgit switch命令2。该-b标志存在以便您可以在最后一步告诉您的 Git复制哪个分支名称。如果您省略该-b标志,您的 Git 会询问他们的 Git 存储库(您要克隆的存储库)他们推荐哪个分支。但无论哪种方式,您都只能获得一个分支。

您实际上不需要任何分支名称即可在 Git 中工作。不过,您确实需要某种名称,而分支名称是这里最好的名称。这就是为什么您的 Git 在git clone流程结束时取一个名字的原因。你的每一个名字都会给你多一件事情。

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


2git switch命令最初是在 Git 2.23 版本中添加的,用于将过于复杂的git checkout命令拆分为两个单独的命令,git switch以及git restore. 现存的git checkout遗骸;您可以使用它代替两个新的、更简单的命令。不过,新的简化命令在某种意义上更安全:git switch命令试图非常安全,就像git checkout它复制的一半一样。git restore然而,该命令是故意不安全的,因为它会不可撤销地破坏工作;它复制git checkout. 因此,如果您使用git checkout,当您认为您正在调用“安全地做事”的一半时,您可能会意外地调用“破坏我的工作”的一半。


Git 是关于提交的

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

这意味着它的关键是要了解什么是承诺,你如何命名一个特定的提交,以及如何使一个新的提交。让我们从名字开始。

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

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

这个数字总是有效的:如果你有这个提交,那就是它的数字,并且git show e31aba42fb12bdeb0f850829e008e1e3f43af500会显示它,例如。如果没有歧义,您通常可以将数字缩写为前四个字符,因此如果您拥有 Git 的 Git 存储库的克隆,git show e31aba42fb12bdeb0f850829e008几乎可以保证可以正常工作。但git show e31a不是因为它可能是此提交或 commit 的缩写e31a17f741...,例如。虽然e31ab今天有效,但随着更多提交的加入,它可能会停止工作。

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


3有计划重做编号系统以使用 2 256 个号码,并进行某种丑陋的过渡。

4事实上,Git 的所有内部对象都使用这种方案。这意味着所有保存的对象始终处于冻结状态。例如,这就是 Git 冻结和删除重复文件内容的方式。


提交中有什么

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

  • 提交包含所有文件的完整快照。这是大多数提交的主要数据(通常也是存储库的大部分)。每个文件都存储为内部blob 对象,使用相同的哈希名称编码技巧。这会自动对文件进行重复数据删除,因此,如果您连续进行 100 次提交,而这些提交主要是重用了他们的大部分文件,那么它们实际上不会占用任何额外空间。

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

Git 为您生成了一部分元数据,然后在以后使用,您很少直接看到它,那就是:每个提交都保存其前一个提交的原始哈希 ID。这个字符串一起向后提交,形成以最新提交结束的提交链。

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

A <-B <-C
Run Code Online (Sandbox Code Playgroud)

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

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

为什么也许是最简单的解释。如果您想查看提交的所有文件,只需查看提交即可。Git 将从提交中复制所有这些文件——记住,它们以特殊的冻结 Git 格式存储,去重(和压缩)——到常规的普通计算机文件。您可能有一堆比 Git 更胜任的文件查看器:它们可以将图像显示图像,在文本编辑器中打开文本文档,使用 PDF 查看器打开 PDF,等等。但是您的文件查看器可能无法将整个快照与之前的整个快照进行比较。吉特可以

Git 可以很容易地将快照C与快照进行比较B,因为 commitC保存了 commitB的哈希 ID。所以 Git 可以只提取两个提交。此外,由于 Git 删除重复文件的方式,Git 可以立即知道——甚至不用费心提取——重复的文件。Git 只需要提取和比较不同的文件。Git 会这样做,并会构建一组更改,将旧文件转换为新文件。这就是 Git 将向您展示的内容:这组指令。

(请注意,Git按需创建指令集。在您要求 Git 比较任意两次提交之前,Git 所拥有的只是这两个快照。您可以根据传递给比较命令的选项获得不同的指令集。例如, Git 可以根据单词进行差异检查,或者忽略某些类型的空白更改。Git 在这里的能力并不总是像我们想象的那么好,但是我们可以使用一些技巧。它们超出了范围不过,对于这个特定的答案。)

按分支名称查找提交

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

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

A--B--C   <-- main
Run Code Online (Sandbox Code Playgroud)

在这里,我们不去尝试记住 C的哈希 ID,我们只知道名称main为我们做了这件事。因此git checkout main(2.23 之前的 Git)或git switch main(2.23 及更高版本)为我们获取最新的提交——当前C——无论它有什么哈希 ID。

我们现在可以添加一个也指向 commit的新名称C

A--B--C   <-- main, dev
Run Code Online (Sandbox Code Playgroud)

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

A--B--C   <-- main (HEAD), dev
Run Code Online (Sandbox Code Playgroud)

如果我们git switch dev,我们将特殊名称重新附加HEAD到 name dev,如下所示:

A--B--C   <-- main, dev (HEAD)
Run Code Online (Sandbox Code Playgroud)

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

A--B--C
       \
        D
Run Code Online (Sandbox Code Playgroud)

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

A--B--C   <-- main
       \
        D   <-- dev (HEAD)
Run Code Online (Sandbox Code Playgroud)

我们现在有两个分支名称,这两个名称指定了两个不同的“最新”提交。最新提交mainC,最新提交devD。CommitD点回提交C,哪个点回B,哪个点回A;所以所有四个提交都分支上dev,而其中三个在main.

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

        E   <-- main (HEAD)
       /
A--B--C
       \
        D   <-- dev
Run Code Online (Sandbox Code Playgroud)

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

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

克隆创建远程跟踪名称

当我们克隆其他人的仓库时,我们得到了他们所有的提交,而没有得到他们的分支。这是如何运作的?好吧,假设我们有上述,有两个实际分支名称maindev选择的提交ED分别。我们现在创建一个新的存储库,在其中复制所有五个提交,给我们:

        E
       /
A--B--C
       \
        D
Run Code Online (Sandbox Code Playgroud)

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

        E   <-- origin/main
       /
A--B--C
       \
        D   <-- origin/dev
Run Code Online (Sandbox Code Playgroud)

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

        E   <-- main (HEAD), origin/main
       /
A--B--C
       \
        D   <-- origin/dev
Run Code Online (Sandbox Code Playgroud)

请注意,我们的分支名称选择我们git clone根据分支名称创建的远程跟踪名称相同的提交。但是我们现在只有一个分支名称,而不是两个。如果我们运行:

git switch dev
Run Code Online (Sandbox Code Playgroud)

这使用了 Git 提供的一个特殊功能,可以找到它们origin/dev并创建我们自己的新名称dev

        E   <-- main, origin/main
       /
A--B--C
       \
        D   <-- dev (HEAD), origin/dev
Run Code Online (Sandbox Code Playgroud)

现在我们有两个分支名称。但我们最初没有。请注意,我们现在也已D签出提交,而不是 commit E,因为git switch(或者git checkout,如果我们使用它)不仅切换分支,而且还选择分支名称标识的提交,作为要签出的提交,因此可供我们使用。


5从技术上讲,远程跟踪名称位于单独的命名空间中。我们的 Git 不只是origin/在前面,它替换refs/heads/refs/remotes/origin/. 这个名字origin实际上是一个遥控器,我们可以在我们的 Git 存储库中拥有任意数量的遥控器。但这是另一个问题的主题。


小智 5

为了确保您拥有 Github(您的远程)分支的所有最新信息,您可以执行以下操作git fetch

git fetch --all
Run Code Online (Sandbox Code Playgroud)

--all标志从所有遥控器获取分支。如果您只想查看所有分支(在您的计算机上和 GitHub 上),您可以执行以下操作git branch

git branch -av
Run Code Online (Sandbox Code Playgroud)

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