Подтвердить что ты не робот

Количественное определение величины изменения в git diff?

Я использую git для немного необычной цели - он хранит мой текст, когда я пишу фикцию. (Я знаю, я знаю... geeky.)

Я пытаюсь отслеживать производительность и хочу измерить степень разницы между последующими коммитами. Писатель-прокси для "работы" - это "слова, написанные", по крайней мере, во время стадии создания. Я не могу использовать счетчик прямых слов, поскольку он игнорирует редактирование и сжатие, как жизненно важные части письма. Я думаю, что хочу отслеживать:

 (words added)+(words removed)

который будет удвоен (слова изменены), но я в порядке с этим.

Было бы здорово набрать какое-то магическое заклинание и git сообщить эту метрику расстояния для любых двух ревизий. Тем не менее, git diffs - это патчи, которые показывают целые строки, даже если вы только крутили один символ на линии; Я не хочу этого, тем более, что мои "строки" - это абзацы. В идеале я бы даже смог указать, что я подразумеваю под словом (хотя\W +, вероятно, будет приемлемым).

Есть ли флаг для git -diff, чтобы дать различие на основе слова? Альтернативно, существует ли решение с использованием стандартных средств командной строки для вычисления метрики выше?

4b9b3361

Ответ 1

wdiff сопоставляется по словам. Git можно настроить для использования внешней программы для выполнения различения. Основываясь на этих двух фактах и ​​этот пост в блоге, следующее должно сделать примерно то, что вы хотите.

Создайте script, чтобы игнорировать большинство ненужных аргументов, которые 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

git diff -word-diff работает в последней стабильной версии git (на git -scm.com)

Есть несколько вариантов, которые позволяют вам решить, в каком формате вы хотите, по умолчанию это вполне читаемо, но вы можете захотеть --word-diff = фарфор, если вы загружаете вывод в script.

Ответ 3

Основываясь на вкладке 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 gitw='echo $(($(gitwa) - $(gitwd)))'

Вывод из gitwa и gitwd обрезается с помощью трюка xargs.

Ответ 4

Я понял способ получить конкретные числа, опираясь на другие ответы здесь. Результат - приблизительное, но оно должно быть достаточно близко, чтобы служить полезным индикатором количества символов, которые были добавлены или удалены. Вот пример с моей текущей веткой по сравнению с 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 символов. Эти отдельные добавленные/удаленные подсчеты накачиваются из-за различий +/- и символов отступа, однако разница в большинстве случаев должна отменить значительную часть этой инфляции.

Ответ 5

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. Предположительно, он скоро перейдет к мастеру, а затем git сможет сделать это все внутренне - либо создав собственный формат слова diff, либо нечто похожее на wdiff. Если вы смел, вы можете построить git из следующего или просто слить, что один фиксатор в локальный мастер для сборки.

Благодаря комментарию Jakub: вы можете дополнительно настроить разницу слов при необходимости, предоставив слово regex (параметр конфигурации diff. *. wordRegex), задокументированный в gitattributes.

Ответ 6

Мне понравился Stoutie ответ и хотел бы сделать его немного более настраиваемым, чтобы ответить на какое-то слово кол-во вопросов, которые у меня были. Закончено со следующим решением, которое работает в 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 [email protected]{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

Так как 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 в псевдониме сообщает Git копировать все измененные файлы во временный каталог перед выполнением diff.

Ответ 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

Отпечатки: добавлены, удалены, дубликаты

(Я беру разницу строк для дубликатов, поскольку это исключает времена, когда 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

У меня есть этот script, чтобы сделать это здесь: 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

Записи, в которых я просто перемещаюсь, очевидны, поэтому я не считаю эти изменения. Он подсчитывает все остальное и сообщает мне общее количество измененных слов.

Ответ 9

Извините, у меня недостаточно очков репутации, чтобы комментировать ответ @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, и если вы colordiff через 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

Чтобы узнать, сколько слов вы добавили, добавьте 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))"