我不知道这个问题有多常见,但我似乎无法很好地掌握如何优雅地解决这个合并问题。
所以,我从一个主分支开始。在几次提交之后,我创建了一个新的分支(让它称之为包装器分支),然后继续处理它(几乎没有提交)。我很满意,但与此同时,我在master分支上做了一些额外的工作,多次提交。然后我意识到我最好创建一个 develop 分支,并将其作为我的主 dev 分支。所以我再次从master创建了一个分支,并将其命名为 develop 分支。
现在我意识到将我的包装器分支合并到 develop 分支而不是master分支是最好的。我试过了: -
git checkout wrapper;
git rebase develop;
但这似乎不起作用。我仍然没有在开发分支中看到包装器的更改。我该如何解决这个问题?
我还附上了一张图片,以便更好地展示该场景。
答案 0 :(得分:2)
你可能不想要rebase
,如果你这样做,你可能希望它的用法略有不同。
让我首先将其重绘为ASCII文本,以便我可以说明。请注意,这与您的图形相同,除了我不确定从master
到develop
的行中的简短拆分(没有中间节点)应该表示什么。与您的图表一样,圆点(o
)表示单独的提交,但我会在下面的讨论中使用*
特别标记一个,并使用x
标记一个:
o - * ------------------- o - o - o - o <-- master
\ \
x - o - o - o <-- wrapper o - o - o <-- develop
现在让我们来看看git rebase
做了什么:
git checkout wrapper
git rebase develop
rebase
命令确实执行了一系列cherry-pick
次操作。每个cherry-pick
复制提交:它会比较&#34;来源&#34;提交其父级以查看更改的内容,然后尝试对目标提交执行相同的更改,并进行新的提交。因此,让我们假装手动挑选x
,即第一个wrapper
- 仅提交,将其添加到develop
的末尾。我们通过获取git diff
x
和*
来查看更改内容。然后我们查看分支develop
(其最后o
个节点)的提示,但不要进入分支(以便develop
保持指向它的最后一个o
)。我们使用与commit git commit
相同的消息进行相同的更改,x
结果。假设一切顺利,这给了我们一份副本,让我们称之为x'
,悬挂在develop
的末尾:
o - * ------------------- o - o - o - o <-- master
\ \
x - o - o - o <-- wrapper o - o - o <-- develop
\
x'
现在我们重复o
上剩余的三个wrapper
,从左到右工作,扩展我们的匿名分支。这给了我们:
o - * ------------------- o - o - o - o <-- master
\ \
x - o - o - o <-- wrapper o - o - o <-- develop
\
x'- o'- o'- o'
几乎所有的事情都是这样的:除了一件事,我们已经完成了整个底板。名称wrapper
仍然指向旧提交的提示,而不是新副本。所以我们删除旧标签,并指出最新的标签:
o - * ----------- o - o - o - o <-- master
\ \
x - o - o - o o - o - o <-- develop
\
x'- o'- o'- o' <-- wrapper
那些旧提交仍保留在存储库中,但不再具有(直接)名称:它们已经被放弃了#34;。 (他们仍然有几种方法可以找到它们,但最终会过期,然后git gc
可以移除它们并回收空间。但是,默认情况下,你至少有30天的时间让它们回来。)
如果那不是你想要的,你还有什么选择?好吧,你可以将wrapper
重新定义到master
:
x'- o'- o'- o' <-- wrapper
/
o - * - o - o - o - o <-- master
\ \
x ... o - o - o <-- develop
然后将develop
重新绑定到wrapper
(这需要更复杂的命令行,告诉rebase只接受三个develop
提交:我们可以使用git rebase --onto wrapper master develop
此):
o'- o'- o' <-- develop
/
x'- o'- o'- o' <-- wrapper
/
o - * - o - o - o - o <-- master
\ \
x ... o ...
[old wrapper] [old develop]
这可能是你想要的,也可能不是。所以还有一个非常简单的选择:使用git merge
,而不是任何这些rebase
选项。
git merge
的作用最好描述为:
第一次提交是总是&#34;你现在是#34;。让我们说你在分支develop
上,所以这是提示 - 分支develop
右边的最后一次提交,其中标签develop
指向。另一个提交是您提出的任何提交:如果您说git merge wrapper
,则表示分支wrapper
的提示。
在这种情况下,合并基础是标记为*
的提交,因为它是从wrapper
向左移动并从{{1}}向左移动的点,两条线首次相遇。
因此,git执行develop
git diff
和*
&#34;的提示。这是develop
之后*
以及master
链上所做的一切。
接下来,git执行develop
git diff
和*
&#34;提示。wrapper
&#34;。这是您在分支wrapper
上完成的所有事情,在这种情况下(因为我们正在使用原来的,未重新定位的wrapper
来自提交{{1} }})。
现在git尽力结合这两组变化,从当前的提交 1 开始(再次是*
的提示)和&# 34;重做&#34; second 差异中的所有更改,只要它们第一差异中已经 。
换句话说,如果在develop
中的某个位置,你修正了一个单词的拼写,但是你在wrapper
或master
也做了同样的事情,git说:aha ,这个变化已经存在,我可以跳过它。或者,如果您在develop
中删除了一堆错误的内容,但是您在wrapper
或master
中执行了同样的操作,那么git再次看到它已经完成,并跳过它
这一切都令人惊讶地好,因为git只知道&#34;这些行被添加,其他行被删除&#34;和类似的低级别的东西。例如,它不知道某个单词的哪个拼写是正确的。它只是结合了变化。它取决于你以确保git能够做到这一点。 (对于cherry-pick / rebase也是如此,因此这两种方法都没有优势。)
在任何情况下,如果git成功组合了两个差异,它会从结果文件中进行新的提交。关于这个新提交的特殊之处在于它有两个父项,而不只是一个:合并提交&#34;指回&#34;当前提交(在这种情况下为develop
的旧提示),以及合并提交(develop
的提示)。与任何新提交一样,您正在进行的分支也会指向新提交。所以这现在看起来像:
wrapper
合并操作 - 组合差异 - 有效地带来了分支o - * ------------------- o - o - o - o <-- master
\ \
x - o - o - o <-- wrapper o - o - o - M <-- develop
\______________________________/
的所有更改,从而可以安全地删除分支标签本身:
wrapper
(除非您想要挂在标签上,以便例如对其进行更多提交)。
(当然,在差异合并期间可能会出现问题; git branch -d wrapper
可能会停止并需要您手动解决冲突。如果是这样,您必须执行最终git merge
以创建合并{ {1}},或使用git commit
停止尝试合并并将其重新置于原始状态。)
1 实际上,M
以工作树中的任何内容开头。但是,文档建议确保工作树是干净的(git merge --abort
没有显示任何修改),因为如果合并失败,则返回原始状态,包括已修改但未提交的任何内容,可能很棘手。
答案 1 :(得分:1)
你只需要签出开发,并在该分支内部将另一个合并到其中。 请记住,通常你正在修改的分支是你要修改的分支。
git checkout develop
然后
git merge wrapper