我是使用Git的新手,我从GitHub克隆了一个分支,并在键入git branch
时显示了所有分支。完成工作后,我将其成功推送到新分支。之后,我将该文件夹复制到另一个目录(因为我想进行备份以避免冲突),输入该文件,然后键入git branch
。仅显示3个分支,知道我在Github上有4个分支。
我试图通过将分支克隆到新文件夹(类型为git clone -b <branch-name> <repo-link-https>
)中来解决此问题,现在仅出现我克隆的分支。
有什么建议吗?
最佳答案
克隆现有存储库时,您的Git会创建一个新的不同的存储库,并将所有提交(而不是原始存储库的任何分支)复制到此新存储库中。 git clone
的最后一步是创建一个分支。此分支名称是您的,而不是他们的;它的拼写与他们的名字之一相同。
当您使用克隆(一个不同的存储库)时,可以向它添加越来越多的分支。如果将原始存储库中所有相同的分支添加到其中,那么您现在将拥有其所有提交和所有分支名称(请注意,作为您自己的分支)。但是直到那时,您才可以拥有他们的所有提交。很好,因为Git与分支无关。 Git是关于提交的。
1确切的描述要比这复杂得多,但是将其视为“复制他们的所有提交而没有他们的分支”将使您入门。
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..
当您创建一个新的克隆时(又是一个新的存储库),您在其中获得了先前存储库的所有提交,但尚未获得任何分支。
git clone
命令的最后一步是运行一个git checkout
或git switch
command2分支。 -b
标志存在,因此您可以在最后一步中告诉Git复制哪个分支名称。如果您省略-b
标志,则您的Git会询问他们的Git存储库(您正在克隆的一个),并建议他们选择哪个分支。但是,无论哪种方式,您只会获得一个分支。您实际上不需要任何分支名称即可在Git中工作。但是,您确实需要某种名称,而分支名称是此处的最佳名称。这就是为什么您的Git在
git clone
过程结束时使用一个名字的原因。您输入的每个名称都会给您带来更多好处。要了解发生了什么,请继续阅读。如果您对已经解决了您的紧迫问题感到满意,则可以在此处停止。
2
git 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
并非如此,因为例如对于此提交或对于e31a17f741...
来说,它可能很短。尽管e31ab
今天可以工作,但是随着更多提交的添加,它可能会停止工作。这些数字看起来是随机的,但不是。实际上,每一个都是提交内容完整的加密校验和。4Git在提取其所有内部对象(包括提交)时会再次检查校验和是否仍然匹配,以检测存储故障:您告诉Git通过哈希ID查找提交(或其他对象),并检查哈希ID是否仍然匹配。因此,这反过来意味着任何提交(或Git的其他内部对象)的任何部分都无法更改。您可以创建新的ID,每个都有一个不同的新ID,但这不会影响存储在存储库中的现有ID。
3有计划重做编号系统以使用2256个号码,并进行某种丑陋的转换。
4实际上,Git的所有内部对象都使用此方案。这意味着所有保存的对象将一直卡住。例如,这就是Git卡住和删除重复文件内容的方式。
提交中有什么
现在,我们知道了(而且是最深的)一种通过其哈希ID查找提交的方式,是时候查看每个提交中的内容了。每个提交包含两个部分:
Git会为您生成一部分元数据,然后再使用它,您很少会直接看到它,那就是:每个提交都保留其直接的前一个提交的原始哈希ID。该字符串向后一起提交到一连串的提交中,这些提交以最新的提交结束。
我们可以画这个。想象一下,我们有一个只有三个提交的存储库。代替真实的哈希ID,我们将使用单个大写字母来代表提交。第一个提交将是
A
,下一个将是B
,第三个提交是commit C
:A <-B <-C
由于commit C
是最后一个,因此它在元数据中具有更早的commit B
的哈希ID。我们说C
指向B
。同样,将B
指向A
。由于A
是有史以来的第一个提交,因此它缺少此向后箭头:它没有指向任何地方。 Git将此称为根提交。这是我们停止向后工作的地方。我刚才提到,每个提交都有每个文件的完整快照。但是,如果您让Git显示提交,则Git会向您显示更改的内容。 Git如何以及为什么这样做?
为什么也许是最容易解释的。如果要查看提交中的所有文件,则只需 checkout 提交即可。 Git将从提交中复制所有这些文件(记住,这些文件以特殊的卡住Git格式存储,并已重复数据删除(也压缩))到普通的普通计算机文件中。您可能有一堆文件查看器,其功能比Git强大得多:它们可以将图像显示为图像,在文本编辑器中打开文本文档,使用PDF查看器打开PDF等。但是您的文件查看器可能无法将整个快照与之前的整个快照进行比较。 git可以。
Git可以很容易地将快照
C
与快照B
进行比较,因为commit C
拥有commit B
的哈希ID。因此,Git可以提取两个提交。此外,由于Git可以对文件进行重复数据删除,因此Git可以立即知道(甚至不费解费)重复的文件。 Git只需要提取和比较不同的文件。 Git将做到这一点,并将构建一组更改,将旧文件转换为新文件。这就是Git向您显示的内容:这套说明。(请注意,Git会按需创建指令集。在您要求Git比较任何两个提交之前,所有Git都具有这两个快照。您可以根据传递给比较命令的选项来获得不同的指令集。例如, Git可以根据单词进行差异检查,或者忽略某些类型的空白变化,这里的Git的能力并不总是像我们想要的那样好,但是我们可以使用一些技巧,它们超出了范围不过,对于这个特定的答案。)
通过分支名称查找提交
我们已经知道,如果我们记住大的丑陋的哈希ID(或将其写下来),则可以使用它们来查找提交。但这太荒谬了。我们有一台电脑。为什么我们不让计算机为我们写下哈希ID?
这就是分支名称的作用。但这有点偷偷摸摸。分支名称的真正作用是仅存储最后一次提交的哈希ID。让我们再次绘制该三提交存储库,并添加一个名称
main
,它标识最后一次提交:A--B--C <-- main
在这里,我们只是想知道C
这个名字为我们所做的,而不是想记住main
的哈希ID。因此,不管它具有什么哈希ID,git checkout main
(2.23之前的Git版本)或git switch main
(2.23和更高版本)都会为我们提供最新的提交(当前为C
)。现在,我们可以添加一个新名称,该名称也指向commit
C
:A--B--C <-- main, dev
现在我们还需要做一件事:我们使用以下哪个名称?现在,这无关紧要,因为两个名称都选择commit C
。但是,让我们将特殊名称HEAD
附加到两个分支名称之一中,如下所示:A--B--C <-- main (HEAD), dev
如果我们使用git switch dev
,则将特殊名称HEAD
重新附加到名称dev
,如下所示:A--B--C <-- main, dev (HEAD)
现在让我们进行一次新的提交。不用担心我们如何进行新的提交,我们只需假设已完成所有操作即可。这个新的提交D
必然会指向现有的提交C
,因为我们是从D
生成C
的。所以看起来像这样:A--B--C
\
D
但是D
现在是最新的提交,因此Git必须更新名称。应该更新哪个名字?答案很明确:应该更新HEAD
所附加的内容:A--B--C <-- main
\
D <-- dev (HEAD)
现在,我们有两个分支名称,这两个名称指定了两个不同的“最新”提交。关于main
的最新提交是C
,关于dev
的最新提交是D
。提交D
指向提交C
,指向B
,指向A
;因此,所有四个提交都在dev
分支上,而其中三个提交在main
上。如果我们切换回名称
main
并在那里进行新的提交,则会得到: E <-- main (HEAD)
/
A--B--C
\
D <-- dev
这意味着我们现在在两个分支上共享三个提交,一个提交仅在main
上,一个提交仅在dev
上。现在我们需要和这两个名称才能找到所有五个提交。一个名称将找到一个提交,该名称将找到三个共享的提交,但是我们需要另一个名称来找到最后剩余的提交。请注意分支名称移动。实际上,当我们进行新的提交时,它们会自动移动:附加了
HEAD
的任何分支名称都会自动移动以包含新的提交。所有其他分支名称都保留在该位置,但是由于它们是我们的分支名称,因此我们处于控制之中。我们可以随时让Git移动这些名称。唯一的限制是我们必须提交将名称移至的提交。克隆创建远程跟踪名称
当我们克隆别人的存储库时,我们得到了他们的所有提交而没有他们的任何分支。这是如何运作的?好吧,假设我们已经有了上述内容,并选择了两个实际的分支名称
main
和dev
分别提交了E
和D
。现在,我们创建一个新的存储库,在其中复制所有五个提交,从而为我们提供: E
/
A--B--C
\
D
实际上,我们确实需要两个名称来查找所有提交。但是我们不需要分支名称。另一个与其他存储库一起使用的Git具有分支名称,因为这些是他的分支,在进行新的提交时,他将四处移动。因此,我们的Git要做的是复制它们的名称,但更改它们。我们让Git取其分支名称,并通过在名称中添加一些内容(通常是origin/
)来创建我们的远程跟踪名称。5因此,我们得到: E <-- origin/main
/
A--B--C
\
D <-- origin/dev
Git将拒绝将特殊名称HEAD
附加到这些远程跟踪名称之一中。 HEAD
仅允许附加到分支名称。因此,我们的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
现在我们有两个分支名称。但是我们最初不是。请注意,我们现在还 checkout 了D
提交,而不是E
,因为git switch
(或git checkout
,如果使用的话)不仅会切换分支,还会选择分支名称标识的提交作为要提交的提交。已 checkout ,因此可供我们使用。5从技术上讲,远程跟踪名称位于单独的namespace中。我们的Git不仅在前面添加了
origin/
,还用refs/heads/
替换了refs/remotes/origin/
。名称origin
实际上是一个远程对象,我们在Git存储库中可以有任意多个远程对象。但这是另一个问题的话题。
关于git - Git分支未显示所有分支,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64838274/