Les historikken: log, show og objektene bak
Den interaktive leksjonen trenger JavaScript. Teksten under er hele leksjonen.
Etter leksjonen kan du
- bruke
git logmed--oneline,--statog-- <fil>for å finne commiten som endret en bestemt fil anvende - peke på eldre commits relativt til HEAD med
HEAD~1ogHEAD~2, og vise en gammel filversjon medgit showanvende - forklare hvorfor en commit-ID endres når innhold, forelder eller melding endres forstå
- følge en commit til tree og blobs med
git cat-file -poggit ls-tree, og forklare hvorfor uendrede filer deler blob anvende
Forstå: Hva historikken består av
git log viser historikken med den nyeste commiten øverst: ID, forfatter, tidspunkt og melding. Valg styrer detaljnivået. --oneline gir én linje per commit, --stat viser hvilke filer som ble endret, og -p viser selve endringene.
ID-en er en hash: et fingeravtrykk på førti tegn som regnes ut fra alt i commiten. Det gjelder snapshotet, forelderen, forfatteren, tidspunktet og meldingen. Endrer du noe av det, får du en annen ID. Som regel holder de første sju tegnene.
Du trenger sjelden å skrive ID-er. HEAD~1 betyr ett steg tilbake fra der du står, altså forelderen. HEAD~2 er to steg tilbake.
Hva er inni en commit? Commit D peker på et tree, innholdsfortegnelsen til snapshotet. Treet lister to filnavn, og hvert navn peker på en blob, altså innholdet i én fil. D lagrer også ID-en til forelderen sin, C. Det er pilen i grafen.
Hver commit lagrer ID-en til forelderen sin, så ID-ene henger sammen i en kjede. Endrer du noe i B, blir det en ny commit med ny ID. C peker fortsatt på den gamle B, så for å bygge videre må C og D også lages på nytt, med nye ID-er. Ingen kan endre historikken i det stille.
Se demo: Fra log til blob
Dette prosjektet har fire commits. --graph tegner linjen mellom dem. Her er den rett. Når vi får brancher senere, deler den seg.
$ git log --oneline --graph
Med --stat ser vi hvilke filer hver commit endret. Med -- og et filnavn viser git log bare commits som endret den filen.
$ git log --oneline --stat
git show HEAD~1 viser forelderen til den nyeste commiten. Vi får ID, forfatter, tid og melding, og under det diffen mot dens egen forelder. Git regner ut den diffen nå. Den er ikke lagret.
$ git show HEAD~1
Nå ser vi selve commit-objektet. Det peker på ett tree og én forelder. Se på parent-linjen: det er ID-en til commiten vi nettopp viste. Den linjen er pilen i grafen.
$ git cat-file -p HEAD
Treet er innholdsfortegnelsen. Hver linje har et filnavn og ID-en til en blob. Merk deg starten på ID-en til README.md.
$ git ls-tree HEAD
Den nyeste commiten endret bare INSTALL.md. Hva tror du skjer med README.md i snapshotet før?
$ git ls-tree HEAD~1
HEAD:README.md betyr blobben til README.md i HEAD. Blobben er bare innholdet. Den har verken navn eller dato. Navnet står i treet. I objektvisningen ser du at begge treene peker på denne samme blobben.
$ git cat-file -p HEAD:README.md
Oppgaver
- Vis historikken kompakt, med grafen.
- Vis hvilke filer hver commit endret.
- Vis commiten rett før den nyeste, med endringene.
- Skriv ut det rå commit-objektet til HEAD.
- List filene i det nyeste snapshotet, med blob-ID-ene.
- List filene i snapshotet før det nyeste, og sammenlign ID-ene.
- Skriv ut innholdet i README.md slik det er lagret i den nyeste commiten.
Øv: Overfør: grav i en handleliste
Et nytt repository med fem commits. Først leser du historikken og finner svar. Til slutt lager du én commit for å undersøke hva en commit-ID består av.
Oppgaver
- Finn commiten som sist endret
README.md, og skriv ID-en dens i en ny filanswer.txt. Kort ID holder. - Lagre
list.txtslik den så ut to commits før den nyeste, i en ny filold-list.txt. Selvelist.txtskal være uendret. - Lag en ny commit som har nøyaktig de samme filene som forelderen sin. Bekreft så at de to commitene har ulike ID-er, men samme tree.
Sjekk deg selv: Hent det fram igjen
Historikken er A, B, C, D, E, der E er nyest og HEAD peker på E. Hvilken commit er HEAD~2?
- D
- C
- B
To commits har nøyaktig de samme filene. Har de samme ID?
- Ja, ID-en regnes bare ut fra filene
- Ikke nødvendigvis: forelder, forfatter, tid og melding inngår også
- Nei, ID-er er tilfeldige tall
Du gir notes.txt det nye navnet ideas.txt uten å endre innholdet, og committer. Hva lagrer den nye commiten for ideas.txt?
- En ny blob med det nye navnet
- En linje i det nye treet som peker på den gamle blobben
- Ingenting, fordi bare endringer lagres
Repetisjon fra leksjonen om gode commits: git log --oneline viser én linje per commit. Hvilken del av commitmeldingen er det?
- Emnelinjen, altså første linje i meldingen
- Brødteksten, fordi det er der forklaringen står
- Et sammendrag Git lager av diffen
Vanlige misforståelser
- «En commit-ID er et tilfeldig tall eller et løpenummer.» Den er en hash av hele commiten, også forelder, forfatter, tid og melding.
- «
HEAD~2er commit nummer to.» Tilde teller bakover fra HEAD, ikke fra starten. - «Fullstendige snapshots må ta mye plass.» En uendret fil peker på den samme blobben som før.
Les mer
De tre objekttypene
Git lagrer filinnhold og historikk som objekter i .git/objects. ID-en til et objekt er en hash av en kort innledning med type og størrelse, fulgt av innholdet.
- En blob er innholdet i én fil, uten navn, dato eller rettigheter. Samme innhold gir alltid samme ID, også i et annet repository.
git hash-objectregner ut ID-en uten å lagre noe:
$ printf 'Welcome\nA tiny demo project.\n' | git hash-object --stdin
5f6e829bc5a7fcde1c549bf31b1292e3762a75ec
- Et tree er en mappe: en liste med modus, type, ID og navn. Undermapper er nye trees.
$ git ls-tree HEAD
100644 blob a2f179b6e361876a40e3debec4ab5d59af100618 INSTALL.md
100644 blob 5f6e829bc5a7fcde1c549bf31b1292e3762a75ec README.md
- En commit peker på ett tree og null, én eller flere foreldre, og lagrer forfatter, committer og melding:
$ git cat-file -p HEAD
tree b1a2df414fdbe20c422e0f593dd0952bfb400b08
parent 2a59dab4dcabe5f8d025d1ab94d1015d680fc576
author Student <student@example.invalid> 1791875457 +0200
committer Student <student@example.invalid> 1791875457 +0200
Use npm ci in install guide
Tallet etter e-posten er tidspunktet i sekunder siden 1970, med tidssone. Author er den som skrev endringen. Committer er den som laget akkurat dette commit-objektet. Når en commit kopieres, for eksempel ved rebase eller cherry-pick, beholdes author, mens committer og committidspunkt blir nye.
git cat-file -t gir typen til et objekt, -p skriver det ut lesbart, og -s gir størrelsen i byte.
Blob-ID-ene over er de samme i ditt repository, fordi innholdet er det samme. Commit-ID-ene blir andre, fordi tidspunktet er en del av commiten.
Samme filer, ny commit
ID-en til en commit dekker hele objektet. En commit uten endringer i filene får likevel en ny ID:
$ git commit --allow-empty -m "Mark review point"
$ git rev-parse HEAD HEAD~1 'HEAD^{tree}' 'HEAD~1^{tree}'
f86942208ecf3829d56f9b045b009cbd2a4f2c73
b3706e8326424b35f7a5c8e7c8870730b728d667
b1a2df414fdbe20c422e0f593dd0952bfb400b08
b1a2df414fdbe20c422e0f593dd0952bfb400b08
To ulike commits, ett felles tree. REV^{tree} betyr treet til commiten.
Navigere: tilde, hatt og kolon
| Skrivemåte | Betyr |
|---|---|
HEAD~1, HEAD^ |
Forelderen til HEAD, alltid første forelder |
HEAD~3 |
Følg første forelder tre ganger |
HEAD~2:list.txt |
Filen list.txt slik den var i den commiten |
HEAD^{tree} |
Treet til commiten |
En merge-commit har to foreldre. HEAD^2 for den andre kommer i leksjonen om treveis merge.
Noen skall gir tegnene selv en betydning. I zsh med valget
extendedglob, som mange oppsett slår på, er^et mønstertegn. I Windows-ledeteksten cmd er^et escape-tegn. Sett anførselstegn rundt slike uttrykk, for eksempelgit rev-parse "HEAD^{tree}". Doble anførselstegn virker i alle vanlige skall, også cmd.
Finne en bestemt endring
| Valg | Viser |
|---|---|
--oneline |
Én linje per commit |
--graph --all |
Grafen for alle brancher |
--stat / -p |
Filer og linjetall / hele diffen |
-3 |
Bare de tre nyeste |
-- fil |
Bare commits som endret filen |
-S tekst |
Commits som endrer hvor mange ganger teksten forekommer |
--grep=ord |
Commits med ordet i meldingen |
--author=navn, --since="2 weeks ago" |
Filtrer på forfatter og tid |
--format='%h %an %ar %s' |
Eget format: kort ID, forfatter, relativ tid, emne |
$ git log --oneline -- INSTALL.md
b3706e8 Use npm ci in install guide
3cd1670 Add install guide
$ git log --oneline -S "npm ci"
b3706e8 Use npm ci in install guide
git show REV viser én commit med diff mot forelderen. git show --stat REV gir bare filoversikten, og git show REV:README.md skriver ut en gammel filversjon. git diff --stat HEAD~2 HEAD sammenligner to vilkårlige snapshots.
Områder: A..B
git log A..B viser commits som kan nås fra B, men ikke fra A. Det er en mengde noder i grafen, ikke et tidsintervall. Etter den tomme commiten over:
$ git log --oneline HEAD~2..HEAD
f869422 Mark review point
b3706e8 Use npm ci in install guide
Med brancher gir main..feature det feature har som main mangler. Du bruker dette mye med brancher og remotes senere.
Feilmeldinger
$ git show HEAD~9
fatal: ambiguous argument 'HEAD~9': unknown revision or path not in the working tree.
$ git show HEAD:nope.md
fatal: path 'nope.md' does not exist in 'HEAD'
Den første betyr at historikken er kortere enn ni steg, eller at Git ikke vet om du mente en commit eller et filnavn. Den andre betyr at filen ikke fantes i det snapshotet. Sjekk navnet med git ls-tree. En kort ID må ha minst fire tegn og være entydig. Passer den på flere objekter, svarer Git med short object ID … is ambiguous og lister kandidatene.
Les videre
- Viewing the Commit History og Git Objects i Pro Git-boka
- git log, git show, git cat-file og gitrevisions
Per-Arne's Git Good!