查找一个 git 分支的提交,这些提交没有被挑选到另一个分支中 其他说明和要求:

问题描述

我在 git 中有两个分支,其中一个分支 master 包含所有提交,另一个分支,例如 release,包含一些从第一个分支中精选的提交 { {1}}。由于提交是在 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 的原始提交日志中识别提交。如果也能实现就好了。

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

解决方法

尝试遵循:

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
,

你的问题与我几个月前读到的另一个问题非常相似,关于一种识别重新提交的方法。与 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 中成功挑选的提交。

正如我所说的,它并不完美,但至少我们有一个很好的起点:现在我们只需要从这个列表中删除所有提交信息对应于 {{1} 中另一个提交的提交信息的提交分支,找到产生冲突的精选提交。这是您唯一要做的检查,不包括引用日志。

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

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