-
1. Aan die slag
- 1.1 Oor Weergawebeheer
- 1.2 Wat is Git?
- 1.3 Die Opdragreël
- 1.4 Git Installeer
- 1.5 Git klaarmaak vir eerste gebruik
- 1.6 Hulp Verkry
- 1.7 Opsomming
-
2. Git Basics
-
3. Git Branching
-
4. Git on the Server
- 4.1 Die Protokolle (The Protocols)
- 4.2 Git op 'n Bediener kry (Getting Git on a Server)
- 4.3 Jou Publieke SSH-sleutel Genereer (Generating Your SSH Public Key)
- 4.4 Die Bediener Opstel (Setting Up the Server)
- 4.5 Git Daemon
- 4.6 Slim HTTP (Smart HTTP)
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Derdeparty-gasheuroplossings (Third-Party Hosting Solutions)
- 4.10 Summary
-
5. Distributed Git
-
6. GitHub
-
7. Git Tools
- 7.1 Hersieningseleksie (Revision Selection)
- 7.2 Interaktiewe Voorbereiding (Interactive Staging)
- 7.3 Bêre en Skoonmaak (Stashing and Cleaning)
- 7.4 Ondertekening van Jou Werk (Signing Your Work)
- 7.5 Soek (Searching)
- 7.6 Herskryf van Geskiedenis (Rewriting History)
- 7.7 Reset Ontmystifiseer (Reset Demystified)
- 7.8 Gevorderde Saamsmelting (Advanced Merging)
- 7.9 Rerere
- 7.10 Ontfouting met Git (Debugging with Git)
- 7.11 Submodules
- 7.12 Bundeling (Bundling)
- 7.13 Vervang (Replace)
- 7.14 Die Stoor van Aanmeldbewyse (Credential Storage)
- 7.15 Summary
-
8. Customizing Git
-
9. Git and Other Systems
- 9.1 Git as a Client
- 9.2 Migrating to Git
- 9.3 Summary
-
10. Git Internals
10.2 Git Internals - Git Objekte (Git Objects)
Git Objekte (Git Objects)
Git is 'n inhoud-adresseerbare lêerstelsel (content-addressable filesystem). Wonderlik. Wat beteken dit? Dit beteken dat daar in die kern van Git 'n eenvoudige sleutel-waarde datastoor is. Wat dit beteken, is dat jy enige soort inhoud in 'n Git-bewaarplek kan plaas, waarvoor Git vir jou 'n unieke sleutel sal teruggee wat jy later kan gebruik om daardie inhoud te herwin.
As 'n demonstrasie, kom ons kyk na die loodgietersopdrag (plumbing command) git hash-object, wat 'n bietjie data neem, dit in jou .git/objects gids (die objekdatabasis) stoor, en vir jou die unieke sleutel teruggee wat nou na daardie data-objek verwys.
Eerstens inisialiseer jy 'n nuwe Git-bewaarplek en verifieer dat daar (soos te wagte) niks in die objects-gids is nie:
$ git init test
Initialized empty Git repository in /tmp/test/.git/
$ cd test
$ find .git/objects
.git/objects
.git/objects/info
.git/objects/pack
$ find .git/objects -type f
Git het die objects-gids geïnisialiseer en pack- en info-subgidse daarin geskep, maar daar is geen gewone lêers nie.
Kom ons gebruik nou git hash-object om 'n nuwe data-objek te skep en dit handmatig in jou nuwe Git-databasis te stoor:
$ echo 'test content' | git hash-object -w --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4
In sy eenvoudigste vorm sou git hash-object die inhoud wat jy daaraan gegee het neem en slegs die unieke sleutel teruggee wat gebruik sou word om dit in jou Git-databasis te stoor.
Die -w opsie sê dan vir die opdrag om nie bloot die sleutel terug te gee nie, maar om daardie objek na die databasis te skryf.
Laastens sê die --stdin opsie vir git hash-object om die inhoud wat verwerk moet word vanaf stdin te kry; anders sou die opdrag 'n lêernaam-argument aan die einde van die opdrag verwag wat die inhoud bevat wat gebruik moet word.
Die afvoer van die bogenoemde opdrag is 'n 40-karakter kontrolesomhuts (checksum hash). Dit is die SHA-1-huts — 'n kontrolesom van die inhoud wat jy stoor plus 'n kopstuk (header), waaroor jy binnekort meer sal leer. Nou kan jy sien hoe Git jou data gestoor het:
$ find .git/objects -type f
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
As jy weer jou objects-gids ondersoek, kan jy sien dat dit nou 'n lêer vir daardie nuwe inhoud bevat.
Dit is hoe Git die inhoud aanvanklik stoor — as 'n enkele lêer per stuk inhoud, benoem met die SHA-1-kontrolesom van die inhoud en sy kopstuk.
Die subgids word benoem met die eerste 2 karakters van die SHA-1, en die lêernaam is die oorblywende 38 karakters.
Sodra jy inhoud in jou objekdatabasis het, kan jy daardie inhoud ondersoek met die git cat-file opdrag.
Hierdie opdrag is 'n soort Switserse weermagsmes vir die inspeksie van Git-objekte.
Deur -p aan cat-file deur te gee, beveel dit die opdrag om eers die tipe inhoud uit te vind, en dit dan gepas te vertoon:
$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
test content
Nou kan jy inhoud by Git voeg en dit weer uittrek. Jy kan dit ook met inhoud in lêers doen. Jy kan byvoorbeeld eenvoudige weergawebeheer op 'n lêer doen. Eerstens, skep 'n nuwe lêer en stoor die inhoud daarvan in jou databasis:
$ echo 'version 1' > test.txt
$ git hash-object -w test.txt
83baae61804e65cc73a7201a7252750c76066a30
Skryf dan 'n bietjie nuwe inhoud na die lêer, en stoor dit weer:
$ echo 'version 2' > test.txt
$ git hash-object -w test.txt
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
Jou objekdatabasis bevat nou beide weergawes van hierdie nuwe lêer (sowel as die eerste inhoud wat jy daar gestoor het):
$ find .git/objects -type f
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
Op hierdie punt kan jy jou plaaslike kopie van daardie test.txt lêer uitvee, en dan Git gebruik om óf die eerste weergawe wat jy gestoor het uit die objekdatabasis te herwin:
$ git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30 > test.txt
$ cat test.txt
version 1
of die tweede weergawe:
$ git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a > test.txt
$ cat test.txt
version 2
Maar om die SHA-1-sleutel vir elke weergawe van jou lêer te onthou, is nie prakties nie; plus, jy stoor nie die lêernaam in jou stelsel nie — net die inhoud.
Hierdie objektipe word 'n blob genoem.
Jy kan Git laat sê wat die objektipe van enige objek in Git is, gegewe sy SHA-1-sleutel, met git cat-file -t:
$ git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
blob
Boomobjekte (Tree Objects)
Die volgende tipe Git-objek wat ons sal ondersoek is die boom (tree), wat die probleem oplos om die lêernaam te stoor en jou ook toelaat om 'n groep lêers saam te stoor. Git stoor inhoud op 'n manier soortgelyk aan 'n UNIX-lêerstelsel, maar 'n bietjie vereenvoudig. Al die inhoud word as boom- en blob-objekte gestoor, met bome wat ooreenstem met UNIX-gidsinskrywings en blobs wat min of meer ooreenstem met inodes of lêerinhoud. 'n Enkele boomobjek bevat een of meer inskrywings, waarvan elkeen die SHA-1-huts van 'n blob of subboom is met sy geassosieerde modus, tipe en lêernaam. Sê byvoorbeeld jy het 'n projek waar die mees onlangse boom so lyk:
$ git cat-file -p master^{tree}
100644 blob a906cb2a4a904a152e80877d4088654daad0c859 README
100644 blob 8f94139338f9404f26296befa88755fc2598c289 Rakefile
040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0 lib
Die master^{tree} sintaksis spesifiseer die boomobjek waarna die laaste vaslegging op jou master-tak wys.
Let op dat die lib-subgids nie 'n blob is nie, maar 'n wyser na 'n ander boom:
$ git cat-file -p 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0
100644 blob 47c6340d6459e05787f644c2447d2595f5d3a54b simplegit.rb
|
Note
|
Afhangende van watter dop (shell) jy gebruik, mag jy foute teëkom wanneer jy die In CMD op Windows word die As jy ZSH gebruik, word die |
Konseptueel lyk die data wat Git stoor min of meer so:
Jy kan redelik maklik jou eie boom skep.
Git skep normaalweg 'n boom deur die toestand van jou voorbereidingsarea (staging area) of indeks te neem en 'n reeks boomobjekte daaruit te skryf.
So, om 'n boomobjek te skep, moet jy eers 'n indeks opstel deur 'n paar lêers voor te berei.
Om 'n indeks met 'n enkele inskrywing te skep — die eerste weergawe van jou test.txt-lêer — kan jy die loodgietersopdrag git update-index gebruik.
Jy gebruik hierdie opdrag om die vroeëre weergawe van die test.txt-lêer kunsmatig by 'n nuwe voorbereidingsarea te voeg.
Jy moet die --add opsie daaraan gee omdat die lêer nog nie in jou voorbereidingsarea bestaan nie (jy het nog nie eers 'n voorbereidingsarea opgestel nie) en --cacheinfo omdat die lêer wat jy byvoeg nie in jou gids is nie, maar in jou databasis.
Dan spesifiseer jy die modus, SHA-1 en lêernaam:
$ git update-index --add --cacheinfo 100644 \
83baae61804e65cc73a7201a7252750c76066a30 test.txt
In hierdie geval spesifiseer jy 'n modus van 100644, wat beteken dat dit 'n normale lêer is.
Ander opsies is 100755, wat beteken dis 'n uitvoerbare lêer; en 120000, wat 'n simboliese skakel spesifiseer.
Die modus is van normale UNIX-modusse afgeneem, maar is baie minder buigsaam — hierdie drie modusse is die enigste wat geldig is vir lêers (blobs) in Git (hoewel ander modusse vir gidse en submodules gebruik word).
Nou kan jy git write-tree gebruik om die voorbereidingsarea na 'n boomobjek uit te skryf.
Geen -w opsie is nodig nie — deur hierdie opdrag te roep word 'n boomobjek outomaties geskep vanaf die toestand van die indeks as daardie boom nog nie bestaan nie:
$ git write-tree
d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579
100644 blob 83baae61804e65cc73a7201a7252750c76066a30 test.txt
Jy kan ook verifieer dat dit 'n boomobjek is deur dieselfde git cat-file opdrag te gebruik wat jy vroeër gesien het:
$ git cat-file -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579
tree
Jy sal nou 'n nuwe boom skep met die tweede weergawe van test.txt asook 'n nuwe lêer:
$ echo 'new file' > new.txt
$ git update-index --cacheinfo 100644 \
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt
$ git update-index --add new.txt
Jou voorbereidingsarea het nou die nuwe weergawe van test.txt sowel as die nuwe lêer new.txt.
Skryf daardie boom uit (wat die toestand van die voorbereidingsarea of indeks in 'n boomobjek aanteken) en kyk hoe dit lyk:
$ git write-tree
0155eb4229851634a0f03eb265b69f5a2d56f341
$ git cat-file -p 0155eb4229851634a0f03eb265b69f5a2d56f341
100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new.txt
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt
Let op dat hierdie boom beide lêerinskrywings het en ook dat die test.txt SHA-1 die “version 2” SHA-1 van vroeër is (1f7a7a).
Net vir die pret sal jy die eerste boom as 'n subgids by hierdie een voeg.
Jy kan bome in jou voorbereidingsarea inlees deur git read-tree te roep.
In hierdie geval kan jy 'n bestaande boom in jou voorbereidingsarea inlees as 'n subboom deur die --prefix opsie met hierdie opdrag te gebruik:
$ git read-tree --prefix=bak d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ git write-tree
3c4e9cd789d88d8d89c1073707c3585e41b0e614
$ git cat-file -p 3c4e9cd789d88d8d89c1073707c3585e41b0e614
040000 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 bak
100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new.txt
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt
As jy 'n werkgids sou skep vanaf die nuwe boom wat jy pas geskryf het, sou jy die twee lêers op die boonste vlak van die werkgids kry en 'n subgids genaamd bak wat die eerste weergawe van die test.txt lêer bevat.
Jy kan dink aan die data wat Git vir hierdie strukture bevat asof dit so lyk:
Vasleggingsobjekte (Commit Objects)
As jy al die bogenoemde gedoen het, het jy nou drie bome wat die verskillende momentopnames van jou projek verteenwoordig wat jy wil naspoor, maar die vroeëre probleem bly: jy moet al drie SHA-1-waardes onthou om die momentopnames te herroep. Jy het ook geen inligting oor wie die momentopnames gestoor het, wanneer dit gestoor is, of hoekom dit gestoor is nie. Dit is die basiese inligting wat die vasleggingsobjek vir jou stoor.
Om 'n vasleggingsobjek te skep, roep jy commit-tree en spesifiseer jy 'n enkele boom SHA-1 en watter vasleggingsobjekte, indien enige, dit direk voorafgegaan het.
Begin met die eerste boom wat jy geskryf het:
$ echo 'First commit' | git commit-tree d8329f
fdf4fc3344e67ab068f836878b6c4951e3b15f3d
|
Note
|
Jy sal 'n ander hutswaarde kry as gevolg van verskillende skeppingstye en outeurdata. Bowendien, hoewel in beginsel enige vasleggingsobjek presies gereproduseer kan word gegewe daardie data, beteken historiese besonderhede van hierdie boek se samestelling dat die gedrukte vasleggingshutse moontlik nie ooreenstem met die gegewe vasleggings nie. Vervang vasleggings- en merkerhutse met jou eie kontrolesomme verder in hierdie hoofstuk. |
Nou kan jy na jou nuwe vasleggingsobjek kyk met git cat-file:
$ git cat-file -p fdf4fc3
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author Scott Chacon <schacon@gmail.com> 1243040974 -0700
committer Scott Chacon <schacon@gmail.com> 1243040974 -0700
First commit
Die formaat vir 'n vasleggingsobjek is eenvoudig: dit spesifiseer die boonste-vlak boom vir die momentopname van die projek op daardie punt; die ouer-vasleggings indien enige (die vasleggingsobjek wat hierbo beskryf is, het geen ouers nie); die outeur/vaslêer (committer)-inligting (wat jou user.name en user.email konfigurasie-instellings en 'n tydstempel gebruik); 'n leë reël, en dan die vasleggingsboodskap.
Vervolgens sal jy die ander twee vasleggingsobjekte skryf, wat elkeen verwys na die vaslegging wat direk daarvoor gekom het:
$ echo 'Second commit' | git commit-tree 0155eb -p fdf4fc3
cac0cab538b970a37ea1e769cbbde608743bc96d
$ echo 'Third commit' | git commit-tree 3c4e9c -p cac0cab
1a410efbd13591db07496601ebc7a059dd55cfe9
Elkeen van die drie vasleggingsobjekte wys na een van die drie momentopname-bome wat jy geskep het.
Vreemd genoeg het jy nou 'n regte Git-geskiedenis wat jy kan bekyk met die git log-opdrag, as jy dit op die laaste vaslegging SHA-1 uitvoer:
$ git log --stat 1a410e
commit 1a410efbd13591db07496601ebc7a059dd55cfe9
Author: Scott Chacon <schacon@gmail.com>
Date: Fri May 22 18:15:24 2009 -0700
Third commit
bak/test.txt | 1 +
1 file changed, 1 insertion(+)
commit cac0cab538b970a37ea1e769cbbde608743bc96d
Author: Scott Chacon <schacon@gmail.com>
Date: Fri May 22 18:14:29 2009 -0700
Second commit
new.txt | 1 +
test.txt | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
commit fdf4fc3344e67ab068f836878b6c4951e3b15f3d
Author: Scott Chacon <schacon@gmail.com>
Date: Fri May 22 18:09:34 2009 -0700
First commit
test.txt | 1 +
1 file changed, 1 insertion(+)
Verbluffend.
Jy het pas die laevlak-operasies gedoen om 'n Git-geskiedenis op te bou sonder om enige van die voorvlak-opdragte (front end commands) te gebruik.
Dit is in wese wat Git doen wanneer jy die git add en git commit opdragte uitvoer — dit stoor blobs vir die lêers wat verander het, werk die indeks by, skryf bome uit en skryf vasleggingsobjekte wat na die boonste-vlak bome verwys en die vasleggings wat onmiddellik voor hulle gekom het.
Hierdie drie hoof Git-objekte — die blob, die boom en die vaslegging — word aanvanklik as aparte lêers in jou .git/objects gids gestoor.
Hier is al die objekte in die voorbeeldgids nou, met kommentaar oor wat hulle stoor:
$ find .git/objects -type f
.git/objects/01/55eb4229851634a0f03eb265b69f5a2d56f341 # tree 2
.git/objects/1a/410efbd13591db07496601ebc7a059dd55cfe9 # commit 3
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a # test.txt v2
.git/objects/3c/4e9cd789d88d8d89c1073707c3585e41b0e614 # tree 3
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30 # test.txt v1
.git/objects/ca/c0cab538b970a37ea1e769cbbde608743bc96d # commit 2
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4 # 'test content'
.git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579 # tree 1
.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 # new.txt
.git/objects/fd/f4fc3344e67ab068f836878b6c4951e3b15f3d # commit 1
As jy al die interne wysers volg, kry jy 'n objekgrafiek wat min of meer so lyk:
Objekberging (Object Storage)
Ons het vroeër genoem dat daar 'n kopstuk (header) gestoor word saam met elke objek wat jy in jou Git-objekdatabasis vaslê. Kom ons neem 'n minuut om te kyk hoe Git sy objekte stoor. Jy sal sien hoe om 'n blob-objek te stoor — in hierdie geval die string “what is up, doc?” — interaktief in die Ruby-skriptaal.
Jy kan interaktiewe Ruby-modus met die irb-opdrag begin:
$ irb
>> content = "what is up, doc?"
=> "what is up, doc?"
Git konstrueer eers 'n kopstuk wat begin deur die tipe objek te identifiseer — in hierdie geval 'n blob. Aan daardie eerste deel van die kopstuk voeg Git 'n spasie by gevolg deur die grootte in grepe (bytes) van die inhoud, en voeg 'n finale nul-greep (null byte) by:
>> header = "blob #{content.bytesize}\0"
=> "blob 16\u0000"
Git aaneenskakel die kopstuk en die oorspronklike inhoud en bereken dan die SHA-1 kontrolesom van daardie nuwe inhoud.
Jy kan die SHA-1 waarde van 'n string in Ruby bereken deur die SHA1 digest-biblioteek met die require-opdrag in te sluit en dan Digest::SHA1.hexdigest() met die string te roep:
>> store = header + content
=> "blob 16\u0000what is up, doc?"
>> require 'digest/sha1'
=> true
>> sha1 = Digest::SHA1.hexdigest(store)
=> "bd9dbf5aae1a3862dd1526723246b20206e5fc37"
Kom ons vergelyk dit met die afvoer van git hash-object.
Hier gebruik ons echo -n om te voorkom dat 'n nuwe reël by die invoer gevoeg word.
$ echo -n "what is up, doc?" | git hash-object --stdin
bd9dbf5aae1a3862dd1526723246b20206e5fc37
Git pers die nuwe inhoud saam met zlib, wat jy in Ruby kan doen met die zlib-biblioteek.
Eerstens moet jy die biblioteek insluit (require) en dan Zlib::Deflate.deflate() op die inhoud uitvoer:
>> require 'zlib'
=> true
>> zlib_content = Zlib::Deflate.deflate(store)
=> "x\x9CK\xCA\xC9OR04c(\xCFH,Q\xC8,V(-\xD0QH\xC9O\xB6\a\x00_\x1C\a\x9D"
Ten slotte sal jy jou zlib-saamgeperste inhoud na 'n objek op die skyf skryf.
Jy sal die pad bepaal van die objek wat jy wil uitskryf (die eerste twee karakters van die SHA-1 waarde is die subgidsnaam, en die laaste 38 karakters is die lêernaam binne daardie gids).
In Ruby kan jy die FileUtils.mkdir_p() funksie gebruik om die subgids te skep as dit nie bestaan nie.
Maak dan die lêer oop met File.open() en skryf die voorheen zlib-saamgeperste inhoud uit na die lêer met 'n write()-roep op die resulterende lêerhandvatsel:
>> path = '.git/objects/' + sha1[0,2] + '/' + sha1[2,38]
=> ".git/objects/bd/9dbf5aae1a3862dd1526723246b20206e5fc37"
>> require 'fileutils'
=> true
>> FileUtils.mkdir_p(File.dirname(path))
=> ".git/objects/bd"
>> File.open(path, 'w') { |f| f.write zlib_content }
=> 32
Kom ons gaan die inhoud van die objek na met behulp van git cat-file:
---
$ git cat-file -p bd9dbf5aae1a3862dd1526723246b20206e5fc37
what is up, doc?
---
Dis dit – jy het 'n geldige Git blob-objek geskep.
Alle Git-objekte word op dieselfde manier gestoor, net met verskillende tipes – in plaas van die string 'blob', sal die kopstuk begin met 'commit' of 'tree'. Verder, hoewel die blob-inhoud byna enigiets kan wees, is die vaslegging- en boom-inhoud baie spesifiek geformateer.