当我们制作 git diff Version1..Version2 - file 时,此命令将返回如下内容:
diff --git a/wp-includes/version.php b/wp-includes/version.php
index 5d034bb9d8..617021e8d9 100644
这里的git比较两个版本的文件,以便您了解它们之间的区别。 我需要知道负责从索引 5d034bb9d8 和索引** 617021e8d9 *中添加有问题的文件的提交。
答案 0 :(得分:1)
这个(未经测试的)脚本可以做你想要的。阅读其余内容,了解其工作原理,是否有效,以及注意事项。
#! /bin/sh
case $# in
2);;
*) echo "usage: script left-specifier right-specifier" 1>&2; exit 1;;
esac
# turn arguments into hashes, then ensure they are commits
L=$(git rev-parse "$1") || exit
R=$(git rev-parse "$2") || exit
L=$(git rev-parse $L^{commit}) || exit
R=$(git rev-parse $R^{commit}) || exit
haveblob=$(git rev-parse $L:wp-includes/version.php) || exit
wantblob=$(git rev-parse $R:wp-includes/version.php) || exit
git rev-list --reverse --topo-order $R ^$L^@ | while read hash; do
thisblob=$(git rev-parse $hash:wp-includes/version.php)
test $thisblob = $haveblob && continue
if [ $thisblob = $wantblob ]; then
echo "target file appears in commit $hash"
exit 0 # we've found it - succeed and quit
fi
echo "note: commit $hash contains a different version than either end"
done
echo "error: got to the bottom of the loop"
exit 1
让我们再澄清一点:你已经跑了:
$ git diff <commit1> <commit2> -- wp-includes/version.php
及其输出读取部分:
index 5d034bb9d8..617021e8d9 100644
让我们调用<commit1>
- 您通过哈希或标记或分支名称或其他任何 - L 指定,其中 L 代表 git diff
的左侧。让我们为右侧调用第二个提交 R 。
您希望在 L 之前或之后, R 之前或之后找到一些提交,其中文件wp-includes/version.php
与中的版本匹配R ,即缩写哈希为617021e8d9
的那个。但是你不想要任何提交:你希望首先这样的提交 - 最接近 L 的提交。
值得注意的是,首先,两次提交之间可能没有明显的关系。也就是说,如果我们要绘制提交历史的图表,那么可能很简单:
...--o--o--L--M--N--...--Q--R--o--o--o <-- branch
但它可能不那么简单。目前,让我们假设它很简单。
L
,而 R 是R
,其间有直线提交在这种情况下,从 L 到 R 有一些直接的因果关系。你的问题的答案将非常有意义。具体来说,它回答了以下问题:这个版本来自哪里?从L
开始到R
结束的直接提交行和#&#的版本39; R
中的s也可能在之前的提交中。让我们看看如何在L
- 到 - R
序列中找到最早的提交,其中包含<{>相同的版本{{1>} 1}}。
首先,请注意每个提交代表该快照中所有文件的完整快照。也就是说,如果我们查看上面的提交R
,它会以某种形式存在所有文件。 N
中wp-includes/version.php
的副本可能与N
中的L
匹配,也可能与R
中的L
匹配。 (它显然无法与两者匹配:如果确实如此,R
中的那个将与index
中的那个匹配,并且没有L
行且没有差异输出。)
该文件可能位于R
和R
但不在任何之间的提交中,但在这种情况下,答案是是:该文件首先出现在L
。
该文件也可能在R
和L
以及某些中,但不是所有,中间提交:说M
有它,然后在N
中删除它,然后它再次出现在R
中的O
形式,然后它& #39;在L
中再次删除,依此类推。它出现在N
,P
,R
和M
中;它在O
,Q
和N
中丢失了。现在问题更加困难:你想在O
中看到它,即使它在R
中再次消失了吗?或者您是否只想在Q
中看到它,因为它在L
中丢失了?
无论如何,我们需要做的是枚举R
到git rev-list L..R
范围内的所有提交。所以我们从:
L
(将省略L
,这有点烦人)。 Git将以反向顺序列举这些;因为我们知道链是线性的,所以这实际上是直接相反的顺序。 (我们将在稍后看到如何针对更复杂的案例强制执行合理的订单。)要检查(git rev-list L..R; git rev-parse L)
本身,我们也可以明确地添加它:
lhash=$(git rev-parse L); git rev-list R ^${lhash}^@
或者我们可以使用相当复杂的技巧:
git rev-list L^..R
(详见the gitrevisions documentation)。更简单:
L
通常也可以正常工作:只有当git rev-list
是根提交时才会失败。
在任何情况下,R
的输出都是一堆提交哈希ID:提交Q
的哈希ID,然后是提交P
的哈希ID,然后是提交L
的哈希ID。 1}}等等,一直回到git rev-list
。因此,我们通过命令管道此L
的输出,以确定我们的特定blob来自何处。但我们希望以其他顺序访问提交:M
首先是N
,然后是R
,然后是--reverse
,一直到git rev-list
。因此,我们将sh
添加到bash
参数。
其余部分假设我们正在git rev-list
或#! /bin/sh
case $# in
2);;
*) echo "usage: script left-specifier right-specifier" 1>&2; exit 1;;
esac
# turn arguments into hashes, then ensure they are commits
L=$(git rev-parse "$1") || exit
R=$(git rev-parse "$2") || exit
L=$(git rev-parse $L^{commit}) || exit
R=$(git rev-parse $R^{commit}) || exit
# get the blob hashes, exit if they don't exist
haveblob=$(git rev-parse $L:wp-includes/version.php) || exit
wantblob=$(git rev-parse $R:wp-includes/version.php) || exit
git rev-list --reverse $R ^$L^@ | while read hash; do
...
done
或类似地编写此脚本。在我们运行 thisblob=$(git rev-parse $hash:wp-includes/version.php)
之前,让我们获取每个版本文件的完整blob-hash。然后我们将它们放在循环中:
|| continue
在循环中,让我们获取此提交的blob哈希:
|| break
如果此失败,则表示该文件已被删除。我们可以选择忽略它并跳过此提交,添加$haveblob
或停止$wantblob
,或者我们可以完全忽略这种可能性,假设文件将存在于每个提交中。由于最后一个是最简单的,我会在这里做。
如果此哈希匹配 test $thisblob = $haveblob && continue
if [ $thisblob = $wantblob ]; then
echo "target file appears in commit $hash"
exit 0 # we've found it - succeed and quit
fi
echo "note: commit $hash contains a different version than either end"
,则不是很有趣。如果它与 M-----N
/ \
...--L R <-- branch
\ /
O--P--Q
匹配,则非常有趣。如果它完全是别的,那就让我们说出来。所以循环的其余部分是:
M--N
/ \
...--L Q--R <-- branch
\ /
O--P
这是顶部的脚本(主要是好)。
图表可能在内部相当分支; R 甚至可以是合并提交:
...--o--o--o--L--o--o <-- branch1
\
o--...--o--R--o <-- branch2
或者来自一个人:
A--B--L <-- br1
C--D--R <-- br2
或者,图表可能是 L 和 R 完全不同:
...--o--R--E--F--G--L--o--...--o <-- branch
或(如果有多个root提交)它们甚至可以完全不相关,以图形方式显示:
git merge-base --is-ancestor A B
或者,它们可能是相关的,无论它是否是一个简单的线性关系,但向后:
A
如果两个提交 这样后退,你应该简单地交换它们。 (该脚本可以执行此操作:B
测试提交L..R
是否为提交L
的祖先。)
如果他们没有直接相关,则R
语法将排除R
提交的提交,同时列出可从L
到达的提交。如果他们完全无关,则R
无法访问从git merge-base
到达的提交,因此这只是&#34;历史记录中所有提交到L
&#34; 。在任何一种情况下,您可能会或可能不会找到答案,而且可能有或没有任何意义。
您可以使用上面的R
来测试这些情况:如果它们都不是另一个的祖先,它们可能通过共同的第三祖先 - 两者的实际合并基础相关联承诺 - 或者他们可能完全不相关。
如果有分支&#34;介于&#34; R
和--topo-order
以便在{{1}}之前或之前进行合并,遍历可能会以某种难以预测的顺序发生。为了强制Git以拓扑排序的顺序枚举提交,我在实际脚本中使用{{1}}。这迫使Git遍历每条腿&#34;一次合并一个。这不一定是关键,但它使得脚本输出的推理更容易。