在另一个分支上从远程分支拉取 - "pull origin main" vs "pull main" vs "change to main then pull"

pulling from remote branch while on another branch - "pull origin main" vs "pull main" vs "change to main then pull"

我在另一个分支上时从远程分支中拉出有点困惑。 例如,如果我更改为 main,则拉:

git checkout main git pull 我得到了远程更改的更新。不错。

但是,如果我在另一个分支上并且我想更新 main 而不更改为 main,我总是得到让我感到困惑的结果(除非我更改为 main,否则我不会真正得到更新)。

假设我在分支 'feature',我尝试: git pull maingit pull origin/maingit pull origin main,我得到了一些我没想到的东西,但从来没有更新过的分支。

一个具体的例子,运行 git pull origin main 在分支 feature 上时,将输出:

remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (1/1), done.
From <repo name>
 * branch            master     -> FETCH_HEAD
   8a84194..d00d469  master     -> origin/main
Updating 340286a..d00d469
Fast-forward

但是当我切换到 main git checkout main,然后 git pull,我得到了实际的更新:

git pull
Updating 8a84194..d00d469
Fast-forward
 Pipfile      |    2 +-
 Pipfile.lock | 1583 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------------------------------------
 setup.py     |    2 +-
 3 files changed, 863 insertions(+), 724 deletions(-)

你能帮我理解我哪里错了吗? 谢谢!

罗伊

首先,您确实 不能 合并到除您现在签出的 b运行ch 之外的任何其他内容.1

二、git pull是什么意思2是:

  1. 运行git fetch,然后
  2. 运行 第二个 Git 命令。

第二个 Git 命令是您的选择,但如果不做出明确的选择,您通常会得到 git merge。所以 pull = fetch + mergemerge 只适用于 current b运行ch.

(您的另一个正常选项是 git rebase。但是 git rebase 具有相同的限制,即只处理您现在 签出的任何 b运行ch 。它也有一些脚注,因为 Git 无法忍受让任何事情变得简单,但我们将再次忽略这些。)

所以,对于第一个近似值:

  • git pull表示选择上游这个b运行ch,然后拉入这个b运行 ch(稍后定义upstream);
  • git pull origin main的意思比较复杂,后面会讲到;和
  • git checkout main然后git pull表示main挑上游,拉入main,因为“这个b 运行ch" 是 main

除非你已经在main,否则这三个命令中的none可以与其他任何命令交换.


1对此有一些警告——特殊情况 看起来像 合并,例如,即将到来的未来 Git 软件可能最终会摆脱这种限制——但现在就把它当作真理吧。

2在过去(如 2015 年之前,Git 2.6 之前),git pull 实际上是一个 shell 脚本运行 git fetch,然后是 运行 适当的第二个命令。 C版本更快更高效,但是内部逻辑还是一样的。


必要的背景,没有它Git就没有任何意义

您可能使用Git来维护一堆文件。但是 Git 并不是真正的 about 文件。您也使用 b运行ches,但是 Git 也不是 b运行ches。最后,Git 就是关于 提交 。确实commitscontain文件,我们使用b运行ch名称来帮助我们(和Git)find提交,但 Git 与文件或 b运行ch 名称无关:它是 raison d'êtrecommit.

因此您需要知道 Git 提交是什么以及对您有什么作用。幸运的是,这部分非常简单。 Git 提交:

  • 已编号。每个提交都会获得一个 唯一的 哈希 ID。 hash ID又大又丑random-looking,人类不可能记住。它 必须 ,因为它必须 唯一: 当你进行新提交时,它必须获得一个从未使用过的数字以前,现在这个号码再也不能用于任何 other 提交。

    (由于 pigeonhole principle,这部分在数学上是不可能的,但是由于哈希 ID 很大,哈希 ID 将不可避免的失败推到了未来,到那时我们都计划死亡,所以我们不在乎。)

    因为每个提交 都有一个 唯一编号,我们可以取任意两个 Git 存储库并简要介绍它们,使用 git fetchgit push。一个 Git 是发送者,一个是接收者。发送者列出他的提交哈希 ID,接收者检查他是否有这些哈希 ID。如果他这样做,他有 那些提交 。如果没有,他可以告诉他已经有哪些提交他需要哪些提交然后接收方让发送方发送接收方需要的任何提交.现在两个 Git 存储库共享提交。 (你必须连接它们两次,每个方向一次,以获得完整的共享,并且有很多方法可以限制共享,但这是一般原则。)

  • 完全read-only:任何提交的任何部分一旦完成就不能更改。这是使哈希 ID 技巧起作用所必需的。

  • 包含两件事:所有源文件的完整快照,以您(或任何人)制作时的形式永久冻结提交,以及一些 元数据 。元数据包括您的姓名和电子邮件地址以及您提交时的 date-and-time-stamp 等内容。但是元数据中还有很多其他内容,特别是 Git 添加了自己的 早期提交哈希 ID 列表 .

这 list-of-earlier-commit-hash-IDs,在每个提交的元数据中,连接 提交,向后。大多数提交(到目前为止)只有一个这样的哈希 ID,并且这些是我们首先要关注的。当我们确实只有一个哈希 ID 时,Git 将这个哈希 ID 称为提交的 parent。我们说提交 指向 它的 parent。这为我们提供了一种绘制提交的方法。

假设您存储库中的 最新 提交有一些丑陋的大哈希 ID,我们将其称为 H 表示“哈希”。提交 H 存储一些早期提交的哈希 ID:一个不同的、丑陋的 random-looking 东西,我们称之为 G。这意味着提交 H 指向 之前的提交 G:

          G <-H

但是 G 是一个提交,所以它也有一个散列 ID 卡在 它的 元数据中。它指向一些较早的提交;我们称它为 F:

... <-F <-G <-H

提交 F 有元数据,所以它指向另一个更早的提交,并且这会一直持续下去——或者至少,直到我们回到 有史以来第一次提交,字面上 不能 指向更早的任何内容。所以它没有:它有一个 empty 以前提交的列表。如果我们称该提交为 A(并声称我们恰好有 8 次提交),则整个链为:

A--B--C--D--E--F--G--H

我懒得把箭头 画成 箭头了。它们都是提交的一部分,永远不会改变:H 总是 向后指向 G,永远,G 向后指向F永远,等等。

为了让所有这些都起作用,Git 需要一种快速的方法来 找到 提交 H。 Git 一般文件所有内部 objects——提交和他们的支持的东西——通过它们的哈希 ID,使用一个简单的 key-value store 索引 by 哈希ID。所以 Git 找到 H 的快速方法是知道它的哈希 ID。

现在,您可以 记住每个 Git 提交哈希 ID。但是that way madness lies。为什么不让 us 记住哈希 ID,而让 computer 来做呢?这是 b运行ch 和其他名称的来源:

...--G--H   <-- main

b运行ch name main——这是一个可变的指针,不同于卡在提交元数据中的指针——告诉我们哪个提交是最新的 main。如果我们进行 newer 提交 I,Git 将 ar运行ge for I 向后指向 H :

...--G--H   <-- main
         \
          I

然后 re-point name main 到更新的提交:

...--G--H
         \
          I   <-- main

现在“在”main 上的最新提交是提交 I,而不是提交 H

更多 b运行ch 名称 = 更多指针

让我们回到这个设置,只有一个 b运行ch 名称 main:

...--G--H   <-- main

现在让我们创建一个新b运行通道名称,例如develop。在 Git 中,b运行ch 名称 必须 指向 恰好一个提交 。我们可以选择八次提交中的任何一次,但这里最合适的可能是 最新的 一次,如果我们使用 git branch develop 来创建它,那就是 [=1022] =] 会选择这里。现在我们有:

...--G--H   <-- develop, main

目前,两个名字select提交H,但是一旦我们开始提交,这会改变。所以我们需要知道我们正在使用哪个 name 来查找提交 H。为了在我们的图纸中标记这一点,让我们将名称 HEAD 全部大写附加到一个 b运行ch 名称:

...--G--H   <-- develop, main (HEAD)

我们现在正在使用提交 H,但是通过 name main 来实现。如果我们 运行:

git checkout develop     # or git switch develop, which does the same

我们得到:

...--G--H   <-- develop (HEAD), main

我们仍然使用提交H,但我们是通过名称develop现在。

如果我们现在进行新的提交 I,它的创建方式与我们在 main 上的方式相同,但是由于我们现在使用 develop,更新的是这个名称:

...--G--H   <-- main
         \
          I   <-- develop (HEAD)

名称 main 没有移动,因为它不是我们 使用的名称

这是 Git 中的一般规则:每当您进行 新提交时 ,Git 将更新 当前 b运行通道名称。这是 HEAD 附加的名称。因此,如果您绘制您的提交(在纸上或白板上,或使用 git log --graph 获取 Git 来绘制它们),您将看到您现在的位置以及新提交的去向。它将 添加 ,在你使用的任何提交之后,通过你使用的任何名称。

让我们看看git merge现在

假设我们开始于:

...--G--H   <-- main

并创建两个新的 b运行ch 名称,br1br2,也指向 H。我们也将很快停止在 name main 中绘图,只是为了 de-clutter 绘图一点。所以现在我们有:

...--G--H   <-- br1, br2, main (HEAD)

我们现在选择 br1 作为 current b运行ch 并进行新的提交

          I   <-- br1 (HEAD)
         /
...--G--H   <-- br2, main

当我们进行第二次提交时,我们得到:

          I--J   <-- br1 (HEAD)
         /
...--G--H   <-- br2, main

然后我们切换到 branch-name br2,以便我们返回到提交 H 时保存的文件,并开始对 br2 进行一些不同的更改:

          I--J   <-- br1
         /
...--G--H   <-- main
         \
          K--L   <-- br2 (HEAD)

如果我们再次 git switch br1 我们将 Git 将提交-L 文件替换为提交-J 文件并“打开”br1 再次:

          I--J   <-- br1 (HEAD)
         /
...--G--H
         \
          K--L   <-- br2

如果我们现在 运行:

git merge br2

Git 会做复杂的合并过程。直接跳过 how 部分,让我们看一下合并的 result:如果一切顺利,Git 会生成一个新的 合并提交 M。使 M 成为合并提交的原因在于,它 只是 指向提交 J,而不是像任何新提交那样 =765=] 指向提交 L,像这样:

          I--J
         /    \
...--G--H      M   <-- br1 (HEAD)
         \    /
          K--L   <-- br2

(现在你知道我为什么停止画名字了main:它还在,只是很难画它and commit M也是)。

这是 true 合并。为了实现它,Git 必须 合并工作: Git 必须弄清楚 HJ 之间发生了什么变化,以及什么在 HL 之间更改。我们已经跳过了 Git 首先如何选择提交 H,但事实是 Git 在这里选择 H 吗?提交 H 是最好的 shared 提交,在 both b运行ches br1 br2H 之前的所有提交都在两个 b运行 上。 Git 为这个合并操作调用 H 合并基础

然后 Git 必须为提交 M 创建一个新快照,将 组合 工作应用于 H 的快照,因此要么保留你的更改并添加他们的更改,要么——如果你想反过来看——保留他们的更改并添加你的更改(请注意,对于正常的日常合并,结果是相同的)。

这种work-combining 需要检查两个b运行ches 之一。合并提交 M 添加到 b运行ch,这就是 br1 现在指向 M 的原因。其他 b运行ch 名称不会移动:只有 current 名称会受到添加新提交的影响。

并非所有合并都是真正的合并。假设不是创建两个 b运行ches br1br2,而是创建新的提交 on 这两个 b运行ches,我们从 mainbr1 开始,只在 br1 上提交两次,然后切换回到 main:

          I--J   <-- br1
         /
...--G--H   <-- main (HEAD)

如果我们现在 运行 git merge br1,这就是 Git 所做的:

  • 它计算出两个 b运行 上的最佳共享提交。那是再次提交H
  • 对于真正的合并,Git 必须找到在提交 H 和提交 H(合并的“我们的”一侧)之间完成了哪些工作,以及哪些工作在提交 H 和提交 J 之间完成(合并的“他们的”方面)。但是提交 H 提交 H。 “我们”这边根本没有工作!

由于合并基础提交 当前 提交,因为 select 通过跟随 HEAD 编辑到提交的 b运行ch 名称,没有 不同的工作要合并 。进行真正合并的结果将是合并提交,其快照与现有快照 in 提交 J.

完全匹配

Git 的 git merge 因此需要一个 short-cut。您可以使用 git merge --no-ff 告诉它 不要 这样做,但默认情况下, Git 会执行 fast-forward 而不是 合并。 (Git 将此称为 fast-forward 合并 ,但实际上并没有涉及合并。)为此,Git 只需滑动当前 b运行ch name 转发指向另一个提交,然后像 git checkout:

一样检出该提交
          I--J   <-- br1, main (HEAD)
         /
...--G--H

(没有理由再为图表中的扭结烦恼,但我暂时保留了它)。

请注意,与常规合并一样,此 fast-forward 合并 移动了 b运行ch 名称 。不过它没有做出任何 新提交,这就是它的特别之处。 它没有进行任何新提交的事实意味着将来,我们可能永远不会知道 Git 做了这件事。 如果我们删除 name br1 现在,我们得到:

...--G--H--I--J   <-- main (HEAD)

看起来我们一直在 main 上提交。

让我们return到git fetch

我已经在基本背景部分提到了git fetch。这就是您如何将您的 Git 软件连接到其他 Git 软件——任何使用 Git 协议的软件,真的。那正在/与其他一些存储库一起工作。如果我们称 your-software-plus-your-repository “你的 Git” 和 other-software-plus-other-repository “他们的 Git”,这:

  • 从他们的 Git 获得他们拥有而你没有的任何提交,并且
  • 让您的 Git 创建 and/or 更新一些名称。

这里的技巧是您的 Git 创建 and/or 更新的名称根本不是 b运行ch 名称。原因是您的 b运行ch 名称是 您的 取得了 br1br2(或没有)。您的main 正在跟踪您 添加的提交。覆盖这些名称以跟踪其他一些 Git 的提交,将是糟糕的!

所以你的 Git 根本不会那样做。相反,您的 Git 取了他们每个 Git 的 b运行ch 名称,例如 maindevelopfeature/tall,并在每个名称前加上 origin(或其他名称,但让我们使用 origin)和斜杠。他们的 main 变成你的 origin/main,他们的 develop 变成你的 origin/develop,依此类推。

你的Git现在创建或更新这些名字,这是你Git记住他们Git的b运行ch 个名字。所以现在你可能有:

          I--J   <-- main (HEAD)
         /
...--G--H
         \
          K--L   <-- origin/main

您的 存储库中,或者:

          I--J   <-- br1 (HEAD)
         /
...--G--H   <-- main
         \
          K--L   <-- origin/main

取决于您对您的 b运行ch 名称所做的操作。 他们的 main——你的origin/main——已经从指向H,你之前指向L,因为他们 添加了两个您没有的提交。您的 Git 从他们的 Git 获得了这两个提交,并将它们放入您的存储库中,现在它们具有与 Git 的两个提交相同的哈希 ID,因为它们是 相同 提交:bit-for-bit 在保存的快照和元数据方面相同,包括 parent 哈希 ID。

如果你运行git checkout main得到这个:

          I--J   <-- br1
         /
...--G--H   <-- main (HEAD)
         \
          K--L   <-- origin/main

您现在可以 运行 git merge origin/main。您的 Git 将为该操作找到 合并基础 ,提交 H,并注意这不需要真正的合并并且可以执行 fast-forward 相反,你会得到:

          I--J   <-- br1
         /
...--G--H--K--L   <-- main (HEAD), origin/main

在您自己的存储库中(这次我理清了问题)。

请注意,这些 origin/ 前缀的名称不是 b运行ch 名称。我称他们为remote-tracking names。 Git 的文档称它们为 remote-tracking b运行ch names,但它们不是 b运行ch 名字:它们反映了其他人的 b运行ch 名字,但它们实际上并不是 b运行ch 名字您的 存储库。

稍等,我们快到了:b运行ch

upstream

现在说一下a b运行ch的上行设置。存储库中的每个 b运行ch 名称(但不是任何 remote-tracking 名称)都可以有 one 上游集。这是可选的,因此每个 b运行ch 名称可以有 no 上游。设置上游不会以任何方式改变 b运行ch。它只是使某些操作更方便:

  • 如果你 运行 git merge 没有命名任何东西,Git 将使用当前 b运行 的 upstream 设置通道即git merge origin/main表示找到origin/main命名的commit,git merge表示找到当前b命名的commit 运行ch 的上游.
  • 如果你 运行 git rebase 不命名任何东西,Git 将使用当前 b运行ch 的上游。
  • 如果你运行 git pull, Git会使用当前b运行ch.
  • 的上游
  • 如果你运行 git status, Git会在status中添加一些使用当前b运行ch的upstream得到的信息。

这些不是唯一的东西,但它们可以说是亮点。设置上游使得 Git 使用起来更 愉快 。但是,唉,每个 b运行ch 名称只能得到 one,而且你应该几乎总是将那个上游 设置为 相应的 remote-tracking 名称。即main的上游应该是origin/maindevelop的上游应该是origin/develop,以此类推

(这里有一个小问题:当您创建自己的新 b运行ch 名称时——例如 feature/roy,或 br1,或其他任何东西——那里 origin上还没有相应的b运行ch。所以没有origin/royorigin/br1,你不能set upstream yet。Git 让你等到你用完 git push。我们稍后再谈。)

综合起来

我们现在知道:

  • git fetch 从其他 Git(例如,在 origin)获取新提交并更新相应的 remote-tracking 名称;和
  • git merge, 运行 单独合并在 上游 ,根据需要进行 fast-forward 或真正的合并。

运行 git pull 首先检查是否存在上游集(以便它可以执行 git merge 步骤),然后为您执行这两个命令。这就是它真正要做的。

但是等一下,等等,git pull origin main 呢?好吧,如果你还没有设置上游——或者即使你有——git pull origin main 会让你的命令 运行。首先,git pull 将行 的整个其余部分传递给 git fetch,这样您就有了 运行:

git fetch origin main

git fetch命令通常使用上游(例如origin/main)知道转到origin,但在这里我们明确告诉它“使用Git 在 origin”。这里最后的 main 告诉它我们对 他们的 b运行ch 名称中的哪个特别感兴趣:它 限制了获取 .通常你的 Git 会让他们的 Git 列出 all 他们的 b运行ch 名字,然后把 all 他们的新提交。使用此命令,您的 Git 仅向他们的 Git 询问他们在 main.

上的新提交

(这通常根本没有节省,因为稍后您需要他们的其他提交运行,如果您一次获得所有内容,那实际上 更有效率 比你 运行 多个 git fetch 操作。但它会加快 this git fetch 一点,有时;就是这样它会使 later git fetch 比原来慢。现在保存,以后付款,就像以前一样。除非你的网络连接速度慢得可怕或获取量异常大,很少值得这样做。)

无论如何,无论是否带来了他们在 main 上的任何新提交,您的 Git 都会更新您的 origin/main。那么你的 git pull 运行s:

git merge -m "merge branch 'main' of <url> [into ...]" origin/main

(或多或少——它实际上在这里使用了原始哈希 ID,而不是名称 origin/main,但效果是一样的)。所以你得到一个引用其他 Git 的 URL 和他们的 b运行ch 名称 main.

的合并

由于不需要上游设置,这将获取他们的提交,更新您的 origin/main,然后与您的 origin/main 合并。您可以通过 运行ning:

获得等价物
git fetch
git merge origin/main

虽然消息现在是:

merge branch 'origin/main' [into ...]

也就是说,只有git fetch知道实际使用的URL;到 git merge 运行 时,它不知道 origin/main 刚刚从 URL 更新。 git pull代码知道,因为git pull代码运行git fetch给你了,所以git pull代码提供了一个替代日志消息。 (两条日志消息都不好,真的。They might as well just say here have code or haaaaaands.

关于git push

的一些说明

您的 存储库中进行了新提交,无论是使用git commitgit merge 还是git merge-as-run-by -git pull,或者其他什么,在某些时候你应该发送这些新的提交一些其他Git。这里通常的候选者是 origin 上的 Git。要推送一些提交,您将 运行,例如:

git push origin br1

此处的 origin 部分表示 联系 Git 在 URL 存储在名称 origin 下应答的 Git,即,含义与git fetch基本相同。这里的 br1 部分是您提供两样东西的方式:

  • last 提交您需要他们拥有(他们会自动获得所有 parent),并且
  • 您希望他们在其存储库中设置的 b运行ch 名称

请注意,与 git fetch 不同,它会在您的存储库中创建或更新 remote-tracking 名称,git push 命令提供 b运行ch 名称,供他们在 他们的 存储库中设置。这是因为这里没有任何 remote-tracking 名称的概念。您通常选择 one b运行ch 您希望他们设置的名称;当您使用 git fetch 时,您通常想要获得他们的 all 个 b运行ch 名称,并且您不知道他们创建了哪些名称,因此我们需要像 remote-tracking 名字这样的奇特的东西。

无论如何,所有这一切的结果是您的 Git 将您的提交交给他们 Git(在您的 br1 上,他们还没有;您的 br1 可能还有数十或数千次他们 do 对他们的 main 或其他任何东西的提交,并且您的 Git 不需要将它们作为两个发送Gits 已交换哈希 ID 并意识到这些已经共享)。然后你的 Git 礼貌地要求他们的 Git 创建或更新 他们的 br1 以合并新的提交。

如果他们还没有 br1,他们可能会遵守。 (一些托管站点,如 Bitbucket 和 GitHub 和 GitLab 添加了权限规则,但基础 Git 没有任何这样的东西。)或者他们 有一个 br1:在这种情况下,他们将遵守 只有当添加那些ommits 为它们fast-forward 操作。也就是说,假设您有:

          I--J   <-- br1
         /
...--G--H   <-- main

还有你运行git push origin br1。他们有:

...--G--H   <-- br1, main

如果他们将 I-J 放入他们的存储库,他们现在可以将名称 br1 向前滑动,以便他们拥有您拥有的所有提交,并且他们的 br1 现在指向 J也是。但是 if 他们有:

...--G--H   <-- main
         \
          K   <-- br1

他们的 存储库中,你给他们I-J,他们这边的结果是这样的:

          I--J   [polite request: move `br1` here]
         /
...--G--H   <-- main
         \
          K   <-- br1

如果他们这样做,他们将 失去 K br1。那是因为 Git finds 通过读取 b运行ch names 然后向后工作,通过那些 backwards-pointing 将提交连接到早期提交的箭头。 Git 不能前进,因为箭头不指向前方(并且是提交的一部分,无法更改:它们永远指向后方)。所以对于这种情况,他们会说:不,我不能将 I-J 添加到我的 br1 中,因为这会丢失一些提交 ,Git 报告作为 非 fast-forward 错误。

请注意 git push 运行 git merge。它只是尝试添加提交 as-is。如果您需要将 I-J 与他们的 K 合并,您必须 运行 git fetch 才能提交 K 并更新您的 origin/br1 .然后你可以合并,以便 I-J do 添加(在你的存储库中):

          I--J-----------M   <-- br1 (HEAD)
         /              /
...--G--H   <-- main   /
         \ ___________/
          K   <-- origin/br1

因为新的合并提交 M 指向 both K and J,这现在添加到他们的 br1(你的origin/br1),他们将接受向他们发送I-J-M并要求他们设置他们的br1 指向 M.

(您也可以选择变基而不是合并,但我们不会在这里讨论。)

现在,如果他们之前没有br1,您将有:

          I--J   <-- br1
         /
...--G--H   <-- main, origin/main

在您的存储库中。你不会有一个origin/br1。所以你 运行:

git push origin br1

并发送给他们 I-J 并要求他们 创建 br1,他们服从。您的 Git 看到他们接受了礼貌的请求,并且 现在 您的 Git 在您的存储库中创建了您的 origin/br1:

          I--J   <-- br1, origin/br1
         /
...--G--H   <-- main, origin/main

您现在可以将(您的)br1 上游 设置为(您的)origin/br1:

git branch --set-upstream-to=origin/br1 br1

例如。但是,git push 不是让您将其作为单独的命令键入,而是允许您添加 -u 标志:

git push -u origin br1

-u标志告诉git push如果推送成功,将just-pushed名称的上游设置为对应的remote-tracking名称。也就是说,在这种情况下,它意味着br1的上游设置为origin/br1。请注意,如果推送 失败 ,则 -u 标志无效。

您可以使用 -u 标志,即使现在有上游设置。它将简单地 运行 git branch --set-upstream-to 命令和 覆盖 当前设置为新设置。 (如果 br1 的上游已经是 origin/br1,这会用 origin/br1 覆盖旧的 origin/br1,所以你甚至无法判断发生了什么。)但是大多数人更喜欢在他们的头脑中,将“在 origin 上创建新的 b运行ch”与“在原点上更新现有的 b运行ch”分开。如果你也喜欢这个,你会知道什么时候你想添加 -u (在那里创建 b运行ch 并在此处设置上游)以及什么时候不需要(更新 b运行ch那里,不要在这里做任何事情)。

一旦你 br1 的上游设置为 origin/br1,通常的 Git 设置意味着你可以 运行 git push 而你 br1。所以你只需要git push -u origin br1 一次,当你创建它时,之后你只需要运行 git push,不需要额外的输入。

结论

您概述的三个命令都非常不同。它们都是基于 git pull 表示 运行 git fetch,然后 运行 第二个命令,默认情况下 git merge second 命令,无论它是什么,总是在 current b运行ch.

上运行

我个人更喜欢 运行 git fetch 我自己。然后,对于像 main 这样我没有做任何工作的案例,我可能甚至懒得让 main 保持最新。我什至可以 删除名称 。我可以只使用名称 origin/main 代替:每个 git fetch 我 运行 更新我的 origin/main。我不需要保留自己的 main 名字。

所以这里的问题是,你需要将分支main合并到分支features。当你 运行 git checkout features 和 运行 git pull origin/main 时,这不会用主提交更新分支 features。它只会更新 main 但不会更新 feature.

所以你需要的是。

  1. 再次结帐到主分支

    git checkout main

  2. 从 main

    拉取提交

    git pull origin/main (or git pull)

  3. 改为feature

    git checkout feature

  4. 合并 mainfeature

    git merge main

就是这样。您从 main 获得了对功能的提交。