Hvordan man ikke længere er bange for Git

Forståelse af maskinerne for at sprede usikkerheden væk

Har du været her før? (web-tegneserie af XKCD)

Hvad er Git alligevel?

"Det er et versionskontrolsystem."

Hvorfor har jeg brug for det?

"Til versionskontrol, fjollet."

Okay, okay, jeg er ikke for hjælpsom endnu. Her er den grundlæggende idé: Når projekter bliver for store og for mange bidragydere, bliver det umuligt at spore, hvem der gjorde hvad og hvornår. Indførte nogen en ændring, der brød hele systemet? Hvordan finder du ud af, hvad den ændring var? Hvordan går du tilbage til, hvordan tingene var før? Tilbage til det ubrutte vidunderland?

Jeg tager det et skridt videre - sig ikke et projekt med mange bidragydere, bare et lille projekt med dig som skaberen, vedligeholderen og distributøren: Du opretter en ny funktion til dette projekt, der introducerer subtile bugs, du finder ud af det senere. Du kan ikke huske, hvilke ændringer du har foretaget i den eksisterende kodebase for at oprette denne nye funktion. Problem?

Svaret på alle disse problemer er versionering! At have versioner til alt, hvad du har kodet, sikrer dig, at du ved, hvem der har foretaget ændringerne, hvad der ændrer sig, og præcist hvor, siden projektets begyndelse!

Og nu inviterer jeg dig til at holde op med at tænke på (g) det som en blackbox, åbne den og finde ud af, hvilke skatte der venter. Find ud af, hvordan Git fungerer, og du har aldrig mere problemer med at få tingene til at fungere. Når du først er nået igennem dette, lover jeg, at du er klar over dårligheden ved at gøre, hvad XKCD-tegneserien ovenfor siger. Det er nøjagtigt, hvad versionering forsøger at forhindre.

Hvordan man får det?

Jeg antager, at du kender de grundlæggende kommandoer i Git, eller har hørt om dem og brugt dem mindst en gang. Hvis ikke, her er et grundlæggende ordforråd, der hjælper dig med at komme i gang.

Repository: et sted til opbevaring af ting. Med Git betyder dette din kodemappe

hoved: En "markør" til den seneste kode, du arbejdede på

tilføj: En handling til at bede Git om at spore en fil

begå: En handling for at redde den nuværende tilstand - sådan at man kan gendanne denne tilstand om nødvendigt

fjernbetjening: Et lager, der ikke er lokalt. Kan være i en anden mappe eller i skyen (for eksempel: Github): hjælper andre mennesker med let at samarbejde, da de ikke behøver at få en kopi fra dit system - de kan bare hente det fra skyen. Sørger også for, at du har en sikkerhedskopi, hvis du ødelægger din bærbare computer

pull: En handling for at få opdateret kode fra fjernbetjeningen

push: En handling til at sende opdateret kode til fjernbetjeningen

fletning: En handling til at kombinere to forskellige versioner af kode

status: Viser oplysninger om den aktuelle status på lageret

Hvor skal jeg give det?

Introduktion af magien kontrolleret af en skjult mappe: .git /

I hvert git-arkiv ser du noget lignende

$ træ. git /
.git /
├── Hoved
├── config
├── beskrivelse
├── kroge
│ ├── Applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── Pre-applicpatch.sample
│ ├── pre-commit.sample
│ ├── præ-push.ample
│ ├── pre-rebase.sample
│ ├── pre-modtage.sample
│ ├── Prepar-commit-msg.sample
│ └── opdatering.eksempel
├── info
│ └── ekskluder
├── genstande
│ ├── info
│ └── pakke
└── refs
    ├── hoveder
    └── tags
8 mapper, 14 filer

Sådan kontrollerer og administrerer Git hele dit projekt. Vi vil gå ind på alle de vigtige bit, en efter en.

Git består af 3 dele: objektlageret, indekset og arbejdsmappen.

Objektbutikken

Sådan opbevarer Git alt internt. For hver fil i dit projekt, du tilføjer, genererer Git en hash til filen og gemmer filen under den hash. For eksempel, hvis jeg nu opretter en helloworld-fil og tilføjer git add helloworld (som fortæller Git at tilføje den fil, der hedder helloworld, til git-objektlageret), får jeg noget lignende:

$ træ. git /
.git /
├── Hoved
├── config
├── beskrivelse
├── kroge
│ ├── Applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── Pre-applicpatch.sample
│ ├── pre-commit.sample
│ ├── præ-push.ample
│ ├── pre-rebase.sample
│ ├── pre-modtage.sample
│ ├── Prepar-commit-msg.sample
│ └── opdatering.eksempel
├── indeks
├── info
│ └── ekskluder
├── genstande
│ ├── a0
│ │ └── 423896973644771497bdc03eb99d5281615b51
│ ├── info
│ └── pakke
└── refs
    ├── hoveder
    └── tags
9 mapper, 16 filer

Et nyt objekt er blevet genereret! For dem, der er interesseret i at gå under hætten, bruger Git internt hash-objekt-kommandoen sådan:

$ git hash-objekt helloworld
a0423896973644771497bdc03eb99d5281615b51

Ja, det er den samme hash, vi ser under mappen objekter. Hvorfor undermappen med hashens to første tegn? Det gør søgningen hurtigere.

Derefter opretter Git et objekt med navnet som ovenstående hash, komprimerer vores givne fil og gemmer det der. Derfor kan du også faktisk se indholdet af objektet!

$ git cat-fil a0423896973644771497bdc03eb99d5281615b51 -p
Hej Verden!

Det hele er under hætten. Du ville aldrig bruge kat-fil i daglige tilføjelser. Du skal blot tilføje og lade Git håndtere resten.

Det er vores første Git-kommando, udført og støvet.

git add opretter en hash, komprimerer filen og tilføjer det komprimerede objekt til objektlageret.

Arbejdsmappen

Som navnet antyder er det her du arbejder. Alle filer, du opretter og redigerer, findes i arbejdsmappen. Jeg oprettede en ny fil, byeworld og kørte git-status:

$ git status
På grenmester
Ingen forpligtelser endnu
Ændringer, der skal begås:
  (brug "git rm - cache  ..." til ustabil)
ny fil: helloworld
Usporede filer:
  (brug "git add  ..." for at inkludere det, der vil blive forpligtet)
byeworld

Usporede filer er filer i det arbejdsmappe, vi ikke har bedt git om at administrere.

Havde der ikke været noget, vi havde gjort i arbejdsmappen, ville vi få følgende meddelelse:

$ git status
På grenmester
intet at forpligte sig, arbejder træ rent

som jeg er temmelig sikker på, at du forstår nu. Ignorer grenen og forpligt dig indtil videre. Nøglen er, at arbejdet træ (katalog) er rent.

Indekset

Dette er kernen i Git. Også kendt som iscenesættelsesområdet. Indekset gemmer kortlægning af filer til objekterne i objektlageret. Det er her forpligtelserne kommer ind. Den bedste måde at se dette på er at teste det ud!

Lad os forpligte os til at tilføje filen helloworld

$ git commit -m "Tilføj helloworld"
[master (root-commit) a39b9fd] Tilføj helloworld
 1 fil ændret, 1 indsættelse (+)
 Opret tilstand 100644 helloworld

Tilbage til vores træ:

$ træ. git /
.git /
├── COMMIT_EDITMSG
├── Hoved
├── config
├── beskrivelse
├── kroge
│ ├── Applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── Pre-applicpatch.sample
│ ├── pre-commit.sample
│ ├── præ-push.ample
│ ├── pre-rebase.sample
│ ├── pre-modtage.sample
│ ├── Prepar-commit-msg.sample
│ └── opdatering.eksempel
├── indeks
├── info
│ └── ekskluder
├── logfiler
│ ├── Hoved
│ └── refs
└── └── hoveder
│ └── master
├── genstande
│ ├── a0
│ │ └── 423896973644771497bdc03eb99d5281615b51
│ ├── a3
│ │ └── 9b9fdd624c35eee08a36077f411e009da68c2f
│ ├── fb
│ │ └── 26ca0289762a454db2ef783c322fedfc566d38
│ ├── info
│ └── pakke
└── refs
    ├── hoveder
    │ └── master
    └── tags
14 mapper, 22 filer

Ah, interessant! Vi har 2 nye objekter i vores objektlager, og nogle ting vi ikke forstår endnu i logfiler og refs. Vender tilbage til vores ven kat-fil:

$ git cat-fil a39b9fdd624c35eee08a36077f411e009da68c2f -p
træ fb26ca0289762a454db2ef783c322fedfc566d38
forfatter = <=> 1537700068 +0100
pendler = <=> 1537700068 +0100
Tilføj helloworld
$ git cat-fil fb26ca0289762a454db2ef783c322fedfc566d38 -p
100644 klat a0423896973644771497bdc03eb99d5281615b51 helloworld

Som du kan gætte, er det første objekt engagementmetadata: hvem gjorde hvad og hvorfor med et træ. Det andet objekt er det faktiske træ. Hvis du forstår unix-filsystem, ved du nøjagtigt, hvad dette er.

Træet i Git svarer til Git-filsystemet. Alt er enten et træ (bibliotek) eller en klods (fil), og med hver engagement lagrer Git træoplysningerne også for at fortælle sig selv: det er sådan, arbejdsmappen skal se ud på dette punkt. Bemærk, at træet peger på et specifikt objekt i hver fil, det indeholder (hash).

Det er tid til at tale om grene! Vores første engagement tilføjede også nogle andre ting til .git /. Vores interesse er nu i .git / refs / heads / master:

$ kat .git / refs / heads / master
a39b9fdd624c35eee08a36077f411e009da68c2f

Her er hvad du har brug for at vide om filialer:

En gren i Git er en let bevægelig markør til en af ​​disse forpligtelser. Standardgrenavnet i Git er master.

Hvad, hvad? Jeg kan godt lide at tænke på grene som en gaffel i din kode. Du vil foretage nogle ændringer, men du vil ikke bryde tingene. Du beslutter at have en stærkere afgrænsning end forpligtelsesloggen, og det er her filialer kommer ind. Master er standardgrenen, også brugt som de-facto-produktionsgren. Derfor oprettelsen af ​​ovenstående fil. Som du kan gætte ved indholdet af filen, peger det på vores første engagement. Derfor er det en pegepind til en forpligtelse.

Lad os udforske dette yderligere. Sig, jeg opretter en ny gren:

$ git branch slutningen
$ git gren
* mester
  slutningen

Der har vi det, en ny gren! Som du kan gætte, skal en ny post være tilføjet til .git / refs / heads / og da der ikke er nogen ekstra commitment, skal den også pege på vores første commit, ligesom master.

$ kat .git / refs / heads / the ending
a39b9fdd624c35eee08a36077f411e009da68c2f

Yup, nøjagtigt! Husk nu byverden? Denne fil blev stadig ikke sporet, så uanset hvilken gren du skifter til, ville filen altid være der. Sig, jeg vil skifte til denne gren nu, så jeg tjekker grenen, som et smokeshow.

slutningen af ​​$ git checkout
Skiftet til gren 'the ending'
$ git gren
  mestre
* slutningen

Nu, under hætten, ville Git ændre alt indholdet i arbejdsmappen for at matche det indhold, som filialforpligtelsen peger på. I øjeblikket, da dette er nøjagtigt det samme som master, ser det det samme ud.

Jeg tilføjer og forpligter byeworld-filen.

Hvad forventer du at ændre i mappen objekter?

Hvad forventer du at ændre i mappen refs / heads?

Tænk over dette, før du går videre.

$ træ. git /
.git /
├── COMMIT_EDITMSG
├── Hoved
├── config
├── beskrivelse
├── kroge
│ ├── Applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── Pre-applicpatch.sample
│ ├── pre-commit.sample
│ ├── præ-push.ample
│ ├── pre-rebase.sample
│ ├── pre-modtage.sample
│ ├── Prepar-commit-msg.sample
│ └── opdatering.eksempel
├── indeks
├── info
│ └── ekskluder
├── logfiler
│ ├── Hoved
│ └── refs
└── └── hoveder
│ ├── master
│ └── slutningen
├── genstande
│ ├── 0b
│ │ └── 17be9dbc34c5a5fbb0b94d57680968efd035ca
│ ├── a0
│ │ └── 423896973644771497bdc03eb99d5281615b51
│ ├── a3
│ │ └── 9b9fdd624c35eee08a36077f411e009da68c2f
│ ├── b3
│ │ └── 00387d818adbbd6e7cc14945fdf4c895de6376
│ ├── d1
│ │ └── 8affe001488123b496ceb34d8b13b120ab4cb6
│ ├── fb
│ │ └── 26ca0289762a454db2ef783c322fedfc566d38
│ ├── info
│ └── pakke
└── refs
    ├── hoveder
    │ ├── master
    │ └── slutningen
    └── tags
17 mapper, 27 filer

3 nye objekter - 1 for tilføjelse, 2 til forpligtelse! Giver mening? Hvad tror du, objekterne indeholder?

  • Foretage metadata
  • tilføj objektindhold
  • Træbeskrivelse

Den sidste del af billedet er: hvordan fungerer disse engagerede metadata med de (n) forrige engagementsmetadata (er)? Nå, kat-fil!

$ git cat-file 0b17be9dbc34c5a5fbb0b94d57680968efd035ca -p
100644 klat d18affe001488123b496ceb34d8b13b120ab4cb6 byeworld
100644 klat a0423896973644771497bdc03eb99d5281615b51 helloworld
$ git cat-fil b300387d818adbbd6e7cc14945fdf4c895de6376 -p
træ 0b17be9dbc34c5a5fbb0b94d57680968efd035ca
forælder a39b9fdd624c35eee08a36077f411e009da68c2f
forfatter = <=> 1537770989 +0100
pendler = <=> 1537770989 +0100
tilføj byeworld
$ git cat-fil d18affe001488123b496ceb34d8b13b120ab4cb6 -p
Bye verden!
$ kat .git / refs / heads / the ending
b300387d818adbbd6e7cc14945fdf4c895de6376

Ser du det med fed skrift? Forældremekeren! Og det er nøjagtigt, hvordan du tænkte på det - en linket liste, der forbinder forpligtelserne sammen!

Og ser du filialens implementering? Det peger på en forpligtelse, den seneste, vi gjorde efter at have tjekket ud! Selvfølgelig skal mesteren stadig pege på helloworld-forpligtelsen, ikke?

$ kat .git / refs / heads / master
a39b9fdd624c35eee08a36077f411e009da68c2f

Okay, vi har været igennem meget, lad os sammenfatte det op til her.

TL; DR

Git fungerer med objekter - komprimerede versioner af filer, du beder Git om at spore.

Hvert objekt har et ID (en hash genereret af Git baseret på filens indhold).

Hver gang du tilføjer en fil, tilføjer Git et nyt objekt til objektlageret. Dette er nøjagtigt hvorfor du ikke kan håndtere meget store filer i Git - den gemmer hele filen hver gang du tilføjer ændringer, ikke diffen (i modsætning til den almindelige tro).

Hver engagement opretter 2 objekter:

  1. Træet: Et ID for træet, der fungerer nøjagtigt som et unix-bibliotek: det peger på andre træer (biblioteker) eller klatter (filer): Dette bygger op hele katalogstrukturen baseret på de objekter, der var til stede på det tidspunkt. Klatter er repræsenteret af de aktuelle objekter oprettet ved tilføjelse.
  2. Engagementsmetadata: En id for begivenheden, der har foretaget en begivenhed, et træ, der repræsenterer begivenheden, begå meddelelsen og overordnet begå. Danner en linket listestruktur, der forbinder forpligtelser sammen.

Grener er tips til at begå metadataobjekter, der alle er gemt i .git / refs / heads

Det er alt sammen for forståelsen bag kulisserne! I den næste del gennemgår vi nogle af Git-handlingerne, der giver folk mareridt:

nulstille, flette, trække, skubbe, hente og hvordan de ændrer den interne struktur i .git /.

Andre historier i denne serie:

  • Hvordan man ikke længere er bange for Vim
  • Hvordan man ikke længere er bange for Python

Nød du det? Gå ikke glip af et indlæg igen - abonner på min mailingliste!