1 [[!meta title="Verzovací systém Git"]]
8 Naučit se pracovat s verzovacím systémem [Git][git]. Konrétně nás bude
11 * využití Gitu pro distribuovaný vývojový model, který je praktikován
12 mnoha open source projekty,
13 * efektivní prohledávání historie projektu, které je užitečné
14 například při rozhodnování jak správně vyřešit určitý konflikt.
16 [git]:http://git-scm.com/
21 Git je velmi univerzální nástroj pro správu a synchronizaci dat
22 v souborech. Kromě verzování softwaru ho lidé používají
23 k [mnoha dalším činnostem][gitsurvey-usedfor]. Jedna z často
24 zmiňovaných nevýhod Gitu je, že oproti jiným verzovacím systémům je
25 těžší se ho naučit a používat. Možným důvodem je to, že git nabízí
26 větší funkcionalitu než většina ostatních systémů, která se ale
27 využije jen ve speciálních případech – například jen u extrémně
28 velkých projektů jako Linuxové jádro. Ať už to tak je, nebo ne, faktem
29 je, že Git se neustále vyvíjí a mnoho úsilí je věnováno právě zlepšení
30 uživatelské přivětivosti.
32 [gitsurvey-usedfor]:http://www.survs.com/app/12/wo/EO2kVRp9LtOtfmGQs0tl3g/0.0.0.7.7.3.0.1.1.5.6.1.1.9.1
34 Pro pochopení Gitu je důležité mít základní představu o tom, jak Git
35 pracuje s větvemi, což bylo popsáno v [[přednášce|prednasky/intro-to-git.pdf]]
36 na slidech "*Working on branches*" a "*Working with remotes*".
38 V tomto cvičení budeme opět pracovat s projektem
39 [Midnight commander][mc] z [[1. cvičení|1]]. Pokud s gitem začínáte,
40 doporučuji v průběhu jednotlivých kroků kontrolovat stav repozitáře
41 pomocí grafických nástrojů.
43 * Příkaz `gitk` zobrazuje graficky historii a dovoluje její
44 interaktivní procházení. Přijímá stejné volby jako `git log`, takže
45 v příkazech níže ho můžete použít i pro "vizualizaci" výstupu `git
48 * Příkaz `git gui` je grafický nástroj částečně nahrazující příkazy
49 `git status` a `git commit`. Pomocí menu je možné provádět i další
50 operace jako např. vytváření větví a práce se vzdálenými repozitáři.
52 * Dále existuje ještě spousta dalších [nástrojů][tools]. Já osobně používám
53 kromě výše zmíněných příkazů ještě `tig` a `qgit`.
55 Dále se vám určitě bude hodit dokumentace:
60 [tools]:http://git-scm.com/tools
65 1. Základní nastavení. Aby vaše commity obsahovaly správné údaje o
66 vás, nastavte si jméno a email:
68 git config --global user.name "Your Name Comes Here"
69 git config --global user.email you@yourdomain.example.com
73 1. Předpokládáme, že máte zdrojové kódy midnight commanderu někde na
74 disku, takže se přesuňte do adresáře s nimi.
81 2. Možná budete chtít zachovat vaší práci z prvního cvičení. Uložíme
82 jí do nové větve *cviceni1*. Nejprve se ale podíváme v jakém stavu
89 # Changed but not updated:
90 # (use "git add <file>..." to update what will be committed)
91 # (use "git checkout -- <file>..." to discard changes in working directory)
94 # modified: po/be-tarask.po
97 # modified: src/cmddef.h
98 # modified: src/main.c
99 Vidíme, že pracujeme na větvi *master* a vzhledem k verzi uložené v
100 repozitáři máme několik změněných souborů.
102 [mc]:http://www.midnight-commander.org/
104 1. Midnight commander má bohužel jednu vlastnost, která může ztížit
105 vaše začátky s gitem. Při kompilaci dojde k automatickému
106 přegenerování některých souborů v repozitáři a git potom hlásí, že
107 jsme tyto soubory změnili a "otravuje" s nimi při každém commitu.
109 Naštěstí existuje možnost jak gitu říct, že nás změny v některých
110 souborech nezajímají:
112 git update-index --assume-unchanged $(git ls-files po m4)
114 Příkaz `git ls-files po m4` vypíše všechny soubory z adresářů *po*
115 a *m4*, které jsou spravované gitem a příkazem `git update-index
116 --assume-unchanged` pak gitu sdělíme, aby si do indexu poznamenal,
117 že má tyto soubory ignorovat.
119 > *Poznámka:* Možná víte o souborech *.gitignore* (viz `man
120 > gitignore`), které slouží k podobnému účelu. Ostatně midnight
121 > commander je také využívá, jak se můžete snadno přesvědčit:
125 > Soubory *.gitignore* se ale vztahují pouze na soubory, které
126 > ještě nejsou součástí repozitáře, což není případ výše zmíněných
129 3. Vytvoříme větev *cviceni1* a přepneme se na ni:
131 git checkout -b cviceni1
133 Nyní už by měl `git status` ukazovat že pracujeme s větví
134 *cviceni1* a vidíme pouze námi modifikované soubory.
136 4. Nyní provedeme *commit*:
138 git commit -a -m 'Zmeny z prvniho cviceni'
140 Parametrem `-a` říkáme, že "commitujeme" vše (all) a `-m` udává
141 komentář ke commitu (message). Kdybychom `-m` vynechali, git spustí
142 editor a nechá nás napsat zprávu v něm.
144 > *Poznámka:* Pokud vám nevyhovuje výchozí editor (většinou `vi`),
145 > nastavte si, že chcete používat jiný editor (např. `pico`):
147 > git config --global core.editor pico
149 5. Nyní se přepneme zpět na větev *master*:
152 V adresáři se vám teď objeví verze bez vašich úprav z 1. cvičení.
154 6. Seznam větví v našem repozitáři zjistíme příkazem
157 Aktuální větev je označena hvězdičkou `*`.
159 Práce s více vzdálenými repozitáři
160 ----------------------------------
162 Do teď jsme pracovali pouze s jedním vzdáleným repozitářem. Mezi velké
163 výhody gitu (a ostatních distribuovaných verzovacích systémů) patří
164 schopnost pracovat s více vzdálenými repozitáři.
166 1. Které vzdálené repozitáře máme nakonfigurované zjistíme pomocí
170 Vidíme, že máme nakonfigurovaný repozitář s názvem *origin* a jeho
173 1. Pokud chceme nějaký vzdálený repozitář používat často,
174 vyplatí se ho pojmenovat krátkým jménem (v příkladu níže *osp*),
175 abychom nemuseli pořád psát dlouhé URL:
177 git remote add osp ssh://git@rtime.felk.cvut.cz/osp/mc
179 V tomto repozitáři je uloženo zadání dnešní úlohy. Abyste se k němu
180 dostali, musí server znát váš veřenjný SSH klíč. Jak toho docílit
181 najdete na [[samostatné stránce|rtime-git-ssh-key]]. Přístup k
182 repozitářům na serveru rtime budete potřebovat i v písemce. **Proto
183 vám důrazně doporučujeme zprovoznit přístup už na tomto cvičení,
184 abyste při písemce neztráceli čas.**
186 2. Nyní můžeme stáhnout obsah právě přidaného repozitáře:
189 V případě úspěchu bude výstup vypadat následovně:
191 remote: Counting objects: 17, done.
192 remote: Compressing objects: 100% (9/9), done.
193 remote: Total 10 (delta 4), reused 7 (delta 1)
194 Unpacking objects: 100% (10/10), done.
195 From ssh://rtime.felk.cvut.cz/osp/mc
196 * [new branch] master -> osp/master
197 * [new branch] only-directories -> osp/only-directories
199 3. Příkaz nám vypíše, že v repozitáři byly dvě nové větve. Všechny
200 větve ze vzdálených repozitářů vypíšeme příkazem
203 Zjednodušeně řečeno, jediný rozdíl mezi lokální a vzdálenou větví
204 je v tom, že jméno vzdálené věteve má prefix `<remote>/`.
206 4. Nyní nás zajímá co je ve větvích, které jsme právě stáhli:
208 git log osp/only-directories ^master
209 git log master..osp/only-directories
210 Tyto dva příkazy jsou ekvivalentní a vypisují commity, které jsou
211 ve větvi *osp/only-directories* a zároveň nejsou (^) ve větvi *master*.
213 Chcete-li vidět i změny v kódu použijte jeden z následujících příkazů:
215 git log -p osp/only-directories ^master
216 gitk osp/only-directories ^master
218 6. Podobně můžeme postupovat i s původním repozitářem. Jméno *origin*
219 je výchozí a proto ho nemusíme zadávat:
222 Co přibylo ve větvi *master* od prvního cvičení zjistíme příkazem:
224 git log master..origin/master
226 Slučování větví (merge)
227 -----------------------
229 Operace slučující dvě a více větví do jedné se nazývá *merge*. V Gitu
230 můžou při slučování nastat tři situace:
232 * *Already up-to-date* je situace, kdy už je větev, kterou chceme
233 sloučit, dosažitelná z aktuální větve (už byla sloučena v
234 minulosti). Při sloučení nedojde k žádné změně.
235 * *Fast-forward* je opak předchozí situace, tj. aktuální větev je
236 dosažitelná ze slučované větve. To odpovídá situaci, kdy
237 aktualizujeme na novější verzi. Při sloučení je větev (tj. ukazatel
238 na poslední commit) posunuta na novější verzi.
239 * *True merge*. Pokud nenastane jedna z předchozích situací, jedná se
240 o skutečné sloučení. Výsledek je nějaká kombinace obou verzí.
242 Pokud došlo v případě *True merge* ke změně stejného místa v kódu v
243 obou větvích, dojde k tzv. *konfliktu*, který musí být vyřešen ručně.
245 1. Zkuste provést sloučení vaší větve *master* s *origin/master*
247 git merge origin/master
248 Výsledkem bude *Fast forward* a uvidíme jaké soubory byly změněny:
250 git merge origin/master
251 Updating 0ebd30c..a99dc51
254 contrib/Makefile.am | 9 +-
256 Toto je velmi častá operace a proto lze oprace `fetch` a `merge`
257 nahradit jedním příkazem
260 2. Vždy, když člověk pracuje na nějaké netriviální změně, je užitečné,
261 založit si na to samostatnou větev. Dnešním úkolem bude sloučit
262 větev *osp/only-directories* s větví *master* a protože to není
263 triviální založte si na to novou větev:
265 git checkout -b homework
266 3. Pokud provedete sloučení
268 git merge osp/only-directories
269 Výsledek bude vypadat pravděpodobně takto:
271 Auto-merging doc/man/mc.1.in
272 Auto-merging src/filemanager/find.c
273 CONFLICT (content): Merge conflict in src/filemanager/find.c
274 Automatic merge failed; fix conflicts and then commit the result.
276 Vidíme, že se automaticky povedlo sloučit změny v souboru
277 `doc/mc.1.in`, ale při slučování změn ve `src/find.c` už takové
278 štěstí nemáme a výsledkem je konflikt.
284 Konflikt lze řešit několika způsoby (viz také `git merge --help`):
286 * Vzdáme to a vrátíme se k verzi před slučováním
290 * Konflikt vyřešíme a oznámíme to gitu příkazy `git add` a `git
291 commit` (jak nám git napovídá v hláškách)
293 V průběhu řešení konfliktu je užitečné používat příkaz `git status`,
294 abychom zjistili co je ještě potřeba vyřešit. V našem případě vypadá
295 výstup zhruba takhle:
298 # Changes to be committed:
300 # modified: doc/man/mc.1.in
303 # (use "git add/rm <file>..." as appropriate to mark resolution)
305 # both modified: src/filemanager/find.c
307 Konflikt lze řešit následujícími způsoby:
309 * V textovém editoru najdeme sekvence `<<<<<<<<`, `=========` a
310 `>>>>>>>>`, kterými jsou označené jednotlivé konfliktní oblasti.
311 Tato místa musíme opravit tak, aby dávala smysl a poté zmíněné
312 sekvence znaků smažeme.
313 * `git mergetool` je nástroj, který spouští grafický nástroj (např.
314 [kdiff3][kdiff3]), který vám se slučováním pomůže.
317 sebe zobrazuje 3 různé verze projektu: poslední společná verze
318 (base), verzi z větve před slučováním (local) a verzi ze slučované
319 větve (remote) tj. té uvedené jako parametr v příkazu `git merge`.
321 Ve spodní části obrazovky je pak vidět výsledek slučování, který
322 můžeme měnit buď přímou editací a nebo výběrem jednotlivých verzí
323 pomocí tlačítek A, B a C. V tomto okně je potřeba zbavit se všech
324 řadek, které maji v levém sloupci `?` - tj. konfliktů.
325 * `gitk --merge` - zobrazí pouze commity, které modifikovaly
328 [kdiff3]:http://kdiff3.sourceforge.net/
330 Repozitář na repo.or.cz
331 ---------------------------
333 K tomu, aby výsledky vaší práce na open source projektech byly snadno
334 dostupné pro ostatní je užitečné založit si vlastní repozitář, odkud
335 si budou moct ostatní vaše změny stáhnout.
337 1. [Zaregistrujte se][reg] na [repo.or.cz][roc]. K repozitáři na
338 repo.or.cz se přistupuje protokolem SSH a autorizace se provádí na
339 základě veřejných klíčů.
341 1. Pokud žádný klíč nemáte, vytvořte si ho příkazem
345 Příkaz se vás zeptá kam klíč uložit. Výchozí volba vám zpočátku
346 bude stačit. Dále se zeptá na heslo (passphrase) k vašemu
347 privátnímu klíči. Pokud žádné heslo nezadáte, bude se moct
348 kdokoli, kdo se dostane k souboru s vaším klíčem (např.
349 administrátor školních serverů), autorizovat jako vy.
351 2. Pokud jste se rozhodli chránit klíč heslem, je otravné zadávat
352 heslo vždy, když klíč používáte. Naštěstí existuje program
353 `ssh-agent`, který uchovává odheslované klíče v paměti a
354 poskytuje je vždy, když je nějaký oprávněný program potřebuje.
355 Vy tak zadáte heslo jen jednou a to když předáváte klíč
356 ssh-agentovi příkazem
360 [reg]:http://repo.or.cz/reguser.cgi
361 [roc]:http://repo.or.cz/
363 2. Vytvoření repozitáře na repo.or.cz.
365 Můžete založit buď [nový projekt][new] a nebo udělat tzv.
366 [fork existujícího projektu][forkmc]. Pro oba typy projektů můžete
367 zvolit zda bude repozitář pouze automaticky aktualizovanou kopií
368 jiného repozitáře (*mirror mode*) a nebo zda-li bude možné do něj přímo
369 ukládat nové commity (*push mode*).
371 Pro účely tohoto cvičení si
372 [založte fork][forkmc] [Midnight commanderu][w/osp.git] v *Push módu*, který se
373 bude jmenovat podle vašeho loginu. Do tohoto repozitáře pak
374 nahrajete úkol z dnešního cvičení.
376 3. Do vašeho vzdáleného repozitáře můžete nahrát vaší větev některým z
379 git push ssh://<mujlogin>@repo.or.cz/srv/git/midnight-commander/osp/<mujfork>.git homework
382 git remote add repo-or-cz ssh://<mujlogin>@repo.or.cz/srv/git/midnight-commander/osp/<mujfork>.git
383 git push repo-or-cz homework
385 [new]:http://repo.or.cz/regproj.cgi
386 [forkmc]:http://repo.or.cz/regproj.cgi?fork=midnight-commander/osp.git
391 Proveďte sloučení větve `ssh://git@rtime.felk.cvut.cz/osp/mc
392 only-directories` s aktuální vývojovou větví `origin/master`. Výsledek
393 uložte do [vámi vytvořeného repozitáře (forku) na repo.or.cz][forkmc]
396 [w/osp.git]:http://repo.or.cz/w/midnight-commander/osp.git