管道到git log --no-walk --stdin 的Steven Penny's approach 是正确的方法,并且在最复杂的情况下是必需的(选定提交ID 的复杂过滤)。
有一些技巧可以用于这个特殊的案例。如果您检查(广泛,虽然令人困惑)documentation for git rev-list,您会发现选项--left-right、--left-only、--right-only、--cherry-mark、--cherry-pick 和 --cherry。
--left-right 选项适用于任何对称差异,即由A...B 选择的提交,这意味着“在分支A 上的所有提交或 分支B,不包括任何both 分支上的提交。"这就是git cherry 做它的第一部分的方式:它找到在对称差异上的提交。但是,git cherry 继续丢弃所有在两个分支上“相同”1 的提交,然后将它们标记在一个一边是-,另一边是+。 --left-right 选项将所有提交标记为<(左侧)或>(右侧)。
由于rev-list 是Swiss Army Chainsaw 命令,它也 能够丢弃相同的提交。它也有更多的能力:它可以完全丢弃一侧的提交,相同或不同。这就是我们在这种情况下想要的:丢弃“相同的提交”(这样我们只保留“不同”的提交),然后丢弃所有“左侧”提交(这样我们只保留那些在右侧但不是左侧)。我们用--cherry-pick 得到前者:它只保留被认为“不同”的提交。然后我们添加--right-only 以仅保留右侧的那些,即当我们说firstbranch...secondbranch 时,仅保留secondbranch 中既不同的和 ...这正是git cherry 会。
因此:
git rev-list --right-only --cherry-pick firstbranch...secondbranch
产生与以下相同的提交 ID 列表:
git cherry firstbranch secondbranch
(唯一的区别是它打印的是+face0ff,而不是+ face0ff...,即+ 标记后没有空格)。
这看起来很傻:我们需要git rev-list 和一堆选项以及两个分支名称和三个句点,而不是四个单词git cherry 和两个分支名称。我们已经输入了更多的字母来得到几乎相同的东西。所以我们可以只使用更短的命令并将其通过管道传递给git log,但现在我们要解决棘手的问题了。
git log 和 git rev-list 几乎是同一个命令
事实上,它们是从相同的源代码构建的,只是设置了一些不同的内部选项(如果你不指定任何其他起点,git log 会假装你说的是HEAD)。由于我们即将将git rev-list 的输出通过管道传输到git log --pretty='%h %ce',也许我们可以直接在git log 中完成整个操作。
果然,我们可以。我们需要与git rev-list 相同的所有选项,但现在我们可以运行:
git log --cherry-pick --right-only --pretty='%h %ce' firstbranch...secondbranch
(你可能也想在这里--no-merges - 我不确定git cherry 对合并做了什么!)。
1在这种情况下,“Sameness”由git patch-id 的输出决定,它基本上从差异中去除了识别行号(并且还去除了某些空白细节)。因此,如果一个提交被挑选到另一个分支中,那么两个这样的提交通常将具有相同的补丁 ID。 (如果有人必须解决合并冲突,补丁 ID 会有所不同。)