git fetch是否拾取已删除的分支



用例:

用户A正在处理从master派生的分支A;用户B创建了从master分支出来的分支B,做了一些工作并提交然后删除了分支B。用户A能看到用户B的工作吗(例如git fetch --all)。用户A从未在分支机构B上工作过。

在这种情况下,用户A没有显示分支B。

简短的回答是"否";。

穆的答案很长:除非人们做出一些跳跃式的解释(我认为大多数人都会这样做),否则所问的问题实际上毫无意义。原因是分支无关紧要;你不会(完全)提取分支。重要的是提交,因此正确的问题是git fetch是否会获取这些的提交的答案通常是"否";😀).

我想你在这里也有一个错误的想法:

例如git fetch --all

git fetch--all选项意味着所有远程,而不是所有分支。

这个答案的其余部分是可选的,但我建议它值得一读:当答案变成"是的";。

Git的工作原理

我们从以下内容开始:

  • Git存储库的核心是一对数据库:

    • 一个数据库包含提交和其他Git内部对象。这些或多或少地存储了所有时间内每个文件的每个版本。

    • 但是提交(和其他对象)是用巨大的、对人类无用的、看似随机的数字("哈希ID"或"对象ID")进行编号的。为了使这些东西对人类可用,Git存储库中的其他数据库从名称转换为内部数字。

  • Git存储库中的名称包括分支名称,但这些并不是唯一的名称。还有标签名称,被称为远程跟踪名称跟踪分支名称

  • 克隆Git存储库的行为意味着获得所有提交,而没有分支。(这可以通过各种选项进行修改,并且不会捕获所有细节,但这是查看克隆的正确起点。)Git不需要分支。它只需要提交和一些名称来查找它们

当我们在本地工作时,在我们从零开始克隆或构建的Git存储库中,实际上我们确实使用分支名称来完成工作。但是这些分支名称是在我们的存储库中创建的。它们不在任何其他存储库中!然而,因为人类就是人类,我们倾向于在两个不同的克隆中使用相同的名称:

  • Bob有一个存储库。在Bob的存储库中,Bob创建了名为alphabeta的分支。

  • 我克隆了鲍勃的存储库。我不知道他的分支名称:我创建我自己的分支名称。但是,因为我打算与Bob一起使用,所以我也将称为我的分支alphabeta

这些是";相同名称";,并且最初它们也可以持有相同的提交ID号。但我的名字是我的,鲍勃的名字是鲍勃的。只有当我们同步它们时,它们才会相遇。

当我第一次克隆Bob的存储库时,我从他那里得到了他所有的提交,没有任何分支:我根本没有分支。但我的Git确实记得他的分支名称。我的Git将这些名称粘贴到我的存储库中,属于远程跟踪名称的一般类别。也就是说,我得到的不是alpha,而是bob/alpha。我得到的不是beta,而是bob/beta。这些是我的Git对Bob分支名称的记忆。

现在,由于我打算使用Bob最近发布的相同提交,我从这两个名称中选择一个,并让我的Git创建,对我来说,是一个同名的分支我现在有一个alphabeta(但不是两者都有)。由于任何名称都包含一个内部Git对象ID,所以我的alphabeta(无论我选择创建哪个)都包含与bob/alphabob/beta相同的提交哈希ID。这是我从Bob那里得到的哈希ID,当时我从Bob那里得到了所有提交,并将Bob的分支名称变成了我的远程跟踪的名称。

git fetch的工作原理

随着时间的推移,Bob可能进行了新的提交,也可能没有进行新的提交。在某个时候,我决定我应该拥有我的Git,使用我的克隆,它有branches(当然还有所有提交,还有我的远程跟踪名称),再次调用Bob的Git并让Bob的Git连接到Bob的存储库。

在这一点上,鲍勃有他所有的分支。他的Git(他的软件,在他的存储库上运行)列出了这些分支名称到我的Git(我的软件,在我的存储库中运行)。它们附带了提交散列ID:提交对象的那些丑陋的随机数字。

我的Git会检查我是否有这些提交。如果我做到了,那太好了!如果没有,我的Git会向Bob的Git询问这些提交,这会导致整个对话运行,这样我的Gits就可以找到Bob的所有新提交,而我没有。我的Git下载所有这些提交,现在我有了Bob的所有提交,就像我第一次克隆时一样。最后,现在我的分支上有了Bob的所有提交——也许还有我自己的提交——我的Git更新了我的远程跟踪名称,以记住Bob的分支名称和提交。

请注意,这对我的任何分支都没有影响但是,我确实会更新我的远程跟踪名称——如果Bob创建了一个新的分支名称,并且我的Git在git fetch期间看到了它,我的Git将创建一个新的远程跟踪名称。如果我设置了fetch.prune或使用-p,Bob删除了他的一些分支名称,我的Git也会也删除相应的远程跟踪名称。所以git fetch对我来说更新了我调用的Git的远程跟踪名称。

这里的关键问题是:我调用了什么Git,该Git有什么名称和提交我在这里说,我调用了Bob的Git,其中有Bob的分支名称和Bob的所有提交,所以我们可以回答这些问题,看看我现在有什么远程跟踪名称,以及这些名称包含什么对象哈希ID。

介绍";叉子";和/或";中央存储库">

在上面的文章中,我一直在直接使用Bob的电脑。当我运行git fetch时,我可以通过ssh访问Bob的计算机,以某种方式登录到它,这样我就可以在那里运行Git命令。这在一些Linux服务器类型的环境中是可以的,比如公司的Git设置。但许多地方不想这样工作,和/或希望有一个单一的";真理之源";集中式存储库,无论是托管在公司还是GitHub或其他什么平台上。

所以现在我不能访问Bob的计算机上的Bob的存储库。相反,某个地方有一个集中式回购,至少在最初只有一个分支,名为master。Bob将克隆该集中式repo并获得origin/master,并使用它在Bob的Git中创建master。然后Bob使用他的master创建一个新的分支名称alpha

当我连接到中央存储库时,我的Git使成为我的克隆,它有所有提交,没有分支名称,只有一个远程跟踪名称origin/master。我(或者我的Git)使用我的origin/master创建一个名为master的分支,然后用它创建我的分支名称beta

当我运行git fetch时,我的Git会转到origin。Bob还没有告诉origin上的Git创建任何新分支名称。因此,我根本看不到任何Bob的分支名称,因为我从不直接与Bob的Git对话,也不会看到Bob的任何分支名称复制到origin,因为他还没有这样做。

当Bob最终运行git push时,他会:

git push -u origin alpha

这使得他的Git在origin调用Git,并向它——向originGit——提供Bob在alpha上的任何origin还没有的提交1他们进行这些提交,然后Bob要求原始Git在原始Git上创建一个新分支名称alpha。如果原始Git服从这个请求——这取决于原始Git和任何可能已经安装和调整的控制旋钮(基本Git在这里没有太多,但大多数托管网站都有)——那么现在原始Git有一个名为alpha的分支。

我的Git在origin调用Git,现在可以看到alpha,并创建我的origin/alpha远程跟踪名称(在获得这五个或其他新的Git提交之后)。对我来说,这是一个远程跟踪名称,也是origin的分支名称,但我只能看到它,因为Bob说服origin创建它。

如果Bob决定制作一个GitHub风格的fork,他所做的就是制作另一个克隆,但这次是在GitHub上托管的。Bob的克隆是另一个独立的Git存储库,这个克隆有自己的分支名称。不过,这个克隆有一两个特别之处:当GitHub创建它时,GitHub会复制所有分支,所以最初该克隆与我将使用的origin克隆具有所有相同的分支。此外,当Bob在Bob的GitHub分叉上创建新的提交和分支名称时,Bob可以向originGit发出拉取请求。(这就是GitHub作为附加组件提供的所有内容,让你想使用GitHub,而不是进行自托管。)

在所有这些情况下,直到Bob以某种方式在originGit上产生一个新分支,我才能看到Bob的提交。我只能看到origin上的分支名称,这些名称将成为我的远程跟踪名称;只有当Bob以某种方式将提交给originGit,并在的originGit上命名后,我才能获得Bob的提交,这样我或我的Git就可以找到他们的提交哈希ID号。


1这个短语涵盖了这样一个事实,即master上的所有提交现在都在两个分支上。因此,origin上的Git在alpha上有大量的提交;只是Bob还有五次提交,或者不管Bob做了多少次。


遥控器

在上述过程中,我的Git总是有一个远程

当我使用直接进入Bob的计算机的例子时——这让我可以在任何时候看到Bob的所有分支——我为这个远程使用了名称bob,所以我的远程跟踪名称bob/alphabob/beta

当我使用GitHub作为示例时,我使用名称origin作为远程,因此我的远程跟踪名称为origin/master,最终(一旦Bob也在那里创建了alpha)为origin/alpha

远程主要是URL的短名称。我可能为Bob的计算机使用的URL可能是ssh://bob.company.com/path/to/repo.git。我可能用于GitHub的URL可能是ssh://git@github.com/company/repo.git

默认情况下,git clone命令将使新克隆的远程名称origin作为其(一个,单个)远程名称。此名称将存储您为git clone提供的URL,以便稍后git fetch origin将返回到同一URL并从中获取任何新的提交。

但是,您可以有多个遥控器。这里唯一的限制是每个都必须有一个唯一的名称。因此,如果我确实可以直接访问Bob的计算机,我可以将其添加到我的克隆中,其中origin指的是GitHub克隆。。。现在可以直接访问Bob的存储库,因此可以将Bob的分支作为我的bob/*远程跟踪名称。所以现在答案变为不,我看不到Bob的分支是的,我可以看到Bob的分支。我会有origin/master,还有bob/alpha(还有bob/master,除非他删除了他的名字master)。

现在我有了多个远程,运行git fetch --all就有了意义。以前,只有一个名为origin的遥控器,git fetch --all的意思是从所有遥控器中提取,这意味着从origin提取,这就是没有--allgit fetch的意思:只有一个遥控器,所以遥控器就是我们从中提取的遥控器。

不过,对于两个远程,不带附加限定符的git fetch意味着中提取一些的远程。哪一个?这里的git fetch文档不是一个清晰的模型,但目前的答案是:

  • 如果我在B分支上,并且B具有R的已配置远程,那就是git fetch使用的
  • 否则,git fetch返回到名称origin

(这可能有一天会改变。)

如果我给git fetch起一个像originbob这样的名字,那就是它将从中提取的那个远程,并且还有更多的选项,比如";远程组";当然还有CCD_ 93。使用--all指示git fetch所有遥控器上运行git fetch,每次一个2

因此:如果您定义了两个或多个遥控器,那么--all只有有用。如果设置了对Bob存储库的远程访问,则可以查看Bob的分支。当然,这需要您对Bob的机器或Bob在GitHub上的fork有访问权限


2理想情况下Git应该运行多个并行获取,但目前没有。


结论

最后,这里真正的关键是提交。我们通过提交的哈希ID来获得提交。我们通过名称来查找那些散列ID——分支名称、标记名称、远程跟踪名称,以及任何名称。git fetch命令可以连接到其他一些Git(软件+存储库)。默认情况下,它使用他们的分支名称(以及他们的标记名称,取决于--tags和其他获取选项)来查找要获取的提交,获取这些提交,然后在我们的repository中创建或更新名称,但使用标准设置,我们在存储库中为他们的分支名称获取的名称是我们的remote-tracing的名称。

我们只能看到的名字是他们提供给我们的,他们只能提供给我们他们拥有的名字。因此,如果";他们的Git";是某个地方的集中式存储库,Bob在Bob的克隆中创建分支并在那里进行提交,但从不将名称提交发送到集中式存储库中,集中式存储库从一开始就没有任何东西可以提供给我们。

我假设用户A和B在不同的计算机(A和B)上,主分支存储在服务器上。

第一

列出已知分支B的存储库列表。

  • 用户B在计算机B上使用的
  • 服务器上的那个。如果用户B在服务器上推送了分支B
  • 其他人?(用户B在备份存储库上推送了分支B)

请确保分支已从所有这些存储库中删除。如果没有,A可以从这里检索分支B(例如:服务器)。

最后

看看reflog,它提供了HEAD(本地)的最新历史,可以帮助用户B在删除后检索分支B。一些git服务器也有一些相同的功能(比如这里解释的github)。