量化 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)))'

gitwagitwd 的输出是 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 wdiffsgit wdiffs-all 获取自上次提交以来的字数。

要与 origin/master 进行比较,请执行 git wdiffs origin/mastergit 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 中添加 insertedchanged

为什么不从第一行开始?答:那些是被删除的词。

这是一个 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 的变化量?的主要内容,如果未能解决你的问题,请参考以下文章

Pytorch模型量化实践并以ResNet18模型量化为例(附代码)

TensorRT--INT8量化

量化给定 3d 点数组的方向变化

金融量化Tick 数据和Bar数据的区别

金融量化Tick 数据和Bar数据的区别

金融量化Tick 数据和Bar数据的区别