问题描述
我在 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-pick
ing 是关于提取在一次提交中完成的更改并将其应用于另一个提交。这些命令都没有跟踪原始提交,只是不需要 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
只是为了确保提交消息不会被解释为正则表达式,例如匹配不应该的内容。