GIT: správa repositářů

9. 5. 2008
Doba čtení: 4 minuty

Sdílet

Každý, kdo spravuje velké množství obsahu, časem přijde na to, že by potřeboval software, který by mu pomohl nad tímto obsahem držet ochrannou ruku. Takovým pomocníkem může být například nástroj GIT. V dnešním článku se budeme zabývat repositáři: jejich tvorbou, vnitřním uspořádáním a především správou.

I když si základní příkazy GITu uvedeme později, pro počáteční studium je vhodné si vytvořit nějaký repositář. Ideálně klon nějakého již existujícího (musíte znát jeho URI). To se provede pomocí příkazu:

$ git clone <uri>

a nebo si vytvořte nový prázdný pomocí:

$ mkdir myproject
$ cd myproject
$ git init

Repositář není nic jiného než adresář .git plný souborů. Lze jej tedy zpřístupnit pomocí http, ssh, rsync apod. (Případně git implementuje vlastní metodu pomocí git-daemona). Existují i různá GTK, KDE, a Web GUI.

GIT repositář se skládá ze tří částí.

  • working directory – místo kde se provádí změny
  • index – mezivrstva sloužící jako cache mezi DB objektů a pracovním adresářem.
  • object DB – vlastní data

Jak je zřejmé, jádrem repositáře je databáze objektů. Platí, že každý objekt je adresován SHA-1 kontrolním součtem obsahu daného objektu. GIT interně nepracuje se jmény souborů. GIT rozlišuje tři základní objekty:

  • blob  – obsahuje data souboru
  • tree  – je seznam blobů a podstromů
  • commit – je spojením mezi tree a jeho rodičem a obsahuje další informace o commitu (komentář, kdo, kdy apod.)

Na tomto místě je důležité říci, že GIT interně neuchovává pouze změny (diff), ale celé soubory. Uděláte-li změnu v nějakém souboru, je celý soubor uložen jako komprimovaný do repositáře v podobě blob objektu. Commit je pak snapshot (tree) obsahující všechny soubory. Pochopitelně pokud se soubor neměnil, je stejné i jeho SHA-1 a je sdílen mezi různými stromy (tree).

Je-li třeba rozdíl (diff) mezi dvěma verzemi, pouze se porovnají blob objekty daných verzí. Podobně pokud se chcete vrátit v pracovním adresáři do stavu, který panoval u nějakého dřívějšího commitu, tak se daný commit nalezne (zadáte například jeho SHA-1) v repositáři. Commit ukazuje na tree, který, jak bylo řečeno, je seznamem blobů. Tyto bloby se nakopírují do vašeho pracovního adresáře. Podobně dělá GIT většinu operací. Nepracuje s diff jednotlivých verzí. Výhodou je obrovská rychlost a nezávislost na jménech souborů (není zde problém s přejmenováním souborů). Nevýhodou je větší náročnost na diskový prostor (ten je ale daleko levnější než čas vývojáře).

GIT ještě interně podporuje tzv. „pack files“. Jedná se o do jednoho souboru zabalené object soubory. Použití je primárně na veřejných repositářích, kde se tímto způsobem zvyšuje efektivita downloadu (více viz man git-repack).

Takto navržený repositář umožňuje velmi snadno a efektivně uchovávat informace o větvení projektu na samostatné větve. Větev není nic jiného, než že se obsah repositáře rozdělí na dále již samostatně se vyvíjející větve. S tím, že každá taková větev je nějak pojmenována. Pojmenování znamená samozřejmě, že se nějakému SHA-1, které ukazuje na nějaký v repositáři uložený objekt (commit), přiřadí jméno. Jinými slovy – vytvoří se reference.

Defaultní větev je „master“. GIT si ale udržuje interně i další (například origin/master apod.), více informaci dostanete příkazem:

$ git branch -a

Reference nemusí být jen název větve, ale taktéž tzv. tag. Například

$ git tag "RELEASE_0.1"

pojmenuje poslední commit jménem „RELEASE0.1“. Toto jméno pak lze používat ve většině příkazů GITu namísto SHA-1.

Standardní referencí je tzv. HEAD – poslední commit aktuální větve. Ten je použit, neuvedete-li nic jiného. Pro hlubší zkoumání doporučuji adresář .git/refs/, kde jsou všechny reference uloženy jako soubory obsahující SHA-1, na která dané jméno odkazuje. Například:

$ cat .git/refs/heads/master
628cab7cde49bfb0387a51d5390d65921d7c3dc4

Další vestavěnou referencí je ORIG_HEAD, který odkazuje na HEAD před příkazem git-reset. Například příkazy:

$ git reset HEAD^
$ git commit -a -c ORIG_HEAD

vrátí repositář do stavu před posledním commitem (ale pracovní adresář ponechá nedotčený) a provede nový commit. Jako commit message (komentář patche) použije komentář z resetovaného patche.

Pozorný čtenář určitě přemýšlí co je to ^ u příkazu git-reset. Jedná se o další způsob odkazu (reference) na objekt. Pro úplnost vyjmenujme všechny základní způsoby, jak se odkazovat v GITu:

  • SHA-1  – checksum objektu, 40 hex číslic, ale lze použít i jen fragment z SHA-1
  • tag  – pro lidi čitelné pojmenování
  • HEAD  – odkazuje na vrchol aktuální větve
  • commit^n  – odkazuje na ‚n‘ rodiče commitu
  • commit~n  – odkazuje na ‚n‘ generaci pra-rodiče, následuje vždy jen prvního rodiče (pozn. u spojení větví může vzniknout víc než jeden rodič.)
  • ref@{date} – odkazuje na datum před referencí ‚ref‘
  • :/text  – commit kde commit message začíná ‚text‘em

Takže například

$ git show HEAD~3

ukáže třetí commit před posledním commitem (tedy čtvrtý).

Protože u řady příkazů se je třeba definovat rozpětí (změny od-do) tak GIT používá tzv. ranges. Základní jsou:

  • r1..r2  – je množina commitů z r2 co nejsou v r1
  • r1…r2  – je množina commitů co jsou v r1 nebo v r2 ale ne v obou

Předpokládejme, že si pojmenováváte (tagujete) release, pak:

bitcoin_skoleni

$ git log RELEASE_0.0..RELEASE_0.1

ukáže seznam patchů mezi releases.

Výše uvedené informace jsou dostatečné k pochopení používání GITu a lze je používat v obměnách u většiny GIT příkazů.

Autor článku