查找 git 分支的提交,这些提交未被挑选到另一个分支中

Find commits of a git branch, which were not cherry picked into another branch

我在 git 中有两个分支,其中一个分支 master 包含所有提交,另一个分支,例如 release,其中包含一些从第一个分支中挑选出来的提交分支master。由于提交是在 release 中精心挑选的,因此它们与 master 中的相应提交具有不同的提交哈希值,但提交消息是相同的。

现在我想找到来自 master 的提交,这些提交没有被精心挑选到 release 中。请注意,由于冲突解决,精心挑选的提交可能与原始提交的代码不同。 我该怎么做? git 对此有原生支持吗?

示例:

master 分支机构:

git checkout master
git log --oneline -7

给予

2cba4b1d (HEAD -> master) Message subject for commit 7
f54fc16f Message subject for commit 6
4d871cbd Message subject for commit 5
a83ed44c Message subject for commit 4
48d0fb73 Message subject for commit 3
931da9a6 Message subject for commit 2
8553323b Message subject for commit 1

release 分支

git checkout release
git log --oneline -5

给予

d65a04c6 (HEAD -> release) Message subject for commit 7
8aeecd92 Message subject for commit 6
2a54e335 Message subject for commit 4
99985f38 Message subject for commit 3
e76a9bb4 Message subject for commit 1

所以这两个分支之间的区别将是两个带有消息主题的提交:

Message subject for commit 5
Message subject for commit 2

如果它显示提交哈希也可以:

4d871cbd Message subject for commit 5
931da9a6 Message subject for commit 2

补充说明和要求:

上面的例子returns diff 以与提交相同的顺序合并。在结果中获得与原始提交日志中相同的顺序有助于在 master 的原始提交日志中标识提交。如果也能实现就好了

在我的例子中,两个分支都有线性历史并且没有合并提交。

尝试以下:

comm -12 <(git rev-list ^master release --oneline | awk '{=""; print [=10=]}' | sort) <(git rev-list ^release master --oneline | awk '{=""; print [=10=]}' | sort)

这应该获取可从发行版访问但不能从 master 访问的提交并对它们进行排序。然后它将获取可以从 master 访问但不能从 release 访问的提交,并对它们进行排序。基于常见的排序字符串,comm 应该根据消息标题告诉你 只有 ,如果两个分支之间有任何共同点。

我们希望对字符串进行排序,因为可能存在某些提交顺序不正确的情况,因此 comm 无法正确地告诉我们它们是否相同。


我在本地对其进行了测试,它应该会为您提供某种形式的正确输出,但也许您有不同的用例。


现在您有了提交的标题,您可以 git log 它们并准确查看哪些提交是 cherry-picked。

我们可以把这个东西放在脚本中看起来像这样

#!/bin/bash

titles=$(comm -12 <(git rev-list ^release master --oneline | awk '{=""; print [=11=]}' | sort) <(git rev-list ^master release --oneline | awk '{=""; print [=11=]}' | sort))

IFS=$'\n' read -rd '' -a ts <<<"$titles"
for t in "${ts[@]}"; do    
    c=$(echo $t | tr -d '\n')
    git log master --oneline --grep "$c"
    # Or if you don't want to see a dialog from git log, use this below instead
    git log master --oneline --grep "$c" | grep "$c"
done

你的问题与我几个月前读到的另一个关于识别变基提交的方法的问题非常相似。与 rebase 一样,cherry-picking 是关于提取在提交中完成的更改并将它们应用到另一个提交。 None 这些命令会跟踪原始提交,只是不需要 git 来区分“副本”,主要是因为它们可能会产生冲突,并且如您所知,结果提交会有所不同。

幸运的是,git 为我们精心挑选的提交提供了很大的帮助:--cherry-pick 选项。我邀请您阅读整个说明(也大约 --left/right-only),但这是有趣的部分:

Omit any commit that introduces the same change as another commit on the “other side” when the set of commits are limited with symmetric difference.

看起来很有希望,对吧?不,这是问题所在:与另一个提交相同的更改。如果在冲突解决后精心挑选的提交有所不同怎么办? Git 无法将其标记为精选,因为它们不再是补丁等效的,而且此选项还不够。从最简单的情况(这不是你的情况)开始,所有精心挑选的提交都已成功应用到另一个分支,你可以这样解决:

git log --format="%h %s" --cherry-pick --oneline --left-only --no-merges master...release 

文档中解释得很好,除了 symmetric difference 的概念,总而言之,它包含了 master 上所有未在 [=17= 中成功挑选的提交].

它并不像我说的那样完美,但至少我们有一个好的起点:现在我们只需要从这个列表中删除所有提交消息对应于[=中另一个提交的提交消息的提交17=] 分支,找到产生冲突的精心挑选的提交。这是您剩下要做的唯一可能的检查,不包括 reflog。

这里是脚本(未完全测试):

git log --format="%h %s" --cherry-pick --oneline --left-only --no-merges master...release |
while read cmt_log 
do
    cmt_msg=`echo "${cmt_log}" | awk '{ =""; print }'`
    git log --format=" %s" master..release | grep --fixed-string -s "${cmt_msg}" > /dev/null || echo ${cmt_log}
done

基本上,从 %h %s 字符串我只保存主题(%s),然后我将它与 grep 一起使用以查找匹配项(如果存在),否则我将其打印在标准输出。我在 grep 选项中指定了 --fixed-string 只是为了确保提交消息不会被解释为正则表达式,匹配它不应该匹配的内容,例如。