如何将远程仓库分支添加到本地仓库

How to get remote repo branch added to local repo

newly created - by creating a folder & running the command git init in short creating a local git repo from the top.

我这里有一个本地 git 存储库(新创建的),有 2 个分支 。现在这些分支只是我创建的虚拟分支,没有什么重要的。

$ git branch
* repo2-branch1
  repo2-branch2

我这里还有一个来自 Github 的远程存储库 (private) 和一个分支 "TLA1",现在记住我上面提到的新创建的本地存储库和这两个分支?我想做的是 ADD 这个 "TLA1" 分支作为 repo2-branch1 & repo2-branch2 的分支之一正如我提到的,在我新创建的本地存储库中。

假设已添加 "TLA1" 分支。所以当我输入 git branch 时,我希望它是这样的。

$ git branch
* repo2-branch1
  repo2-branch2
  TLA1

当然,当我输入 git log 时,当我切换到 "TLA1" 时,我也会有远程存储库中的提交,如您在图片,因为对我来说这些提交非常重要。

我尝试过的解决方案:

我做了很多研究,发现 this,我认为这已经是它了,因为它与我的目标相似。但是当我尝试它时出现错误。

$ 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>,而且这似乎不是我正在寻找的解决方案。

有什么解决办法吗?我真的很想在我新创建的存储库中有这个分支。

您需要告诉 Git 先跟踪远程存储库。所以你必须做

git remote add origin <url>

接下来你可以告诉git只从远程获取那个分支

git fetch origin TLA1

那你就可以切换到分支了

git checkout TLA1

将遥控器添加到您的本地存储库,然后从中获取。

git remote add REMOTE_NAME REMOTE_URL
git fetch REMOTE_NAME
git checkout -b branch_name REMOTE_NAME/branch_name

最后一个命令从远程引用创建一个本地分支。

然后 git 分支应该显示:

git branch

LOCAL1
LOCAL2
branch_name

TL;DR

您需要 运行 git fetch origin 才能 运行 git checkout TLA1.

你走在正确的轨道上,但还有很多东西要知道——人们捡到的很多错误的东西你应该小心。

要忘记的事情

在熟练使用Git之前,您需要un-学习一些东西。这些虚假声明如下:

  • “分支很重要”:这没有错,但也不对。第一个问题是单词 branch,它在 Git 中根本就是模棱两可的。如果我们坚持使用 two-word 短语 分支名称 ,我们会得到更有用的东西。分支名称很重要,但仅对 人类 而言。 Git 使用它们来帮助我们找到 提交; 真正重要的是提交。

  • “远程分支”:这个 two-word 短语,如果有的话,比“分支”这个词本身更糟糕。人们用它来表示至少三种不同的事物。让我们也避免这个短语。 Git 文档使用术语 remote-tracking branchremote-tracking branch name,它们是列出的名称通过 git branch -r。这个短语还不错,但是里面的branch这个词毫无意义。我们就称它为 remote-tracking 名称.

需要学习的东西

Git 中重要的是 提交 。了解有关提交的这些事情:

  • 每一个都有一个唯一的哈希ID。某个提交的哈希 ID 表示 that 提交。没有其他提交——任何地方,在 any Git 存储库中——将具有 that 哈希 ID。 That 提交——任何地方,在 any Git 存储库中——将具有 that 哈希 ID。

  • 提交是在不同的 Git 克隆之间共享的内容。分支名称不是 共享的 。你的 Git 克隆有 你的 分支名称,而其他一些克隆有 its 分支名称。您可能 想要 使用相同的名称,以保持直截了当,但这取决于您(尽管 Git 会在这里提供帮助,因为这是很常见的事情)。

  • 每个提交由两部分组成:

    • 提交的主要数据是所有文件的快照。这些文件将永远冻结:它们以压缩的 read-only、Git-only 和 de-duplicated 形式存储。 de-duplication 处理这样一个事实,即大多数时候,大多数新提交大多包含与上一次提交相同的 文件。存储在提交中的文件被冻结,甚至不能被非 Git 的程序 读取 (更不用说写入)这一事实当然是一个问题.

    • 提交的另一部分是它的元数据。这包括做出提交的人的姓名、他们的电子邮件地址以及 他们做出提交时的 date-and-time-stamp。所有这些东西也是 read-only。对于 Git 本身至关重要,Git 向此元数据添加一些 previous 提交或提交的哈希 ID。我们称这些为提交的 parents

提交表单链;分支名称帮助我们(和Git)找到提交

鉴于我们有一些简单的提交序列:

... <-F <-G <-H

这里的每个字母代表一个实际的 Git 哈希 ID,我们最终得到一个线性提交链, 结束 与提交 H .如果我们知道 H 的实际哈希 ID,我们可以让 Git 提取此提交(见下文)。或者,我们可以让 Git 读取 H 的元数据并向我们展示提交者是谁……或者使用它来查找 H 的 [=447= 的实际哈希 ID ] 提交 G.

因为GH 都有快照,我们可以Git 比较这两个快照。所有匹配的文件都是无趣的,因为它们匹配。 匹配的任何文件都更有趣,我们可以让Git弄清楚它们有什么不同并向我们展示区别。这样,我们就可以看到我们改变了什么。 Git 不会 存储 更改:它只存储快照。但是我们可以看到一个快照 as 变化,因为一个提交有一个 parent.

我们也可以让 Git 返回到 G,并使用它来查找 F,从而将 G 视为更改。从那里,我们可以回到 F,并使用它来查找更早的提交,等等。但是要做到这一切,我们需要链中 last 提交的实际哈希 ID。这就是分支名称的来源:像 repo-branch1 这样的分支名称只是存储一些哈希 ID。根据定义,名称中存储的哈希 ID 是分支中的 last 提交。 Git 会在那里开始并向后工作。在之后是否有以后的提交并不重要:

...--E--F   <-- br1
         \
          G--H   <-- br2

此处 Hlast 提交(例如在 FG 之后)在 br2 中,而提交Fbr1 中的 最后 提交。通过 F 的提交在 两个分支 中,但是 br1 starts-or-ends(取决于你如何看待它)在 F 和向后工作,而 br2 结束于 H 并向后工作。

提取的提交

因为提交是 read-only,我们实际上不能直接处理或使用它们。我们必须选择一些提交并将其设为 当前提交 。当我们这样做时,Git 提取 所有提交到工作区的文件,Git 调用 工作树 work-tree。这些是您可以查看和使用的文件。它们是普通的日常计算机文件,计算机上的每个程序都可以使用。但它们实际上并不是 in Git.

我们运行:

git checkout br2

(或 Git 2.23 或更高版本中的 git switch br2)。 Git 使用名称 br2 来查找该分支的最后一次(或 提示)提交(注意歧义词,在这种情况下意味着一些提交结束于H)。 Git 然后从该提交中提取文件,以便我们可以查看和使用它们,并使该提交成为 当前提交 同时使该分支名称成为 当前分支。我喜欢这样画:

...--E--F   <-- br1
         \
          G--H   <-- br2 (HEAD)

特殊名称 HEAD 附加到 分支名称。这就是“在分支上”的意思:名称 HEAD 定位分支名称 br2。分支名称本身定位提交 H,这是 Git 提取的提交。

如果我们进行 new 提交,Git 将打包快照,添加元数据,设置 parent 的新提交成为 current 提交 H,并使用所有这些来写出新提交。这为提交分配了新的、丑陋的 random-looking——但实际上根本不是随机的——哈希 ID,我将其称为 I。由于 I 的 parent 是 H,因此 I 指向 H。然后 Git 简单地将 I 的哈希 ID 写入当前 name, br2, 给出:

...--E--F   <-- br1
         \
          G--H--I   <-- br2 (HEAD)

因此 分支名称 的特殊功能是它 在我们创建时自动移动以指向新的提交 。 Git 通过将名称 HEAD 附加到分支名称来完成此操作。

Git 有其他名称——例如标签名称和 remote-tracking 名称—— 指向提交(通过存储提交哈希 ID),但是您不能将 HEAD 附加到它们。

Remote-tracking 姓名和 git fetch

Remote-tracking 名称的形式类似于 origin/TLA1:它们以 远程名称 开头,例如 origin。远程名称就是你使用git remote add时使用的名称; origin 只是第一个 标准 。如果您使用 git clone 到 运行 git initgit remote add 等等,git clone 将使用 origin 作为标准的第一个远程名称。 注意:您没有使用 git clone,所以当您 运行 git remote add.

时,名称将由您决定

如上所述,您不能将 HEAD 附加到 remote-tracking 名称。此外,您通常不会自己创建这些名称。您可以使用git branch -r列出您现在拥有的那些,但是如果不创建它们,您如何获得它们?

最后一个问题的答案是 git fetch 命令创建了它们。 fetch 命令非常复杂(原因有好有坏),我绝对不会在这里介绍太多,但我们可以相对简单地描述它,如下所示:git fetch 有你的 Git 调用上传一些其他 Git 并从中获取内容:

  • 首先,您的 Git 让他们的 Git 列出所有分支名称、标签名称和其他类似名称。它们带有散列 ID——主要是提交散列 ID,尽管标签名称有时会变得有点复杂。

  • 然后您的 Git 会选择这些名称和哈希 ID。你 Git 可以判断你是否有提交,因为每个 Git 使用 相同的 random-looking-but-not-random hash IDs 相同提交。所以你的 Git 立即知道你是否有 他们的 分支的提示提交。

    如果您不这样做,您的 Git 会要求他们的 Git 进行提交。他们也提供提交的 parents,并且您的 Git 检查您是否有 那些 提交。通过这种 have/want 序列(通过一些重要的优化,避免每次都必须列出每个哈希 ID),您的 Git 计算出他们有什么提交,你没有,你会需要,并要求他们。

  • 他们将所有这些提交打包并发送给您。这里的细节可以很多,但在通常情况下你会看到“计数”和“压缩”等等,然后你的 Git 收到一个充满提交和其他内部 Git objects 的包。然后,您的 Git 会将其全部保存在您的存储库中。

    您现在拥有您之前拥有的所有提交,加上他们拥有但您没有拥有的任何提交(除非您的 Git 不想要它们,例如,single-branch 克隆)。

  • 最后,您的 Git 现在 创建或更新您的 remote-tracking 名称 。对于他们拥有的每个 分支 名称,您的 Git 会为您的存储库创建或更新相应的 remote-tracking 名称。

这意味着您永远无法直接获得它们的分支名称。您获得他们的分支名称,然后 将它们 更改为您的 remote-tracking 名称。这作为您的 Git 对它们分支名称的 记忆 。这些由 git fetch. 创建或更新,直到你 运行 git fetch,你才会 拥有 origin/TLA1.

结论

重要的是提交。分支名称和其他名称可以帮助您(和 Git)找到 提交。

您的 remote-tracking 名字来自 运行宁 git fetch。您告诉 git fetch 调用哪个 远程 。您的 Git 调出那个遥控器并查看其分支并获取其提交,除非您已经拥有它们。然后您的 Git 根据需要更新或创建 remote-tracking 名称。 (旁注:你的 Git 不会 删除 一个“死”的名字,除非你告诉它,所以一旦他们删除了一些分支名称,你就会留下陈旧的 remote-tracking 个名字。)

您可以随时创建自己的分支名称,但要创建名称,您必须提交 才能point-to。所以你通常会想先得到他们最新的:git fetch然后第二个Git命令。

旁白:git pull 表示 运行 git fetch,然后 运行 第二个 Git 命令 。因为它需要两个命令来做有用的事情,所以人们喜欢 git pull,运行 是两个命令。我不喜欢 git pull,因为我喜欢在这两个 之间插入命令 ,并且可能使用 git pull 为第二个命令提供的相对较少的选项集以外的其他选项, 但这取决于你。