Es ist mir eine Ehre, am Lux Adventskalender 2016 für Heiligabend verantwortlich zu sein. Ich poste verantwortungsbewusst bei Eve (während ich die M-Station beobachte).
Der Befehl git log
ist ein praktisches Tool, das für die Versionsverwaltung unverzichtbar ist und mit dem Sie wissen, wann, wer was und wie festgeschrieben hat. Wenn Sie dies jedoch für die Qualitätskontrolle und -analyse nutzen möchten, müssen Sie ein wenig überlegen. In Einführung habe ich versucht, zusammenzufassen, wie "Git Log" mit einem Liner formatiert wird, aber es gibt eine Grenze für die Möglichkeiten in einer Zeile. In der eigentlichen Arbeit gibt es nach dem Formen der Protokolle noch andere Dinge zu tun, z. B. jeden Morgen Protokolle zu sammeln, um den Entwicklungsstatus zu überprüfen, Tests zu planen und die Qualität am Ende des Projekts zu bewerten. Diesmal also Ich dachte darüber nach, wie man das Protokoll praktisch benutzt.
Dieses Mal habe ich zusammengefasst, wie Git-Protokolle mit Git Python aggregiert und analysiert werden. Ich denke, es gibt Möglichkeiten, mit Git in anderen Sprachen umzugehen, aber ich kann gut tabellieren und analysieren, da es eine Sprache ist, mit der Sie den Code einfach umschreiben können, damit Sie die Qualität entsprechend der Situation des Projekts leicht überprüfen können. Ich dachte, Python sei als Skriptsprache einfach zu handhaben. Übrigens hat Python nur Code auf Einweg-Ebene geschrieben, wie zum Beispiel eine kleine Aggregation wie diese. Bitte verzeihen Sie mir, obwohl der Code möglicherweise einen schlechten Teil enthält. Bitte kommentieren Sie, wenn Sie welche haben.
Der hier zusammengefasste Beispielcode wird mit Python 2.7
bestätigt.
GitPython
Es gibt eine praktische Bibliothek namens GitPython für die Arbeit mit Git in Python. Informationen zur Installation finden Sie unter Offizielle Dokumente.
Geben Sie dies an, indem Sie das lokale Repository mit "Repo (" / path ") angeben. Die Commit-Informationen für einen bestimmten Zweig erhalten Sie mit Repo.iter_commits. ..
from git import *
import datetime, time
repo = Repo('./')
for item in repo.iter_commits('master', max_count=10):
dt = datetime.datetime.fromtimestamp(item.authored_date).strftime("%Y-%m-%d %H:%M:%S")
print("%s %s %s " % (item.hexsha, item.author, dt))
Informationen zum Festschreiben finden Sie unter Objects.Commit API-Referenz.
Im obigen Beispiel werden der Hashwert, der festgeschriebene Benutzer sowie das festgeschriebene Datum und die festgeschriebene Uhrzeit der letzten 10 Git-Protokolle aus dem Hauptzweig des Repositorys im aktuellen Verzeichnis ausgegeben.
Ausgabebeispiel
ddffe26850e8175eb605f975be597afc3fca8a03 Sebastian Thiel 2016-12-22 20:51:02
3d6e1731b6324eba5abc029b26586f966db9fa4f Sebastian Thiel 2016-12-22 20:48:59
82ae723c8c283970f75c0f4ce097ad4c9734b233 Sebastian Thiel 2016-12-22 20:44:14
15b6bbac7bce15f6f7d72618f51877455f3e0ee5 Sebastian Thiel 2016-12-22 20:35:30
c823d482d03caa8238b48714af4dec6d9e476520 Sebastian Thiel 2016-12-09 00:34:04
b0c187229cea1eb3f395e7e71f636b97982205ed Sebastian Thiel 2016-12-09 00:07:11
f21630bcf83c363916d858dd7b6cb1edc75e2d3b Sebastian Thiel 2016-12-09 00:01:35
06914415434cf002f712a81712024fd90cea2862 Sebastian Thiel 2016-12-08 22:32:58
2f207e0e15ad243dd24eafce8b60ed2c77d6e725 Sebastian Thiel 2016-12-08 21:20:52
a8437c014b0a9872168b01790f5423e8e9255840 Vincent Driessen 2016-12-08 21:14:27
Das obige Ausgabebeispiel ist übrigens das Festschreibungsprotokoll von GitPython vom 23. Dezember 2016.
Die in der Einführung eingeführte Commit-Begrenzung von "git log" kann durch das Argument "iter_commits" angegeben werden (im obigen Beispiel wird "max_count = 10" angegeben). Sie können mehrere mit ,
angeben. Ersetzen Sie außerdem den -
Teil durch _
, um dies anzugeben.
Die Angabe von "no_merge" führte übrigens zu einem Syntaxfehler. Wie in [hier](https://git-scm.com/docs/git-rev-list#git-rev-list --- no-merges) als optionale Spezifikation von "git log" beschrieben Da es dasselbe ist wie "max-parent = 1", konnte ich "max_parents = 1" verwenden.
Sie können auch die in der Einführung eingeführte Doppelpunktsyntax angeben. Dies lässt den obigen "Master" -Teil einfach wie "Master ... Experiment" aussehen.
Betrachten wir nun die Anwendung zum Protokollieren der Aggregation und Analyse. Alle Informationen der festgeschriebenen Datei sind in den Festschreibungsinformationen enthalten, die durch den obigen Code erhalten werden. Sie können eine Liste der festgeschriebenen Dateiinformationen mit stats.files
erhalten.
Dies ist ein Beispiel für eine Standardausgabe im CSV-Format zusammen mit Informationen wie Datum und Uhrzeit, zu denen die hinzugefügte und gelöschte Zeile für jede festgeschriebene Datei festgeschrieben wurden.
from git import *
import datetime, time
repo = Repo('./')
print('hexsha,author,authored_date,file_name,deletions,lines,insertions')
for item in repo.iter_commits('master', max_count=10):
file_list = item.stats.files
for file_name in file_list:
dt = datetime.datetime.fromtimestamp(item.authored_date).strftime("%Y-%m-%d %H:%M:%S")
insertions = file_list.get(file_name).get('insertions')
deletions = file_list.get(file_name).get('deletions')
lines = file_list.get(file_name).get('lines')
print("%s,%s,%s,%s,%s,%s,%s" % (item.hexsha, item.author, dt, file_name, insertions, deletions, lines))
Es ist ziemlich schwierig, diese Informationen mit nur einem Befehl abzurufen und in eine Zeile zu setzen, aber mit GitPython müssen Sie nur die Schleife umgehen und sie abrufen.
Zählen wir nun die Anzahl der während des Zeitraums vorgenommenen Änderungen nach Datei anstatt nach Festschreiben. Im Folgenden wird angegeben, wie oft innerhalb von 6 Monaten für jede Datei ein Commit durchgeführt wurde.
from git import *
import datetime, time
repo = Repo('./')
print('file_name,commit_count')
file_list = {}
for item in repo.iter_commits('master', since='6 months ago'):
for fileName in item.stats.files:
if file_name not in file_list:
fileList[fileName] = []
author = {}
author[item.author] = datetime.datetime.fromtimestamp(item.authored_date).strftime("%Y-%m-%d %H:%M:%S")
file_list[file_name].append(author)
for file_name in file_list:
print("%s,%d" % (file_name, len(fileList[file_name])))
Darüber hinaus scheint dies angewendet werden zu können, um die Anzahl der geänderten Zeilen für jede Datei innerhalb des Projektzeitraums zu aggregieren und das Festschreibungsintervall (die Anzahl der Tage seit der letzten Änderung) zu berechnen. Sie können nicht nur nach Datei, sondern auch nach Tag, Monat und engagierter Person aggregieren.
Wenn Python frei für die Aggregation verwendet werden kann, können Protokolle für neue Zwecke verwendet werden, z. B. zum Importieren in andere Python-Bibliotheken zur Analyse und zum Verknüpfen mit externen Tools und Diensten.
Denken wir also darüber nach, die Analyse von Commit-Informationen zuzuordnen. Was ich tun möchte, ist, wie ich die Information bekomme, dass "die Person, die diese Datei geändert hat, auch diese geändert hat". In Python kann die Assoziationsanalyse mit einer Bibliothek namens Orange durchgeführt werden.
Gemäß dem offiziellen Dokument Zuordnungsregeln und häufige Artikelgruppen lautet die zu analysierende Liste ".basket" im CSV-Format. Wenn Sie es mit der Erweiterung `speichern, scheint Orange die Zuordnung zu analysieren. Daher geben wir die Dateien, die zusammen in einem Commit festgeschrieben wurden, im CSV-Format aus.
Zunächst wird GitPython durch Anwenden der oben genannten Methode verwendet, um die gleichzeitig festgeschriebenen Dateien durch Kommas getrennt auszugeben.
commit-file-list.py
from git import *
repo = Repo('./')
for item in repo.iter_commits('master', since='1 years ago'):
print(",".join(item.stats.files.keys()))
$ python commit-file-list.py > commit-file-list.basket
Lassen Sie uns die oben erstellte Zuordnung von commit-file-list.basket
anhand des Beispielcodes von Orange analysieren.
import Orange
data = Orange.data.Table('commit-file-list.basket')
rules = Orange.associate.AssociationRulesSparseInducer(data, support=0.02, confidence=0.5)
print "%4s %4s %4s" % ("Supp", "Conf", "Rule")
for r in rules:
if 'git/config.py' in r.name:
print "%4.1f %4.1f %s" % (r.support, r.confidence, r)
Ausgabebeispiel
Supp Conf Rule
0.0 0.6 git/test/test_git.py -> git/cmd.py
0.0 0.2 git/cmd.py -> git/test/test_git.py
0.1 0.7 git/test/test_diff.py -> git/diff.py
0.1 0.7 git/diff.py -> git/test/test_diff.py
0.0 0.4 git/test/test_diff.py -> git/diff.py doc/source/changes.rst
"Unterstützung" ist in diesem Fall nicht sehr wichtig, da es sich um den Prozentsatz der Regel handelt, der im Ganzen erscheint. Stellen Sie daher den Schwellenwert so niedrig wie möglich ein. "Vertrauen" ist das Verhältnis der gesamten Regel, das unter der Voraussetzung eines Teils der Regel erscheint (das Verhältnis von A und B basierend auf allen Mustern einschließlich A), also ist dieser Wert genau in diesem Fall. Ich möchte die hohen Regeln hervorheben.
Geben Sie zunächst die zusammenzuführende Datei im CSV-Format mit einer Zeile für jedes Commit aus und erstellen Sie eine ".basket" -Datei. Da es sich um ein Ziel handelt, das mit dem Master zusammengeführt werden soll, erhält es ein Commit mithilfe der Doppelpunktsyntax und gibt die zugehörige Datei aus.
merge-target-list.py
from git import *
repo = Repo('./')
for item in repo.iter_commits('master..experiment', max_parents=1):
print(",".join(item.stats.files.keys()))
$ python merge-target-list.py > merge-target-list.basket
Es ist möglicherweise besser, die oben erwähnte Methode zum Extrahieren von Regeln basierend auf dem Git-Protokoll für ein Jahr anzuwenden, diejenige zu finden, deren zusammenzuführender "merge-target-list.basket" mit der linken Seite der Regel übereinstimmt, und auch rechts festzuschreiben. Gibt das Ergebnis aus, dass dies nicht möglich ist (es wird beispielsweise angenommen, dass A, B, C ein Jahr lang als Regeln aus dem Protokoll extrahiert werden und C als Kandidat ausgegeben wird, wenn A, B im Zusammenführungsziel vorhanden sind).
import Orange
data = Orange.data.Table('commit-file-list.basket')
rules = Orange.associate.AssociationRulesSparseInducer(data, support=0.02, confidence=0.5)
for r in rules:
if 'git/config.py' in r.name:
print "%4.1f %4.1f %s" % (r.support, r.confidence, r)
merge_data = Orange.data.Table('merge-target-list.basket')
for d in merge_data:
for rule in rules:
#print rule
if rule.applies_left(d):
print (u"%Wer hat s begangen%3.1f %%Mit der Rate von%s ist auch verpflichtet" %(rule.left.get_metas(str).keys(), (rule.confidence*100), rule.right.get_metas(str).keys()))
Ausgabebeispiel
['git/test/test_remote.py']Wer hat 55 begangen.0 %Mit der Rate von['git/test/lib/helper.py']Ist auch verpflichtet
['git/test/test_remote.py']Wer hat 55 begangen.0 %Mit der Rate von['git/test/test_base.py']Ist auch verpflichtet
['git/test/test_remote.py']50 Menschen, die sich verpflichtet haben.0 %Mit der Rate von['git/util.py']Ist auch verpflichtet
['git/test/test_base.py']Wer hat 55 begangen.0 %Mit der Rate von['git/test/test_git.py']Ist auch verpflichtet
...(Kürzung)
Die Kandidaten wurden mehr ausgegeben, als ich erwartet hatte, um das Vertrauen auf 0,5 zu setzen. Ich denke, es ist am besten, diesen Bereich entsprechend der Situation und den Merkmalen des Projekts abzustimmen.
Da Python viele nützliche Bibliotheken hat, scheint es, dass es nicht nur auf Assoziationsanalysen, sondern auch auf verschiedene Anwendungen angewendet werden kann. Eigentlich habe ich darüber nachgedacht, matplotlib zu verwenden, das Diagramme verarbeiten und ChatBot erstellen kann, aber diesmal liegt es daran, dass die Assoziationsanalyse länger geworden ist als erwartet. Ich möchte meine Hausaufgaben in den Winterferien fortsetzen.
Apriori von Python mit Orange ausführen Git-Protokoll mit einem Liner formatieren
Der Spaß Lux Adventskalender 2016 ist morgen endlich der letzte Tag. Bitte freuen Sie sich auf den Abschluss von @ kawanamiyuu. Ich wünsche allen ein schönes Weihnachtsfest.
Recommended Posts