Parenteser

Betraktninger fra Mat-teamets grønne enger

Git repo – En analyse

Du vet, Github sine diagrammer på profil-siden? De har jeg alltid sett på og tenkt: “Det hadde vært gøy å lage flere av disse – se koden sin gang over tid”.

Men hvordan får vi til dette? Hvor starter man?

Jo, det vi må gjøre først er å trekke ut dataene vi trenger fra git. Så må vi putte dataene inn i en database. Ved å gjøre dette får vi alle fordelene en database gir oss, så som å kjøre spørringer mot dataene. Sist men ikke minst må vi tegne opp dataene på et eller annet vis.

Steg 1 - Samle inn dataene vi trenger

For å få innblikk i et repo sin git-historikk kan vi anvende git log. Den gir oss en tekstlig historikk av alt som har skjedd i repo-et siden det ble opprettet.

Tekst kan vi ikke gjøre så mye med, så vi må parse dataene først. Ved å kjøre git log får vi dette:

commit 4582c769dcf751b029f0934bc60afc62df0e9616
Author: Christian Johansen <christian@jaliksom.du>
Date:   Sat Jun 14 10:19:06 2025 +0200

    Oppdater Dataspex

commit eb15e5b76f28ec124944e7fbda5f06e47b6d4572
Author: Magnar Sveen <magnar@ja.serr>
Date:   Fri Jun 13 14:54:54 2025 +0200

    Fullfør enhetliggjøringen av sentinels: submission-sentinel

    Istedenfor å bruke bare form-id for å finne tilbake til en submission i
    sentinelen, så bruker vi nå :form/kind med en optional :form/key. Dette
    tilsvarer da command-sentinel og query-sentinel, og tillater flere instanser av
    samme skjema på samme side.

Men dette er ikke så enkelt å parse. Ikke stress, for git log støtter å formatere resultatet. Med flagget --pretty=format: kan du legge til hvilke felter du ønsker og i hvilken form. I tilleg kan du legge til --numstat som gir deg antal linjer lagt til og fjernet for hver fil i hver commit. Da får vi denne kommandoen:

git log --numstat --pretty=format:%H%n%an%n%ae%n%aI%n%cn%n%ce%n%cI%n%s%n%b

Feltene vi har her er:

  • %H: full git sha
  • %an: forfatter navn
  • %ae: forfatter epost
  • %aI: forfatter dato
  • %cn: committer navn
  • %ce: committer epost
  • %cI: committer dato
  • %s: commit melding
  • %b: commit beskrivelse
  • %n: ny linje
Forskjellen mellom forfatter og committer er f.eks. når du cherrypicker. Da blir du satt som committer, mens original forfatteren blir bevart.

Resultatet vi får ut da, blir:

4582c769dcf751b029f0934bc60afc62df0e9616
Christian Johansen
christian@jaliksom.du
2025-06-14T10:19:06+02:00
Christian Johansen
christian@jaliksom.du
2025-06-15T22:38:44+02:00
Oppdater Dataspex

2       2       deps.edn
7       2       src/matnyttig/ui/dev.cljs

eb15e5b76f28ec124944e7fbda5f06e47b6d4572
Magnar Sveen
magnar@ja.serr
2025-06-13T14:54:54+02:00
Magnar Sveen
magnar@ja.serr
2025-06-13T14:54:54+02:00
Fullfør enhetliggjøringen av sentinels: submission-sentinel
Istedenfor å bruke bare form-id for å finne tilbake til en submission i
sentinelen, så bruker vi nå :form/kind med en optional :form/key. Dette
tilsvarer da command-sentinel og query-sentinel, og tillater flere instanser av
samme skjema på samme side.

3       3       org/røkla.org
1       1       src/matnyttig/imperative_shell/client/director.cljs
11      9       src/matnyttig/imperative_shell/client/submission_center.cljs
3       14      src/matnyttig/sider/adressevaskside/konkluder_adressevask_skjema.cljc
3       3       src/matnyttig/sider/adressevaskside/sjekk_adresse_skjema.cljc
8       5       src/matnyttig/sider/planlegging_av_tur/ui.cljc
27      15      src/matnyttig/sider/preferanser/ui.cljc
2       2       src/matnyttig/sider/smilefjessteder/ui.cljc
1       1       src/matnyttig/sider/sok/sokeskjema.cljc
1       1       src/matnyttig/sider/sok/ui.cljc
5       5       src/matnyttig/sider/vask_av_opptatt_lokale/konkluder_opptatt_lokale_skjema.cljc
16      9       src/matnyttig/ui/submission_sentinel.cljc

Dette er brukbart! 🙌

Med verdiene på hver sin linje blir det veldig enkelt å parse.

Steg 2 - Parse dataene

Dette er ganske rett frem. Nesten 😅

Vi kommer veldig langt med å splitte på \n. Men for den skarpe leseren kan commit beskrivelsen være over flere linjer. Dette må håndteres på en eller annen vis. Det samme med linje-endringene.

Men med litt fiks-fakseri ender vi opp med:

[["4582c769dcf751b029f0934bc60afc62df0e9616"
  "Christian Johansen"
  "christian@jaliksom.du"
  "2025-06-14T10:19:06+02:00"
  "Christian Johansen"
  "christian@jaliksom.du"
  "2025-06-15T22:38:44+02:00"
  "Oppdater Dataspex"
  ""
  [["2" "2" "deps.edn"] ["7" "2" "src/matnyttig/ui/dev.cljs"]]]
 ["eb15e5b76f28ec124944e7fbda5f06e47b6d4572"
  "Magnar Sveen"
  "magnar@ja.serr"
  "2025-06-13T14:54:54+02:00"
  "Magnar Sveen"
  "magnar@ja.serr"
  "2025-06-13T14:54:54+02:00"
  "Fullfør enhetliggjøringen av sentinels: submission-sentinel"
  "Istedenfor å bruke bare form-id for å finne tilbake til en submission i\nsentinelen, så bruker vi nå :form/kind med en optional :form/key. Dette\ntilsvarer da command-sentinel og query-sentinel, og tillater flere instanser av\nsamme skjema på samme side."
  [["3" "3" "\"org/r\\303\\270kla.org\""]
   ["1" "1" "src/matnyttig/imperative_shell/client/director.cljs"]
   ["11" "9" "src/matnyttig/imperative_shell/client/submission_center.cljs"]
   ["3"
    "14"
    "src/matnyttig/sider/adressevaskside/konkluder_adressevask_skjema.cljc"]
   ["3" "3" "src/matnyttig/sider/adressevaskside/sjekk_adresse_skjema.cljc"]
   ["8" "5" "src/matnyttig/sider/planlegging_av_tur/ui.cljc"]
   ["27" "15" "src/matnyttig/sider/preferanser/ui.cljc"]
   ["2" "2" "src/matnyttig/sider/smilefjessteder/ui.cljc"]
   ["1" "1" "src/matnyttig/sider/sok/sokeskjema.cljc"]
   ["1" "1" "src/matnyttig/sider/sok/ui.cljc"]
   ["5"
    "5"
    "src/matnyttig/sider/vask_av_opptatt_lokale/konkluder_opptatt_lokale_skjema.cljc"]
   ["16" "9" "src/matnyttig/ui/submission_sentinel.cljc"]]]]

Da er vi bare en mapping-funksjon unna en god datastruktur! 💯

Steg 3 - Put dataene inn i en database

Ved å putte dataene inn i en database, blir den veldig enkel å jobbe med 💪

Ved å putte dataene inn i Datomic eller Datascript, blir den enda enklere å jobbe med 🙌

Uansett hvilket diagram vi ønsker oss å tegne, har vi dataene rett ved fingerspissene og kan forespørre den om hva vi vil på kryss og tvers 🎉

Helt strålende!

Mapping-funksjonen nevnt over tar bare hver item i hver commit og mapper til en mer medgjørlig datastruktur – og èn databasen vil skjønne. Resultatet av mappinga ser omtrent sånn ut:

({:commit/full-hash "4582c769dcf751b029f0934bc60afc62df0e9616"
  :commit/subject "Oppdater Dataspex"
  :commit/body ""
  :commit/author-date #inst "2025-06-14T08:19:06.000-00:00"
  :commit/commit-date #inst "2025-06-15T20:38:44.000-00:00"
  :commit/author {:person/email "christian@jaliksom.du"
                  :person/name "Christian Johansen"}
  :commit/committer {:person/email "christian@jaliksom.du"
                     :person/name "Christian Johansen"}
  :commit/filestats [{:file/name "deps.edn"
                      :file/added 2
                      :file/removed 2}
                     {:file/name "src/matnyttig/ui/dev.cljs"
                      :file/added 7
                      :file/removed 2}]}
 {:commit/full-hash "eb15e5b76f28ec124944e7fbda5f06e47b6d4572"
  :commit/subject "Fullfør enhetliggjøringen av sentinels: submission-sentinel"
  :commit/body "Istedenfor å bruke bare form-id for å finne tilbake til en submission i\nsentinelen så bruker vi nå :form/kind med en optional :form/key. Dette\ntilsvarer da command-sentinel og query-sentinel og tillater flere instanser av\nsamme skjema på samme side."
  :commit/author-date #inst "2025-06-13T12:54:54.000-00:00"
  :commit/commit-date #inst "2025-06-13T12:54:54.000-00:00"
  :commit/author {:person/email "magnar@ja.serr"
                  :person/name "Magnar Sveen"}
  :commit/committer {:person/email "magnar@ja.serr"
                     :person/name "Magnar Sveen"}
  :commit/filestats [{:file/name "org/røkla.org"
                      :file/added 3
                      :file/removed 3}
                     {:file/name "src/matnyttig/imperative_shell/client/director.cljs"
                      :file/added 1
                      :file/removed 1}
                     {:file/name "src/matnyttig/imperative_shell/client/submission_center.cljs"
                      :file/added 11
                      :file/removed 9}
                     {:file/name "src/matnyttig/sider/adressevaskside/konkluder_adressevask_skjema.cljc"
                      :file/added 3
                      :file/removed 14}
                     {:file/name "src/matnyttig/sider/adressevaskside/sjekk_adresse_skjema.cljc"
                      :file/added 3
                      :file/removed 3}
                     {:file/name "src/matnyttig/sider/planlegging_av_tur/ui.cljc"
                      :file/added 8
                      :file/removed 5}
                     {:file/name "src/matnyttig/sider/preferanser/ui.cljc"
                      :file/added 27
                      :file/removed 15}
                     {:file/name "src/matnyttig/sider/smilefjessteder/ui.cljc"
                      :file/added 2
                      :file/removed 2}
                     {:file/name "src/matnyttig/sider/sok/sokeskjema.cljc"
                      :file/added 1
                      :file/removed 1}
                     {:file/name "src/matnyttig/sider/sok/ui.cljc"
                      :file/added 1
                      :file/removed 1}
                     {:file/name "src/matnyttig/sider/vask_av_opptatt_lokale/konkluder_opptatt_lokale_skjema.cljc"
                      :file/added 5
                      :file/removed 5}
                     {:file/name "src/matnyttig/ui/submission_sentinel.cljc"
                      :file/added 16
                      :file/removed 9}]})

Steg 4 – Tegn resten av ugla 🤷‍♂️

Nå har vi alt vi trenger for å gjøre noe med disse dataene, og vi har kommet til artiklens faktiske formål.

🌶️ THE SPICE 🌶️

Jeg har valgt å bruke D3 som er et javascript visualiseringsbibliotek. Det eksponerer mange funksjoner for å jobbe med data og bruker kraften av SVG til å tegne opp diagrammer - akkurat som du vil ha dem ✨

Jeg kommer ikke til å gå i dypden med hvordan D3 fungerer. Det er det ikke plass til her så det får bli en annen bloggpost.

Jeg etterlater dere med antall commits lagt til i Matnyttig hver måned:

2024-22024-32024-42024-52024-62024-72024-82024-92024-102024-112024-122025-12025-22025-32025-42025-52025-6050100150200250300350400450500550Commits per month

Mer spennende ting kommer, men enn så lenge – her er koden så langt

Med denne, kanskje litt anti-klimatiske, avslutningen, da var poenget her ikke å vise kule innsikter i kodebasen vår eller imponerende grafer, men rettere hvordan man kan få tak i dataene under panseret til git og gjøre den om til en deilig, smibar masse. Og da ligger mulighetene helt oppe.

Inspirasjonen og granskningen bak

Mathias

Om Git og Data