量化git diff的变化量?

时间:2010-05-20 13:50:01

标签: git word-count

我使用git有一个不寻常的目的 - 它在我写小说时存储我的文本。 (我知道,我知道......太讨厌了。)

我正在努力追踪生产力,并希望衡量后续提交之间的差异程度。作者对“工作”的代理是“写字”,至少在创作阶段。我不能使用直字数,因为它忽略了编辑和压缩,这两者都是写作的重要部分。我想我想跟踪:

 (words added)+(words removed)

会重复计算(单词更改),但我没关系。

输入一些神奇的咒语并让git报告任何两个版本的距离指标都很棒。但是,git diffs是补丁,即使你只是在线上捣乱了一个字符,也会显示整行。我不希望这样,特别是因为我的“行”是段落。理想情况下,我甚至可以用“单词”来指定我的意思(虽然\ W +可能是可以接受的)。

git-diff是否有一个标志,可以逐个字地给出差异?或者,是否有使用标准命令行工具来计算上述指标的解决方案?

9 个答案:

答案 0 :(得分:11)

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

答案 1 :(得分:9)

git diff --word-diff在最新的稳定版git中工作(在git-scm.com上)

有一些选项可以让你决定你想要它的格式,默认是可读的,但如果你将输出提供给脚本,你可能需要--word-diff = porcelain。

答案 2 :(得分:9)

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 gitw='echo $(($(gitwa) - $(gitwd)))'

gitwagitwd的输出为trimmed using xargs trick

答案 3 :(得分:8)

我想出了一种方法,可以通过建立其他答案来获得具体数字。结果是近似值,但它应足够接近,以作为添加或删除的字符数量的有用指示。以下是我的当前分支与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个字符。由于差异+ / -和缩进字符,这些单独添加/删除的计数会被夸大,但是,在大多数情况下,这种差异应抵消该通货膨胀的很大一部分。

答案 4 :(得分:4)

Git已经(很长一段时间)--color-words git diff选项。这不会让你计算,但它确实让你看到差异。

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。据推测,它不久将从下一个进入master,然后git将能够在内部完成所有这些 - 或者生成自己的单词diff格式或类似于wdiff的东西。如果你很大胆,你可以从下一个构建git,或者只是将一个提交合并到你的本地master中来构建。

感谢Jakub的评论:如果需要,您可以通过提供gitattributes中记录的单词regex(配置参数diff。*。wordRegex)来进一步自定义单词差异。

答案 5 :(得分:2)

我喜欢Stoutieanswer,并想让它更具可配置性来回答我的一些字数问题。结束以下在ZSH中工作的解决方案,并且应该在Bash中工作。每个函数都会使用revision or revision difference,默认情况下将世界当前状态与origin/master进行比较:


# Calculate writing word diff between revisions. Cribbed / modified from:
# https://stackoverflow.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 :(得分:1)

对不起,我没有足够的声誉积分来评论@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来获取自上次提交以来的字数。

要与来源/母版进行比较,请执行git wdiffs origin/mastergit wdiffs-all origin/master

我最喜欢这个答案,因为它同时给出了字数和差异,并且如果您通过colordiff进行传递,它的外观也会很漂亮而且颜色也很丰富。 (@Miles答案也不错,但需要您弄清楚要使用什么时间。但是,我喜欢寻找移动文本的想法。)

wdiff的统计信息最后输出如下:

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))"

答案 7 :(得分:0)

由于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选项设置为仅解释输入。相反,别名中-d的额外difftool参数告诉g​​it在执行diff之前将所有已修改的文件复制到临时目录。

答案 8 :(得分:0)

上述答案因某些需要排除移动文本的用例而失败(例如,如果我在代码中移动一个函数或者在文档中进一步向下移动文档,我不想将所有这些作为更改计算! )

为此,您还可以计算重复行数,如果副本太多,则从查询中排除这些行。

例如,基于其他答案,我可以这样做:

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

打印:添加,删除,重复

(我对重复项采用了行差异,因为它排除了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

我只是在处理事物的提交是显而易见的,所以我不计算这些变化。它会计算其他所有内容并告诉我更改单词的总数。