-
1. Začetek
- 1.1 O nadzoru različic
- 1.2 Kratka zgodovina Gita
- 1.3 Kaj je Git?
- 1.4 Ukazna vrstica
- 1.5 Namestitev Gita
- 1.6 Prva nastavitev Gita
- 1.7 Pridobivanje pomoči
- 1.8 Povzetek
-
2. Osnove Git
- 2.1 Pridobivanje repozitorija Git
- 2.2 Snemanje sprememb v repozitorij
- 2.3 Pregled zgodovine potrditev
- 2.4 Razveljavljanje stvari
- 2.5 Delo z daljavami
- 2.6 Označevanje
- 2.7 Aliasi Git
- 2.8 Povzetek
-
3. Veje Git
- 3.1 Veje na kratko
- 3.2 Osnove vej in združevanja
- 3.3 Upravljanje vej
- 3.4 Poteki dela z vejami
- 3.5 Oddaljene veje
- 3.6 Ponovno baziranje
- 3.7 Povzetek
-
4. Git na strežniku
- 4.1 Protokoli
- 4.2 Pridobitev Gita na strežniku
- 4.3 Generiranje vaših javnih ključev SSH
- 4.4 Nastavitev strežnika
- 4.5 Prikriti proces Git
- 4.6 Pametni HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Možnosti gostovanja pri tretjih ponudnikih
- 4.10 Povzetek
-
5. Porazdeljeni Git
- 5.1 Porazdeljeni poteki dela
- 5.2 Prispevek k projektu
- 5.3 Vzdrževanje projekta
- 5.4 Povzetek
-
6. GitHub
-
7. Orodja Git
- 7.1 Izbira revizije
- 7.2 Interaktivno pripravljanje
- 7.3 Shranjevanje na varno (angl. stashing) in čiščenje
- 7.4 Podpisovanje vašega dela
- 7.5 Iskanje
- 7.6 Prepisovanje zgodovine
- 7.7 Demistifikacija ponastavitve
- 7.8 Napredno združevanje
- 7.9 Rerere
- 7.10 Razhroščevanje z Gitom
- 7.11 Podmoduli
- 7.12 Povezovanje v pakete
- 7.13 Zamenjava
- 7.14 Shramba poverilnic
- 7.15 Povzetek
-
8. Prilagoditev Gita
- 8.1 Konfiguracija Git
- 8.2 Atributi Git
- 8.3 Kljuke Git
- 8.4 Primer pravilnika, ki ga uveljavlja Git
- 8.5 Povzetek
-
9. Git in ostali sistemi
- 9.1 Git kot odjemalec
- 9.2 Migracija na Git
- 9.3 Povzetek
-
10. Notranjost Gita
- 10.1 Napeljava in keramika
- 10.2 Objekti Git
- 10.3 Reference Git
- 10.4 Packfiles (datoteke zmanjšanih podatkov)
- 10.5 Refspec
- 10.6 Protokoli prenosa
- 10.7 Vzdrževanje in obnovitev podatkov
- 10.8 Spremenljivke okolja
- 10.9 Povzetek
-
A1. Dodatek A: Git v drugih okoljih
- A1.1 Grafični vmesniki
- A1.2 Git v Visual Studio
- A1.3 Git v Visual Studio Code
- A1.4 Git v IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine
- A1.5 Git v Sublime Text
- A1.6 Git v Bashu
- A1.7 Git v Zsh
- A1.8 Git v Powershellu
- A1.9 Povzetek
-
A2. Dodatek B: Vdelava Gita v vašo aplikacijo
- A2.1 Git v ukazni vrstici
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. Dodatek C: Ukazi Git
- A3.1 Nastavitev in konfiguracija
- A3.2 Pridobivanje in ustvarjanje projektov
- A3.3 Osnove posnetkov
- A3.4 Veje in združevanje
- A3.5 Deljenje in posodabljanje projektov
- A3.6 Pregled in primerjava
- A3.7 Razhroščevanje
- A3.8 Popravljanje
- A3.9 E-pošta
- A3.10 Zunanji sistemi
- A3.11 Administracija
- A3.12 Orodja za sisteme napeljave
10.2 Notranjost Gita - Objekti Git
Objekti Git
Git je sistem za shranjevanje vsebine s poudarkom na datotečnem sistemu. Odlično. Kaj to pomeni? To pomeni, da je v jedru Git preprosta shramba podatkov ključ-vrednost. To pomeni, da lahko v repozitorij Git vstavite katero koli vrsto vsebine, za katero vam bo Git vrnil edinstven ključ, ki ga lahko pozneje uporabite za pridobitev te vsebine.
Kot demonstracijo si poglejmo ukaz napeljave git hash-object
, ki sprejme nekaj podatkov, jih shrani v vaš direktorij .git/objects
(objektna baza podatkov) in vam vrne edinstven ključ, ki se nanaša na ta podatkovni objekt.
Najprej inicializirajte nov repozitorij Git in preverite, da v mapi objects
(pričakovano) ni ničesar:
$ 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 je inicializiral mapo objects
in v njej ustvaril podmapi pack
in info
, vendar v njej ni navadnih datotek.
Sedaj pa uporabimo git hash-object
, da ustvarimo nov podatkovni objekt in ga ročno shranimo v vašo novo podatkovno zbirko Git:
$ echo 'test content' | git hash-object -w --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4
V najpreprostejši obliki bi git hash-object
sprejel vsebino, ki ste jo posredovali, in preprosto vrnil edinstven ključ, ki bi bil uporabljen za shranjevanje v vaši podatkovni zbirki Git.
Možnost -w
ukazu pove, naj ne vrne le ključa, ampak objekt tudi zapiše v zbirko.
Možnost --stdin
pove git hash-object
, naj vsebino za obdelavo prebere iz stdin; sicer bi ukaz na koncu pričakoval argument imena datoteke, ki vsebuje vsebino, ki jo je treba uporabiti.
Izhod iz zgornjega ukaza je 40 znakov dolga zgoščena vrednost kontrolne vsote. To je zgoščena vrednost SHA-1 oz. kontrolna vsota vsebine, ki jo shranjujete, in glava, o kateri boste izvedeli več v kratkem. Sedaj lahko vidite, kako je Git shranil vaše podatke:
$ find .git/objects -type f
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
Če ponovno pregledate mapo objects
, lahko vidite, da vsebuje datoteko za to novo vsebino.
To je način, kako Git sprva shranjuje vsebino — kot posamezno datoteko za vsak kos vsebine, poimenovan s kontrolno vsoto SHA-1 vsebine in njegove glave.
Podmapa je poimenovana s prvima dvema znakoma SHA-1, ime datoteke pa predstavlja preostalih 38 znakov.
Ko imate vsebino v svoji objektni podatkovni bazi, lahko to vsebino pregledate z ukazom git cat-file
.
Ta ukaz je nekakšen švicarski vojaški nož za pregledovanje objektov Git.
Možnost -p
, podana cat-file
, ukazu naroči, naj najprej ugotovi vrsto vsebine in jo ustrezno prikaže:
$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
test content
Sedaj lahko dodajate vsebino v Git in jo nato spet povlečete iz njega. To lahko storite tudi z vsebino v datotekah. Na primer, lahko opravite nekaj preprostega nadzora različic datoteke. Najprej ustvarite novo datoteko in shranite njeno vsebino v svojo podatkovno zbirko:
$ echo 'version 1' > test.txt
$ git hash-object -w test.txt
83baae61804e65cc73a7201a7252750c76066a30
Nato napišite nekaj vsebine v to datoteko in jo ponovno shranite:
$ echo 'version 2' > test.txt
$ git hash-object -w test.txt
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
Vaša objektna podatkovna baza sedaj vsebuje obe različici te nove datoteke (kot tudi prvo vsebino, ki ste jo tam shranili):
$ find .git/objects -type f
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
V tem trenutku lahko izbrišete svojo lokalno kopijo datoteke test.txt
in nato uporabite Git, da iz objektne podatkovne baze pridobite prvo različico, ki ste jo shranili:
$ git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30 > test.txt
$ cat test.txt
version 1
ali pa drugo različico:
$ git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a > test.txt
$ cat test.txt
version 2
Vendar zapomniti si ključa SHA-1 za vsako različico datoteke ni praktično; poleg tega ne shranjujete imena datoteke v svojem sistemu — samo vsebino.
Ta vrsta objekta se imenuje blob.
Git vam lahko pove, kakšne vrste objekta je katerikoli objekt v Git, glede na njegov ključ SHA-1, z git cat-file -t
:
$ git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
blob
Drevesni objekti
Naslednjo vrsto objekta Git, ki jo bomo pregledali, je drevo (angl. tree), ki rešuje problem shranjevanja imena datoteke in vam omogoča, da skupino datotek shranite skupaj. Git vsebino shranjuje na način, podoben UNIX-ovi datotečni strukturi, vendar nekoliko poenostavljeno. Vse vsebine so shranjene kot objekti dreves in blobov, pri čemer drevesa ustrezajo vnosom v UNIX-ovem imeniku, blobi pa približno ustrezajo inodom ali vsebini datotek. Posamezni objekt drevesa vsebuje enega ali več vnosov, pri čemer je vsak vnos zgoščene vrednosti SHA-1 koda bloba ali poddrevesa s pripadajočim načinom, vrsto in imenom datoteke. Na primer, recimo, da imate projekt, kjer je zadnje drevo videti nekako takole:
$ git cat-file -p master^{tree}
100644 blob a906cb2a4a904a152e80877d4088654daad0c859 README
100644 blob 8f94139338f9404f26296befa88755fc2598c289 Rakefile
040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0 lib
Sintaksa master^{drevo}
določa objekt drevesa, na katerega kaže zadnja potrditev v vaši veji master
.
Bodite pozorni, da poddirektorij lib
ni blob ampak kazalec na drugo drevo:
$ git cat-file -p 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0
100644 blob 47c6340d6459e05787f644c2447d2595f5d3a54b simplegit.rb
Opomba
|
Odvisno od uporabljene lupine se lahko pri uporabi sintakse V CMD na sistemu Windows se znak Če uporabljate ZSH, se znak |
Konceptualno so podatki, ki jih Git shranjuje, videti nekako takole:
Dokaj enostavno lahko ustvarite lastno drevo.
Git običajno ustvari drevo tako, da vzame stanje vašega področja za pripravo ali indeksa in iz njega napiše zaporedje drevesnih objektov.
Zato morate za ustvarjanje drevesnega objekta najprej nastaviti indeks tako, da nekatere datoteke pripravite.
Za ustvarjanje indeksa s posameznim vnosom — prvo različico datoteke test.txt
— lahko uporabite ukaz git update-index
.
Ta ukaz uporabite, da umetno dodate prejšnjo različico datoteke test.txt
v novo področje za pripravo.
Podati mu morate možnost --add
, ker datoteka še ni v vašem področju za pripravo (niti še niste nastavili področja za pripravo) in --cacheinfo
, ker datoteke, ki jo dodajate, ni v vaši mapi, ampak v vaši bazi podatkov.
Nato navedete način, SHA-1 in ime datoteke:
$ git update-index --add --cacheinfo 100644 \
83baae61804e65cc73a7201a7252750c76066a30 test.txt
V tem primeru navajate način 100644
, kar pomeni, da je to običajna datoteka.
Druge možnosti so 100755
, kar pomeni, da gre za izvršljivo datoteko; in 120000
, kar določa simbolično povezavo.
Način je vzet iz običajnih načinov UNIX, vendar je veliko manj prilagodljiv — ti trije načini so edini veljavni za datoteke (spletne objekte) v Gitu (čeprav se za mape in podmodule uporabljajo drugi načini).
Zdaj lahko uporabite git write-tree
, da izpišete področje za pripravo v drevesni objekt.
Možnost -w
ni potrebna — klic tega ukaza samodejno ustvari drevesni objekt iz stanja indeksa, če tega drevesa še ni:
$ git write-tree
d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579
100644 blob 83baae61804e65cc73a7201a7252750c76066a30 test.txt
Prav tako lahko preverite, da je to drevesni objekt z uporabo enakega ukaza git cat-file
, kot ste ga videli prej:
$ git cat-file -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579
tree
Zdaj boste ustvarili novo drevo z drugo različico test.txt
in tudi z novo datoteko:
$ echo 'new file' > new.txt
$ git update-index --cacheinfo 100644 \
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt
$ git update-index --add new.txt
Vaše področje za pripravo zdaj vsebuje novo različico test.txt
in novo datoteko new.txt
.
Izpišite to drevo (zabeležite stanje področja za pripravo ali indeksa v drevesni objekt) in preverite, kakšno je videti:
$ git write-tree
0155eb4229851634a0f03eb265b69f5a2d56f341
$ git cat-file -p 0155eb4229851634a0f03eb265b69f5a2d56f341
100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new.txt
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt
Opazite, da ima to drevo vnos tako za datoteke kot tudi to, da je SHA-1 test.txt
»različica 2« SHA-1 iz prejšnjega (1f7a7a
).
Le za zabavo boste dodali prvo drevo kot podmapo v to.
Drevesa lahko preberete v vaše področje za pripravo s klicem git read-tree
.
V tem primeru lahko obstoječe drevo preberete v svoje področje za pripravo kot poddrevo s tem ukazom, ki uporablja možnost --prefix
:
$ 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
Če bi iz novega drevesa ustvarili delovni imenik, bi dobili dve datoteki na vrhnji ravni delovnega imenika in podmapo z imenom bak
, ki bi vsebovala prvo različico datoteke test.txt
.
Podatke, ki jih Git vsebuje za te strukture, si lahko predstavljate takole:
Objekti potrditev
Če ste naredili vse zgoraj našteto, imate sedaj tri drevesa, ki predstavljajo različne posnetke vašega projekta, ki jih želite spremljati, vendar ostaja prejšnji problem: morate si zapomniti vse tri vrednosti SHA-1, da se lahko spomnite posnetkov. Prav tako nimate nobenih informacij o tem, kdo je shranil posnetke, kdaj so bili shranjeni ali zakaj so bili shranjeni. To je osnovna informacija, ki jo za vas shrani objekt potrditve.
Za ustvarjanje objekta potrditve kličete commit-tree
in navedete eno SHA-1 drevesa ter kakršne koli objekte potrditev, ki so ji neposredno sledili.
Začnite s prvim drevesom, ki ste ga napisali:
$ echo 'First commit' | git commit-tree d8329f
fdf4fc3344e67ab068f836878b6c4951e3b15f3d
Opomba
|
Zdaj boste dobili drugačno zgoščeno vrednost zaradi različnega časa ustvarjanja in avtorskih podatkov. Poleg tega, čeprav se lahko v teoriji vsak objekt potrditve natančno reproducira s temi podatki, zgodovinski podatki o gradnji te knjige pomenijo, da se natisnjene zgoščene vrednosti potrditve morda ne ujemajo z določenimi potrditvami. V tem poglavju nadomestite zgoščene vrednosti potrditve in oznak s svojimi kontrolnimi vsotami. |
Sedaj lahko pogledate vaš novi objekt potrditve z 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
Oblika objekta potrditve je preprosta: določi se drevo najvišje ravni za posnetek projekta v tem trenutku, predhodnih potrditev, če obstajajo (zgoraj opisana potrditev nima nobenih predhodnikov); informacije o avtorju/potrjevalcu (ki uporabljajo vaše nastavitve konfiguracije user.name
in user.email
ter časovni žig); prazna vrstica in nato besedilo potrditve.
Nato boste napisali še drugi dve potrditvi, ki se sklicujeta na potrditev, ki je neposredno sledila potrditvi:
$ echo 'Second commit' | git commit-tree 0155eb -p fdf4fc3
cac0cab538b970a37ea1e769cbbde608743bc96d
$ echo 'Third commit' | git commit-tree 3c4e9c -p cac0cab
1a410efbd13591db07496601ebc7a059dd55cfe9
Vsak izmed treh objektov potrditev kaže na eno od treh posnetkov dreves, ki ste jih ustvarili.
Presenetljivo imate sedaj dejansko zgodovino Git, ki jo lahko pregledujete z ukazom git log
, če ga poženete na SHA-1 zadnje potrditve:
$ 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(+)
Neverjetno.
Pravkar ste izvedli nizko nivojske operacije za gradnjo zgodovine Git brez uporabe nobenega od ukazov ospredja.
To je v bistvu tisto, kar Git naredi, ko zaženete ukaze git add
in git commit
— shrani blobe za datoteke, ki so se spremenile, posodablja indeks, zapisuje drevesa in zapisuje objekte potrditev, ki se sklicujejo na drevesa najvišje ravni in potrditve, ki so jim takoj sledile.
Ti trije glavni objekti Git — blob, drevo in potrditev — so na začetku shranjeni kot ločene datoteke v vaši mapi .git/objects
.
Tukaj so vsi objekti v mapi iz primera, opremljeni s komentarji o tem, kaj shranjujejo:
$ 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
Če ste sledili vsem internim kazalnikom, dobite objektni graf, ki je videti nekako takole:
Shramba objekta
Zgoraj smo omenili, da ima vsak objekt, ki ga potrdimo v objektno bazo podatkov Git, shranjeno glavo. Za minuto si oglejmo, kako Git shranjuje svoje objekte. Prikazali bomo, kako shraniti objekt blob — v tem primeru niz »what is up, doc?« — interaktivno v programskega jezika Ruby.
Interaktivni način Ruby lahko zaženete z ukazom irb
:
$ irb
>> content = "what is up, doc?"
=> "what is up, doc?"
Git najprej sestavi glavo, ki se začne z identifikacijo vrste objekta — v tem primeru gre za blob. Git k prvemu delu glave doda presledek, nato pa še velikost v bajtih vsebine in na koncu še ničelni bajt:
>> header = "blob #{content.bytesize}\0"
=> "blob 16\u0000"
Git združuje glavo in izvirno vsebino ter izračuna kontrolne vsote SHA-1 te nove vsebine.
Vrednost SHA-1 niza lahko izračunate v Rubyju z vključitvijo knjižnice SHA1 digest s pomočjo ukaza require
, nato pa kličete Digest::SHA1.hexdigest()
s tem nizom:
>> store = header + content
=> "blob 16\u0000what is up, doc?"
>> require 'digest/sha1'
=> true
>> sha1 = Digest::SHA1.hexdigest(store)
=> "bd9dbf5aae1a3862dd1526723246b20206e5fc37"
Primerjajmo to z izhodom git hash-object
.
Tukaj uporabljamo echo -n
, da preprečimo dodajanje nove vrstice vhodu.
$ echo -n "what is up, doc?" | git hash-object --stdin
bd9dbf5aae1a3862dd1526723246b20206e5fc37
Git stisne novo vsebino z zlib, kar lahko storimo v Rubyju z uporabo knjižnice zlib.
Najprej morate knjižnico zahtevati in nato na vsebini zagnati Zlib::Deflate.deflate()
:
>> 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"
Na koncu boste napisali vašo zlib-stisnjeno vsebino na objekt na disku.
Določili boste pot objekta, ki ga želite zapisati (prvi dve številki vrednosti SHA-1 predstavljata ime poddirektorija, preostalih 38 znakov pa predstavlja ime datoteke znotraj tega direktorija).
V jeziku Ruby lahko funkcijo FileUtils.mkdir_p()
uporabite za ustvarjanje poddirektorija, če ta še ne obstaja.
Nato odprite datoteko s File.open()
in zapišite predhodno zlib-stisnjeno vsebino v datoteko s klicem write()
na nastalem datotečnem oprimku:
>> 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
Poglejmo vsebino objekta z uporabo git cat-file
:
---
$ git cat-file -p bd9dbf5aae1a3862dd1526723246b20206e5fc37
what is up, doc?
---
To je to — ustvarili ste veljaven Gitov objekt blob.
Vsi objekti Git se shranjujejo na enak način, samo z različnimi vrstami — namesto niza blob, se bo glava začela s potrditvijo ali drevesom. Poleg tega, čeprav je lahko vsebina bloba skorajda karkoli, je vsebina potrditve in drevesa zelo natančno oblikovana.