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

Does git fetch pick up deleted branches?

用例:

用户 A 在从 master 分支出来的分支 A 上工作;用户 B 创建从 master 分支出来的分支 B 做一些工作并提交,然后删除分支 B。用户 A 可以看到用户 B 的工作(例如 git fetch --all)。用户 A 从未在分支 B 上工作过。

在这种情况下,分支 B 不会显示给用户 A。

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

第一个

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

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

第二个

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

终于

看看reflog, it provides the recent history of HEAD(local) and can help user B to retrieve branch B after delete. Some git servers also have some identical feature (like github as explained here).

简短的回答是“否”。

长答案是Mu:所问的问题实际上毫无意义,除非有人做出一些跳跃性的解释(我认为大多数人都会这样做)。原因是 分支无关紧要;你不(完全)获取分支。重要的是 commits,所以正确的问题是 git fetch 是否会获取这些 commits(以及 then 答案通常是“否”)。

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

e.g. git fetch --all

git fetch--all 选项表示 所有遥控器 ,而不是 所有分支

这个答案的其余部分是可选的,但我建议它值得一读:当答案变成“是”时你就会发现。

Git 的工作原理

我们从以下内容开始:

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

    • 一个数据库保存提交和其他Git内部对象。这些存储每个文件的每个版本,或多或少。

    • 但是提交(和其他对象)的编号很大,useless-to-humans,seemingly-random 数字(“哈希 ID”或“对象 ID”)。为了使这些东西可供人类使用并因此有用,Git 存储库中的 other 数据库将 names 转换为内部编号。

  • Git 存储库中的名称包括分支名称,但这些不是唯一的名称。还有标签名称,有趣的东西叫做remote-tracking名称remote-tracking分支名称,等等。人们(相当错误地)有时将 remote-tracking 名称称为“远程分支名称”,但这是非常误导的。

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

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

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

    的分支
  • 我克隆了 Bob 的存储库。我不知道他的分支名称:我创建了 我自己的 分支名称。但是因为我打算 with Bob,所以我也调用 my 分支 alphabeta

这些是“相同的名称”,最初它们也可能拥有相同的提交 ID 号。但是我的名字是我的,Bob 的名字是Bob 的。他们只有在我们同步他们时才会见面。

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

现在,由于我打算 on/with Bob 最近发布的同一个提交,我选择这两个名称之一并让我的 Git create,对我来说,同名分支 我现在有一个 alpha 或一个 beta(但不是两者)。由于任何名称都包含一个内部 Git 对象 ID,因此我的 alphabeta(无论我选择创建哪个)都包含 相同的 提交哈希ID 为我的 bob/alphabob/beta。这是我从 Bob 那里得到的哈希 ID,当我从 Bob 那里得到所有的提交,并将 Bob 的 branch 名字变成我的 remote-tracking个名字.

git fetch 的工作原理

随着时间的推移,Bob 可能会也可能不会进行新的提交。在某些时候,我决定我应该让我的 Git 与 my 克隆一起工作,它有 my 分支(当然还有所有提交,加上我的 remote-tracking 名称),再次调用 Bob 的 Git,并让 Bob 的 Git 连接到 Bob 的存储库。

此时,Bob 拥有他拥有的任何分支。他的 Git(他的软件,运行ning 在他的存储库中)将这些分支名称列出到我的 Git(我的软件,运行ning 在我的存储库中)。这些带有提交哈希 ID:提交对象的那些丑陋的大 random-looking 数字。

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

请注意,这对我的任何分支都没有影响。 但是,我会更新我的 remote-tracking 名称——如果 Bob 创建了一个 new 分支名称,而我的 Git 在这个 git fetch 期间看到了它,我的 Git 将创建一个 new remote-tracking 名称与之搭配。如果我设置 fetch.prune 或使用 -p,并且 Bob 删除了 他的一些分支名称,我的 Git 将 删除 对应的 remote-tracking 名称也是。所以 git fetch 更新,对我来说, remote-tracking 我打电话给 Git 的名字。

这里的关键问题是:我调用了什么Git,Git有什么名称和提交?我在这里说我调用了 Bob 的 Git,其中包含 Bob 的分支名称和 Bob 的所有提交,因此我们可以回答这些问题并查看我现在有哪些 remote-tracking 名称,以及这些名称包含哪些对象哈希 ID。

引入“分叉”and/or“中央存储库”

在上面,我一直直接使用Bob的电脑。当我 运行 git fetch 时,我获得 Bob 计算机的 ssh 访问权限(或其他),以某种方式登录到它,以便我可以 运行 Git那边指挥。这在某些 Linux-server-type 环境中很好,例如公司 Git 设置。但是许多地方不想这样工作,and/or 想要一个单一的“真实来源”集中式存储库,无论是托管在 in-company 还是在 GitHub 或其他什么地方。

所以现在我不会 访问 Bob 的存储库,在 Bob 的计算机上。取而代之的是,在某处有一个中央仓库,至少在最初只有 一个分支 ,名为 master。 Bob 将克隆该集中式存储库并获得 origin/master 并使用它在 Bob 的 Git、master 中创建。然后 Bob 使用他的 master 创建一个新的分支名称 alpha.

当我连接到中央存储库时,我的 Git 生成了 我的 克隆,它具有所有提交但没有分支名称和一个 remote-tracking 名称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 并向它提供 - origin Git - Bob 的任何提交在 origin 还没有的 alpha 上。1 他们接受这些提交,然后 Bob 要求来源 Git 在来源上创建,一个新分支名称alpha。如果 origin Git 服从这个请求——这取决于 origin Git 和任何人可能已经安装和调整的控制旋钮(基本 Git 这里没有太多,但是大多数托管站点do)—然后 现在 原点 Git 有一个名为 alpha.

的分支

我的 Git,在 origin 调用 Git,现在可以看到 alpha,并创建我的 origin/alpha remote-tracking 名字(在获得这五个或其他任何东西之后,new-to-my-Git 提交)。那是我的remote-tracking名字origin的分支名字,但我只能看到它因为 Bob说服 origin 创建它。

如果 Bob 决定制作一个 GitHub-style fork,他所做的就是制作另一个克隆,但这次是托管在 GitHub 上。 Bob 的克隆是 另一个单独的 Git 存储库 并且这个克隆有自己的分支名称。这个克隆有一个或两个特殊的事情:当 GitHub 创建它时,GitHub 确实 复制所有分支,所以最初那个克隆有所有与我将使用的 origin 克隆相同的分支。此外,当 Bob 在 Bob 的 GitHub 分支上创建新的提交和分支名称时,Bob 可以向 origin Git。 (这就是 GitHub 作为 add-ons 提供的所有内容,让您想使用 GitHub 而不是 self-hosting。)

在所有这些情况下,直到Bob 以某种方式在 origin Git 上创建了一个新分支,我看不到 Bob 的 提交 。我只能看到 origin 上的 分支名称 ,它们将成为我的 remote-tracking 名称;我只能在 Bob 以某种方式将 交给 origin Git 之后得到 Bob 的提交,并在 on 上命名origin Git 以便我——或者我的 Git——可以找到他们的提交哈希 ID 号。


1这个措辞涵盖了 master 上的所有提交现在都在 两个分支 上的事实。因此 origin 处的 Git 有大量在 alpha 上的提交;只是 Bob 有五个 更多 次提交,或者 Bob 提交的次数。


遥控器

在上面的过程中,我的Git一直正好有一个remote

当我使用直接进入 Bob 的计算机的示例时——这让我可以随时看到 Bob 的所有分支——我为这个遥控器使用了名称 bob,所以我的 remote-tracking 个名字 分别是 bob/alphabob/beta.

当我使用 GitHub 作为示例时,我使用名称 origin 作为遥控器,所以我的 remote-tracking 名称是 origin/master,最终(一旦 Bob 也在那里创建了一个 alphaorigin/alpha.

A remote 主要是 URL 的简称。我可能用于 Bob 计算机的 URL 可能是 ssh://bob.company.com/path/to/repo.git。我可能用于 GitHub 的 URL 可能是 ssh://git@github.com/company/repo.git.

默认情况下,git clone 命令将使您的新克隆具有作为其(一个,单个)远程的远程名称 origin。此名称将存储您给 的 URL 到 git clone,以便稍后,git fetch origin 将返回相同的 URL 和从他们那里得到任何新的提交。

但是,您可以拥有多个遥控器。这里唯一的限制是每个人都必须有一个唯一的名字。因此,如果我 可以直接访问 Bob 的计算机,我可以 添加 到我的克隆,其中 origin 指的是 GitHub clone ... 和 now 可以 直接访问 Bob 的存储库,因此可以看到 Bob 的分支,就像我的 bob/* remote-tracking 个名字。所以现在答案更改,从不,我看不到 Bob 的分支是,我可以看到 Bob 的分支。我会有 origin/master,还有 bob/alpha(还有 bob/master,除非他删除了他的名字 master)。

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

使用 两个 遥控器,但是 git fetch 没有额外的限定符意味着 一些 遥控器获取。哪一个? The git fetch documentation这里不是模型的清晰度,目前的答案是:

  • 如果我在分支 B 并且 B 有一个 remote 的配置 R,这是 git fetch 使用的;
  • 否则,git fetch 将返回到名称 origin

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

如果我给 git fetch 起一个像 originbob 这样的名字,那就是它将从中获取的远程,还有更多选项,例如“远程组”和课程 --all。使用 --allgit fetch 定向到 所有 遥控器上的 运行 git fetch,一次一个。2

因此:--all 只有在您定义了两个或多个遥控器时才 有用。如果您设置了对 Bob 的存储库的远程访问,您可以看到 Bob 的分支。这当然需要您 访问 到 Bob 的机器,或者 Bob 在 GitHub 上的分支,或其他任何东西。


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


结论

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

我们唯一能看到的名字是他们提供给我们的名字,他们只能提供给我们他们拥有的名字。因此,如果“他们的Git”是某处的中央存储库,并且 Bob 创建Bob 的克隆中的牧场并在那里进行提交,但从不将名称 提交到集中式存储库,集中式存储库首先没有任何东西可以给我们。