【问题标题】:Why git gives priority to a cherry-pick commit over the revert commit?为什么 git 优先于樱桃选择提交而不是恢复提交?
【发布时间】:2014-07-30 09:44:09
【问题描述】:

假设我有以下树:

$ 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 的还原提交,f0b008f32f08ae 的精选。

当我将分支 b 合并到 master 时,我发现 git 会默默地忽略还原提交(7b261e3),结果与变基方法的结果不同。在这种情况下,我似乎仍然需要评估退化并亲自查看。

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

$ cat test

foo
hoge
buz

有什么方法可以检测冲突吗?我做错了什么?

git 版本 2.0.0

【问题讨论】:

  • tl;dr on @torek 的回答:由于两个分支分歧,合并对累积变化起作用。您的 master 分支的累积内容更改现在没有什么不同。如果您希望 git 知道已正确应用提交的效果,请将其合并。你听说 git 非常擅长不重复精选的提交,确实如此,但那是因为它非常擅长识别合并基础——在复杂的情况下,它甚至会派生出一个 ersatz 理想基础。

标签: git merge rebase revert cherry-pick


【解决方案1】:

这是 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 中的状态。所以第一个差异是空的。

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

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

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

commit M 的树保存来自A' 的更改。因此:

有什么方法可以检测冲突吗?我做错了什么?

没有冲突;你唯一可以说你做错的事情就是对 git 有更多期望。 :-)

说真的,这类事情只是众多原因之一——为什么您必须始终以某种方式检查合并的结果,无论是通过查看被合并项目的日志,还是运行自动化测试,或者只是观察结果。

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

“rebase”操作与合并根本不同。 Merge 通过比较合并基础与每个历史提示并组合所有更改来获取两个(或更多)开发历史并将它们组合起来。 Rebase 接受一组提交(通常是一个单一的开发历史),并尝试从某个新点开始“重播”每个提交(作为挑选)。如果全部成功,则标签将移动到生成的一系列提交的尖端。

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

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

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

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

这会产生与将b 合并到master 相同的树。 (然后一旦cherry-pick工作,git会将分支b移动到提议的新位置。)

另一方面,在b 上重播master(通过签出b 并进行一系列挑选)是不同的:这意味着挑选(复制)A,然后@ 987654371@。由于 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

也就是说,樱桃采摘(因此也进行了变基)一次提交一个提交;但 merge 试图将结果作为一个整体,而不考虑它可能采取的所有中间步骤。

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

【讨论】:

    猜你喜欢
    • 2014-11-21
    • 2021-04-16
    • 2018-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-05
    • 2016-04-16
    • 1970-01-01
    相关资源
    最近更新 更多