为什么git优先考虑恢复提交的樱桃选择提交?

时间:2014-06-09 19:12:06

标签: git merge rebase revert cherry-pick

假设我有一棵树:

$ git log --graph --oneline --decorate --all

* 7b261e3 (HEAD, master) Revert "A"
* 32f08ae A
| * f0b008f (b) A
|/
* 83c0052 init

$ git log -p --graph --decorate --all

* commit 7b261e3534e12446b75d286ef94556d077a9ee87 (HEAD, master)
| Author: Shin Kojima <shin@kojima.org>
| Date:   Tue Jun 10 03:13:29 2014 +0900
| 
|     Revert "A"
|     
|     This reverts commit 32f08ae6e6a8036a7ed0a72568ac41b3e0fe806a.
| 
| diff --git a/test b/test
| index f16344d..26604dc 100644
| --- a/test
| +++ b/test
| @@ -1,3 +1,3 @@
|  foo
| -hoge
| +bar
|  buz
|  
* commit 32f08ae6e6a8036a7ed0a72568ac41b3e0fe806a
| Author: Shin Kojima <shin@kojima.org>
| Date:   Tue Jun 10 03:12:18 2014 +0900
| 
|     A
| 
| diff --git a/test b/test
| index 26604dc..f16344d 100644
| --- a/test
| +++ b/test
| @@ -1,3 +1,3 @@
|  foo
| -bar
| +hoge
|  buz
|    
| * commit f0b008f3da2426611b40560ce4b64be6e32707e5 (b)
|/  Author: Shin Kojima <shin@kojima.org>
|   Date:   Tue Jun 10 03:12:18 2014 +0900
|   
|       A
|   
|   diff --git a/test b/test
|   index 26604dc..f16344d 100644
|   --- a/test
|   +++ b/test
|   @@ -1,3 +1,3 @@
|    foo
|   -bar
|   +hoge
|    buz
|  
* commit 83c00525a1d8168ae251cf33c00178d398ef4b54
  Author: Shin Kojima <shin@kojima.org>
  Date:   Tue Jun 10 03:11:38 2014 +0900

      init

  diff --git a/test b/test
  new file mode 100644
  index 0000000..26604dc
  --- /dev/null
  +++ b/test
  @@ -0,0 +1,3 @@
  +foo
  +bar
  +buz

7b261e332f08ae的还原提交,而f0b008f是来自32f08ae的樱桃选择。

当我将分支b合并到master时,我发现git默默地忽略了恢复提交(7b261e3),结果与rebasing方法的结果不同。在这种情况下,似乎我仍然需要评估降级并亲眼看看。

Merge made by the 'recursive' strategy.
 test | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

$ cat test

foo
hoge
buz

有没有办法检测冲突?我做错了什么?

git version 2.0.0

1 个答案:

答案 0 :(得分:3)

这是git如何定义合并操作的结果。让我详细说明一下:

您在分支master上(即HEADref: refs/heads/master),master指的是提交7b261e3b引用提交f0b008f。提交图如下所示:

I - A - R   <-- HEAD=master
  \
    A'      <-- b

其中I是初始提交(83c0052),A是您的提交消息“A”,RA的返回提交,A'f0b008f)是A的副本,您将其挑选到分支b

您现在运行:

$ git merge b

Git找到HEADb的“合并基础”,即提交I

Git现在实际上有两个差异:

git diff 83c0052 HEAD  # or: git diff 83c0052 7b261e3
git diff 83c0052 b     # or: git diff 83c0052 f0b008f

(如果您没有撤消合并,请使用原始SHA-1自己尝试使用原始命令。)

IR的更改完全没有:您在A中进行了更改,然后您做出了逆转更改,以恢复到I中的状态{1}}。所以第一个差异是空的。

IA'的更改是您在A中所做的更改。

你告诉git将这两个(不是三个或四个,只有两个)变化结合起来。 “无变化”和“将bar更改为hoge”的组合是将bar更改为hoge,这就是您所得到的:

I - A - R - M   <-- HEAD=master
  \   ____/
    A'          <-- b

其中,commit M的树保留了A'的更改。因此:

  

有没有办法检测冲突?我做错了什么?

没有冲突;唯一可以说你做错了的就是从git中获得更多。 : - )

说真的,这种事情只是其中一个原因 - 为什么你必须总是以某种方式检查合并的结果,无论是通过查看合并项目的日志,还是运行自动化测试,或者只是看看结果。

  

......与变基法不同的结果。

“rebase”操作与merge完全不同。合并需要两个(或更多)开发历史并通过比较合并基础与每个历史提示并组合所有更改来组合它们。 Rebase采用一组提交(通常是单个开发历史记录)并尝试从一个新点开始“重放”每个提交(作为一个樱桃选择)。如果一切都成功,标签将移动到最终提交的一系列提示。

但是,在这种情况下,如果您要将“分支b”重播到“分支master”上:

I - A - R    <-- master
  \
    A'       <-- b

告诉git在A'上挑选R,给出:

I - A - R        <-- master
  \       ` A''  [proposed new b]
    A'           <-- b

与将b合并到master中的结果相同。 (然后,一旦樱桃选择工作,git会将分支b移动到拟议的新地点。)

另一方面,在master上重播b(通过查看b并进行一系列樱桃选择)是不同的:这意味着挑选(复制) A,然后是R。由于A已被复制,因此再次尝试将A置于其中的尝试将重复:

I - A - R      <-- master
  \
    A' - A''?  [proposed, but we discover A'' is duplicate, discard]

I - A - R    <-- old master
  \
    A' - R'  <-- new master, after proposed cherry pick sequence accepted

也就是说,樱桃采摘(以及因此更新)一次只能进行一次提交;但是合并尝试将结果作为一个整体,而不考虑它可能已经采取的所有中间步骤。

(要考虑中间步骤,请执行“incremental merge”。请注意,这不是内置于git中。)