git:具有提交限制的累积差异 [英] git: cumulative diff with commit-limiting

查看:122
本文介绍了git:具有提交限制的累积差异的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

git log 有一些非常有用的提交限制选项,例如 - 不合并 - 第一亲本。我希望能够在为一系列提交生成累计差异补丁/统计/数量统计时使用这些选项。



使用以下命令:

  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
code>

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

使用以下命令:

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

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

所以我想要的是 git diff 的累积diff功能与提交限制f git log 。



我的一个想法是使用 git log 生成提交散列列表,然后以某种方式将该列表管理到 git diff 来生成指定提交的累积差异。像这样的东西(很明显,这种管道散列方式< git diff 实际上并不工作):

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

其中 - pretty =格式:%h 输出匹配提交的散列值。




更新



感谢@torek和@twalberg,我现在更清楚地理解了 git diff 的操作。范围语法 29665b0..0b76a27 确实是误导性的,现在我明白它实际上并没有在一系列提交中执行累计差异。仔细查看文档,我发现这个:


diff是关于比较两个端点,而不是范围和范围记号(< commit> ..< commit> < commit> ...< commit> )并不意味着范围在 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

对于每个匹配的提交,单独列出更改。我怎样才能将这些单独的修改结合起来,产生一个累积的补丁/统计/数量统计?



链接的答案可能的重复问题很有帮助,建议解决方案:创建一个临时分支,挑选相关的提交,然后生成diff。



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

解决方案

这里至少有一个基本的误解。特别是, git diff 根本不是真正的累积:相反,它只是成对的。



具体而言,这两个命令做同样的事情:

  git diff rev1 rev2 
git diff rev1..rev2

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






让我们看看后面的 git log git log 用范围确实是 1 来传递范围到 git rev-list

  git rev-list 29665b0 .0b76a27 

吐出可从 0b76a27 不能从 29665b0 中访问。添加 - 第一父母 - max-parents = 1 (aka - no-merges ),等等过滤掉这里列出的一些rev。



最终结果返回给 git log ,然后按照 git rev-list 的顺序查看每个修订版将其吐出 - 这也可以通过 - date-order - topo-order 等等;请参阅 git rev-list - 并向您显示每个日志条目,可能还会生成 git diff-tree (对于单亲提交,将提交与其父代进行比较)。



然后,您可以直接调用 git rev-list ,然后从其输出中剥离顶部和底部修订版。 (在这种情况下,您可能也需要 - topo-order ,以确保最后一个rev的确是最早的,图形式的,无论日期如何。)例如,在脚本中:

 #! / bin / sh 
tempfile = $(mktemp -t mydiff)
traprm -f $ tempfile1 2 3 15
git rev-list 29665b0..0b76a27 --first-parent --no-merges --topo-order> $ tempfile
#请记住,列出的第一个rev是范围
last = $(head -1 $ tempfile)中的最后一个rev
first = $(tail -1 $ tempfile)
rm -f $ tempfile#完成它,不要在显示diff
git diff $ first $ last

$时放置它b
$ b

通过使用 git rev-parse 来解析选项并将它们拆分为diff选项和rev-list选项,这超出了你在这里所需要的范围。以上改进主要是为了摆脱硬编码的修订范围。






1 一些git命令真的 $ c> git rev-list 和其他git命令来处理这个问题。其他的都是一起构建的,所以 git log git rev-list 实际上是一个二进制文件,在任何情况下,请注意 git log master >简单地把 master 关闭到 git rev-list ,它产生一个从分支标签到达的所有转速列表。如果添加 - no-walk git rev-list 只产生一次转换,所以 git log 只显示一个版本。


git log has some very useful commit-limiting options, such as --no-merges and --first-parent. I'd like to be able to use these options when generating a cumulative diff patch/stat/numstat for a range of commits.

With these commands:

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

the diff is not cumulative (the changes are listed individually for each commit).

With these commands:

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

the diff is cumulative, but unfortunately git diff doesn't support the commit-limiting options.

So what I'd like is the cumulative diff functionality of git diff combined with the commit-limiting functionality of git log.

One idea I had is to use git log to generate a list of commit hashes, and then somehow pipe that list to git diff to generate a cumulative diff of the specified commits. Something like this (obviously this method of piping hashes to git diff doesn't actually work):

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

where --pretty=format:%h outputs the hashes of the matching commits.


Update

Thanks to @torek and @twalberg, I now understand git diff's operation more clearly. The range syntax 29665b0..0b76a27 is indeed misleading, and I now understand that it's not actually performing a cumulative diff over a range of commits. Looking through the docs, I found this:

"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 gitrevisions(7).

Taking this into account, I'll rephrase my question. With these commands:

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

the changes are listed individually for each matching commit. How can I combine those individual changes, to produce a cumulative patch/stat/numstat?

The answers to the linked possible duplicate question are helpful, suggesting a solution: create a temporary branch, cherry-pick the relevant commits, and then generate the diff.

I've just posted an answer which uses this technique, but I'm still interested to know if there's a solution which doesn't require a temporary branch?

解决方案

There is at least one basic misapprehension here. Specifically, git diff is not really cumulative at all: instead, it's simply pairwise.

Specifically, these two commands do the same thing:

git diff rev1 rev2
git diff rev1..rev2

That is, in git diff, there really is no such thing as a range in the first place.


With that out of the way, let's take a look behind the scenes at git log. What git log does with a range is really1 to hand the range to git rev-list, which produces a list of every rev in the range, applying the modifiers along the way:

git rev-list 29665b0..0b76a27

spits out every rev reachable from 0b76a27 that is not also reachable from 29665b0. Adding --first-parent, --max-parents=1 (aka --no-merges), and so on filters away some of the revs that would be listed here.

The final result is given back to git log, which then looks at each revision in the order git rev-list spits them out—this is also controllable via --date-order and --topo-order and so on; see the documentation for git rev-list—and shows you each log entry, perhaps along with a diff as produced by git diff-tree (which for single-parent commits, compares the commit to its parent).

What you can do, then, is invoke git rev-list yourself, directly, and then peel off the top and bottom revisions from its output. (In this particular case you probably want --topo-order too, to make sure that the last rev really is the earliest, graph-wise, regardless of dates.) For instance, in a script:

#! /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

You can get considerably fancier by using git rev-parse to parse options and split them into diff options vs rev-list options, but that's way beyond what you need here. The main thing to improve above is to get rid of the hard-coded revision-range.


1Some git commands really really do hand arguments off to git rev-list, as they're just shell scripts that use git rev-list and other git commands to handle this. Others are built together, so that git log and git rev-list are actually a single binary, and one part hands a job off to another part, but without invoking a new program.

In any case, note that git log master simply hands master off to git rev-list, which produces a list of all revs reachable from the branch-label master. If you add --no-walk, git rev-list produces just one rev, so that git log shows only that one revision.

这篇关于git:具有提交限制的累积差异的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆