为什么分支 B 在第一次合并提交后从 A 到 B 的所有合并提交都有冲突?

Why does branch B have conflicts in all merge commits from A to B after the first merge commit?

我有一个历史悠久的主要开发分支 (A)。 A 中的所有发布提交都被标记为这样。我检查了 A 的根提交,并分支到测试 (B)。

所以我有一个主分支 A,分支 B 的头部指向 A 的根提交。我的目标是通过将每个标记的提交从 A 合并到 B 来创建所有版本的历史记录。

从 A 到 B 的第一次合并按预期工作,没有冲突。

$git checkout B
$git merge [release-commit-ID] --squash
$git commit -m "release#"

第一次提交效果很好,但所有其他提交都将所有合并提交视为完全冲突。我看到 B 的根与 A 的根相同,但是在从 A 中的第一个发布提交到 B 的第一个压缩合并提交之后,没有识别到​​共享历史。是什么导致了冲突以及如何获得共享历史被认可?

What is causing the conflicts and how do I get shared history to be recognized?

没有共享历史记录(或者说,还不够)。没有什么可以识别的,所以才会有冲突。

关键是--squash标志:

$ git checkout B
$ git merge [release-commit-ID] --squash

第一步将您的 HEAD 附加到分支名称 B,检查名称 B 标识的任何提交:

...--*--C--D--E   <-- B (HEAD)
      \
       F--G--H   <-- somebranch

这里的名称 B 标识提交 E(每个字母代表真实的哈希 ID)。提交 *(我会命名为 B,但你为你的分支使用了那个名称)是两个开发流分叉的点,这意味着当我们向后工作时(如 Git 确实)这是他们聚在一起的地方。您现在 运行 git merge --squash <hash> 其中 <hash> 标识提交 F,或 G,或 H,因此 Git 比较内容commit * 到 commit E 的内容来找出你改变了什么:

git diff --find-renames <hash-of-*> <hash-of-E>   # what we changed

并重复 G:

git diff --find-renames <hash-of-*> <hash-of-G>   # what they changed

Git 现在合并这两组更改,将合并的更改应用于提交 * 的内容,并进行新的提交。

如果你使用--squash,Git用两个记录新的提交parents:

...--*--C--D--E--I   <-- B (HEAD)
      \         /
       F-------G--H   <-- somebranch

现在两条线之间最近的共同起点是提交 G。但是如果你 do 使用 --squash,Git 只用 one parent 记录新的提交:

...--*--C--D--E--I   <-- B (HEAD)
      \
       F--G--H   <-- somebranch

现在共同起点不变。通过 G 的所有工作都在提交 I 中,但提交 I记得 为什么那个工作在那里。来自提交 H 的未来 git merge 必须在提交 *.

重新开始

Git不会阻止你在分支somebranch上开发,但一般来说,在git merge --squash之后,你应该考虑你合并的分支 成为 "dead" 并完全停止使用它。一旦我们将 Ggit merge --squash 合并,我们就应该完全停止使用 FG。这意味着我们也必须完全停止使用 H。如果 H 有用,我们应该将它复制到一个新的不同的提交:

$ git checkout -b newbranch B

给我们:

...--*--C--D--E--I   <-- B, newbranch (HEAD)
      \         /
       F-------G--H   <-- somebranch

其次是:

$ git cherry-pick somebranch   # or <hash of H>

H 复制到一个非常相似但不完全相同的提交 H':

                   H'   <-- newbranch (HEAD)
                  /
...--*--C--D--E--I   <-- B
      \         /
       F-------G--H   <-- somebranch

我们现在可以丢弃 somebranch:

$ git branch -D somebranch

如果我们愿意,可以将 newbranch 重命名为 somebranch

(请注意,我们可以使用 git rebase --ontogit checkout somebranch; git rebase --onto B <hash of G> 一步完成此 copy-with-name-moving 事情。无论如何,请注意 git cherry-pick 无法复制合并提交,并且 任何人 使用我们想要杀死的提交 - 这里是 F-G-H 链 - 必须做这个 killing-off-with-copying-the-ones-to-save 事情。所以在使用 --squash 之前,请确保您了解所有含义。)