Visualisierung eines Git Repos - a short (visual) history of your code

Anselm Bunsen02.03.2021

Immer wenn ich auf ein neues git repo stoße, suche ich früher oder später den "language breakdown graph" und finde ihn entweder elegant an der Seite (github) oder stolz im Header prangend (gitlab). Ein kleiner bunter Balken, der mir verrät, in welchen Sprachen hier programmiert wurde. Seit wir das Frontend unserer Anwendung Exply von JavaScript zu TypeScript migrieren, beobachte ich den Balken besonders gespannt. Wieviel Prozent haben wir mit dem letzten Merge Request migriert? Wie verändert sich unsere Codebasis? Wie sah das Projekt eigentlich früher aus, bevor ich daran gearbeitet habe?

KI generiert: Das Bild zeigt die Benutzeroberfläche eines Projekt-Repositories auf einer Plattform, die Informationen wie die Anzahl der Commits, Branches und Tags anzeigt. Unten ist eine farbcodierte Leiste zu sehen, die die Aufteilung von Dateien und Speicherplatz visualisiert.

Da Linguist, githubs Tool zum Erzeugen der language breakdown graphs, open source ist, lässt sich das alles mit Hilfe eines kleinen Python Scripts und Exply beantworten. Linguist lässt sich als cli-tool installieren und spuckt auf Befehl die Sprachen und entsprechenden Prozentanteile aus. Jetzt nur noch schnell über alle commits des repositories iterieren, jeden commit auschecken, analysieren, die Werte in eine CSV Datei speichern und in Exply importieren! 

Nun, ganz so einfach war es leider doch nicht. Beim Probieren geriet ich schnell an zwei Grenzen:

  1. Die Laufzeit: Linguist braucht pro commit zwischen 10 und 60 Sekunden. Exply hat knapp 6000 commits. Eine komplette Analyse aller commits dauerte gute 10 Stunden.
  2. Die Datenmenge: Knapp 6000 commits über acht Jahre mit insgesamt 16 verwendeten Sprachen ergaben 42536 Zeilen CSV - zu viele Datenpunkte, um sie in Exply sinnvoll visualisieren zu können.

Also müssen wir reduzieren. Ich habe mich dafür entschieden, nur einen commit pro Woche zu analysieren und automatisch erstellte Merge-Commits zu ignorieren. Das reduzierte die Laufzeit erheblich (nur noch knapp 3 Stunden) und lieferte 3549 Zeilen. 

So sieht das finale Skript aus:

import git import subprocess import datetime import csv import argparse def main(): begin_time = datetime.datetime.now() parser = argparse.ArgumentParser( description='Create a history of the programming languages used in your git repo.') parser.add_argument('--path', nargs='?', default='.', help='path to your git repo, defaults to the current directory') args = parser.parse_args() repo = git.Repo(args.path) commitCount = len(list(repo.iter_commits())) branch = repo.active_branch writtenLines = 0 print('analysing', commitCount, 'commits. This is going to take some time...') # setup the result CSV File result = csv.writer( open('gitLanguageHistory.csv', 'w'), delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL ) headers = ['commitSha', 'commitMessage', 'commitDate', 'language', 'percent'] result.writerow(headers) yearsAndWeeks = [] for index, commit in enumerate(list(repo.iter_commits()), start=1): # filter out semcolons and linebreaks for parsing commitMsg = commit.message.rstrip().replace(';', ',').replace('\n', ',') if "Merge branch" not in commitMsg: # get commit date and check if there is already a commit for this week date = datetime.datetime.fromtimestamp(commit.committed_date) yearWeek = str(date.year) + '-' + str(date.isocalendar()[1]) if yearWeek not in yearsAndWeeks: yearsAndWeeks.append(yearWeek) # checkout the commit repo.git.checkout(commit.hexsha) # run linguist as subprocess linguistResult = subprocess.check_output( 'github-linguist', cwd=args.path.encode('utf-8') ).decode("utf-8").split('\n') # parse the results for language in linguistResult: row = [ commit.hexsha, commitMsg, commit.committed_datetime.strftime("%d.%m.%Y"), "", "" ] percentAndLanguage = language.split() if len(percentAndLanguage) > 1: row[3] = percentAndLanguage[1] row[4] = percentAndLanguage[0].replace('%', '') result.writerow(row) writtenLines = writtenLines + 1 if (index > 0 and index % 10 == 0): timePassed = datetime.datetime.now() - begin_time print('- done with', index, 'commits (', timePassed.seconds, ')s, wrote', writtenLines, 'lines') # reset the repository repo.git.checkout(branch) timePassed = datetime.datetime.now() - begin_time print('analysed', commitCount, 'commits (100%) in ', timePassed.seconds, 's') print('saved', writtenLines, 'lines to gitLanguageHistory.csv') if __name__ == "__main__": main()

Das Ergebnis habe ich in Exply importiert und in einem Area - Time Series Chart angezeigt. Man erkennt, dass sich zu der ursprünglich fast reinen Groovy Anwendung mit der Zeit neue Sprachen wie Java, JavaScript und zuletzt TypeScript gesellt haben, und manche nach einer gewissen Zeit auch wieder verschwunden sind. Wer mehr über Exply und die darin verwendeten Technologien erfahren will, dem empfehle ich Folge 27 vom Sandpapier: Exply - Unter der Haube!

KI generiert: Das Bild zeigt ein gestapeltes Flächendiagramm, das die Entwicklung der Codequalität im Laufe der Zeit darstellt. Die verschiedenen Farben repräsentieren unterschiedliche Kategorien von Codeproblemen oder -komponenten.

Danke für's Lesen. Über Tipps zur Performance–Optimierung des Python Scripts oder andere Kommentare zum Thema Visualisierung freue ich mich immer!

Dein Besuch auf unserer Website produziert laut der Messung auf websitecarbon.com nur 0,28 g CO₂.