Frage Quantifizieren der Menge der Veränderung in einem Git diff?


Ich benutze Git für einen etwas ungewöhnlichen Zweck - es speichert meinen Text, während ich Fiktion schreibe. (Ich weiß, ich weiß ... geeky.)

Ich versuche, die Produktivität im Auge zu behalten, und möchte den Grad der Differenz zwischen den folgenden Commits messen. Der Stellvertreter des Autors für "Arbeit" ist "geschriebene Wörter", zumindest während der Schaffungsphase. Ich kann die Anzahl der geraden Wörter nicht verwenden, da sie das Bearbeiten und Komprimieren ignoriert, beides wichtige Teile des Schreibens. Ich denke, ich möchte verfolgen:

 (words added)+(words removed)

das wird doppelt zählen (Wörter geändert), aber damit bin ich einverstanden.

Es wäre toll, eine Zauberformel zu tippen und diese Entfernungsmetrik für zwei Revisionen zu melden. Git diffs sind jedoch Patches, die ganze Zeilen anzeigen, selbst wenn Sie nur ein Zeichen auf der Zeile gezwirbelt haben. Ich will das nicht, vor allem, weil meine "Zeilen" Absätze sind. Idealerweise könnte ich sogar angeben, was ich unter "Wort" verstehe (obwohl \ W + wahrscheinlich akzeptabel wäre).

Gibt es ein Flag zu git-diff, um Diffs Wort für Wort zu geben? Gibt es alternativ eine Lösung, die Standard-Befehlszeilentools zum Berechnen der obigen Metrik verwendet?


27
2018-05-20 13:50


Ursprung


Antworten:


wdiff macht einen wortweisen Vergleich. Git kann so konfiguriert werden, dass es ein externes Programm zum Ausführen des Diffing verwendet. Auf der Grundlage dieser beiden Tatsachen und dieser BlogbeitragFolgendes sollte ungefähr das tun, was Sie wollen.

Erstellen Sie ein Skript, um die meisten unnötigen Argumente zu ignorieren git-diff stellt und übergibt sie an wdiff. Speichern Sie Folgendes als ~/wdiff.py oder etwas ähnliches und mach es ausführbar.

#!/usr/bin/python

import sys
import os

os.system('wdiff -s3 "%s" "%s"' % (sys.argv[2], sys.argv[5]))

Sagen git um es zu benutzen.

git config --global diff.external ~/wdiff.py
git diff filename

10
2018-05-20 14:02



git diff --word-diff funktioniert in der neuesten stabilen Version von git (at git-scm.com)

Es gibt ein paar Optionen, mit denen Sie entscheiden können, welches Format Sie wollen, der Standard ist gut lesbar, aber Sie möchten vielleicht --word-diff = Porzellan, wenn Sie die Ausgabe in ein Skript einspeisen.


9
2017-08-18 00:24



Aufbauend auf James 'und cornmacrelfs Eingabe, Ich habe hinzugefügt arithmetische Expansion, und einige wiederverwendbare Alias-Befehle zum Zählen von Wörtern in einem git-diff entwickelt:

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

Ausgabe von gitwa und gitwd ist getrimmt mit Xargs Trick.


8
2017-08-05 04:42



Ich habe einen Weg gefunden, um konkrete Zahlen zu bekommen, indem ich hier auf die anderen Antworten baue. Das Ergebnis ist eine Annäherung, aber es sollte nah genug sein, um als ein nützlicher Indikator für die Menge Zeichen zu dienen, die hinzugefügt oder entfernt wurden. Hier ist ein Beispiel mit meinem aktuellen Zweig im Vergleich zum Ursprung / 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

Der Unterschied zwischen den entfernten Zeichen (46664) und die hinzugefügten Zeichen (38741) zeigt, dass mein aktueller Zweig ungefähr entfernt wurde 7923 Figuren. Diese einzelnen hinzugefügten / entfernten Zählungen werden aufgrund der Diffs aufgebläht +/- und Eindrückzeichen, jedoch sollte der Unterschied in den meisten Fällen einen signifikanten Teil dieser Inflation aufheben.


7
2018-01-28 02:41



Git hatte (lange Zeit) eine --color-words Option für git diff. Das bringt dich nicht zum Zählen, aber es lässt dich die Diffs sehen.

scompt.com's Vorschlag von wdiff ist auch gut; Es ist ziemlich einfach, in einem anderen zu schieben (siehe Git-Difftool). Von dort müssen Sie nur von der Ausgabe gehen, die wdiff zu dem Ergebnis geben kann, das Sie wirklich wollen.

Es gibt noch eine aufregende Sache zu teilen, obwohl von git's was kocht:

* 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

Hier ist die Commit Einführung von Word-Diff. Vermutlich wird es bald seinen Weg vom Master zum Master finden, und dann wird git alles intern machen können - entweder mit einem eigenen Word-Diff-Format oder ähnlichem wie wdiff. Wenn Sie mutig sind, können Sie git von Next erstellen oder einfach diesen Commit in Ihren lokalen Master zusammenführen, um ihn zu erstellen.

Dank Jakubs Kommentar: Sie können Word-Diffs bei Bedarf weiter anpassen, indem Sie ein Wort regex (config parameter diff. *. WordRegex) bereitstellen, dokumentiert in gitattributes.


4
2018-05-20 14:28



ich mochte Stutieist es Antworten und wollte es etwas konfigurierbarer machen, um einige Fragen zu beantworten, die ich hatte. Schluss mit der folgenden Lösung, die in ZSH funktioniert und in Bash funktionieren sollte. Jede Funktion nimmt irgendwelche Revision oder Revision Unterschied, mit einem Standard, den aktuellen Zustand der Welt mit zu vergleichen 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)))
}

Dann kannst du es so benutzen:


$ 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

Hoffe das hilft jemandem!


1
2017-08-13 20:30



Seit Git 1.6.3 gibt es auch git difftool, die so konfiguriert werden kann, dass fast jedes externe Diff-Tool ausgeführt werden kann. Dies ist viel einfacher als einige der Lösungen, die das Erstellen von Skripten usw. erfordern. Wenn Ihnen die Ausgabe von wdiff -s Sie können Folgendes konfigurieren:

git config --global difftool.wdiffs.cmd 'wdiff -s "$LOCAL" "$REMOTE"'
git config --global alias.wdiffs 'difftool -t wdiffs'

Jetzt kannst du einfach rennen git difftool -t wdiffs oder sein Alias git wdiffs.

Wenn Sie es vorziehen, Statistiken für alle geänderten Dateien zusammen zu erhalten, führen Sie stattdessen Folgendes aus:

git config --global difftool.wdiffs.cmd 'diff -pdrU3 "$LOCAL" "$REMOTE" | wdiff -sd'
git config --global alias.wdiffs 'difftool -d -t wdiffs'

Dies nimmt die Ausgabe eines typischen vereinheitlichten diff und leitet es hinein wdiff mit -d Option eingestellt, um nur die Eingabe zu interpretieren. Im Gegensatz dazu das Extra -d Argument zu difftool Im Alias ​​weist git an, alle geänderten Dateien vor dem Diff in ein temporäres Verzeichnis zu kopieren.


0
2017-11-28 17:08



Die obigen Antworten schlagen für einige Anwendungsfälle fehl, in denen Sie verschobenen Text ausschließen müssen (z. B. wenn ich eine Funktion in Code oder Absatz in Latex weiter unten im Dokument verschiebe, möchte ich nicht alle als Änderungen zählen!)

Dazu können Sie auch die Anzahl der doppelten Zeilen berechnen und diese von Ihrer Abfrage ausschließen, wenn zu viele Duplikate vorhanden sind.

Zum Beispiel, aufbauend auf den anderen Antworten, kann ich Folgendes tun:

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

berechnet die Anzahl der doppelten Wörter im Diff, wo sha ist deine Verpflichtung.

Sie können dies für alle Commits innerhalb des letzten Tages (ab 6 Uhr morgens) tun durch:

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

Drucke: hinzugefügt, gelöscht, Duplikate

(Ich nehme das Zeilen-Diff für Duplikate, da es die Zeiten ausschließt, wo git diff versucht, zu clever zu sein und nimmt an, dass Sie den Text gerade geändert haben, anstatt ihn zu verschieben. Es werden auch Fälle diskontiert, in denen ein einzelnes Wort als Duplikat gezählt wird.)

Oder, wenn Sie es ausgeklügelt haben möchten, können Sie Commits vollständig ausschließen, wenn mehr als 80% Duplikate vorhanden sind, und den Rest zusammenfassen:

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

Ich habe dieses Skript, um es hier zu tun: https://github.com/MilesCranmer/git-stats.

Dies druckt aus:

➜  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

Die Commits, bei denen ich mich gerade bewege, sind offensichtlich, also zähle ich diese Änderungen nicht. Es zählt alles andere auf und sagt mir die Gesamtzahl der veränderten Wörter.


0
2018-06-18 12:58