Количественное определение величины изменения в git diff?
Я использую git для немного необычной цели - он хранит мой текст, когда я пишу фикцию. (Я знаю, я знаю... geeky.)
Я пытаюсь отслеживать производительность и хочу измерить степень разницы между последующими коммитами. Писатель-прокси для "работы" - это "слова, написанные", по крайней мере, во время стадии создания. Я не могу использовать счетчик прямых слов, поскольку он игнорирует редактирование и сжатие, как жизненно важные части письма. Я думаю, что хочу отслеживать:
(words added)+(words removed)
который будет удвоен (слова изменены), но я в порядке с этим.
Было бы здорово набрать какое-то магическое заклинание и git сообщить эту метрику расстояния для любых двух ревизий. Тем не менее, git diffs - это патчи, которые показывают целые строки, даже если вы только крутили один символ на линии; Я не хочу этого, тем более, что мои "строки" - это абзацы. В идеале я бы даже смог указать, что я подразумеваю под словом (хотя\W +, вероятно, будет приемлемым).
Есть ли флаг для git -diff, чтобы дать различие на основе слова? Альтернативно, существует ли решение с использованием стандартных средств командной строки для вычисления метрики выше?
Ответы
Ответ 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))"