2010-05-20 15 views
25

Ich benutze Git für einen etwas ungewöhnlichen Zweck - es speichert meinen Text, während ich Fiktion schreibe. (Ich weiß, ich weiß ... geeky.)Quantifizierung der Größe der Änderung in einem Git diff?

Ich versuche, die Produktivität zu verfolgen, und möchte den Grad der Differenz zwischen den nachfolgenden 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:

die doppelt zählt (Wörter geändert), aber ich bin damit einverstanden.

Es wäre großartig, eine magische Beschwörungsformel einzugeben und git 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?

+3

Das ist wirklich eine ausgezeichnete Verwendung - Git ist ein Content-Tracker. Und es ist nicht so ungewöhnlich - werfen Sie einen Blick auf die letztjährige Git Umfrage https://git.wiki.kernel.org/index.php/GitSurvey2009#07._I_use_Git_for_.28check_all_that_apply.29: – Cascabel

Antwort

10

wdiff führt einen wortweisen Vergleich durch. Git kann so konfiguriert werden, dass es ein externes Programm zum Ausführen des Diffing verwendet. Basierend auf diesen zwei Fakten und this blog post, sollte das folgende grob tun, was Sie wollen.

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

#!/usr/bin/python 

import sys 
import os 

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

Tell git, um es zu verwenden.

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

Ich werde es versuchen. –

+0

Das war eine gute Antwort für die Zeit bevor es 'git diff --word-diff' gab – Jarus

+0

@Jarus Es ist immer noch die beste Antwort, soweit ich das beurteilen kann:' --word-diff' wird nicht pro Wort angezeigt Statistiken. –

4

Git hat (für eine lange Zeit) hatte 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 Unterschied 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 eine weitere spannende Sache, aber zu teilen, von git ist, was Kochen ist:

* 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 introducing 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 Wortdiffs bei Bedarf weiter anpassen, indem Sie ein Wort regex (config parameter diff. *. WordRegex) bereitstellen, das in gitattributes dokumentiert ist.

+0

Ausgezeichnet. Ich merke nur, dass ich es bald auf meinem Server haben werde (ein Solaris, wo ich regelmäßig Git neu kompiliere). +1 – VonC

+1

Es gibt auch 'diff. .wordRegex Konfigurationsvariable, zusammen mit 'diff = ' gitattribute, wo Sie definieren können, was Wort bildet. –

9

git diff --word-diff funktioniert in der neuesten stabilen Version von git (bei 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 füttern.

+1

Es gibt jedoch keine pro-Wort-Statistiken, worum geht es in der Frage. –

+0

Sie bekommen 99% des Weges dorthin. Zum Beispiel: '' git diff --word-diff = Porzellan | grep -e '^ + [^ +] \ |^- [^ -]' '' – ariddell

+0

Meine Antwort unten baut auf Ariddells Kommentar auf, um eine ungefähre Antwort zu erhalten: http://stackoverflow.com/a/28183710/204480 –

7

Ich habe einen Weg gefunden, 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 zu 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 

Der Unterschied zwischen den entfernten Zeichen (46664) und den hinzugefügten Zeichen (38741) zeigt, dass mein aktueller Zweig hat ca. 7923 Zeichen entfernt. Diese einzelnen hinzugefügten/entfernten Zählungen werden aufgrund der Diff- und Einrückungszeichen des Diffs aufgebläht, jedoch sollte der Unterschied in den meisten Fällen einen signifikanten Teil dieser Inflation aufheben.

+5

Warum nicht den ganzen Weg gehen und die ursprüngliche Frage beantworten? 'wc -w' würde stattdessen Wortzählungen finden. Viel nützlicher. – cormacrelf

+0

Sie können 'sed '/^.//'' zur Pipeline hinzufügen, um das nachgestellte '+'/'-' zu entfernen. – superbob

7

Aufbauend auf James' and cornmacrelf's input, ich habe arithmetic expansion hinzugefügt, und kam mit einem paar wiederverwendbaren alias Befehlen nach oben für Worte in einem git diff zählen:

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 trimmed using xargs trick.

+0

Danke, @Stoutie, das funktioniert gut. Ich habe versucht, mehr Raffinesse hinzuzufügen: sehe meine Antwort für den Schutz gegen Text, der nur in einem Dokument bewegt wurde ... – Miles

1

Ich mochte Stoutie 's answer und wollte es ein wenig konfigurierbarer machen, um einige Wörter zu beantworten, die ich hatte. Schluss mit der folgenden Lösung, die in ZSH funktioniert und in Bash funktionieren sollte. Jede Funktion nimmt jede revision or revision difference, mit einem Standard von den aktuellen Zustand der Welt mit origin/master Vergleich:


# 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 können Sie es wie so verwenden können:


$ 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 

hoffe, das hilft jemand!

+1

Es hat mir geholfen :) Gute Arbeit. Vielen Dank! – lucapette

0

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. Das ist viel einfacher, als einige der Lösungen, die Skripte erfordern die Schaffung usw. Wenn Sie die Ausgabe von wdiff -s mögen Sie so etwas wie konfigurieren:

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

Jetzt können Sie einfach laufen git difftool -t wdiffs oder seinen Alias ​​git wdiffs.

Wenn Sie es vorziehen Statistiken für alle geänderten Dateien zusammen zu bekommen, sondern so etwas wie:

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

Das die Ausgabe eines typischen einheitlichen diff und Rohren es in wdiff gesetzt mit seiner -d Option nimmt nur zu interpretieren die Eingabe. Im Gegensatz dazu sagt das zusätzliche -d Argument zu difftool im Alias ​​git, alle modifizierten Dateien in ein temporäres Verzeichnis zu kopieren, bevor das Diff ausgeführt wird.

0

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

Dafür 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 auf den anderen Antworten bauen, kann ich:

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

berechnet die Anzahl der doppelte Wörter in der diff, wo sha ist Ihr zu begehen.

Sie können dies für alle Commits innerhalb des letzten Tages (seit 06.00) von:

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

(Ich nehme die Linie diff für Duplikate Duplikate, wie es die Zeiten ausschließt, in denen git diff versucht, zu schlau zu sein, und davon ausgeht, dass Sie gerade Text geändert und nicht verschoben haben. Er rabattiert auch Fälle, in denen ein einzelnes Wort als Duplikat gezählt wird.)

Oder wenn Sie möchten dazu anspruchsvoll sein Sie können Commits vollständig, wenn mehr als 80% Duplikation ist, auszuschließen und den Rest zusammenzufassen:

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 dieses Skript haben, es zu tun hier: https://github.com/MilesCranmer/git-stats.

Dies gibt:

➜ 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 

Das verpflichtet, wo ich gerade um die Dinge offensichtlich bewegen werde, so rechne ich nicht jene Änderungen. Es zählt alles andere auf und sagt mir die Gesamtzahl der veränderten Wörter.

Verwandte Themen