如何将远程仓库分支添加到本地仓库
新创建 - 通过创建一个文件夹并运行命令
git init
,从顶部创建一个本地 git repo。
我在这里有一个带有2 个分支的本地 git 存储库(新创建的)。现在这些分支只是我创建的虚拟分支,对它来说没什么重要的。
$ git branch
* repo2-branch1
repo2-branch2
我这里还有一个来自 Github的远程存储库(私有),带有一个分支“TLA1”,现在还记得我上面提到的带有这两个分支的新创建的本地存储库吗?我想要做的是加入这个“TLA1”分支与分支之一repo2-branch1-repo2-branch2在我的新创建的本地存储库正如我所提到。
假设已添加“TLA1”分支。所以当我打字时,git branch我希望它像这样。
$ git branch
* repo2-branch1
repo2-branch2
TLA1
当然,当我git log在切换到“TLA1”时输入时,我也会有远程存储库中的提交,如您在图像中看到的那样,因为对我来说这些提交非常重要。
我尝试过的解决方案:
我做了很多研究并发现了这一点,我认为这已经是它了,因为它与我的目标相似。但是当我尝试它时,我得到一个错误。
$ git checkout -b TLA1 origin/TLA1
fatal: 'origin/TLA1' is not a commit and a branch 'TLA1' cannot be created from it
我也没有尝试过这个,因为这件事可能会对我的远程仓库做些什么,git reset --hard <remote>/<branch_name>这似乎不是我找到的解决方案。
有什么解决办法吗?我真的很想在我新创建的存储库上有这个分支。
回答
TL; 博士
您需要先运行,git fetch origin然后才能运行git checkout TLA1。
长
你走在正确的轨道上,但有很多东西需要知道——人们发现了很多错误的东西,你应该小心。
需要忘记的事情
在你能熟练地使用 Git 之前,你需要un- learn 一些东西。这些虚假声明如下:
-
“分支很重要”:这没有错,但这也不对。第一个问题是分支这个词,它在 Git 中从根本上是模棱两可的。如果我们坚持使用两个词短语branch name,我们会得到更有用的东西。分支名称很重要,但仅限于人类。Git 使用它们来帮助我们找到提交;真正重要的是提交。
-
“远程分支”:这个两个词的短语,如果有的话,比“分支”这个词本身更糟糕。人们用它来表示至少三个不同的东西。让我们也避免使用这个短语。Git 文档使用术语remote-tracking branch或remote-tracking branch name,它们是由 列出的名称
git branch -r。这句话并没有那么糟糕,但其中的分支这个词毫无意义。让我们称之为远程跟踪名称。
要学习的东西
Git 中重要的是commits。了解这些关于提交的事情:
-
每个都有一个唯一的哈希 ID。某些提交的哈希 ID 表示该提交。没有其他提交——在任何地方,在任何Git 存储库中——将具有该哈希 ID。 该提交——在任何Git 存储库中的任何地方——都将具有该哈希 ID。
-
提交是在不同的 Git 克隆之间共享的内容。分支名称不共享。您的 Git 克隆具有您的分支名称,而其他一些克隆具有其分支名称。您可能想使用相同的名称,以保持简洁,但这取决于您(尽管 Git 会在这里提供帮助,因为这是很常见的事情)。
-
每个提交由两部分组成:
-
提交的主要数据是所有文件的快照。这些文件始终处于冻结状态:它们以压缩、只读、仅 Git 和重复数据删除的形式存储。重复数据删除处理这样一个事实,即大多数情况下,大多数新提交都包含与前一次提交相同的文件。存储在提交中的文件被冻结并且甚至不能被非 Git 程序读取(更不用说写入)的事实是一个问题,当然。
-
提交的另一部分是它的元数据。这包括诸如进行提交的人的姓名、他们的电子邮件地址以及他们进行提交时的日期和时间戳等信息。所有这些东西也是只读的。对 Git 本身至关重要的是,Git 将一些先前提交或提交的哈希 ID 添加到此元数据中。我们称这些为提交的父项。
-
提交形式链;分支名称帮助我们(和 Git)找到提交
鉴于我们有一些简单的提交序列:
... <-F <-G <-H
这里的每个字母代表一个实际的 Git 哈希 ID,我们最终得到一个以 commit结尾的线性提交链H。如果我们知道H的实际哈希 ID,我们可以让 Git 提取此提交(见下文)。或者,我们可以让 Git 读取H的元数据并向我们展示谁进行了提交……或者使用它来查找H的父提交的实际哈希 ID G。
由于G和H都持有快照,我们可以让 Git 比较这两个快照。所有匹配的文件都无趣,因为它们匹配。任何不匹配的文件都更有趣,我们可以让 Git 找出其中的不同之处并向我们展示不同之处。这样,我们就可以看到我们改变了什么。Git 不存储更改:它只存储快照。但是我们可以将快照视为更改,因为提交具有父项。
我们也可以让 Git 返回到G并使用它来查找F,从而查看G更改。从那里,我们可以返回到F,并使用它来查找更早的提交,依此类推。但要做到这一切,我们需要链中最后一次提交的实际哈希 ID 。这就是分支名称的用武之地:分支名称就像repo-branch1只是存储一些哈希 ID。根据定义,名称中存储的哈希 ID 是分支中的最后一次提交。Git 将从那里开始并反向工作。在此之后是否有后续提交也无关紧要:
...--E--F <-- br1
G--H <-- br2
这里H是最后的提交(后F并G为实例)的br2,而提交F的是最后的提交br1。提交通过F在两个分支中,但br1开始或结束(取决于你如何看待它)在F和向后工作,而在br2结束H和向后工作。
提取的提交
因为提交是只读的,我们实际上不能直接处理或使用它们。我们必须选择一些提交并使其成为当前提交。当我们这样做时,Git将与该提交相关的所有文件提取到一个工作区中,Git 将其称为工作树或工作树。这些是您可以查看和使用的文件。它们是普通的日常计算机文件,您计算机上的每个程序都可以使用。但是,他们没有真正的Git的。
我们跑:
git checkout br2
(或git switch br2在 Git 2.23 或更高版本中)。Git 使用名称br2来查找该分支的最后一次(或tip)提交(注意歧义词,在这种情况下意味着一些以 结尾的提交H)。Git 然后从该提交中提取文件,以便我们可以查看和使用它们,并使该提交成为当前提交,同时将该分支命名为当前分支。我喜欢这样画:
...--E--F <-- br1
G--H <-- br2 (HEAD)
特殊的名字HEAD被连接到一个分支的名字。这就是“在分支上”的意思:名称HEAD定位分支名称br2。分支名称本身定位提交,H,这是 Git 提取的。
如果我们进行新的提交,Git 将打包快照,添加元数据,将新提交的父项设置为当前提交H,并使用所有这些来写出新提交。这为提交分配了新的、丑陋的、随机的——但实际上根本不是随机的——哈希 ID,我将称之为I. 由于I的父是H,I指向H. 然后 Git 简单地将I的哈希 ID 写入当前名称, br2,给出:
...--E--F <-- br1
G--H--I <-- br2 (HEAD)
因此,分支名称的特殊之处在于,当我们创建它时,它会自动移动以指向新的提交。Git 通过将名称附加HEAD到分支名称来完成此操作。
Git 有其他名称——例如标签名称和远程跟踪名称——也指向提交(通过存储提交哈希 ID),但你不能附加HEAD到它们。
远程跟踪名称和 git fetch
远程跟踪名称的形式如下origin/TLA1:它们以远程名称开头,例如origin. 远程名称是您在使用时使用的名称git remote add;origin只是第一个标准的。如果您使用git clone为您运行git init等git remote add,git clone将origin用作标准的第一个远程名称。 注意:您没有使用,git clone所以当您运行git remote add.
如上所述,您不能附加HEAD到远程跟踪名称。此外,您通常不会自己创建这些名称。您可以使用git branch -r列出您现在拥有的那些,但是如果您不创建它们,您如何获得它们?
最后一个问题的答案是git fetch命令创建它们。fetch 命令非常复杂(有好有坏),我绝对不会在这里介绍太多,但我们可以相对简单地描述它:git fetch让您的 Git 调用其他一些 Git 并从中获取内容:
-
首先,你的 Git 有他们的 Git 列出他们所有的分支名称、标签名称和其他类似的名称。这些带有散列 ID——主要是提交散列 ID,尽管标签名称有时会变得更复杂一些。
-
然后您的 Git 选择这些名称和哈希 ID。你的 Git 可以判断你是否有提交,因为每个 Git 对相同的提交使用相同的随机但不是随机的哈希 ID。所以你的 Git 立即知道你是否有他们分支的提示提交。
如果你不这样做,你的 Git 会向他们的 Git 询问他们的提交。它们也提供提交的父项,并且您的 Git 会检查您是否有这些提交。通过这种拥有/想要序列(有一些重要的优化可以避免每次都列出每个哈希 ID),您的 Git 会弄清楚他们有哪些提交,哪些没有,哪些是您需要的,并要求提交.
-
他们将所有这些提交打包并发送给您。这里的细节可能会有很大差异,但在通常情况下,您会看到“计数”和“压缩”等,然后您的 Git 会收到一个包含提交和其他内部 Git 对象的包。然后您的 Git 将所有这些都保存在您的存储库中。
您现在拥有之前的所有提交,以及他们拥有但您没有的任何提交(除非您的 Git 不想要它们,例如,单分支克隆)。
-
最后,您的 Git 现在创建或更新您的远程跟踪名称。对于他们拥有的每个分支名称,您的 Git 都会为您的存储库创建或更新相应的远程跟踪名称。
这意味着您永远不会直接获得他们的分支名称。您获取他们的分支名称并将它们更改为您的远程跟踪名称。这充当您的 Git对其分支名称的记忆。这些是由git fetch. 在你运行之前git fetch,你不会有一个origin/TLA1.
结论
重要的是提交。分支名称和其他名称可帮助您(和 Git)找到提交。
您可以通过运行获得远程跟踪名称git fetch。你告诉git fetch哪个遥控器要调用。您的 Git 调用该遥控器并查看其分支并获取其提交,除非您已经拥有它们。然后您的 Git 会根据需要更新或创建远程跟踪名称。(旁注:除非您告诉它,否则您的 Git 不会在此处删除“死”名称,因此一旦他们删除了某个分支名称,您将留下陈旧的远程跟踪名称。)
您可以随时创建自己的分支名称,但要创建名称,您必须有一个指向它的提交。因此,您通常希望首先获得他们最新的:git fetch,然后是第二个 Git 命令。
旁白:git pull表示运行git fetch,然后运行第二个 Git 命令。由于需要这两个命令才能执行有用的操作,因此人们喜欢git pull运行这两个命令的 。我不喜欢,git pull因为我喜欢在这两个命令之间插入命令,并且可能使用除git pull为第二个命令提供的相对较薄的一组选择之外的其他命令,但这取决于您。