# Repo

## Hva det er

Et repo, kort for repository, er stedet der et versjonskontrollsystem holder
hele historikken til et prosjekt: hver lagrede tilstand av hver fil, hvem som
gjorde hver endring og hvorfor, og navnene som leder inn i den historikken.
Arbeidsfilene du redigerer, er én visning av det; repoet er alt som ligger bak
den visningen.

Git definerer ordet ganske presist i sin egen ordliste: en samling referanser
(refs) sammen med en objektdatabase som inneholder alle objektene som kan nås
fra disse referansene. Begge halvdelene er verdt å pakke ut. Objektdatabasen
holder innholdet. Hver versjon av en fil, hver katalogliste og hver commit
lagres som et objekt og får navn etter en hash av innholdet, noe Git-boken
beskriver som et innholdsadressert filsystem: et nøkkel-verdi-lager der nøkkelen
regnes ut fra verdien. To identiske filer er ett objekt, og enhver endring,
uansett hvor liten, gir et nytt navn. Referansene er den menneskelige siden:
grener og tagger, korte navn som hver peker på én commit og gir folk en vei inn.

De fleste repoer folk jobber i, har et arbeidstre knyttet til seg. Filene du
åpner i en editor, ligger i prosjektkatalogen, og selve repoet bor ved siden av
dem i en skjult `.git`-katalog. Et nakent repo (bare repository) har ikke noe
arbeidstre i det hele tatt. Katalognavnet slutter etter konvensjon på `.git`,
repoet holder bare historikken, og det er formen en server holder, fordi ingen
redigerer filer der; folk pusher til det og henter fra det. Ordlisten til Git
sier det samme: de som publiserer offentlige repoer, gjør som regel nakne repoer
tilgjengelige. Git-boken viser hvordan du lager et fra et eksisterende prosjekt
med `git clone --bare my_project my_project.git`.

Kloning er der designet til Git viser seg. En klone mottar nesten alle dataene
serveren har: som standard kommer hver versjon av hver fil gjennom hele
prosjektets historikk ned sammen med den. Hver klone er derfor et komplett repo,
som kan tre inn i stedet for serveren hvis serveren går tapt. Unntakene er små,
men de betyr noe, og Git-boken nevner dem: hooks på serversiden og lignende
innstillinger blir igjen.

To varianter dukker ofte opp. Et grunt repo (shallow repository), laget ved å
klone med `--depth`, beholder bare de nyeste commitene; ordlisten sier at de
eldre foreldrene er «cauterized away», og historikken kan gjøres dypere senere
med en fetch. En submodul er et repo som ligger inne i et annet, med sin egen
historikk, og som det ytre repoet låser til én bestemt commit.

## Hvorfor det er viktig

Et repo er hukommelsen til et prosjekt. Når en kodelinje ser feil ut, sier
historikken når den kom, i hvilken commit og med hvilken forklaring. Det er
bare så godt som de commitene folk skriver, og derfor setter team som bryr seg
om historikken sin, regler for commitmeldinger og håndhever dem med hooks i
stedet for med god vilje.

Det er også enheten de fleste verktøy jobber på. Tilgang gis per repo, hooks
kjører per repo, og kontinuerlig integrasjon starter som regel fra ett. Trekk
grensen for et repo, og du har trukket grensen for hva én gjennomgang, én sjekk
og én tillatelse dekker.

Repoer beholder det de får. En commit som sletter en fil, legger til en ny
tilstand; den lar hver tidligere tilstand ligge der den var, i hver klone som
allerede har den. Den varigheten er hele poenget med versjonskontroll, og den
skjærer begge veier: en gammel versjon kan alltid hentes fram igjen, og det
samme kan et passord noen committet ved en feil.

Til slutt kan et repo holde langt mer enn applikasjonskode.
Infrastrukturdefinisjoner, dokumentasjon og konfigurasjon får alle det samme ut
av det: en historikk, et steg for gjennomgang og en måte å gjenskape en kjent
tilstand på. En server der oppsettet ligger i et repo, kan bygges opp igjen av
noen som aldri har sett den.

## Slik ser det ut i praksis

[Fire repoer bygd over atten måneder av én person](/nb/portfolio/held-five-repositories-to-one-history-standard-165/)
deler én commitstandard: et konvensjonelt prefiks som angir typen endring, et
omfang, en emnelinje under en fast lengde, ingen emojier og ingen
attribusjonslinjer. En hook for commitmeldinger i hvert repo der det gjøres
arbeid, avviser alt som bryter med standarden, og det gjør standarden til en
egenskap ved repoet og ikke ved den som committer. Historikken viser da hva
arbeidet var. I det største repoet står det 156 commits med funksjoner, 133
rettinger, 128 dokumentasjon, 81 vedlikehold, 26 refaktoreringer, 20 stil, 5
ytelse og 3 test. Siden nevner også grensen: formatet sjekkes, men innholdet
gjør det ikke.

For selskapets eget driftsmiljø — en tilstedeværelse på nettet, en git-forge, en
database, sikkerhetskopier, e-posttransport og DNS — var oppgaven å få det
[beskrevet i et repo i stedet for i et hode](/nb/portfolio/built-the-companys-infrastructure-as-code-108/).
Det ble 19 playbooks og 34 roller fordelt på 12 065 linjer YAML. Hvilke roller
hver vert kjører, står i repoet som variabler på nivågruppen verten ligger i. En
konvergert vert rapporterer null endringer, og hele driftsmiljøet kan gjenskapes
fra repoet. Kostnaden står også skrevet: hver endring går tregere enn å redigere
en fil på serveren, og en preflight-play ble senere lagt inn foran
konvergeringen.

Et tredje prosjekt viser hva et repo beholder når oppmerksomheten flytter seg et
annet sted.
[Arbeid for et annet selskap fulgte med gjennom en omstrukturering av repoet](/nb/portfolio/wrote-a-scope-rule-after-a-credential-leak-124/),
blant annet en kladdelogg med gyldige påloggingsopplysninger i klartekst, som lå
i treet forbi alle sperrer gjennom det meste av repoets historikk. Det overlevde
nettopp fordi ingenting av det kjørte. Svaret ble en omfangsregel øverst i
repoets instruksjoner, ett selskaps infrastruktur og bare den, pluss karantene i
stedet for sletting, slik at historikken forblir lesbar, mens
hemmelighetsskanneren, vault-vakten og emoji-sjekken fortsatt leser filene i
karantenen.

## Hvor det går galt

- **En hemmelighet som er committet én gang, lever lenger enn slettingen.**
  Fjerner du filen i neste commit, ligger den fortsatt i den forrige og i hver
  klone som er laget siden. Behandle påloggingsopplysningen som offentlig: roter
  den først, og avgjør så om det er verdt å skrive om historikken, med bryderiet
  det gir alle som har en kopi.
- **En push treffer en utsjekket gren.** Å pushe inn i et repo som ikke er
  nakent, der den gjeldende grenen er den som oppdateres, ville satt
  arbeidstreet i utakt med historikken, så Git avviser en slik push som standard
  gjennom `receive.denyCurrentBranch`. Et repo folk pusher til, bør være nakent.
- **En grunn klone svarer på spørsmål om historikken ut fra en del av den.** En
  klone laget med `--depth` har bare de nyeste commitene, så `git log`,
  `git blame` og ethvert skript som utleder en versjon fra den siste taggen, ser
  bare den biten. Bygg- og utgivelsesjobber som trenger fortiden, bør hente hele
  historikken.
- **En lokal hook stopper ved neste klone.** Hooks på klientsiden ligger i
  `.git/hooks`, og Git-boken sier tydelig at en klone ikke kopierer dem. En
  regel som bare håndheves der, slutter å gjelde den dagen noen kloner på nytt.
  Hold hook-skriptene sporet i repoet og la `core.hooksPath` peke på dem, eller
  håndhev regelen på serversiden med `pre-receive`.
- **Store binærfiler gjør hver klone større for alltid.** Fordi en klone har med
  hver versjon av hver fil, blir hver erstattet kopi av en stor binærfil
  liggende i historikken, og hver ny klone laster ned alle sammen.
- **Filer som ingenting kjører, blir ikke lest.** En deaktivert oppgave, en
  utdatert inventarfil eller en kladdelogg ligger fortsatt i hver klone, men
  ingen test, intet bygg og ingen produksjonssetting rører den, så den slipper
  unna gjennomgang. Innholdet i repoet bør stemme med det prosjektet er
  ansvarlig for, og alt annet bør fjernes eller settes til side der sperrene
  fortsatt kan se det.

## Les mer

- [gitglossary: repository](https://git-scm.com/docs/gitglossary#def_repository)
  — Gits egen definisjon, ved siden av nakent repo, arbeidstre, grunt repo og
  submodul.
- [Getting a Git Repository](https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository)
  — de to måtene å få et på, å starte på nytt i en katalog eller å klone, og hva
  en klone tar med seg ned.
- [Git Objects](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects) —
  hvordan objektdatabasen lagrer innhold under en hash av det samme innholdet.
- [gitrepository-layout](https://git-scm.com/docs/gitrepository-layout) — hva
  hver fil og katalog inne i et repo er til for.
- [Getting Git on a Server](https://git-scm.com/book/en/v2/Git-on-the-Server-Getting-Git-on-a-Server)
  — å gjøre et eksisterende repo om til et nakent repo og dele det.
- [Git Hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) — hooks
  på klientsiden og serversiden, og hvorfor en klone lar dem på klientsiden
  ligge igjen.

<https://engineer.company/nb/glossary/repository/>
