git:具有提交限制的累积差异 [英] git: cumulative diff with commit-limiting
问题描述
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 log
我的一个想法是使用 git log
生成提交散列列表,然后以某种方式将该列表管理到
实际上并不工作): 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 rev-list $ c $
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屋!