git pull--重基--保留合并重写合并提交未修改的父级

git pull--重基--保留合并重写合并提交未修改的父级,git,git-merge,git-rebase,git-pull,Git,Git Merge,Git Rebase,Git Pull,以历史为例 D origin/master / | C master | |\ | | B \|/| A : | : 在git拉取--rebase--preserve合并之后 C' master |\ D | | B |/| A : | : 但事实证明是这样的 C' master |\ | B' |/| D : | A | : 换句话说,B c

以历史为例

   D  origin/master
  /
 | C  master
 | |\
 | | B
  \|/|
   A :
   |
   :
git拉取--rebase--preserve合并之后

   C'  master
   |\
   D |
   | B
   |/|
   A :
   |
   :
但事实证明是这样的

   C'  master
   |\
   | B'
   |/|
   D :
   |
   A
   |
   :
换句话说,B commit被重写为B',即使不需要它,因为双亲都没有被修改。我知道我正在重新调整到D,但这是有问题的,因为B合并中的所有冲突都必须再次解决

当前功能是否有好处

有什么方法可以获得我想要的功能吗?

我不能真正回答“好处”问题,但我可以描述rebase为什么会这样做,以及如何获得想要的结果

Rebase,无论是交互式的还是非交互式的,都使用“三个分支或类似提交”的参数,它将这些参数调用为
newbase
上游
、和
分支

git-rebase
[
-i
|
--交互式
][
选项
][
--exec cmd
][
--to-newbase
]
[
上游
分支机构]

如果您忽略了其中的部分或全部内容,rebase仍会使用它们,它只会自行查找它们:

  • 分支
    默认为当前分支,或
    头部
    (包括分离的头部)
  • upstream
    默认为当前分支的上游,由
    git branch设置——将上游设置为
    或类似设置。无论您在这里指定什么,或默认值,都将被赋予
    git rev list
    以使其停止执行提交,即,将被重定基础的提交是那些由
    git rev list upstream..HEAD
    打印的提交
  • newbase
    默认为与上游
    相同的提交ID
(如果您在git中提供足够新的
--fork point
,这些都会得到一定程度的修改,但这里的总体思路仍然适用:默认情况下,要重定基础的提交是基于上游的“后”提交选择的,直到并包括HEAD)

当您让
git pull
为您运行
git rebase
时,它将提供当前分支的实际上游,作为
上游
(同样,在较新的git中通过分叉点计算进行修改,但如果您从中提取的实际上游没有自己进行重新定基,那么这应该没有效果)。(
--on
通常也是同样的上游。在这种情况下,它相当于
git-rebase[options]--on-origin/master-origin/master

如您所见(通过运行
git rev list origin/master..master
),这意味着“请将提交的
B
C
重新设置为
D
”。添加
--preserve merges
只需使用交互机制,并在根据请求对两个提交进行重定基址后,根据请求保留合并

如果您只需运行
git fetch
,您将像往常一样获得commit
D
,并将绘制的图形作为第一个图形:

   D  origin/master
  /
 | C  master
 | |\
 | | B
  \|/|
   A :
   |
   :
现在,您可以尝试手动运行
git rebase-p
以重新设置提交
C
的基础,将
B
指定为其“上游”,以便重新设置基础不会复制提交
B
,并将
D
指定为提交到
上,以便将
C
重新设置到
D

$ git rebase -p --onto origin/master master^2 master
不幸的是,这会产生一个新的合并提交,其两个父级分别是
origin/master
(提交
D
)和原始
master^
(提交
a
)。(原因尚不清楚,尽管这显然与交互保留模式内部的工作方式有关。)

因此,本例中的技巧是进行您自己的合并:

$ git branch -m master old-master
$ git checkout master
(这会使一个新的
master
指向提交
D
),然后:

(这会对未修改的
B
进行新的合并)

不过,你也提到:

。。。这是有问题的,因为必须再次解决B合并中的所有冲突

不幸的是,您无法真正避免这种情况,因为有可能
D
A
有很大的不同,实际的合并冲突解决方案也应该有所不同

如果您确定它们(分辨率)不应该(不同),您也可以通过从旧的提交
C
中获取合并结果来回避此问题。例如,假设冲突发生在
dir1/file1
dir2/file2
中,而
A
D
之间的区别都在
README.txt
或类似的地方。然后,当您进行上述合并时,您可以非常简单地通过以下方式“解决”冲突:

$ git checkout old-master -- dir1/file1 dir2/file2
它提取提交
C
的这些文件版本,更新索引和工作树以恢复以前的合并解析。您的冲突现在已经解决(与以前完全一样),您可以
git commit
结果合并

$ git checkout old-master -- dir1/file1 dir2/file2