【问题标题】:Git branch is not displaying all branchesGit 分支未显示所有分支
【发布时间】:2021-02-26 11:40:05
【问题描述】:

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

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

有什么建议吗?

【问题讨论】:

  • 但是为什么在需要所有分支时只克隆一个分支
  • 因为我只需要那个特定的分支来工作......这是我首先想到的

标签: git github git-branch


【解决方案1】:

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

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


1确切的描述比这要复杂得多,但将其视为“复制所有提交且没有任何分支”会让你开始。


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

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

您实际上并不需要 任何 分支名称来在 Git 中工作。不过,您确实需要一些 类的名称,而分支名称是这里最好的名称。这就是为什么您的 Git 在git clone 进程结束时会独树一帜的原因。你的每一个名字都会给你更多的东西。

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


2git switch 命令最初是在 Git 版本 2.23 中添加的,用于将过于复杂的 git 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 show e31aba42fb12bdeb0f850829e008 几乎可以保证工作。但是git show e31a 不会,因为它可能是这个提交的缩写,或者是提交e31a17f741...,例如。虽然 e31ab 今天有效,但随着更多提交的添加,它可能会停止工作。

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


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

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


提交中有什么

现在我们知道了一种——而且是最深入的——通过哈希 ID 来查找提交的方法,是时候查看每个提交中的内容了。每个提交有两个部分:

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

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

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

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

A <-B <-C

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

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

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

Git 可以很容易地比较快照C 和快照B,因为提交C 持有提交B 的哈希ID。所以 Git 可以只提取 both 提交。此外,由于 Git 删除重复文件的方式,Git 可以立即知道——甚至麻烦提取——重复文件。 Git 只需要提取和比较 不同的 文件。 Git 会这样做,并将构建一组更改,将旧文件转换为新文件。这就是 Git 将向您展示的内容:这套说明。

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

按分支名称查找提交

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

这就是分支名称的作用。但这有点偷偷摸摸。分支名称的真正作用是仅存储 last 提交的哈希 ID。让我们再次绘制那个包含三个提交的存储库,并添加一个名称 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

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

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

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

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

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

A--B--C
       \
        D

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

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

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

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

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

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

注意分支名称​​move。事实上,当我们进行新的提交时,它们会自动移动:带有HEAD 的分支名称会自动移动以包含新的提交。所有其他分支名称都保留在那一点上,但是因为它们是我们的分支名称,所以我们可以控制。我们可以让我们的 Git 随时移动这些名称。唯一的限制是我们必须有一个 commit 才能将名称移动到。

克隆创建远程跟踪名称

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

        E
       /
A--B--C
       \
        D

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

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

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

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

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

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,如果我们使用它)不仅切换分支,而且选择分支名称标识的提交,作为要签出的提交,因此可供我们使用。


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

【讨论】:

  • 非常感谢!你把一切都说清楚了,解决了我的问题。
【解决方案2】:

为了确保您从 Github(您的遥控器)获得有关分支的所有最新信息,您可以发送git fetch

git fetch --all

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

git branch -av

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

【讨论】:

    【解决方案3】:

    注意事项:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-21
      • 2020-05-13
      • 1970-01-01
      • 2022-01-09
      • 2010-12-15
      • 2011-04-09
      • 2018-03-17
      相关资源
      最近更新 更多