当我们 'fetch upstream' 分叉存储库时会发生什么?

What happens when we 'fetch upstream' a forked repository?

我正在学习 GitHub 并发现 downstram/upstream 分叉存储库的概念。我也经历了各种 documentations/blogs 但无法弄清楚当我们按下 'fetch upstream'?

时实际发生了什么

git fetch upstream 从远程存储库 upstream 获取(“下载”)所有更改,并使用 upstream 在本地存储它们。然后,您可以使用此前缀引用这些本地副本(例如,检查它们、设置跟踪分支、cherry pick 提交等)。例如,git checkout upstream/some-branch 将检出您刚刚从 upstream.

获取的 some-branch 的本地副本

要正确理解这一点,您需要了解以下有关 Git 的信息:

  • Git 就是关于 提交 。这与文件无关(尽管提交包含文件)。这与分支无关(尽管分支名称对我们有帮助,并且 Git 可以找到提交)。这真的是关于提交
  • 提交已编号。这些数字是巨大的、丑陋的、看起来随机的东西,用 hexadecimal; each commit gets a unique number, different from every other commit in every Git repository everywhere. If two different Git repositories have the same commit number in them, they have the same commit in them: the number is the commit, in a sense (though you have to have the commit itself: the number is just the key, in the key-value database 表示,Git 用来 查找 ,即查找,提交)。
  • 除了branch名字如mainmasterdevfeature/tall等,Git 还有其他名称:标签名称如 v3.14,以及称为 remote-tracking names 的东西(Git 实际上称这些 remote-tracking 分支名称,但我发现如果在这里省略不必要的单词 branch 会更有意义)。每个名称都可以存储一 (1) 个哈希 ID。这就是我们所需要的,因为 commits also store hash IDs.

当我们克隆一个 Git 存储库时,我们得到 所有其他存储库的提交none 它们的分支.1 我们的 Git 不是分支名称,而是采用其他克隆的分支名称并将它们变成我们的 远程跟踪名称.如果我们调用另一个 Git——我们现在正在克隆的那个——origin,这是标准的第一个 remote 名称,它们的 main 轮流进入我们的 origin/main、他们的 dev turns into our origin/dev` 等等。

这意味着我们的分支名称是我们的。我们不必使用与其他 Git 存储库相同的名称。我们通常这样做,只是为了理智,但我们 不必 .

这也间接地告诉我们,什么“远程”是:远程是一个短名称,它存储URL——我们从中克隆的 URL,对于 origin——并且还为远程跟踪名称提供前缀。 origin/dev中的origin来自远程名称origin.2

当你 运行:

git fetch origin

你的 Git 软件,在你的存储库中工作,在某处调用一些其他 Git 软件 - 在 URL 存储在名称 origin 下 - 并拥有它使用 URL 连接到其他存储库。其他软件(可以说是“其他 Git”)读出他们的提交——特别是哈希 ID——和分支名称,并将它们发送到“我们的 Git”(我们的软件在我们的存储库中运行).我们的 Git 和他们的 Git 有一个涉及哈希 ID 的小型对话,这样我们的 Git 可以看到他们有什么提交,而我们没有。

然后我们的 Git 将带来他们拥有的任何(对我们来说是新的)提交,而我们没有。这包括我们手动从 Git 存储库中小心丢弃的任何提交,因为我们发现它们在某些方面很糟糕:3 所以在这方面,它就像 Git-与可能携带某种病毒的 Git 发生性关系,我们将不断被重新感染,直到他们 放弃那个糟糕的承诺。但大多数情况下这很好,因为大多数情况下我们 想要他们拥有的每个提交,而我们没有。

但是:upstream 呢?好吧,这个词 upstream 有一个小问题,因为 Git 使用这个 同一个词 来表达其他意思。4 但在这种情况下,upstream 是 GitHub 的名称,特别鼓励人们在他们的 第二个遥控器 中使用 [=] =222=] 存储库。我们可以拥有多个遥控器!

使用 git 远程添加上游 <em>url</em>,我们创建一个 second 远程名为 upstream。之后:

git fetch upstream

使用保存的URL调用其他Git,就像git fetch origin一样。无论托管站点是 GitHub 还是其他站点,我们的 Git 和他们的 Git 都会进行与以前相同的对话。我们的 Git 将找出他们有哪些我们没有的提交,将这些提交下载到我们的 Git 存储库中,并创建或更新远程跟踪名称,如 upstream/mainupstream/dev.我们将在 URL 中的另一个 git 中为每个分支名称获取一个 upstream/* 名称,存储在名称 upstream.

这就是它的全部内容。不过,有一个特别的地方会让人们在这里绊倒。假设您 git clone 一个存储库,那么您现在有 origin/mainorigin/feature/tall。但是 origin 存储库是从其他存储库派生出来的,所以你使用 git remote add 添加你的 fork2upstream 或任何你想调用它的东西,然后你 运行:

git fetch fork2

或者随便你怎么称呼它。您现在有 fork2/mainfork2/feature/tall。所以你有 origin/feature/tallfork2/feature/tall.

你还没有制作自己的feature/tall.你运行:

git switch feature/tall

或:

git checkout feature/tall

期待你的 Git 从...创建你的 feature/tall 好吧,等等:你期待你的新分支名称 feature/tall 到 spring 来自 origin/feature/tall 并将其用作上游设置?或者您是否期待您的新分支名称 feature/tallfork2/feature/tall 到 spring 并使用 that 作为其上游?或者您可能需要 两个 feature/tall 分支,一个与 origin/feature/tall 一起使用,一个与 fork2/feature/tall.

一起使用

你不能同时调用 feature/tall. 这意味着如果你 do 想要两个分支名称,一个用于每个远程跟踪名称,您将被迫打破通常的“我的名称 = 我的远程跟踪名称,减去远程”设置。 最重要的是,一旦您拥有两个或更多遥控器,您的Git生活就会变得更加复杂。没有办法解决这个问题:您必须 了解遥控器和远程跟踪名称的含义和用途。


1您可以在 git clone 时稍微修改此行为,并且通常有垃圾 and/or 存储库中丢弃的提交被维护清理命令稍后并且 git clone 通常不会复制那些。所以这只是一个近似值,有助于理解事物。

2与 Git 一样,git fetch origin 导致他们的 dev 变成 origin/dev 的过程不是很简单。你可以用它做各种疯狂的事情。不过,为了理智,在任何普通用户克隆中做任何奇怪和疯狂的事情都是不明智的:让他们的 dev 成为你的 origin/dev.

3也许,例如,我们小心地丢弃了一个意外的提交,该提交添加了一个正在阻塞磁盘的 TB 数据库。哎呀,又来了!

4特别是,Git 允许每个 分支名称 存储单个 上游名称。通常我们会将分支 br1 的上游设置为 origin/br1origin 处的远程跟踪名称对应 他们的 分支名称 br1。这样 我们的 分支名称 br1 可以很容易地引用我们的 origin/br1,这是我们对 [=117 的 Git 的记忆的副本=]他们的分行名称br1.

这与名为 upstream 远程 完全不同。如果 GitHub 鼓励人们使用 fork2 或类似名称作为第二个远程名称,那可能会有所帮助。