代码之家  ›  专栏  ›  技术社区  ›  Alex Feinman

量化git diff中的变化量?

  •  29
  • Alex Feinman  · 技术社区  · 15 年前

    我使用git的目的有点不同寻常——它在我写小说时存储我的文本。(我知道,我知道……怪的。)

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

     (words added)+(words removed)
    

    它会加倍计算(单词变了),但我同意。

    输入一些魔法咒语并让Git在任何两个版本中报告这个距离度量是很好的。但是,git diff是补丁,它显示整个行,即使您在行中只使用了一个字符;我不希望这样做,特别是因为我的“行”是段落。理想情况下,我甚至可以指定我所说的“单词”(尽管\w+可能是可以接受的)。

    是否有Git Diff的标志来逐字给出不同之处?或者,是否存在使用标准命令行工具计算上述度量的解决方案?

    9 回复  |  直到 6 年前
        1
  •  10
  •   Edward Dale    15 年前

    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
    
        2
  •  9
  •   David Claridge    15 年前

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

    有几个选项可以让您决定要使用哪种格式,默认值是可读的,但如果您要将输出输入到脚本中,则可能需要--word diff=carpec。

        3
  •  9
  •   Community CDub    8 年前

    建立在 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)))'
    

    输出自 gitwa gitwd trimmed using xargs trick .

        4
  •  7
  •   James Wald    10 年前

    我想出了一个方法,通过在其他答案的基础上建立具体的数字。结果是一个近似值,但它应该足够接近,以作为添加或删除字符数量的有用指示器。以下是我当前分支与源/主分支的比较示例:

    $ 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 字符。由于差异,增加/删除的单个计数会膨胀。 + / - 然而,在大多数情况下,缩进字符之间的差异应该会抵消通货膨胀的很大一部分。

        5
  •  4
  •   Cascabel    15 年前

    Git已经(很长时间)有了 --color-words 选择权 git diff . 这并不能让你数数,但它让你看到了不同之处。

    scompt.com对wdiff的建议也很好;很容易就产生不同的意见(参见 git-difftool )从那里你只需要从输出WDiff就可以得到你真正想要的结果。

    不过,还有一件更令人兴奋的事要分享,来自Git的“What's Cooking:

    * 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 . 很可能很快它就会从下一个版本进入主版本,然后Git就可以在内部完成这一切——要么生成自己的word diff格式,要么生成类似于wdiff的格式。如果你有勇气,你可以从下一个开始构建Git,或者将这个提交合并到你的本地主文件中进行构建。

    感谢Jakub的评论:如果需要,您可以通过提供word regex(配置参数diff.*.word regex),进一步自定义word diff,如中所述。 gitattributes .

        6
  •  2
  •   Community CDub    8 年前

    我喜欢 Stoutie answer 想让它更具可配置性,来回答我的一些字数问题。最后得到了以下在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
    

    希望这能帮助别人!

        7
  •  1
  •   Flurrywinde    6 年前

    抱歉,我没有足够的信誉点来评论@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 从上一次提交后开始计算字数。

    要与原始/主控形状进行比较,请执行以下操作: git wdiffs origin/master git wdiffs-all origin/master .

    我最喜欢这个答案,因为它既能计算单词数,又能区分单词数,而且如果你能用管道输入的话。 colordiff 很漂亮很有颜色。(@miles-answer也很好,但需要您找出使用的时间。但是,我喜欢查找移动文本的想法。)

    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
    

    要了解添加了多少个单词,请添加 inserted changed 在上面的示例中,从第二行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))"
    
        8
  •  0
  •   codebeard    8 年前

    因为Git 1.6.3还有 git difftool ,可以配置为运行几乎所有外部diff工具。如果您喜欢 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 在别名中,告诉Git在执行diff之前将所有修改过的文件复制到一个临时目录中。

        9
  •  0
  •   Miles romeric    8 年前

    对于需要排除移动文本的某些用例,上述答案是失败的(例如,如果我将代码中的函数或乳胶中的段落进一步移动到文档中,我不想将所有这些都计算为更改!)

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

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

    git diff $sha~1..$sha|grep -e"^+[^+]" -e"^-[^-]"|sed -e's/.//'|sort|uniq -d|wc -w|xargs
    

    计算diff中的重复单词数,其中 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
    

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

    推荐文章