第一次 git 结帐后需要 git 拉取

Need for git pull right after a git checkout for the first time

根据我对 git 的理解,每次我执行 git checkout 都会发生以下两种情况之一:

  1. 该分支已经在本地存在,因此 HEAD 只是位于它的顶部。
  2. 该分支在本地不存在,因此 git "clones" 它来自远程存储库(假设 git 引用已更新)

但是,我多次对远程分支(本地不存在的分支)执行 git checkout,但得到的内容已过时。然后我执行 git pull 并收到新的提交。

有没有人也遇到过这个问题?你知道为什么会这样吗?

Git checkout 分支将工作目录中的文件更新为存储在该分支中的版本。

要拉取远程更改,您必须 运行 git 拉取原始远程分支。

git 签出不会从远程存储库克隆任何内容。它最多将本地分支指向您上次获取它时远程分支的最后一个头。如果上次取出的头像上有任何东西,那么您将不得不 fetch/pull.

您可以避免使用 git pull(完全或只是有时,这取决于您)。有时您确实需要 运行 git fetch,有时需要一些其他命令。

让这一切在你脑海中保持清晰的方法本身有点复杂,但从这些开始:

  • 涉及 两个 个存储库:您的和位于 origin 的存储库。 (甚至可以有两个以上,但从两个开始,越加越多!)
  • 您的 Git 存储库有 Git 所谓的 remote,它本质上只是一个名称: origin。此名称存储其他 Git 存储库的 URL。
  • 每个存储库都是 self-contained。每个存储库都有 自己的 组 b运行 目录、标签等。
  • 任何一个 Git 存储库都可以通过一些 URL 调用任何其他 Git 存储库,使用 Internet 作为一种电话或消息连接。使用远程名称,如 origin,几乎总是到达这里的方式。除其他事项外,这意味着您只需输入一次长 URL。

如果您 运行 git config -l(列出所有配置)或 git config --get-regex "remote\..*" 您应该至少看到两个条目:

remote.origin.url <some url>
remote.origin.fetch +refs/heads/*:refs/remotes/*

第一个是保存的URL。第二个是 git fetch 命令的一些指令。

连接两个 Git 存储库

由于这里涉及两个 Git 存储库,您必须不时将它们相互连接。有两个主要的 Git 命令可以执行此操作,git fetchgit pull。都是直接你的Git去调用另一个Git,所以区别就是t运行sfer的方向:

  • git fetch 让你的 Git 调用他们的 Git 和 get 东西;
  • git push 让你的 Git 调用他们的 Git 并 东西。

您在这里付出或得到的是承诺。虽然提交 持有 文件(通过持有完整的快照),但提交本身并不是文件,因此将其视为推送或获取 文件是错误的。总是 整个提交 .

但是有一个很大的问题:在 Git 中的提交必须 可找到

查找提交

让我们绘制一个只有三个提交的小型存储库。提交有大而难看的哈希 ID,它们出现在 运行dom 中(尽管它们不是);让我们使用单个大写字母,而不是发明一些。这将我们的 pseudo-repository 限制为仅 26 个 ASCII 提交(虽然也许我们可以用挪威语命名一个提交 Ø,例如,以获得更多),但它更方便。

一个提交存储它的 parent commit 的 hash ID 在里面,所以提交 指向 到它的父:

A <-B <-C

C 是我们最近的提交,它记录了 B 是其父级的事实。 B 记录 AB 的父级。由于 A 是我们的第一个提交,因此它没有父项(在 Git 术语中是 root 提交 ),我们就到此为止。但是我们如何找到C呢?答案是我们使用 b运行ch name 就像 master:

A <-B <-C   <--master

为了向我们的存储库添加一个 new 提交,我们计算它的哈希 ID——在我们的简化图中,这只是 D——然后写出来,设置它是 current 提交 C 的父级。然后我们 更改 master 使其指向 D 而不是 C:

A--B--C--D   <-- master

我们从不更改任何 现有的 提交,而且我们真的不需要记录箭头的方向:它们总是指向后方。但是我们一直在更改 b运行ch 名称,所以我们应该记下它们的箭头,因为它们会移动。

Git 因此可以向后。它始终包含有关 最新 提交的信息。它使用这些来查找较旧的提交。 Git 将名称 HEAD 附加到 b运行 之一,以便它知道你在哪个 b运行ch 上。当您 运行 git checkout 时,它所做的其中一件事就是将 HEAD 附加到您签出的任何 b运行ch。我将在下面开始包括。

Remote-tracking 姓名

让我们回到这里涉及两个 Git 存储库的事实。其中之一是你的。您有自己的 b运行ch 名称,例如 masterdevelop 以及 feature/shortfeature/tall 等等。但是在 origin 有另一个 Git 存储库,它有 its b运行ch 名称。

当您的 Git 调用他们的 Git 并获得他们的提交时,他们的 Git 一直在通过 他们的 b[ 找到他们的提交=383=]通道名称。如果他们的 master 和您的 master 不同意他们应该指向哪个提交怎么办?您已经添加了 D 而他们还没有 D,所以 thei例如, master 仍然指向 C

您的 Git 通过 重命名 记录 他们的 b运行ch 指针。你的 origin/master 记得他们的 master:

        D   <-- master (HEAD)
       /
A--B--C   <-- origin/master

如果他们在您上次同步后向他们的 master 添加了 new 提交,则该提交具有不同的(且唯一的)哈希 ID。我们称它为 E:

        D   <-- master (HEAD)
       /
A--B--C
       \
        E   <-- origin/master

git checkout 创建 b运行ch 如果合适

假设您的存储库中有一些提交系列以及一些名称:

        D   <-- master (HEAD)
       /
A--B--C   <-- origin/master, origin/dev

如果您现在说 git checkout dev,那么,您 没有 dev。但是您确实有 origin/dev,指向提交 C。您的 Git 注意到这一点并自动 创建 您的 dev 现在:

        D   <-- master
       /
A--B--C   <-- dev (HEAD), origin/master, origin/dev

请注意 b运行ch name 是新的,即使提交不是。名称 HEAD 现在附加到新的 b运行ch 名称。

如果你再次git checkout master,你的dev继续存在,指向C:

        D   <-- master (HEAD)
       /
A--B--C   <-- dev, origin/master, origin/feature

唯一发生的事情是您的 HEAD 附加到您现有的 master(当然 Git 也会检查提交 D)。

如果您现在再次从 origin git fetch,并且他们已将提交 E 添加到他们的 master 并将 F 添加到他们的 devE 指向 CF 指向 E,你得到:

        D   <-- master (HEAD)
       /
A--B--C   <-- dev
       \
        E   <-- origin/master
         \
          F   <-- origin/dev

综合起来

当你 运行 git fetch 时,你 Git 调用他们的 Git,列出他们所有的 b运行ch 名称和他们的提交哈希,然后您的 Git 从他们的 Git 获得任何您没有的提交 他们 。您的 Git 将这些添加到您的存储库并更新您的 remote-tracking 名称.

当您第一次 git clone 他们的存储库时,git clone 会创建一个新的空存储库(如 git init),其中什么都没有,甚至没有 master b运行还没有。您的 git clone 使用 URL 和默认 fetch 行设置远程 origin。然后你的 Git 调用他们的 Git (git fetch),询问他们的 b运行ch 名称,询问他们有你没有的提交——这当然是每次提交——并将所有这些提交放入您的空存储库中,仅使用 remote-tracking 名称:

A--B--C   <-- origin/master

作为最后一步,git clone 生效 运行s git checkout master。这 创建 你的 master,也指向提交 C

以后每次 git fetch 更新您所有的 remote-tracking 名称——您的 origin/* 名称——同时获取(共享)提交。因此,您的 remote-tracking 名称会记住它们的 b运行ch 名称,而您自己现有的 b运行ch 名称将保持不变。

因此,如果您 git fetch 在 运行 之前 git checkout 将创建一个 new b运行ch 名称, 您的 new b运行ch 名称将从更新后的 remote-tracking 名称创建。如果您 git checkout 名称过早,您将从 old 值创建它——您已经拥有的提交的旧哈希 ID。

使用git pull,或git merge,或git rebase

git pull 命令只是 运行 为您提供的两个命令:

  • git fetch,执行以上所有操作:它获取任何新提交并更新您的 remote-tracking 名称,但从不影响 您的 b运行ches.
  • 第二个Git命令,从而影响你的current b运行ch.

通常你 运行 git fetch 因为你希望从其他 Git 存储库中获取新内容。如果您 得到了新东西,您可能想用它做点什么。这意味着用 your b运行ch(es).

做一些事情

主要有两种方法可以将您所做和承诺的任何工作与其他人完成和承诺的工作结合起来。它们是 git mergegit rebase。所以很典型,在 git fetch 之后,您想使用这两个命令之一。

应该使用哪一个?好吧,这是一个见仁见智的问题,对此有不同的思想流派。我喜欢根据我做了多少工作、他们做了多少工作以及这些工作之间的关系来选择使用哪一个。为此,我必须看看他们所做的工作。

使用git pull,必须事先决定是merge还是rebase,才有机会看。所以我避免git pull。我运行git fetch,然后看,然后决定做什么。如果您使用 git pull,则无法执行此操作:您必须弄清楚要执行哪些操作,合并或变基,然后才能看到您想要的操作。有时您可能只是知道,在这种情况下,git pull 就可以了!

在任何情况下,如果你使用 git pull,你告诉 Git 要做什么:合并(默认),或 --rebase 变基。然后 运行 为您提供 git fetch,并且 运行 为您提供第二个命令——git mergegit rebase。这就是它真正做的一切!1 知道这个是个好主意现在 git mergegit rebase 有效,我认为如果您手动 运行 学习它们,而不是让 git pull 运行 为您学习它们,但您现在拥有了在这里做出自己的决定所需的所有部分。


1嗯,如果有子模块,你可以让它递归地拉入子模块。但这完全是另一回事。