如果我从一个分支中挑选一个提交,然后稍后合并整个分支,git 历史会发生什么?
If I cherry-pick a commit from a branch and then merge the whole branch later what happens to the git history?
所以我遇到了这样一种情况,我只想要来自 branchA 的特定提交,因为该分支中的其他提交尚未准备好合并。因此,如果我从 branchA 中挑选 commitX 到 master,然后将 branchA 合并到 master(比方说,中间有一些提交),就 git 历史而言,commitX 会发生什么?它是否因为已经存在于 master 中而被忽略,或者是否发生某种重复?
git merge
操作不会像以前那样查看 "merge path" 中的单个提交。相反,它只查看开始和结束快照。但是请注意,有 两个 个结束快照(和一个开始):
...--o--B--o--...--o--L
\
o--...--o--R
这里,B
是合并基础提交,L
和R
是左(或本地或--ours
)和右(或其他,远程,或 --theirs
) 分别提交。同时,每一轮 o
代表一些 Git 甚至没有看的提交。1 Git 只是做了,实际上:
git diff --find-renames B L # figure out what we did
git diff --find-renames B R # figure out what they did
这对您的 git cherry-pick
来说意味着 "what we did" 和 "what they did" 可能包含对 相同 的一些更改 same files—但这没问题,因为 git merge
在获得这两个 en-masse diffs 之后所做的步骤是 combine更改,完全复制出现在合并的左侧和右侧 "sides" 中的任何更改的副本。
但是,假设您挑选了一个提交,然后沿着一条路径再次将其还原,例如:
...--o--B--X'--!X'--L
\
X--o--R
(X'
有勾号的原因是它是提交 X
的 copy:当你挑选一个提交时,新的副本获得不同的哈希 ID。)
这里,你为分支(图中下行)写了 commit X
,cherry-picked 到主线(上行),意识到它还没有准备好或者被破坏了,还原它(添加!X'
),然后在主线中进行最终提交L
。现在当你合并时,X
的副本进入,然后又出去,这一事实是不可见的:Git 比较 B
与 L
并且根本看不到任何迹象X'--!X'
序列。因此,它仍然需要一份由提交 X
.
引入的更改的副本
如果提交 X
已在以 R
结尾的分支上准备就绪,这是正确的操作。
如果提交 X
仍然失败,这是错误的操作——但正确的解决方法可能是在合并之前在分支上恢复 X
。
1除了找到B
,即:Git必须同时从L
和R
开始并且工作通过图形向后查找合并基础。这意味着 Git 必须遍历一些不感兴趣的提交节点。
没有什么特别的事情发生,但让我们分解一下。
M
-o---o---o master
\
\----o--o--o branchA
X
现在你来挑选吧。那么情况是:
M MX
-o---o---o--o master
\
\----o--o--o branchA
X
到目前为止一切顺利。然后你再做一些提交...
M MX
-o---o---o---o---o---o---o master
\
\----o---o---o---o---o---o branchA
X
现在合并...
M MX
-o---o---o---o---o---o---o-----o master
\ /
\----o---o---o---o---o---o branchA
X
一切如常。 Git 不存储提交 MX
是 cherry-pick 的结果这一事实,它也不需要。 cherry-pick 操作不同于合并,因为选择的提交 X
和新提交 MX
彼此没有任何关系。它们也不能,因为(通过合并)"parent-child" 关系的语义在末尾 master
包含 all branchA
的历史,不仅更改引入了单个提交。
文件级别的实际更改就像您手动编辑它们一样。也就是说,如果 cherry-pick 引入的变化停留在 master
,git 会注意到(在简单的情况下,通过在相关行的合并期间没有注意到任何差异)并且事情只会被合并。
编辑:关于您在评论中提出的问题...
lets say my commitX had a message "Foobarbaz". Given the scenario, would I have two commits now in my master branch with commit messages "Foorbarbaz" or just 1.
每个提交在提交级别都有一个消息,在文件级别有一些内容。精选仅在内容层面有效;也就是说,它从一次提交中获取文件更改,并将其应用于当前工作目录中的任何内容。可能令人困惑的是命令 git cherry-pick
确实在应用该更改后为您创建一个新提交(本例中为 MX
)。这个新提交只是一个普通的旧提交——它与原始提交 X
没有任何关系,除了 git cherry-pick
复制旧提交消息(您可以编辑)以方便使用。
澄清一下,您可以 git cherry-pick -n
并避免 git
为您执行提交 - 这将使您有机会在自己提交之前编辑 cherry-pick 所做的任何事情。
所以,cherry-pick 实际上只是一种方便的方法,就像您自己编辑更改并自己提交一样。新提交消息可能与旧提交消息相似或相同这一事实对 git 根本无关紧要,稍后在合并时。
所以我遇到了这样一种情况,我只想要来自 branchA 的特定提交,因为该分支中的其他提交尚未准备好合并。因此,如果我从 branchA 中挑选 commitX 到 master,然后将 branchA 合并到 master(比方说,中间有一些提交),就 git 历史而言,commitX 会发生什么?它是否因为已经存在于 master 中而被忽略,或者是否发生某种重复?
git merge
操作不会像以前那样查看 "merge path" 中的单个提交。相反,它只查看开始和结束快照。但是请注意,有 两个 个结束快照(和一个开始):
...--o--B--o--...--o--L
\
o--...--o--R
这里,B
是合并基础提交,L
和R
是左(或本地或--ours
)和右(或其他,远程,或 --theirs
) 分别提交。同时,每一轮 o
代表一些 Git 甚至没有看的提交。1 Git 只是做了,实际上:
git diff --find-renames B L # figure out what we did
git diff --find-renames B R # figure out what they did
这对您的 git cherry-pick
来说意味着 "what we did" 和 "what they did" 可能包含对 相同 的一些更改 same files—但这没问题,因为 git merge
在获得这两个 en-masse diffs 之后所做的步骤是 combine更改,完全复制出现在合并的左侧和右侧 "sides" 中的任何更改的副本。
但是,假设您挑选了一个提交,然后沿着一条路径再次将其还原,例如:
...--o--B--X'--!X'--L
\
X--o--R
(X'
有勾号的原因是它是提交 X
的 copy:当你挑选一个提交时,新的副本获得不同的哈希 ID。)
这里,你为分支(图中下行)写了 commit X
,cherry-picked 到主线(上行),意识到它还没有准备好或者被破坏了,还原它(添加!X'
),然后在主线中进行最终提交L
。现在当你合并时,X
的副本进入,然后又出去,这一事实是不可见的:Git 比较 B
与 L
并且根本看不到任何迹象X'--!X'
序列。因此,它仍然需要一份由提交 X
.
如果提交 X
已在以 R
结尾的分支上准备就绪,这是正确的操作。
如果提交 X
仍然失败,这是错误的操作——但正确的解决方法可能是在合并之前在分支上恢复 X
。
1除了找到B
,即:Git必须同时从L
和R
开始并且工作通过图形向后查找合并基础。这意味着 Git 必须遍历一些不感兴趣的提交节点。
没有什么特别的事情发生,但让我们分解一下。
M
-o---o---o master
\
\----o--o--o branchA
X
现在你来挑选吧。那么情况是:
M MX
-o---o---o--o master
\
\----o--o--o branchA
X
到目前为止一切顺利。然后你再做一些提交...
M MX
-o---o---o---o---o---o---o master
\
\----o---o---o---o---o---o branchA
X
现在合并...
M MX
-o---o---o---o---o---o---o-----o master
\ /
\----o---o---o---o---o---o branchA
X
一切如常。 Git 不存储提交 MX
是 cherry-pick 的结果这一事实,它也不需要。 cherry-pick 操作不同于合并,因为选择的提交 X
和新提交 MX
彼此没有任何关系。它们也不能,因为(通过合并)"parent-child" 关系的语义在末尾 master
包含 all branchA
的历史,不仅更改引入了单个提交。
文件级别的实际更改就像您手动编辑它们一样。也就是说,如果 cherry-pick 引入的变化停留在 master
,git 会注意到(在简单的情况下,通过在相关行的合并期间没有注意到任何差异)并且事情只会被合并。
编辑:关于您在评论中提出的问题...
lets say my commitX had a message "Foobarbaz". Given the scenario, would I have two commits now in my master branch with commit messages "Foorbarbaz" or just 1.
每个提交在提交级别都有一个消息,在文件级别有一些内容。精选仅在内容层面有效;也就是说,它从一次提交中获取文件更改,并将其应用于当前工作目录中的任何内容。可能令人困惑的是命令 git cherry-pick
确实在应用该更改后为您创建一个新提交(本例中为 MX
)。这个新提交只是一个普通的旧提交——它与原始提交 X
没有任何关系,除了 git cherry-pick
复制旧提交消息(您可以编辑)以方便使用。
澄清一下,您可以 git cherry-pick -n
并避免 git
为您执行提交 - 这将使您有机会在自己提交之前编辑 cherry-pick 所做的任何事情。
所以,cherry-pick 实际上只是一种方便的方法,就像您自己编辑更改并自己提交一样。新提交消息可能与旧提交消息相似或相同这一事实对 git 根本无关紧要,稍后在合并时。