第一次 git checkout 后需要 git pull

use*_*932 1 versioning git github git-checkout gitlab

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

  1. 该分支已经存在于本地,因此 HEAD 简单地位于其顶部。
  2. 该分支在本地不存在,因此 git 从远程存储库“克隆”它(我们假设 git refs 已更新)

但是,有几次我git checkout对远程分支(本地从未存在过)执行 a 操作,并且得到了过时的内容。然后我执行 agit pull并收到新的提交。

有人也遇到过这个问题吗?你知道为什么会发生这种情况吗?

tor*_*rek 11

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

\n\n

将这一切牢记在心的方法本身有点复杂,但可以从以下开始:

\n\n
    \n
  • 涉及两个存储库:您的存储库和位于 的存储库origin。(甚至可以超过两个,但从两个开始,如果添加更多,它只会变得更加毛茸茸!)
  • \n
  • 你的Git 存储库有 Git 所说的远程,它本质上只是一个名称:origin。此名称存储其他 Git 存储库的 URL。
  • \n
  • 每个存储库都是独立的。每个存储库都有自己的一组分支、标签等。
  • \n
  • 任何一个 Git 存储库都可以通过某个 URL 调用任何其他 Git 存储库,使用互联网作为一种电话或消息连接。使用远程名称(例如origin)几乎总是此处的方法。除此之外,这意味着您只需输入一次长 URL。
  • \n
\n\n

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

\n\n
remote.origin.url <some url>\nremote.origin.fetch +refs/heads/*:refs/remotes/*\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

连接两个 Git 存储库

\n\n

由于这里涉及两个 Git 存储库,因此您必须不时地将它们相互连接。有两个主要的 Git 命令可用于执行此操作,git fetch以及git pull. 两者都指示你的Git 调用另一个 Git,所以区别在于传输方向:

\n\n
    \n
  • git fetch让你的 Git 调用他们的 Git 并获取东西;
  • \n
  • git push让你的 Git 调用他们的 Git 并提供东西。
  • \n
\n\n

你在这里给予或接受的是承诺。虽然提交保存文件(通过保存完整的快照),但提交本身并不是文件,因此将其视为推送或获取文件是错误的。它总是完整的提交

\n\n

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

\n\n

查找提交

\n\n

让我们画一个只有三个提交的小型存储库。提交有大而难看的哈希 ID,它们看起来是随机的(尽管它们不是);让我们使用单个大写字母,而不是发明一些字母。这将我们的伪存储库限制为仅 26 个 ASCII 提交(例如,尽管我们可以用挪威语将提交命名为 \xc3\x98,以获得更多),但它要方便得多。

\n\n

提交将其父提交的哈希 ID 存储在其中,以便提交指向其父提交:

\n\n
A <-B <-C\n
Run Code Online (Sandbox Code Playgroud)\n\n

CB是我们最近的提交,它记录了其父级的 事实。B记录AB\ 的父级。因为A这是我们的第一次提交,所以它没有父提交( Git 术语中的根提交),我们就到此为止。但我们如何找到呢C?答案是我们使用分支名称,例如master

\n\n
A <-B <-C   <--master\n
Run Code Online (Sandbox Code Playgroud)\n\n

为了向我们的存储库添加新的提交,我们在简化的绘图中计算其哈希 ID\xe2\x80\x94,这只是D\xe2\x80\x94 并将其写出来,将其父级设置为当前提交C。然后我们更改master ,使其指向D而不是C

\n\n
A--B--C--D   <-- master\n
Run Code Online (Sandbox Code Playgroud)\n\n

我们从不更改任何现有的提交,并且我们实际上不需要记录它们的箭头方向:它们总是指向向后。但我们确实一直在改变分支名称,所以我们应该记下它们的箭头,因为它们会移动。

\n\n

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

\n\n

远程跟踪名称

\n\n

让我们回到这里涉及到的两个 Git 存储库这一事实。其中之一就是你的。您有自己的分支名称,master例如developfeature/short等等feature/tall。但是 at 上还有另一个 Git 存储库origin,它有自己的分支名称。

\n\n

当你的 Git 调用他们的 Git 并获取他们的提交时,他们的 Git 一直在通过他们的分支名称查找他们的提交。如果他们master和你master不同意他们应该指向哪个提交怎么办?例如,您已经添加D,而他们还没有添加D,因此它们 master仍然指向。C

\n\n

你的 Git通过重命名来记录它们分支指针。你还记得他们的:origin/mastermaster

\n\n
        D   <-- master (HEAD)\n       /\nA--B--C   <-- origin/master\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果自您上次同步以来他们添加了新的提交,则该提交具有不同的(且唯一的)哈希 ID。master我们称之为E

\n\n
        D   <-- master (HEAD)\n       /\nA--B--C\n       \\\n        E   <-- origin/master\n
Run Code Online (Sandbox Code Playgroud)\n\n

git checkout如果合适的话将创建一个分支

\n\n

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

\n\n
        D   <-- master (HEAD)\n       /\nA--B--C   <-- origin/master, origin/dev\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您现在说git checkout dev,好吧,您没有. dev但你确实有origin/dev,指向承诺C。你的 Git 会注意到这一点并自动创建你的devnow:

\n\n
        D   <-- master\n       /\nA--B--C   <-- dev (HEAD), origin/master, origin/dev\n
Run Code Online (Sandbox Code Playgroud)\n\n

请注意分支名称是新的,即使提交不是新的。该名称HEAD现已附加到新的分支名称中。

\n\n

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

\n\n
        D   <-- master (HEAD)\n       /\nA--B--C   <-- dev, origin/master, origin/feature\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

如果你现在git fetch再次origin,他们已经添加了E对 他们masterF他们 的提交dev,并E指向CF指向E,你会得到:

\n\n
        D   <-- master (HEAD)\n       /\nA--B--C   <-- dev\n       \\\n        E   <-- origin/master\n         \\\n          F   <-- origin/dev\n
Run Code Online (Sandbox Code Playgroud)\n\n

把这个放在一起

\n\n

当您运行时git fetch,您的 Git 会调用他们的 Git,列出他们所有的分支名称和提交哈希值,然后您的 Git 从他们的 Git 中获取他们拥有但您没有的任何提交。您的 Git 将这些添加到您的存储库并更新您的远程跟踪名称

\n\n

当您第一次使用git clone他们的存储库时,git clone会创建一个新的空存储库(例如git init),什么都没有,甚至还没有master分支。您使用 URL 和默认线路git clone设置遥控器。然后你的 Git 调用他们的 Git ( ),询问他们的分支名称,询问他们有你没有的提交\xe2\x80\x94,当然这是每个提交,\xe2\x80\x94 并把所有仅使用远程跟踪名称将这些提交到您的空存储库中:originfetchgit fetch

\n\n
A--B--C   <-- origin/master\n
Run Code Online (Sandbox Code Playgroud)\n\n

作为最后一步,git clone实际上运行git checkout master. 这将创建您的master,也指向 commit C

\n\n

稍后每次git fetch更新您的所有远程跟踪名称\xe2\x80\x94您的origin/*名称\xe2\x80\x94,同时获取(共享)提交。因此,您的远程跟踪名称会记住它们的分支名称,而您自己的现有分支名称则保持不变。

\n\n

因此,如果您git fetch在运行git checkout将创建分支名称之前,您的分支名称将从更新的远程跟踪名称创建。如果您太早命名,您将根据值 xe2x80x94(您已有的提交的旧哈希 ID)git checkout创建它。

\n\n

使用git pull、 或git merge、 或git rebase

\n\n

git pull命令只为您运行两个命令:

\n\n
    \n
  • git fetch,它执行上述所有操作:它获取任何新的提交并更新您的远程跟踪名称,但永远不会影响您的分支。
  • \n
  • 第二个 Git 命令,从而影响您当前的分支。
  • \n
\n\n

通常您运行是git fetch因为您希望从其他 Git 存储库获取新内容。如果你确实得到了新东西,你可能想用它做点什么。这意味着对您的分支机构进行一些操作。

\n\n

主要有两种方法可以将您已完成和承诺的任何工作与其他人已完成和承诺的工作合并。这些是git mergegit rebasegit fetch因此,在之后,您想要使用这两个命令之一是非常典型的。

\n\n

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

\n\n

使用git pull,您必须在有机会查看之前提前决定是合并还是变基。所以我回避 git pull。我跑步git fetch,然后看看,然后决定做什么。如果你使用 ,你就不能这样做git pull:你必须先弄清楚要做什么,合并或变基,然后才能看到你想要哪一个。有时你可能只是知道,在这种情况下,git pull没关系!

\n\n

无论如何,如果您正在使用git pull,您可以告诉 Git 做什么:合并(默认)或--rebase变基。然后它会git fetch为您运行,并为您运行第二个命令\xe2\x80\x94git mergegit rebase\xe2\x80\x94。这就是它真正所做的一切!1 了解如何操作git mergegit rebase工作是一个好主意,我认为如果您手动运行它们,而不是为您运行它们,您会学得更快git pull,但您现在拥有制作您的应用程序所需的所有部分。自己的决定在这里。

\n\n
\n\n

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

\n