45

我将 git 用于一个稍微不寻常的目的——它在我写小说时存储我的文本。(我知道,我知道……令人讨厌。)

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

 (words added)+(words removed)

这将重复计算(单词已更改),但我可以接受。

输入一些魔法咒语并让 git 为任何两个修订报告这个距离度量会很棒。然而,git diffs 是补丁,即使你只在一行中旋转了一个字符,它也会显示整行;我不希望这样,特别是因为我的“行”是段落。理想情况下,我什至可以指定“单词”的含义(尽管 \W+ 可能是可以接受的)。

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

4

9 回答 9

14

基于James 和 Cornmacrelf 的输入,我添加了算术扩展,并提出了一些可重用的别名命令,用于计算在 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使用 xargs 技巧gitwd进行修剪。

从 Miles 的回答中添加的重复单词。

于 2015-08-05T04:42:30.780 回答
13

wdiff进行逐字比较。可以将 Git 配置为使用外部程序进行比较。基于这两个事实和这篇博文,下面的内容应该大致符合您的要求。

创建一个脚本来忽略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
于 2010-05-20T14:02:31.503 回答
11

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

有几个选项可以让你决定你想要的格式,默认值是可读的,但如果你将输出输入脚本,你可能需要 --word-diff=porcelain 。

于 2010-08-18T00:24:36.970 回答
10

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

于 2015-01-28T02:41:37.630 回答
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

这是介绍 word-diff 的提交。大概它很快就会从 next 进入 master ,然后 git 将能够在内部完成这一切——要么产生自己的 word diff 格式或类似于 wdiff 的东西。如果你有胆量,你可以从 next 构建 git,或者只是将那个提交合并到你的本地 master 中进行构建。

感谢 Jakub 的评论:如有必要,您可以通过提供单词正则表达式(配置参数 diff.*.wordRegex)来进一步自定义单词差异,记录在gitattributes中。

于 2010-05-20T14:28:13.787 回答
4

我喜欢Soutie回答,并希望让它更易于配置,以回答我遇到的一些字数统计问题。最终得到了以下在 ZSH 中工作并且应该在 Bash 中工作的解决方案。每个函数都接受任何修订或修订差异,默认情况下将世界的当前状态与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

希望这对某人有帮助!

于 2016-08-13T20:30:14.580 回答
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

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

于 2017-06-18T12:58:05.210 回答
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 wdiffsorgit wdiffs-all获取自上次提交以来的字数。

要与 origin/master 进行比较,请执行git wdiffs origin/masteror 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

要找出您添加了多少单词,请在上面的示例insertedchanged从第二行添加 184+11。

为什么不是第一行的任何东西?答:这些是删除的单词。

这是一个获取单个统一字数的 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))"
于 2018-12-11T08:01:59.403 回答
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管道。相反,别名中的额外参数告诉 git 在进行差异之前将所有修改过的文件复制到临时目录。wdiff-d-ddifftool

于 2016-11-28T17:08:45.690 回答