Geo*_*nis 0 git git-merge git-patch
我在 git repo 中有一个分支(例如 Feature-X)。
我所做的是以下
git checkout master
git merge Feature-X
Run Code Online (Sandbox Code Playgroud)
我解决了很多冲突。我还没有提交更改。
但是,事实证明我想要的是进行反向合并(即合并 master 到 Feature-x),因为分支可能仍然不稳定。
是否有任何命令可以挽救我在解决冲突时所做的工作,或者我是否需要再次进行解决,这次是在分支 Feature-X 中?
我在想是否有办法获取当前补丁,但以“相反”的顺序将其应用到分支 Feature-X 中。例如,当补丁说行 X 更改为 Y 时,这与 Feature-X 将要掌握有关。如果我想做相反的事情,补丁应该说行 Y 更改为 X,对吗?这只是我的想法。任何解决方案都可以。
结尾有“食谱”;向下滚动以找到它(然后向上滚动一点以找到描述和解释以及稍微安全的方法)。
从某种意义上说,合并并不完全有“方向”。(当您查看合并时“看到”的方向是您想象力的产物,加上合并提交的消息,再加上一件更重要的事情,这将是我们的“坏消息”——但最终它并不是致命的坏消息。 )
考虑这个以提交*作为合并基础的分支的图:
o--o--...--o <-- branch1
/
...--o--*
\
o--o--...--o <-- branch2
Run Code Online (Sandbox Code Playgroud)
合并(“合并作为动词”)的方法,branch1和branch2通过实现:
git diff合并基础提交*与提示提交branch1;branch2;因此,实际的合并本身应该是这样的:
o--o--...--o
/ \
...--o--* M
\ /
o--o--...--o
Run Code Online (Sandbox Code Playgroud)
(我命名为合并提交M,因为我们不知道,还是真的不在乎,什么疯狂的Git哈希IDdeadbeef或badc0ffee不管它都会有或)最终的合并提交M 的合并(“合并为一个名词”); 它基于合并作为动词的组合过程存储最终的源树。
这次合并是哪个“方向”?嗯,这至少部分取决于我们在上面贴了什么标签,不是吗?:-) 请注意,我小心地剥离了branch1和branch2。让我们把它们放回去:
o--o--...--o <-- branch1?
/ \
...--o--* M <-- branch1? branch2?
\ /
o--o--...--o <-- branch2?
Run Code Online (Sandbox Code Playgroud)
这可能看起来令人困惑。这可能是混淆。这是整个问题的很大一部分。 坏消息是,这不是全部。 在这些图纸中我们无法完全捕捉到一些东西。这对你来说可能无关紧要,如果确实如此,那也没什么大不了的。我们将通过再进行一次合并提交来修复它。但是现在,让我们继续前进。
一旦我们自己进行合并提交M,我们就必须选择——或者让 Git 选择——它在哪个分支上。在一个简单的、无冲突的合并中,我们总是让 Git 选择这个。Git所做的很简单:无论我们在哪个分支,当我们启动git merge命令时,都是获得合并提交的分支。所以:
git checkout branch1
git merge branch2
Run Code Online (Sandbox Code Playgroud)
意味着最终的图片是:
o--o--...--o
/ \
...--o--* M <-- branch1
\ /
o--o--...--o <-- branch2
Run Code Online (Sandbox Code Playgroud)
我们可以重新绘制为:
...--o--*--o--...--o--M <-- branch1
\ /
o---...---o <-- branch2
Run Code Online (Sandbox Code Playgroud)
这很明显我们将 branch2 合并到 branch1,反之亦然。尽管如此,附加到提交的源代码M并不依赖于这个“合并方向”。
就最终结果而言,有冲突的合并就像无冲突的合并。只是 Git 不能自己做合并。它停止并让您解决冲突,然后运行git commit以进行合并提交。最后git commit一步使合并提交M。
新的合并提交在当前分支上进行,就像任何其他提交一样。所以就这样M结束了branch1。请注意,合并提交的消息还说明了哪个分支名称被合并到哪个其他分支名称中——但是您有机会在提交时编辑它,因此您可以更改它以适应您可能有的任何想法。:-)
(实际上,这与无冲突的情况没有什么不同:两者都运行git commit以进行最终的合并提交,并且都给了您编辑消息的机会。)
坏消息是这些图表忽略了一些对你来说可能很重要的东西。Git 有第一个父级的概念:合并提交就像M有两个父级,所以其中一个是第一个父级,一个不是。1 这第一个父是你如何告诉你,当你做出合并的哪个分支。
因此,虽然这些图和合并作为动词的过程表明不存在确切的“合并方向”,但这个第一父概念证明确实存在。 如果你会使用这个第一父概念,你会关心这个,并且想要把它弄对。幸运的是,有一个解决方案。(实际上,有多种解决方案,但我将首先展示笨拙但直接的解决方案。)
1显然,另一个是第二个父母:只有两个父母。但是,Git 允许与三个或更多父项合并提交。您可以在必要时枚举所有父母;但是 Git 认为第一个特别重要,并且--first-parent对各种命令都有标志,以便遵循“原始分支”。
让我们继续进行“错误的”合并提交:
# git add ... (if needed)
git commit
Run Code Online (Sandbox Code Playgroud)
现在我们有:
o--o--...--o <-- [old branch1 tip]
/ \
...--o--* M <-- branch1
\ /
o--o--...--o <-- branch2
Run Code Online (Sandbox Code Playgroud)
哪里M的第一个父级是旧branch1提示。
我们现在需要的是做一个新的合并提交(用不同的ID),那是相同的M不同之处在于:
branch2 指向它branch2branch1诀窍是以M某种方式保存提交——这很容易,我们将使用一个临时名称,这样我们就不必复制下来M的哈希 ID——然后重置branch1:
git branch temp # or git tag temp
git reset --hard HEAD~1 # move branch1 back, dropping M
Run Code Online (Sandbox Code Playgroud)
(请注意,到目前为止,这与brehonia 的答案相同)。我们现在有这个图,除了附加到每个提交的标签外,它几乎没有变化:
o--o--...--o <-- branch1
/ \
...--o--* M <-- temp
\ /
o--o--...--o <-- branch2
Run Code Online (Sandbox Code Playgroud)
现在检查branch2并再次运行合并;它会像以前一样因冲突而失败:
git checkout branch2
git merge branch1 # use --no-commit if Git would commit the merge
Run Code Online (Sandbox Code Playgroud)
我们尚未提交的提交在某种意义上与合并提交相同M——但是一旦我们提交,它的第一个父项将是 的当前提示branch2,其第二个父项将是 的当前提示branch1。我们只需要获得正确的来源来进行我们将要进行的提交。
现在我们使用以 name 保存的合并结果temp。这假设您位于树的顶层,因此可以.命名所有内容:
git rm -rf -- . # remove EVERYTHING
git checkout temp -- . # get it all back from temp
Run Code Online (Sandbox Code Playgroud)
我们现在正在使用我们之前提交的合并结果。我们甚至不需要做git add任何事情,因为这种形式会git checkout更新索引,因此:
git commit
Run Code Online (Sandbox Code Playgroud)
我们根据需要M2在 branch 上创建了新的 merge branch2:
o--o--...--o__ <-- branch1
/ \ \
...--o--* M \ <-- temp
\ / \
o--o--...--o-----M2 <-- branch2
Run Code Online (Sandbox Code Playgroud)
现在我们可以删除临时分支或标签:
git branch -D temp # or git tag -d temp
Run Code Online (Sandbox Code Playgroud)
我们都完成了。随着临时名称的消失,我们再也看不到原来的合并M了。
实际上有一种更短的方法来完成这一切,使用 Git 的“管道”命令,但它有点棘手。一旦我们完成了所有的git add-ed 和 run git commit,我们就有了正确的树,我们只有一个具有错误的第一个和第二个父 ID 的提交。您可能还希望编辑提交消息,以便看起来您正在将消息中的 branch1 合并到 branch2 中。然后:
# git add ... (if / as needed, as above)
# git commit (make the merge)
git branch -f branch2 $(git log --pretty=format:%B --no-walk HEAD |
git commit-tree -p HEAD^2 -p HEAD^1 -F -)
git reset --hard HEAD^1
Run Code Online (Sandbox Code Playgroud)
该git commit-tree步骤创建了一个新的合并提交,它是我们刚刚创建的一个的副本,但是两个父项交换了。然后我们branch2使用git branch -f. 确保branch2在此步骤中正确获取名称 ( )。 在HEAD^1和HEAD^2名字指的是两个(第一和第二)的父母目前的承诺,这是当然的合并提交,我们只是做。那有正确的树和正确的提交消息文本;它只是有错误的第一个和第二个父哈希。
一旦我们安全地将新提交添加到branch2,我们只需重置当前 ( branch1) 分支以删除我们所做的合并提交。