好的,让我们根据问题编辑解决一个不同的“相当精确”的定义。
鉴于:
- 合并提交
M
- 最终树
T
包含文件f1
, f2
, ...,fn
- 和直系父母
p1
, p2
, ...,pn
你想要——不管其他可能的祖先1——所有文件,在给定任何两个不同的父母和的情况下,在和中都被“修改” 。fi
pa
pb
fi
pa
pb
这里“修改”的定义是,对于 commitp
和 file f
,p
它本身有一个单亲,p^
(p
既不是合并也不是根提交),并且p:f
(f
提交中的文件p
)不同于p^:f
(可能甚至不存在)中p^
)。
这表明使用以下明显(且完全未优化)的算法来查找树中满足此约束的所有文件:fi
T
# set M = merge commit ID and P to its complete list of parents
# (see previous scripts for how to achieve that)
for f in $(git ls-tree -r $M); do
found=false twice=false
for p in $P; do
$twice && continue # already announced
if modified_in $p $f; then
$found && twice=true || found=true
fi
$twice && echo $f # announce if found twice
done
done
其中modified_in
定义为:
modified_in() {
local p=$1 p_hat=$1^ path="$2"
assert_single_parent $p # optional: verify neither root commit nor merge
# (if you want to do this, it would be more efficient to do it once
# outside the "for f in ..." loop)
test ! -z "$(git diff-tree -r --diff-filter=AM $p_hat $p -- "$path")"
}
在这里,该git diff-tree
命令将输出如下一行:
:100644 100644 <sha1_in_p^> <sha1_in_p> M c
$p_hat
对于在和之间修改的文件$p
(sha1
值是 blob SHA-1),并且:
:000000 100644 <null_sha1> <sha1_in_p> A fgh
对于在那里添加的文件。--diff-filter=AM
确保没有输出用于删除(否则你会在这里得到一个)R
,并将-- "$path"
检查限制为给定的文件名路径。我很确定(但尚未测试)您不必担心(复制编辑C
和R
重命名),并且由于这些是提交树差异,而不是索引差异,U
因此(未合并)不会发生。所以我们只需要git diff-tree
使用该过滤器运行,并测试该命令是否打印任何内容。
(为了使这[可能]更有效,git diff-tree
在所有“有趣的”父母上运行所有可能的命令一次,不指定路径,保存他们的输出,然后交叉关联列出的所有文件。出现两次或更多的那些是你的候选人. 但这在sh
脚本中要困难得多:你需要像awk
这里这样的东西。)
awk
[编辑:不,你毕竟不需要,sort | uniq -d
会成功的。请参阅jthill 的新答案,它实现了对问题略有不同解释的更高效的版本,可能更接近真正的意图,我承认我仍然感到困惑。]
1也就是说,如果提交图看起来像这样,例如:
A -- B -- C -- D -- M -- ..
\-- E -- F --/
您只关心D
和F
相关的M
更改,而不关心C
和的更改E
。
如果您确实关心,您可能希望M
针对例如通过分别将 C-and-D 和 E-and-F 挤压在一起制成的临时树进行差异提交;或者一直做成对比较,或者类似的。基本上,您需要列出合并基础(commit B
,此处)和合并本身(M
)之间的 revs,然后弄清楚您希望如何处理它们。