为什么 Git 合并命令给我不同的输出

Why Git merge command gives me different ouptut

我已经使用 Git 一年了,今天我在合并分支时偶然发现了一件令人困惑的事情。这是我本地的 .config 文件部分:

[branch "develop"]
    remote = origin
    merge = refs/heads/develop

[branch "Feature/Sprint4.4/mybranch"]
    remote = origin
    merge = refs/heads/Feature/Sprint4.4/mybranch

现在我在本地分支mybranch。我按顺序执行了以下命令。

1) git pull
 --> 2d6cf1e..655001e  develop    -> origin/develop
 --> already up-to-date
2) git merge origin develop
 --> already up-to-date
3) git merge origin/develop
 --> merge made by recursive strategy...
     ....(all the changes were indicated)
      7 files changed, 202 insertions(+), 181 deletions(-)

我的问题是上面的命令 (2) 和 (3) 之间的确切区别是什么,为什么输出会改变?

我知道我可以阅读 Git 文档和其他书籍,但寻找更简单语言的答案。

你在mybranch

1) git pull  // This will pull changes from origin and write them into your my branch.(It will fetch for your develop branch)
 --> 2d6cf1e..655001e  develop    -> origin/develop
 --> already up-to-date
2) git merge origin develop  // This will take almost no effect as you already pulled as it shows develop -> origin/develop
                            // This is same as git merge origin/develop develop
 --> already up-to-date
3) git merge origin/develop  // This will merge changes from origin/develop to mybranch
                             // This is same as git merge origin/develop mybranch
 --> merge made by recursive strategy...
     ....(all the changes were indicated)
      7 files changed, 202 insertions(+), 181 deletions(-)

我看到这已经得到回答和接受,但我认为接受的答案中缺少一个关键点。

你几乎不想 运行 git merge 有超过 一个 个附加参数。

换句话说,git merge origin develop 很少是个好主意;不要那样做。要了解原因,请继续阅读。


git pullgit merge 采用不同的参数

确实(即正确)git pull 只是 运行s git fetch 后跟 git merge(或者,如果您指示它,git pull 后跟 git rebase)。棘手的部分是如何它运行它们,并且pull命令通常需要两个参数:

$ git 拉 <em>远程</em> <em>refspec</em>

但是merge命令一般需要一个:

$git合并<em>提交</em>

注意这里的名字(remote,refspec,和commit)都是不同的:这是理解命令的关键之一。另外,在谈论使用的参数数量时请注意 "generally" 一词:有时可以省略一些参数,或者使用更多参数。

什么是"remote"?

在最简单的形式中,遥控器只是一个名称,如 origin,它指的是 git 配置文件 (.git/config) 中的一个条目。 pull 脚本有很长的使用它的替代方法的历史,所以你可以把其他东西放在这里,但只要你只使用这些名字,事情就很好并且不那么混乱,所以让我们把它留在那。

什么是"refspec"?

一个完整的参考规范最多包含三个部分。这三者对于 git push 都很重要,但对于 git fetch 就没那么重要了,而且由于 git pull 运行s git fetch,我们可以使用最简单的版本,即只是一个 b运行ch 或标签名称。当使用 git fetch(因此也是 git pull)时,您提供的 b运行ch 名称是遥控器上 b运行ch 的名称。因此:

git拉原点<em>b运行ch</em>

让你的 git 联系遥控器 origin 并询问它的 b运行ch 名为 branch.

假设 b运行ch 名称存在于遥控器上,您的 git 将获得该 b运行ch 的最新版本(通常,更新 refs/remotes/origin/<em>b运行ch</em>,至少如果你有 git 版本 1.8.4 或更高版本——那是,使用更新版本的 git,您的 "remote-tracking branches" 总是会更新)。

作为git pull依赖的副作用,git fetch 写入所有更新的b运行ch信息1 到 git 的私有 (.git) 目录中名为 FETCH_HEAD 的文件。您真正需要知道的唯一一件事是 pull 脚本使用它(但如果您愿意,您可以阅读文件的内容)。

到目前为止的总结:git pull 运行s git fetch 并将更新存储在 FETCH_HEAD 中,有时也会存储在您的"remote-tracking branches" 像 origin/masterorigin/develop 等等。


使用git merge

现在让我们继续git merge

我上面说了它"generally"需要三个参数,git merge <em>commit</em>.

pull 命令总是给出 merge 三个参数(和很多标志,包括一些提供合并提交消息的标志)。 pull 脚本从 FETCH_HEAD 文件中获取 commit 参数——就此而言,合并提交消息。2

如果您想手动执行此操作,可以像 pull 脚本一样执行此操作,但提取和输入长 SHA-1 ID 会很痛苦。而且,无论如何,你想要做的是合并一个不同的 localb运行ch,而不是 localb运行ch,而不是 fetch步.

这是您最初输入命令时出错的地方:

git merge origin develop

这将 两个 个参数传递给 merge。它们都被视为提交 ID,这告诉 git merge 做一些叫做 "octopus merge" 的事情。除非你是高级 git 大师,否则你可能不想这样做。 (幸运的是,在您的情况下,鉴于您提供给 git 的多个提交 ID,无论如何都无事可做。)

我会在这里指出 origin 不像 看起来像提交 ID,develop 也不像 SHA-1。但实际上,这两个名称 do 都指定了特定的提交——如果它们没有指定,您会从 git merge 命令中收到一条错误消息。要找出 哪个 提交,您可以使用命令 git rev-parse:

$ git rev-parse origin
a17c56c056d5fea0843b429132904c429a900229

(你的不匹配这个SHA-1,但是思路应该够清楚了)。 b运行ch 名称也解析为原始 SHA-1——我没有 develop 但我有 masterorigin/master 所以我会在这里使用它们:

$ git rev-parse master
a17c56c056d5fea0843b429132904c429a900229
$ git rev-parse origin/master
a17c56c056d5fea0843b429132904c429a900229

(在这种情况下,两者相同,因为我的 master 现在是 origin/master 的最新版本)。

通常,b运行ch 名称解析为 b运行ch 末尾的提交。

我会重复一遍,因为它是理解 git 的另一个关键。 A b运行ch 名称解析为 b运行ch 顶端的提交, 有一个非常大且非常重要的例外,即在执行 git checkout。由于 git checkout 是人们对 git 所做的第一件事,他们了解到 b运行ch 名称是特殊的——这对于 git checkout 是正确的,但对于大多数其他名称则不然git 命令。大多数命令只是将名称转换为原始 SHA-1,当它们这样做时,它们会得到 b运行ch.

的提示
git merge origin/develop

运行 git merge 带有符号名称,如 origin/develop,是相当合理的:git 会为你查找原始提交 ID,并将设置默认的合并消息使用符号名称,这对以后阅读日志的人来说意义更大。如果您使用原始提交 ID,您将在日志中获得它。

合并总结: git merge origin/develop 是合理的做法。这意味着 "find the tip commit of origin/develop and merge that, as a regular merge." 然而,git merge origin develop 不是 一个好主意,因为它意味着 "find the tip commits of origin and develop and do an octopus merge of those two commits."


1当你使用四参数形式时,git fetch <em>remote</em> <em>refspec</em>fetch 步骤只带来 你要求的 b运行ch。使用三个参数,即 git fetch <em>remote</em>fetch 步骤通常会带来所有 b运行切。只有两个参数,git fetch 通常会根据需要自动从 origin 中获取,并再次带来所有 b运行ches。因此,您通常可以 运行 git fetch.

2如果您检查该文件,您会看到如下内容:

a17c56c056d5fea0843b429132904c429a900229        branch 'master' of [url]

您可能还会看到更多行,例如:

ca00f80b58d679e59fc271650f68cd25cdb72b09    not-for-merge   branch 'maint' of [url]

(或者你可能不会)——这就是 git fetch 带来的所有东西。在这种特殊情况下,我 运行 git pull 没有参数,它带来了 mastermaint (以及更多项目)。所有提交 ID 都在 FETCH_HEAD 中结束,然后 pull 脚本通过排除所有标记为 [=264] 的内容来提取 "for merge" 的那个=].