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

时间:2014-08-20 11:37:56

标签: git diff

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

差异不是累积的(每次提交都会单独列出更改)。

使用以下命令:

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

diff 是累积的,但不幸的是git diff不支持提交限制选项。

所以我喜欢的是git 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,我发现了这个:

  

&#34; DIFF&#34;是关于比较两个端点,而不是范围,范围符号(<commit>..<commit><commit>...<commit>)并不意味着&#34;指定范围&#34中定义的范围; gitrevisions(7)中的部分。

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

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的答案很有帮助,建议一个解决方案:创建一个临时分支,挑选相关的提交,然后生成差异。

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

2 个答案:

答案 0 :(得分:2)

这里至少有一个基本的误解。具体而言,git diff根本不是累积的:相反,它只是成对的。

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

git diff rev1 rev2
git diff rev1..rev2

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


有了这个,让我们来看看git log的幕后故事。 git log对范围的作用实际上是 1 将范围传递给git rev-list,这会生成范围内每个转速的列表,并沿途应用修改器: / p>

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解析选项并将它们拆分为差异选项和转换列表选项,您可以获得更多的体验,但这超出了您的需求。上面要改进的主要是摆脱硬编码的修订范围。


1 一些git命令确实确实将参数传递给git rev-list,因为它们只是使用git rev-list的shell脚本和其他git命令来处理这个。其他的是一起构建的,因此git loggit rev-list实际上是一个二进制文件,一部分将工作交给另一部分,但不调用新程序。

在任何情况下,请注意git log master只需将master关闭到git rev-list,这会生成一个可从分支标签master到达的所有转速的列表。如果您添加--no-walk,则git rev-list只会生成一次转化,因此git log仅显示该修订版。

答案 1 :(得分:0)

# Create a temporary branch to mark the start of the cherry-picked commits
git branch tmpstart

# Create and checkout a temporary branch for the cherry-picked commits
git checkout -b tmpend

# Use git log to filter the range of commits with the desired
# commit-limiting options, and then cherry-pick each matching commit
git log \
    --first-parent \      # Commit-limiting
    --no-merges \         # Commit-limiting
    --reverse \           # Reverse the order (ascending chronological order)
    --pretty=format:%h \  # Output the abbreviated hash of each matching commit
    29665b0..0b76a27 \    # Range of commits
  | xargs -n 1 git cherry-pick

# Generate the patch/stat/numstat of the cherry-picked commits
git diff --patch   tmpstart tmpend
git diff --stat    tmpstart tmpend
git diff --numstat tmpstart tmpend