量化 git diff 的变化量?
Posted
技术标签:
【中文标题】量化 git diff 的变化量?【英文标题】:Quantifying the amount of change in a git diff? 【发布时间】:2011-02-21 21:23:50 【问题描述】:我使用 git 有点不寻常的目的——它在我写小说时存储我的文本。 (我知道,我知道……怪胎。)
我正在尝试跟踪生产力,并希望衡量后续提交之间的差异程度。作者对“作品”的代理是“文字”,至少在创作阶段是这样。我不能使用直接字数统计,因为它忽略了编辑和压缩,这两个都是写作的重要部分。我想我想跟踪:
(words added)+(words removed)
这会重复计算(单词已更改),但我可以接受。
输入一些魔法咒语并让 git 报告任何两个修订版的这个距离指标会很棒。然而,git diffs 是补丁,即使你只在一行中旋转了一个字符,它也会显示整行;我不希望这样,特别是因为我的“行”是段落。理想情况下,我什至可以指定“单词”的含义(尽管 \W+ 可能是可以接受的)。
git-diff 是否有一个标志可以逐字给出差异?或者,是否有使用标准命令行工具来计算上述指标的解决方案?
【问题讨论】:
这真是一个极好的用法——git 是一个内容跟踪器。这并不罕见 - 看看去年的 git 调查git.wiki.kernel.org/index.php/…: 实际上,我所有的写作都使用 git。我是在写论文时开始的,它以备份所没有的方式节省了我的培根很多次。我也用 markdown 或者 Latex 来写,git 配合得很好。 【参考方案1】:在James' and cornmacrelf's input 的基础上,我添加了arithmetic expansion, 并提出了一些可重用的别名命令,用于计算 git diff 中添加、删除和重复的单词:
alias gitwa='git diff --word-diff=porcelain origin/master | grep -e "^+[^+]" | wc -w | xargs'
alias gitwd='git diff --word-diff=porcelain origin/master | grep -e "^-[^-]" | wc -w | xargs'
alias gitwdd='git diff --word-diff=porcelain origin/master |grep -e"^+[^+]" -e"^-[^-]"|sed -e's/.//'|sort|uniq -d|wc -w|xargs'
alias gitw='echo $(($(gitwa) - $(gitwd)))'
gitwa
和 gitwd
的输出是 trimmed using xargs trick。
从 Miles 的answer 添加的重复单词。
【讨论】:
【参考方案2】:抱歉,我没有足够的声望点来评论 @codebeard 的回答。这是我使用的那个,我将他的两个版本都添加到了我的 .gitconfig 文件中。他们给出了不同的答案,我在第二个版本(将所有修改的文件组合在一起的版本)中将问题追溯到wdiff -sd
,计算diff -pdrU3
输出顶部两行中的单词。它会是这样的:
--- 1 2018-12-10 22:53:47.838902415 -0800
+++ 2 2018-12-10 22:53:57.674835179 -0800
我通过管道通过tail -n +4
解决了这个问题。
这是我完整的 .gitconfig 设置,修复到位:
[alias]
wdiff = diff
wdiffs = difftool -t wdiffs
wdiffs-all = difftool -d -t wdiffs-all
[difftool "wdiffs"]
cmd = wdiff -n -s \"$LOCAL\" \"$REMOTE\" | colordiff
[difftool "wdiffs-all"]
cmd = diff -pdrU3 \"$LOCAL\" \"$REMOTE\" | tail -n +4 | wdiff -sd
如果您更愿意使用git config
,请使用以下命令:
git config --global difftool.wdiffs.cmd 'wdiff -n -s "$LOCAL" "$REMOTE"' | colordiff
git config --global alias.wdiffs 'difftool -t wdiffs'
git config --global difftool.wdiffs-all.cmd 'diff -pdrU3 "$LOCAL" "$REMOTE" | wdiff -sd'
git config --global alias.wdiffs-all 'difftool -d -t wdiffs-all'
现在,您可以通过 git wdiffs
或 git wdiffs-all
获取自上次提交以来的字数。
要与 origin/master 进行比较,请执行 git wdiffs origin/master
或 git wdiffs-all origin/master
。
我最喜欢这个答案,因为它同时给出了字数和差异,如果你通过colordiff
进行管道传输,它会变得漂亮且有颜色。 (@Miles 的回答也不错,但需要您弄清楚使用什么时间。但是,我喜欢寻找移动文本的想法。)
最后 wdiff 的 stats 输出如下所示:
file1.txt: 12360 words 12360 100% common 0 0% deleted 5 0% changed
file2.txt: 12544 words 12360 99% common 184 1% inserted 11 0% changed
要了解您添加了多少字,请在上例中的第二行 184+11 中添加 inserted
和 changed
。
为什么不从第一行开始?答:那些是被删除的词。
这是一个 bash 脚本,用于获取单一、统一的字数:
wdiffoutput=$(git wdiffs-all | tail -n 1)
wdiffins=$(echo "$wdiffoutput" | grep -oP "common *\K\d*")
wdiffchg=$(echo "$wdiffoutput" | grep -oP "inserted *\K\d*")
echo "Word Count: $((wdiffins+wdiffchg))"
【讨论】:
【参考方案3】:对于某些需要排除移动文本的用例(例如,如果我将代码中的函数或乳胶中的段落移动到文档下方,则上述答案会失败,我不想将所有这些都计为更改! )
为此,您还可以计算重复行数,如果重复行数过多,则将其排除在查询之外。
例如,在其他答案的基础上,我可以这样做:
git diff $sha~1..$sha|grep -e"^+[^+]" -e"^-[^-]"|sed -e's/.//'|sort|uniq -d|wc -w|xargs
计算差异中重复单词的数量,其中sha
是您的提交。
您可以通过以下方式对最后一天(从早上 6 点开始)内的所有提交执行此操作:
for sha in $(git rev-list --since="6am" master | sed -e '$ d'); do
echo $(git diff --word-diff=porcelain $sha~1..$sha|grep -e"^+[^+]"|wc -w|xargs),\
$(git diff --word-diff=porcelain $sha~1..$sha|grep -e"^-[^-]"|wc -w|xargs),\
$(git diff $sha~1..$sha|grep -e"^+[^+]" -e"^-[^-]"|sed -e's/.//'|sort|uniq -d|wc -w|xargs)
done
打印:添加、删除、重复
(我将行 diff 用于重复,因为它排除了 git diff
试图过于聪明的时间,并假设您实际上只是更改了文本而不是移动它。它还打折计算单个单词的实例作为副本。)
或者,如果你想更复杂一点,如果重复超过 80%,你可以完全排除提交,然后总结其余部分:
total=0
for sha in $(git rev-list --since="6am" master | sed -e '$ d'); do
added=$(git diff --word-diff=porcelain $sha~1..$sha|grep -e"^+[^+]"|wc -w|xargs)
deleted=$(git diff --word-diff=porcelain $sha~1..$sha|grep -e"^-[^-]"|wc -w|xargs)
duplicated=$(git diff $sha~1..$sha|grep -e"^+[^+]" -e"^-[^-]"|sed -e's/.//'|sort|uniq -d|wc -w|xargs)
if [ "$added" -eq "0" ]; then
changed=$deleted
total=$((total+deleted))
echo "added:" $added, "deleted:" $deleted, "duplicated:"\
$duplicated, "changed:" $changed
elif [ "$(echo "$duplicated/$added > 0.8" | bc -l)" -eq "1" ]; then
echo "added:" $added, "deleted:" $deleted, "duplicated:"\
$duplicated, "changes counted:" 0
else
changed=$((added+deleted))
total=$((total+changed))
echo "added:" $added, "deleted:" $deleted, "duplicated:"\
$duplicated, "changes counted:" $changed
fi
done
echo "Total changed:" $total
我在这里有这个脚本:https://github.com/MilesCranmer/git-stats。
打印出来:
➜ bifrost_paper git:(master) ✗ count_changed_words "6am"
added: 38, deleted: 76, duplicated: 3, changes counted: 114
added: 14, deleted: 19, duplicated: 0, changes counted: 33
added: 1113, deleted: 1112, duplicated: 1106, changes counted: 0
added: 1265, deleted: 1275, duplicated: 1225, changes counted: 0
added: 4207, deleted: 4208, duplicated: 4391, changes counted: 0
Total changed: 147
我只是在四处移动的提交是显而易见的,所以我不计算这些更改。它会计算其他所有内容并告诉我更改的单词总数。
【讨论】:
要使这项工作与Stoutie's answer 完全一样,您可以使用git diff --word-diff=porcelain origin/master |grep -e"^+[^+]" -e"^-[^-]"|sed -e's/.//'|sort|uniq -d|wc -w|xargs
【参考方案4】:
从 Git 1.6.3 开始,还有 git difftool
,可以配置为运行几乎任何外部差异工具。这比一些需要创建脚本等的解决方案容易得多。如果你喜欢wdiff -s
的输出,你可以配置如下:
git config --global difftool.wdiffs.cmd 'wdiff -s "$LOCAL" "$REMOTE"'
git config --global alias.wdiffs 'difftool -t wdiffs'
现在您可以运行 git difftool -t wdiffs
或其别名 git wdiffs
。
如果您希望同时获取所有修改文件的统计信息,请执行以下操作:
git config --global difftool.wdiffs.cmd 'diff -pdrU3 "$LOCAL" "$REMOTE" | wdiff -sd'
git config --global alias.wdiffs 'difftool -d -t wdiffs'
这采用典型的统一diff
的输出并将其通过管道传送到wdiff
,其-d
选项设置为仅解释输入。相比之下,别名中 difftool
的额外 -d
参数告诉 git 在进行差异之前将所有修改过的文件复制到临时目录。
【讨论】:
【参考方案5】:我喜欢Stoutie 的answer 并希望它更易于配置以回答我遇到的一些字数统计问题。最终得到了以下在 ZSH 中工作并且应该在 Bash 中工作的解决方案。每个函数采用任意revision or revision difference,默认将当前世界状态与origin/master
进行比较:
# Calculate writing word diff between revisions. Cribbed / modified from:
# https://***.com/questions/2874318/quantifying-the-amount-of-change-in-a-git-diff
function git_words_added
revision=$1:-origin/master
git diff --word-diff=porcelain $revision | \
grep -e "^+[^+]" | \
wc -w | \
xargs
function git_words_removed
revision=$1:-origin/master
git diff --word-diff=porcelain $revision | \
grep -e "^-[^-]" | \
wc -w | \
xargs
function git_words_diff
revision=$1:-origin/master
echo $(($(git_words_added $1) - $(git_words_removed $1)))
那么你可以这样使用它:
$ git_words_added
# => how many words were added since origin/master
$ git_words_removed
# => how many words were removed since origin/master
$ git_words_diff
# => difference of adds and removes since origin/master (net words)
$ git_words_diff HEAD
# => net words since you last committed
$ git_words_diff master@yesterday
# => net words written today!
$ git_words_diff HEAD^..HEAD
# => net words in the last commit
$ git_words_diff ABC123..DEF456
# => net words between two arbitrary commits
希望这对某人有所帮助!
【讨论】:
【参考方案6】:我想出了一种方法,通过建立在此处的其他答案之上来获得具体数字。结果是一个近似值,但它应该足够接近以作为添加或删除字符数量的有用指标。这是我当前分支与 origin/master 相比的示例:
$ git diff --word-diff=porcelain origin/master | grep -e '^+[^+]' | wc -m
38741
$ git diff --word-diff=porcelain origin/master | grep -e '^-[^-]' | wc -m
46664
删除字符 (46664
) 和添加字符 (38741
) 之间的差异表明我当前的分支已删除大约 7923
字符。由于差异的+
/-
和缩进字符,这些单独添加/删除的计数被夸大了,但是,在大多数情况下,差异应该抵消了大部分的膨胀。
【讨论】:
为什么不一路回答原问题呢?wc -w
会改为查找字数。更有用。
您可以将sed 's/^.//'
添加到管道中以删除尾随的'+'/'-'。【参考方案7】:
git diff --word-diff 适用于最新的稳定版 git(在 git-scm.com)
有几个选项可以让您决定您想要的格式,默认值是可读的,但如果您将输出输入到脚本中,您可能需要 --word-diff=porcelain。
【讨论】:
它没有给出每个单词的统计信息,这就是问题所在。 它可以帮助您完成 99% 的任务。例如:git diff --word-diff=porcelain | grep -e '^+[^+]\|^-[^-]'
【参考方案8】:
Git (很长一段时间以来)为git diff
提供了--color-words
选项。这不会让您数数,但确实可以让您看到差异。
scompt.com 对 wdiff 的建议也不错;在不同的地方全推很容易(见git-difftool)。从那里你只需要从输出 wdiff 可以得到你真正想要的结果。
不过,还有一件更令人兴奋的事情要分享,来自 git 的烹饪之道:
* tr/word-diff (2010-04-14) 1 commit
(merged to 'next' on 2010-05-04 at d191b25)
+ diff: add --word-diff option that generalizes --color-words
这是commit introducing word-diff。据推测,它很快就会从 next 进入 master ,然后 git 将能够在内部完成这一切 - 生成自己的 word diff 格式或类似于 wdiff 的东西。如果你有胆量,你可以从 next 构建 git,或者只是将那个提交合并到你的本地 master 中进行构建。
感谢 Jakub 的评论:如有必要,您可以通过提供单词正则表达式(配置参数 diff.*.wordRegex)来进一步自定义单词差异,记录在 gitattributes 中。
【讨论】:
还有diff.<diff driver>.wordRegex
配置变量,和diff=<diff driver>
gitattribute一起,你可以定义什么是word。【参考方案9】:
wdiff 进行逐字比较。可以将 Git 配置为使用外部程序进行比较。基于这两个事实和this blog post,以下内容应该大致符合您的要求。
创建一个脚本来忽略git-diff
提供的大部分不必要的参数,并将它们传递给wdiff
。将以下内容另存为 ~/wdiff.py
或类似名称并使其可执行。
#!/usr/bin/python
import sys
import os
os.system('wdiff -s3 "%s" "%s"' % (sys.argv[2], sys.argv[5]))
告诉git
使用它。
git config --global diff.external ~/wdiff.py
git diff filename
【讨论】:
在git diff --word-diff
出现之前,这是一个很好的答案
@Jarus 据我所知,这仍然是最好的答案:--word-diff
不显示每个单词的统计信息。
现在有了git difftool
,不再需要永久修改git的diff
输出,你可以定义任意多的自定义差异命令,当然你也可以给它们起别名。见***.com/a/40849802/1764245以上是关于量化 git diff 的变化量?的主要内容,如果未能解决你的问题,请参考以下文章