NOVINKA - Online rekvalifikační kurz Java programátor. Oblíbená a studenty ověřená rekvalifikace - nyní i online.
NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!

Lekce 14 - Git - Vnitřní struktura - Dokončení

V minulé lekci, Git - Vnitřní struktura, jsme se podívali na vnitřní strukturu Gitu. Ponořili jsme se do detailů způsobu úschovy commitů a vykonávaných změn v souborech provedených Gitem.

V dnešním Git tutoriálu dokončíme seznámení s vnitřní strukturou Gitu detailnějším prozkoumáním objektů typu blob, tree, commit a tag. Následně si vysvětlíme, jak Git uchovává názvy souborů.

Všechny příkazy si opět vyzkoušíme v našem naklonovaném repozitáři laravel z lekce Git - Základy - Dokončení. Otevřeme terminál MinTTY a pomocí příkazu cd laravel se do repozitáře přesuneme.

Typy objektů

Již víme, že vnitřní struktura Gitu je tvořená z objektů typu blob, tree, commit a tag. Tyto objekty jsou v Gitu na sebe takto navázány:

Git

Objekty blob, tree, commit a tag jsme si představili v lekci Git - Vnitřní struktura.

Nyní si jednotlivé objekty popíšeme a vysvětlíme si, jak Git uchovává soubory.

Objekt typu commit

Commit je pouze textovým souborem, který prošel kompresí, obdržel hash a byl uložen v úložišti objektů. Obsahuje metadata, jako jsou informace o autorovi, commiterovi, datu vytvoření commitu a commit zprávě. Pro zobrazení obsahu nebo typu libovolného objektu v úložišti lze použít nízkoúrovňový příkaz git s názvem cat-file ve formátech:

  • git cat-file <hash commitu> -t,
  • git cat-file <hash commitu> -p.

Oba si nyní vyzkoušíme 😀

Příkaz git cat-file <hash commitu> -t

Nyní tedy potřebujeme hash nějakého commitu. Pro ten nebudeme chodit nikam daleko, ale použijeme náš hash 61f09d5980757dc0a9c05570d24584714b7cd635, se kterým jsme pracovali v lekci Git - Vnitřní struktura.

Zadáme tedy příkaz git cat-file 61f09d5980757dc0a9c05570d24584714b7cd635 -t, kterým získáme typ objektu:

MINGW64:/c/mujgit/laravel/.git
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git (GIT_DIR!)
$ git cat-file 61f09d5980757dc0a9c05570d24584714b7cd635 -t
commit
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git (GIT_DIR!)
$

Příkaz git cat-file <hash commitu> -p

S týmž hashem spustíme příkaz git cat-file 61f09d5980757dc0a9c05570d24584714b7cd635 -p, kterým získáme výpis obsahu commitu:

MINGW64:/c/mujgit/laravel/.git
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git (GIT_DIR!)
$ git cat-file 61f09d5980757dc0a9c05570d24584714b7cd635 -p
tree 2992d926d6f3495b3b04b88564a1a0fc958d5696
parent 700444ac9735f2a393116705951511421061458d
parent 53c4ef4ea87f4d7169a8fa0b74aff591b3508bd8
author itnetwork <[email protected]> 1703077311 +0100
committer itnetwork <[email protected]> 1703077311 +0100

Oprava kolize při sloučení větví
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git (GIT_DIR!)
$

Vidíme, že náš commit obsahuje:

  • metadata o commitu jako informace o stromu (tree),
  • rodičovské commity,
  • autora,
  • commitera,
  • samotnou zprávu ke commitu.

Objekt typu tree

tree neboli strom je objekt používaný k ukládání složek v našem projektu. Strom může ukazovat na jiné stromy a vytvářet tak úplnou hierarchii souborů a podsložek.

Každý commit ukazuje na stromový objekt, který v jednom snímku zachycuje stav úložiště v době provedení commitu. Tento snímek je verzí projektu, kterou ukládáme do naší historie Gitu.

Podíváme se, jak takový strom vypadá. Spusťme příkaz git cat-file <hash commitu> -t pro hash stromu (tree) 2992d926d6f3495b3b04b88564a1a0fc958d5696:

MINGW64:/c/mujgit/laravel/.git
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git (GIT_DIR!)
$ git cat-file 2992d926d6f3495b3b04b88564a1a0fc958d5696 -t
tree
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git (GIT_DIR!)
$

Získáme typ objektu tree.

Spusťme ještě pro tentýž hash stromu příkaz git cat-file <hash commitu> -p, kterým získáme výpis souborů obsažených ve stromu:

MINGW64:/c/mujgit/laravel/.git
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git (GIT_DIR!)
$ git cat-file 2992d926d6f3495b3b04b88564a1a0fc958d5696 -p
...
100644 blob 8f0de65c560259bd171d746d12aa187f666893a3    .editorconfig
100644 blob ea0665b0a60cfaafaa7b2c80992a0e406700e1a2    .env.example
100644 blob fcb21d396d657f597ef8b6729f73d89b0a871c9b    .gitattributes
...
100644 blob 2c95e74d918d0ea0aef057d2ee97536521101c72    README.md
...
040000 tree a9b549189653697bdcc2597e2a81e93fae10cea6    storage
040000 tree 49a71909565d8572e9d915c039b0fab49d0f4a1a    tests
100644 blob 421b5695627db43c022947cfc7c0ecce6b9689be    vite.config.js
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git (GIT_DIR!)
$

Vidíme, že pro každý řádek z objektu stromu Git zaznamenává:

  • oprávnění,
  • typ objektu,
  • hash objektu,
  • název souboru.

Názvy souborů jsou řízeny objektem stromu, nikoli soubory samotnými. Později si vysvětlíme, proč tomu tak je.

Objekt typu blob

blob objekty obsahují binární nebo textová data souborů. Jde o čistá data bez informací o souborovém názvu, cestě nebo jakýchkoli metadatech, které by identifikovaly samotný soubor.

Bloby reprezentují konkrétní verzi souboru v daném okamžiku, a proto jsou klíčové pro sledování změn v rámci projektu. Každá změna provedená v souboru vytváří novou verzi blobu. Bloby umožňují Gitu efektivně ukládat obsah souborů včetně jejich historie.

Nyní se na takový blob podíváme. Spusťme příkaz git cat-file <hash commitu> -t pro hash souboru ./README.md:

MINGW64:/c/mujgit/laravel/.git
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git (GIT_DIR!)
$ git cat-file 2c95e74d918d0ea0aef057d2ee97536521101c72 -t
blob
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git (GIT_DIR!)
$

Získáme typ objektu blob. Pro tentýž hash spusťme příkaz git cat-file s parametrem -p:

MINGW64:/c/mujgit/laravel/.git
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git (GIT_DIR!)
$ git cat-file 2c95e74d918d0ea0aef057d2ee97536521101c72 -p

The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
Přidaný text 1
Přidaný text 2
Editace souboru na větvi 10.x
Editace souboru na větvi feature
...
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git (GIT_DIR!)
$

Vidíme, že jsme získali výpis obsahu souboru README.md.

Objekt typu tag

tag čili značka představuje speciální odkaz používaný k označení commitů v historii repozitáře. Je to statický odkaz na konkrétní commit, který následně nemění svou hodnotu. Jakmile je pro určitý commit tag vytvořen, jeho hodnota zůstává neměnná. Tagy slouží k označení specifických bodů v historii repozitáře. Vytvořené tagy jsou uložené ve složce tags/ uvnitř složky refs/.

Pojďme si zkusit prozkoumat nějaký tag 😀 Nejprve se ale vrátíme zpět do kořenového adresáře .git/ příkazem cd ... Tagy jsou uloženy ve složce refs/tags/. Přesuneme se tam příkazem cd refs/tags/:

MINGW64:/c/mujgit/laravel/.git
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git (GIT_DIR!)
$ cd refs/tags/

ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git/refs/tags (GIT_DIR!)
$

Spusťme příkaz ls, kterým získáme výpis tagů v repozitáři:

MINGW64:/c/mujgit/laravel/.git/refs/tags 
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git/refs/tags  (GIT_DIR!)
$ ls
anotovany_tag  lehky_tag
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git/refs/tags (GIT_DIR!)
$

Získejme hash tagu anotovany_tag příkazem cat anotovany_tag:

MINGW64:/c/mujgit/laravel/.git/refs/tags 
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git/refs/tags  (GIT_DIR!)
$ cat anotovany_tag
04a272f847c916d9f3ebe7070be57e60142f0ab8
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git/refs/tags (GIT_DIR!)
$

Když máme hash, zjistěme typ objektu příkazem:

MINGW64:/c/mujgit/laravel/.git/refs/tags 
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git/refs/tags  (GIT_DIR!)
$ git cat-file 04a272f847c916d9f3ebe7070be57e60142f0ab8 -t
tag
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel/.git/refs/tags (GIT_DIR!)
$

Získáme potvrzení, že anotovany_tag je objekt typu tag:

MINGW64:/c/mujgit/laravel/.git/refs/tags 
tag

Tímto jsme dokončili naše zkoumání objektů typu blob, tree, commit a tag. Můžeme se tedy pustit do další části, ve které si vysvětlíme, jak Git uchovává názvy souborů.

Názvy souborů

V této kapitole pochopíme, proč Git uchovává názvy souborů v objektu typu tree (strom), a nikoli v typu blob. Pojďme na to 😀

Nejprve se vrátíme zpět do kořenového adresáře laravel/ příkazem cd ../... Začneme tím, že si vytvoříme dva nové commity. Spustíme následující příkazy, které vytvoří:

  • první commit s názvem souboru testovaci_soubor.txt a obsahem Testovaci soubor,
  • složku tasks/,
  • druhý commit s názvem souboru soubor.txt a s obsahem Testovaci soubor umístěném ve složce tasks/.

Kód je následující:

MINGW64:/c/mujgit/laravel
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$ echo "Testovaci soubor" > testovaci_soubor.txt
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$ git add .
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$ git commit -m "Vytvořen nový testovací soubor"
[10.x a0cac287] Vytvořen nový testovací soubor
 1 file changed, 1 insertion(+)
 create mode 100644 testovaci_soubor.txt

ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$ mkdir tasks
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$ echo "Testovaci soubor" > ./tasks/soubor.txt
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$ git add .
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$ git commit -m "Vytvořena složka tasks"
[10.x 0c52e84c] Vytvořena složka tasks
 1 file changed, 1 insertion(+)
 create mode 100644 tasks/soubor.txt
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$

Spusťme příkaz git show, abychom získali hash posledního commitu:

MINGW64:/c/mujgit/laravel
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$ git show
commit 9d75672de3f87202b615435d3a5ebbe55d4f99ec (HEAD -> 10.x)
Author: itnetwork <[email protected]>
Date:   Thu Dec 21 14:30:51 2023 +0100

    Vytvořena složka tasks

diff --git a/tasks/soubor.txt b/tasks/soubor.txt
new file mode 100644
index 00000000..2d750b67
--- /dev/null
+++ b/tasks/soubor.txt
@@ -0,0 +1 @@
+Testovaci soubor
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$

Hash jsme získali. Spusťme s ním tedy příkaz git cat-file 9d75672de3f87202b615435d3a5ebbe55d4f99ec -p, abychom získali výpis obsahu commitu:

MINGW64:/c/mujgit/laravel
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$ git cat-file 9d75672de3f87202b615435d3a5ebbe55d4f99ec -p
tree f53345c1ed5beaf290ee8edf4881c5a09f14226b
parent ec4c318d68c305e42f32f2832cc8507b371f237f
author itnetwork <[email protected]> 1703165451 +0100
committer itnetwork <[email protected]> 1703165451 +0100

Vytvořena složka tasks
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$

Obdrželi jsme výpis obsahu commitu. Vidíme, že tento commit obsahuje nadřazený rodičovský objekt parent, kterým je předešlý commit.

Kromě prvního commitu každý commit v Gitu obsahuje odkaz parent na předešlý commit.

Abychom se podívali do objektu tree, spusťme příkaz git cat-file <hash commitu> -p s hashem předchozího commitu:

MINGW64:/c/mujgit/laravel
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$ git cat-file f53345c1ed5beaf290ee8edf4881c5a09f14226b -p
100644 blob 8f0de65c560259bd171d746d12aa187f666893a3    .editorconfig
100644 blob ea0665b0a60cfaafaa7b2c80992a0e406700e1a2    .env.example
...
040000 tree 8229fefca7d57f5c87adeef4e35dffa79bb691a6    tasks
100644 blob 2d750b67d34524994ba425d168d5ed2576c11492    testovaci_soubor.txt
040000 tree 49a71909565d8572e9d915c039b0fab49d0f4a1a    tests
100644 blob 421b5695627db43c022947cfc7c0ecce6b9689be    vite.config.js
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$

Získali jsme výpis obsahu objektu. Jak můžeme vidět, strom obsahuje soubor testovaci_soubor.txt s hashem 2d750b67d34524994ba425d168d5ed2576c11492 z předešlého commitu. Nově obsahuje objekt tree s hashem 8229fefca7d57f5c87adeef4e35dffa79bb691a6.

Zobrazme si obsah tohoto stromu, tedy hash složky tasks/, tímto příkazem:

MINGW64:/c/mujgit/laravel
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$ git cat-file 8229fefca7d57f5c87adeef4e35dffa79bb691a6 -p
100644 blob 2d750b67d34524994ba425d168d5ed2576c11492    soubor.txt
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$

Spusťme příkaz git cat-file <hash commitu> -p pro zobrazení obsahu souboru testovaci_soubor.txt s jeho hashem 2d750b67d34524994ba425d168d5ed2576c11492 (viz výše):

MINGW64:/c/mujgit/laravel
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$ git cat-file 2d750b67d34524994ba425d168d5ed2576c11492 -p
Testovaci soubor
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$

Příkaz nám vypsal jeho obsah, tedy Testovaci soubor.

Závěr

Teď vidíme, že hash souboru soubor.txt je stejný jako hash souboru testovaci_soubor.txt, přestože oba jsou různé soubory na jiných místech. Git totiž rozpoznal, že oba soubory mají přesně týž obsah. A proto se rozhodl vytvořit pouze jeden blob a použít stejnou referenci dvakrát, místo aby vytvořil dva identické objekty.

Příkaz hash-object

Pojďme si ještě náš závěr potvrdit nízkoúrovňovým příkazem hash-object na stejném obsahu Testovaci soubor obou výše uvedených souborů. Příkaz vezme část obsahu a vrátí pro něj hash klíč.

Spusťme následující příkaz hash-object pro vytvoření hashe řetězce Testovaci soubor:

MINGW64:/c/mujgit/laravel
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$ echo "Testovaci soubor" | git hash-object --stdin
2d750b67d34524994ba425d168d5ed2576c11492
ItNetwork@DESKTOP-ADEVTG4 MINGW64 /c/mujgit/laravel (10.x)
$

Pokud se nám zobrazí chyba, použijme příkaz printf "Testovaci soubor" | git hash-object --stdin.

Získali jsme hash 2d750b67d34524994ba425d168d5ed2576c11492 pro řetězec Testovaci soubor. To znamená, že pokud vytvoříme další soubor .txt s obsahem Testovaci soubor, nový commit bude odkazovat na stejný blob se stejným hashem.

Git zpracovává objekty vytvořené v databázi tak efektivně, že znovu používá objekty blob pokaždé, když je obsah stejný. Nezáleží na tom, zda se jedná o jiný soubor vytvořený v jiné složce. To je důvod, proč jsou názvy souborů uloženy ve stromech. Toto rozhodnutí umožňuje systému Git zpracovávat odkazy na stejný objekt blob s různými názvy souborů.

V příští lekci, Git - Grafické uživatelské rozhraní Git GUI, si představíme grafické uživatelské rozhraní Git GUI. Ukážeme si, jak v něm vytvořit commit a spravovat větve.


 

Předchozí článek
Git - Vnitřní struktura
Všechny články v sekci
Git
Přeskočit článek
(nedoporučujeme)
Git - Grafické uživatelské rozhraní Git GUI
Článek pro vás napsal Filip Studený
Avatar
Uživatelské hodnocení:
25 hlasů
.
Aktivity