git:具有提交限制的累积差异

git log有一些非常有用的
commit-limiting options,例如–no-merges和–first-parent.我希望能够在为一系列提交生成累积差异补丁/ stat / numstat时使用这些选项.

使用这些命令:

git log --oneline --first-parent --no-merges --patch   29665b0..0b76a27
git log --oneline --first-parent --no-merges --stat    29665b0..0b76a27
git log --oneline --first-parent --no-merges --numstat 29665b0..0b76a27

diff不是累积的(每次提交都会单独列出更改).

使用这些命令:

git diff --patch   29665b0..0b76a27
git diff --stat    29665b0..0b76a27
git diff --numstat 29665b0..0b76a27

差异是累积的,但遗憾的是git diff不支持提交限制选项.

所以我想要的是git diff的累积diff功能与git log的提交限制功能相结合.

我有一个想法是使用git log生成一个提交哈希列表,然后以某种方式将该列表传递给git diff以生成指定提交的累积差异.像这样的东西(显然这种管道散列到git diff的方法实际上并不起作用):

git log --pretty=format:%h --first-parent --no-merges 29665b0..0b76a27 | git diff

其中–pretty = format:%h输出匹配提交的哈希值.

更新

感谢@torek和@twalberg,我现在更清楚地了解git diff的操作.范围语法29665b0..0b76a27确实具有误导性,我现在明白它实际上并没有在一系列提交中执行累积差异.纵观docs,我发现了这个:

“diff” is about comparing two endpoints, not ranges, and the range notations (<commit>..<commit> and <commit>...<commit>) do not mean a range as defined in the “SPECIFYING RANGES” section in 07002.

考虑到这一点,我会重新解释我的问题.使用这些命令:

git log --oneline --first-parent --no-merges --patch   29665b0..0b76a27
git log --oneline --first-parent --no-merges --stat    29665b0..0b76a27
git log --oneline --first-parent --no-merges --numstat 29665b0..0b76a27

每个匹配的提交都会单独列出更改.如何组合这些单独的更改,以生成累积补丁/ stat / numstat?

链接possible duplicate question的答案很有帮助,建议一个解决方案:创建一个临时分支,挑选相关的提交,然后生成差异.

我刚刚发布了一个使用这种技术的答案,但我仍然有兴趣知道是否有一个不需要临时分支的解决方案?

最佳答案 这里至少有一个基本的误解.具体来说,git diff根本不是累积的:相反,它只是成对的.

具体来说,这两个命令做同样的事情:

git diff rev1 rev2
git diff rev1..rev2

也就是说,在git diff中,首先确实没有范围这样的东西.

有了这个,让我们看一下git log的幕后故事. git log对范围的作用是将范围交给git rev-list,它会生成范围内每个转速的列表,沿途应用修改器:

git rev-list 29665b0..0b76a27

吐出从0b76a27到达的每个转速,也不能从29665b0到达.添加–first-parent, – max-parents = 1(又名–no-merges)等过滤掉这里列出的一些转速.

最后的结果返回给git log,然后查看git rev-list命令中的每个修订版本 – 它也可以通过–date-order和–topo-order控制等等;请参阅documentation for git rev-list-并显示每个日志条目,可能还有git diff-tree生成的差异(对于单父提交,将提交与其父级进行比较).

那么,您可以直接调用git rev-list,然后从其输出中剥离顶部和底部修订. (在这种特殊情况下,您可能也需要–topo-order,以确保最后一次转换确实是最早的,图形方面,无论日期如何.)例如,在脚本中:

#! /bin/sh
tempfile=$(mktemp -t mydiff)
trap "rm -f $tempfile" 1 2 3 15
git rev-list 29665b0..0b76a27 --first-parent --no-merges --topo-order > $tempfile
# remember that the first rev listed is the last rev in the range
last=$(head -1 $tempfile)
first=$(tail -1 $tempfile)
rm -f $tempfile # done with it, don't leave it around while showing diff
git diff $first $last

通过使用git rev-parse来解析选项并将它们拆分为diff选项和rev-list选项,你可以获得相当的更高级,但这超出了你的需要.上面要改进的主要是摆脱硬编码的修订范围.

1一些git命令确实真正将参数关闭到git rev-list,因为它们只是使用git rev-list和其他git命令来处理这个问题的shell脚本.其他的是一起构建的,因此git log和git rev-list实际上是一个单独的二进制文件,一部分将一个部分交给另一部分,但是没有调用新的程序.

在任何情况下,请注意git log master只需将master关闭到git rev-list,它会生成一个可从分支标签主机到达的所有转速的列表.如果你添加–no-walk,git rev-list只产生一个rev,所以git log只显示那个版本.

点赞