查找一个Git分支的提交记录,这些提交记录没有被cherry-pick到另一个分支中。

6
我在 git 中有两个分支,其中一个分支 master 包含所有提交记录,另一个分支,例如 release,包含从第一个分支 master 中挑选的某些提交记录。由于这些提交记录是在 release 中挑选的,所以它们具有不同的提交哈希值,但提交信息相同。
现在我想找到在 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

发布分支

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

额外的解释和要求:

上面的示例以合并提交的顺序返回diff。在结果中获得与原始提交日志相同的顺序,有助于在master的原始提交日志中识别提交。如果可能的话,也希望能够实现。

在我的情况下,两个分支都具有线性历史记录,并且没有合并提交。


请看这个链接: https://dev59.com/sFQJ5IYBdhLWcg3wx4-p - Dev-vruper
2个回答

5

您的问题与我几个月前读到的有关识别变基提交的方法非常相似。就像使用 rebase 一样,cherry-pick也是提取提交中所做更改并将其应用于另一个提交的操作。这些命令都不会跟踪原始提交,因为 Git 没有区分“副本”的必要性,而且它们可能会产生冲突,导致生成的提交与您所知道的提交不同。

幸运的是,Git 为我们提供了一个很好的帮助来处理 cherry-pick 的提交:--cherry-pick 选项。我邀请您阅读整个描述(关于 --left/right-only 也是如此),但这是有趣的部分:

如果另一侧的一组提交中引入了与另一个提交相同的更改,则在对称差分设置的限制下省略任何引入相同更改的提交。

看起来很有前途,对吧?但是,问题在于:和另一个提交相同的更改。如果经过冲突解决后,挑选的提交与原提交不同,怎么办?Git 无法将其标记为 cherry-picked,因为它们不再是补丁等效的了,而这个选项是不够的。从最简单的情况开始(这不是你的情况),即所有的 cherry-pick 提交都已成功应用到其他分支上,你可以使用以下方法解决:

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

文档解释得非常清楚,除了对称差异的概念,简而言之,它获取了在主分支master上未成功挑选到发布分支release的所有提交。

正如我所说,并不完美,但至少我们有了一个很好的起点:现在我们只需要从这个列表中删除那些提交消息与release分支中的另一个提交消息相对应的提交,找到产生冲突的挑选提交。这是你能够做的唯一可能的检查,不包括引用日志。

以下是脚本(未经充分测试):

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 '{ $1=""; print }'`
    git log --format=" %s" master..release | grep --fixed-string -s "${cmt_msg}" > /dev/null || echo ${cmt_log}
done

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

谢谢!工作得非常好。我还通过指定一个提交来与“release”进行比较,从而减少了对“master”的提交。 - k_rus

1
尝试以下操作:
comm -12 <(git rev-list ^master release --oneline | awk '{$1=""; print $0}' | sort) <(git rev-list ^release master --oneline | awk '{$1=""; print $0}' | sort)

此命令将获取从 release 可达但 master 不可达的提交并对其进行排序。接着,它将获取从 master 可达但 release 不可达的提交并同样进行排序。基于这两个分支的共同排序字符串,comm 会告诉你是否存在相同的提交信息标题。我们希望对这些字符串进行排序,因为有可能某些提交不是按照完美顺序排列的,因此 comm 无法准确判断它们是否相同。
我在本地进行了测试,它应该会为您提供某种形式的正确输出,但也许您有不同的用例。
现在您已经有了提交的标题,可以使用git log来查看确切被挑选的提交。我们可以将这个东西放在一个脚本中,看起来像这样。
#!/bin/bash

titles=$(comm -12 <(git rev-list ^release master --oneline | awk '{$1=""; print $0}' | sort) <(git rev-list ^master release --oneline | awk '{$1=""; print $0}' | 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

1
我马上会更新我的回答,以便为您提供实际提交的日志。 - mnestorov
我能够运行脚本,但结果很难解释。我正在尝试弄清楚如何仅从“master”获取提交,因为该脚本会打印所有现有分支的提交。 - k_rus
@k_rus 不好意思,我在git log命令中添加了“master”,这样它只会检查该分支的标题。现在它只会检查“master”分支。 - mnestorov
谢谢你的提示。现在我只获取主分支的提交记录。我会进一步尝试按时间排序。 - k_rus
没问题。希望我的回答有所帮助。由于您通过git log提交,它们应该已经按时间排序。您可以尝试使用“--oneline”和其他格式化参数来使输出更适合您的需求 :) - mnestorov
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接