【问题标题】:Find commits of a git branch, which were not cherry picked into another branch查找 git 分支的提交,这些提交不是樱桃挑选到另一个分支
【发布时间】:2021-07-05 03:03:18
【问题描述】:

我在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

其他说明和要求:

上面的示例以与合并提交相同的顺序返回差异。在结果中获得与原始提交日志相同的顺序有助于在 master 的原始提交日志中识别提交。如果也能实现就好了。

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

【问题讨论】:

标签: git cherry-pick


【解决方案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)

这应该获取可从发布但不能从主控访问的提交并对它们进行排序。然后它将获取从 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

【讨论】:

  • 感谢您的建议。它可以找到差异。但是,提交消息是按提交消息主题排序的,这使得在原始提交日志中难以识别。您是否了解如何改进代码以按时间排序结果?就我而言,这是线性历史。我会更新问题。
  • 如何使用git log通过提供提交消息主题(即脚本的结果)来获取提交?我想在此之后我可以在提交历史顺序中得到结果。
  • 我稍后会更新我的答案,以获取实际提交的日志
  • @k_rus 我添加了一个示例脚本,但在本地运行它时遇到问题。你能确保它适合你吗?
  • @k_rus 我添加了一些选项。我的 bash-fu 无可非议,但它可能对你有用。
【解决方案2】:

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

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

忽略任何引入与另一个提交相同的更改的提交 当提交集受到对称限制时的“另一边” 区别。

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

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

文档中对此进行了很好的解释,除了symmetric difference 的概念外,总而言之,它采用了master 上的所有未在release 中成功挑选的提交。

这并不像我说的那样完美,但至少我们有一个好的起点:现在我们只需从这个列表中删除所有提交消息对应于release 分支中另一个提交的提交消息的提交,找到产生冲突的精心挑选的提交。这是您剩下要做的唯一可能的检查,不包括 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 '{ $1=""; 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,只是为了确保提交消息不会被解释为正则表达式,例如匹配不应该匹配的内容。

【讨论】:

  • 谢谢!效果很好。我还通过指定与release 比较的提交来减少master 的提交。
猜你喜欢
  • 1970-01-01
  • 2018-12-28
  • 2016-05-30
  • 2014-11-21
  • 2018-07-23
  • 1970-01-01
  • 1970-01-01
  • 2016-04-16
  • 1970-01-01
相关资源
最近更新 更多