Git 如何用两个 parents 进行空提交
Git how to make an empty commit with two parents
我在为工作找到正确的 git 命令时遇到了一些问题。
我工作的地方目前正在切换源代码控制 (TFVC -> Git)。但是,TFVC 中的旧版本仍然需要不时修复错误。
我已经通过使用一些 PowerShell 脚本创建构建来处理这个问题,这些脚本确保我的“tfvc-merge”分支包含这些更改。
这就是我之后要做的事情。
- 从集成分支结帐
- 我cherry-pick 从“tfvc-merge”分支提交到我的
“mergerhandler”分支
- (我的问题)现在我想做一个有 2 parents 的空提交。
首先 parent 是我在 "MergeHandler" 分支上所做的最后一次提交,接下来
parent 是“tfvc-merge”分支的提交。
我试着把它画出来。所以基本上,拉取请求中的空提交将在我的绘图中具有 1 和 2 的 parent。
我一直在四处寻找几个小时,但没有找到正确的方法。因此,如果有提示可以为我指明正确的方向,那就太好了。提前致谢。
TL;DR
我想你只是想要 git merge -s ours
。
描述
Cherry 选择一些现有提交进行新提交,不会 link 返回原始提交(新提交有一个 parent;这个 parent 是 HEAD
当你 运行 git cherry-pick
时的提交。你画的图显示提交 #2 有 two parents,就好像你的 cherry-pick 是一个合并。因此我不确定我是否正确理解了情况本身。
短语empty commit是Git本身使用的短语,但我发现它非常具有误导性:提交通常没有空的tree object(见Is git's semi-secret empty tree object reliable, and why is there not a symbolic name for it?);使用 git commit --allow-empty
进行的提交的空白是该提交与其 parent.
之间的 差异
有几种方法可以进行这样的提交,显然包括 git commit --allow-empty
,但第一种方法不会进行 merge 提交。唯一进行合并提交的 user-facing 命令是 git merge
。这可能会给您留下 "merging without merging" 的问题,如果该短语甚至意味着什么的话。有时会:git merge -s ours
.
(请注意,由于合并提交有 两个 parent,使用短语 "empty commit" 来谈论此合并比它更没有意义在谈论 single-parent 提交时做了,因为对于合并提交的两个 parent 有 两个 差异。很可能如果一个是空的,其他不是。)
当您 运行 git merge
时,您可以提供一个 合并策略 名称。 Git 有四个内置策略,名称为 recursive
、resolve
、octopus
和 ours
。人们在正常合并时遇到的正常情况是 recursive
。 resolve
策略是 -s recursive
的简化变体,它几乎是同一件事(两者仅在相对罕见的具有多个合并基础的合并情况下有所不同)。 octopus
策略是 Git 在与 比两个 parent 进行真正合并时使用的策略,我们不会在都在这里。剩下 -s ours
。在我们开始之前,让我们花点时间回顾一下正常的 (recursive/resolve) 方法:
- 你检查了一些特定的 b运行ch:
git checkout mainline
.
- 你运行
git merge sidebranch
.
此时,Git 计算 合并基础 在 mainline
的尖端提交之间 - b运行 的提交ch 名称点,我将 L
称为 left/local/--ours
— 以及 sidebranch
的提示提交,我将其称为 R
[=183] =]/--theirs
:
...--o--*--o--L <-- mainline
\
o----R <-- sidebranch
合并基础是两个 b运行ches 连接的点:在这里,它是提交 *
。 Git 然后,本质上,计算两个 git diff
s:
git diff --find-renames <hash-of-base> <hash-of-L> # what we did
git diff --find-renames <hash-of-base> <hash-of-R> # what they did
Git 然后尝试 组合 两组更改,使这些组合更改应用于合并基础 *
的内容,并且如果一切顺利,Git 进行新的 合并提交 M
,第一个 L
parent 和 R
作为第二个 parent:
...--o--*--o--L--M <-- mainline
\ /
o----R <-- sidebranch
比较(diffing)L
vs M
将显示此合并从 *
-vs-R
中获得的更改 [=42] =]-vs-L
。也就是说,我们接了他们的工作。比较 R
与 M
将显示此合并从 *
-vs-L
获得的更改,这些更改尚未在 *
-vs-R
.也就是说,我们还保留了我们的工作。
-s ours
策略
-s ours
的作用非常简单。 Git 不用理会所有 diff
-ing,而是让树远离 L
。 Git 以 L
作为第一个 parent 和 R
作为第二个 parent 进行新的合并提交,因此 graph 看起来完全一样:
...--o--*--o--L--M <-- mainline
\ /
o----R <-- sidebranch
但是现在如果我们运行git diff mainline^1 mainline
比较L
和M
,输出是空。如果我们运行git diff sidebranch mainline
来比较R
和M
,它们可以完全不同。 Git 完全忽略了 commit R
的内容:它已经 M
使用了 L
.[=93= 的内容]
如果你称一个空差异为 "empty commit",那么 L
与 M
就是这样一个空提交。但是仍然有 R
和 M
作为这个合并提交的一部分,所以正如我们上面提到的,短语 "empty commit" 在这里没有真正意义。
长篇大论
如果存在从旧 tvfc b运行ch 到新 b运行ches 的现有合并提交,整个事情会更有意义:
...--o--M--o--<arbitrarily complex work>--I <-- integration
/
...--o--* <-- tvfc
随着时间的推移变成:
...--o--M--o--<arbitrarily complex work>--I <-- integration
/
...--o--*--o--<arbitrarily complex work>-----X <-- tvfc
在这种情况下,添加到 tvfc
本身的任何和所有新提交都可以简单地合并到 integration
,其中 git checkout integration; git merge tvfc
作为 integration
和 tvfc
现在提交 *
。 Git 将比较 *
与 X
以查看它们更改了什么,并将其与 *
与 I
相结合以查看您更改了什么。合并这些更改后,Git 将进行新的合并提交 N
,将来新的合并基础将是 X
本身:
...--o--M--o--<arbitrarily complex work>--I--N <-- integration
/ /
...--o--*--o--<arbitrarily complex work>-----X <-- tvfc
但这似乎不是你在做什么。我想知道您现在是否只是 设置 这种情况。如果是这样,上面的 git merge -s ours
命令可能就是您想要的。
我在为工作找到正确的 git 命令时遇到了一些问题。
我工作的地方目前正在切换源代码控制 (TFVC -> Git)。但是,TFVC 中的旧版本仍然需要不时修复错误。
我已经通过使用一些 PowerShell 脚本创建构建来处理这个问题,这些脚本确保我的“tfvc-merge”分支包含这些更改。
这就是我之后要做的事情。
- 从集成分支结帐
- 我cherry-pick 从“tfvc-merge”分支提交到我的 “mergerhandler”分支
- (我的问题)现在我想做一个有 2 parents 的空提交。 首先 parent 是我在 "MergeHandler" 分支上所做的最后一次提交,接下来 parent 是“tfvc-merge”分支的提交。
我试着把它画出来。所以基本上,拉取请求中的空提交将在我的绘图中具有 1 和 2 的 parent。
我一直在四处寻找几个小时,但没有找到正确的方法。因此,如果有提示可以为我指明正确的方向,那就太好了。提前致谢。
TL;DR
我想你只是想要 git merge -s ours
。
描述
Cherry 选择一些现有提交进行新提交,不会 link 返回原始提交(新提交有一个 parent;这个 parent 是 HEAD
当你 运行 git cherry-pick
时的提交。你画的图显示提交 #2 有 two parents,就好像你的 cherry-pick 是一个合并。因此我不确定我是否正确理解了情况本身。
短语empty commit是Git本身使用的短语,但我发现它非常具有误导性:提交通常没有空的tree object(见Is git's semi-secret empty tree object reliable, and why is there not a symbolic name for it?);使用 git commit --allow-empty
进行的提交的空白是该提交与其 parent.
有几种方法可以进行这样的提交,显然包括 git commit --allow-empty
,但第一种方法不会进行 merge 提交。唯一进行合并提交的 user-facing 命令是 git merge
。这可能会给您留下 "merging without merging" 的问题,如果该短语甚至意味着什么的话。有时会:git merge -s ours
.
(请注意,由于合并提交有 两个 parent,使用短语 "empty commit" 来谈论此合并比它更没有意义在谈论 single-parent 提交时做了,因为对于合并提交的两个 parent 有 两个 差异。很可能如果一个是空的,其他不是。)
当您 运行 git merge
时,您可以提供一个 合并策略 名称。 Git 有四个内置策略,名称为 recursive
、resolve
、octopus
和 ours
。人们在正常合并时遇到的正常情况是 recursive
。 resolve
策略是 -s recursive
的简化变体,它几乎是同一件事(两者仅在相对罕见的具有多个合并基础的合并情况下有所不同)。 octopus
策略是 Git 在与 比两个 parent 进行真正合并时使用的策略,我们不会在都在这里。剩下 -s ours
。在我们开始之前,让我们花点时间回顾一下正常的 (recursive/resolve) 方法:
- 你检查了一些特定的 b运行ch:
git checkout mainline
. - 你运行
git merge sidebranch
.
此时,Git 计算 合并基础 在 mainline
的尖端提交之间 - b运行 的提交ch 名称点,我将 L
称为 left/local/--ours
— 以及 sidebranch
的提示提交,我将其称为 R
[=183] =]/--theirs
:
...--o--*--o--L <-- mainline
\
o----R <-- sidebranch
合并基础是两个 b运行ches 连接的点:在这里,它是提交 *
。 Git 然后,本质上,计算两个 git diff
s:
git diff --find-renames <hash-of-base> <hash-of-L> # what we did
git diff --find-renames <hash-of-base> <hash-of-R> # what they did
Git 然后尝试 组合 两组更改,使这些组合更改应用于合并基础 *
的内容,并且如果一切顺利,Git 进行新的 合并提交 M
,第一个 L
parent 和 R
作为第二个 parent:
...--o--*--o--L--M <-- mainline
\ /
o----R <-- sidebranch
比较(diffing)L
vs M
将显示此合并从 *
-vs-R
中获得的更改 [=42] =]-vs-L
。也就是说,我们接了他们的工作。比较 R
与 M
将显示此合并从 *
-vs-L
获得的更改,这些更改尚未在 *
-vs-R
.也就是说,我们还保留了我们的工作。
-s ours
策略
-s ours
的作用非常简单。 Git 不用理会所有 diff
-ing,而是让树远离 L
。 Git 以 L
作为第一个 parent 和 R
作为第二个 parent 进行新的合并提交,因此 graph 看起来完全一样:
...--o--*--o--L--M <-- mainline
\ /
o----R <-- sidebranch
但是现在如果我们运行git diff mainline^1 mainline
比较L
和M
,输出是空。如果我们运行git diff sidebranch mainline
来比较R
和M
,它们可以完全不同。 Git 完全忽略了 commit R
的内容:它已经 M
使用了 L
.[=93= 的内容]
如果你称一个空差异为 "empty commit",那么 L
与 M
就是这样一个空提交。但是仍然有 R
和 M
作为这个合并提交的一部分,所以正如我们上面提到的,短语 "empty commit" 在这里没有真正意义。
长篇大论
如果存在从旧 tvfc b运行ch 到新 b运行ches 的现有合并提交,整个事情会更有意义:
...--o--M--o--<arbitrarily complex work>--I <-- integration
/
...--o--* <-- tvfc
随着时间的推移变成:
...--o--M--o--<arbitrarily complex work>--I <-- integration
/
...--o--*--o--<arbitrarily complex work>-----X <-- tvfc
在这种情况下,添加到 tvfc
本身的任何和所有新提交都可以简单地合并到 integration
,其中 git checkout integration; git merge tvfc
作为 integration
和 tvfc
现在提交 *
。 Git 将比较 *
与 X
以查看它们更改了什么,并将其与 *
与 I
相结合以查看您更改了什么。合并这些更改后,Git 将进行新的合并提交 N
,将来新的合并基础将是 X
本身:
...--o--M--o--<arbitrarily complex work>--I--N <-- integration
/ /
...--o--*--o--<arbitrarily complex work>-----X <-- tvfc
但这似乎不是你在做什么。我想知道您现在是否只是 设置 这种情况。如果是这样,上面的 git merge -s ours
命令可能就是您想要的。