1 [[!meta title="Verzovací systém Git"]]
8 Naučit se pracovat s verzovacím systémem [Git][git]. Jedná se o velmi
9 rozšířený verzovací systém používaný mnoha open source projekty.
10 Konkrétně nás bude zajímat:
12 * využití Gitu pro distribuovaný vývojový model, který je praktikován
13 mnoha open source projekty,
14 * efektivní prohledávání historie projektu, které je užitečné
15 například při rozhodování, jak správně vyřešit určitý konflikt a
18 [git]:http://git-scm.com/
23 Git je poměrně univerzální nástroj pro správu a synchronizaci dat
24 v souborech. Kromě verzování softwaru ho lidé používají k mnoha dalším
25 činnostem (viz [Git User's Survey 2012, otázka 8][gitsurvey]). Jedna
26 z často zmiňovaných nevýhod Gitu je, že oproti jiným verzovacím
27 systémům je těžší se ho naučit a používat. Možným důvodem je to, že
28 git nabízí větší funkcionalitu než většina ostatních systémů, která se
29 ale využije jen ve speciálních případech – například jen u extrémně
30 velkých projektů jako Linuxové jádro. Ať už to tak je, nebo ne, faktem
31 je, že Git se neustále vyvíjí a mnoho úsilí je věnováno právě zlepšení
32 uživatelské přivětivosti.
34 [gitsurvey]:https://www.survs.com/results/QPESOB10/ME8UTHXM4M
36 Pro pochopení Gitu je důležité mít základní představu o tom, jak Git
37 pracuje s větvemi, což bylo popsáno v [[přednášce|prednasky/intro-to-git.pdf]]
38 na slidech "*Working on branches*" a "*Working with remotes*".
40 V tomto cvičení budeme opět pracovat s projektem
41 [Midnight commander][mc] z [[1. cvičení|1]]. Pokud s gitem začínáte,
42 doporučuji v průběhu jednotlivých kroků kontrolovat stav repozitáře
43 pomocí grafických nástrojů:
45 * Příkaz `gitk` zobrazuje graficky historii a dovoluje její
46 interaktivní procházení. Přijímá stejné volby jako `git log`, takže
47 v příkazech níže ho můžete použít i pro "vizualizaci" výstupu `git
50 * Příkaz `git gui` je grafický nástroj částečně nahrazující příkazy
51 `git status` a `git commit`. Pomocí menu je možné provádět i další
52 operace jako např. vytváření větví nebo pracovat se vzdálenými
55 * Dále existuje ještě spousta dalších [nástrojů][tools]. Já osobně používám
56 kromě výše zmíněných příkazů ještě `tig` a `qgit`.
58 Dále se vám určitě bude hodit dokumentace:
63 [tools]:http://git-scm.com/docs/git-tools
68 1. Základní nastavení. Aby vaše commity obsahovaly správné údaje o
69 vás, nastavte si jméno a email:
71 git config --global user.name "Your Name Comes Here"
72 git config --global user.email you@yourdomain.example.com
76 1. Předpokládáme, že máte zdrojové kódy midnight commanderu někde na
77 disku, takže se přesuňte do adresáře s nimi.
84 2. Možná budete chtít zachovat vaší práci z prvního cvičení. Uložíme
85 jí do nové větve *cviceni1*. Nejprve se ale podíváme v jakém stavu
92 # Changes not staged for commit:
93 # (use "git add <file>..." to update what will be committed)
94 # (use "git checkout -- <file>..." to discard changes in working directory)
96 # modified: lib/keybind.h
97 # modified: src/filemanager/midnight.c
100 # (use "git add <file>..." to include in what will be committed)
104 no changes added to commit (use "git add" and/or "git commit -a")
106 Vidíme, že pracujeme na větvi *master* a vzhledem k verzi uložené
107 v repozitáři máme několik změněných souborů.
109 [mc]:http://www.midnight-commander.org/
111 3. Vytvoříme větev *cviceni1* a přepneme se na ni:
113 git checkout -b cviceni1
115 Nyní už by měl `git status` ukazovat že pracujeme s větví
118 4. Provedeme *commit*, t.j. uložíme všechny naše změny do lokálního repozitáře:
120 git commit -a -m 'Zmeny z prvniho cviceni'
122 Parametrem `-a` říkáme, že "commitujeme" vše (all) a `-m` udává
123 komentář ke commitu (message). Kdybychom `-m` vynechali, git spustí
124 editor a nechá nás napsat zprávu v něm.
126 > *Poznámka:* Pokud vám nevyhovuje výchozí editor (většinou `vi`),
127 > nastavte si, že chcete používat jiný editor (např. `nano`):
129 > git config --global core.editor nano
131 5. Nyní se přepneme zpět na větev *master*:
134 V adresáři se vám teď objeví verze bez vašich úprav z 1. cvičení.
136 6. Seznam větví v našem repozitáři zjistíme příkazem
139 Aktuální větev je označena hvězdičkou `*`.
141 Práce s více vzdálenými repozitáři
142 ----------------------------------
144 Doposud jsme pracovali pouze s jedním vzdáleným repozitářem. Mezi velké
145 výhody gitu (a ostatních distribuovaných verzovacích systémů) patří
146 schopnost pracovat s více vzdálenými repozitáři.
148 1. Které vzdálené repozitáře máme nakonfigurované zjistíme pomocí
152 Vidíme, že máme nakonfigurovaný repozitář s názvem *origin* a jeho
155 1. Pokud chceme nějaký vzdálený repozitář používat často,
156 vyplatí se ho pojmenovat krátkým jménem (v příkladu níže *osp*),
157 abychom nemuseli pořád psát dlouhé URL:
159 git remote add osp ssh://git@rtime.felk.cvut.cz/osp/mc
161 V tomto repozitáři je uloženo zadání dnešní úlohy. Abyste se k němu
162 dostali, musí server znát váš veřejný SSH klíč. Jak toho docílit
163 najdete na [[samostatné stránce|rtime-git-ssh-key]]. Přístup k
164 repozitářům na serveru rtime budete potřebovat i v písemce. **Proto
165 vám důrazně doporučujeme zprovoznit přístup už na tomto cvičení,
166 abyste při písemce neztráceli čas.**
168 2. Nyní můžeme stáhnout obsah právě přidaného repozitáře:
171 V případě úspěchu bude výstup vypadat následovně:
173 remote: Counting objects: 34, done.
174 remote: Compressing objects: 100% (17/17), done.
175 remote: Total 20 (delta 17), reused 6 (delta 3)
176 Unpacking objects: 100% (20/20), done.
177 From ssh://rtime.felk.cvut.cz/osp/mc
178 * [new branch] master -> osp/master
179 * [new branch] only-directories -> osp/only-directories
180 * [new branch] only-directories-old -> osp/only-directories-old
182 3. Příkaz nám vypíše, že v repozitáři byly tři nové větve. Všechny
183 větve ze vzdálených repozitářů vypíšeme příkazem
186 Zjednodušeně řečeno, jediný rozdíl mezi lokální a vzdálenou větví
187 je v tom, že jméno vzdálené větve má prefix `<remote>/` (v našem
188 případě `origin/` nebo `osp/`).
190 4. Nyní nás zajímá co je ve větvích, které jsme právě stáhli:
192 git log osp/only-directories ^master
193 git log master..osp/only-directories
194 Tyto dva příkazy jsou ekvivalentní a vypisují commity, které jsou
195 ve větvi *osp/only-directories* a zároveň nejsou (^) ve větvi *master*.
197 Chcete-li vidět i změny v kódu (patch) použijte jeden
198 z následujících příkazů:
200 git log -p osp/only-directories ^master
201 gitk osp/only-directories ^master
203 Výše uvedené příkazy zobrazují změny provedené jednotlivými
204 commity. Pokud nás zajímá celková změna bez rozdělení na jednotlivé
205 committy, pomůže nám příkaz `git diff`:
207 git diff master..osp/only-directories
208 git diff master osp/only-directories
210 První příkaz zobrazí změny, které obsahuje verze *only-directories* a
211 nejsou ve větvi master. Druhý příkaz zobrazí kompletní rozdíl, tj.
212 i změny, které jsou ve větvi *master* a ne v *only-directories*.
214 6. Podobně můžeme postupovat i s původním repozitářem. Jméno *origin*
215 je výchozí a proto ho nemusíme zadávat:
218 Co přibylo ve větvi *master* od prvního cvičení zjistíme příkazem:
220 git log master..origin/master
222 Slučování větví (merge)
223 -----------------------
225 Operace slučující dvě a více větví do jedné se nazývá *merge*. V Gitu
226 můžou při slučování nastat tři situace:
228 * *Already up-to-date* je situace, kdy už je větev, kterou chceme
229 sloučit, dosažitelná z aktuální větve (už byla sloučena v
230 minulosti). Při sloučení nedojde k žádné změně.
231 * *Fast-forward* je opak předchozí situace, tj. aktuální větev je
232 dosažitelná ze slučované větve. To odpovídá situaci, kdy
233 aktualizujeme na novější verzi. Při sloučení je větev (tj. ukazatel
234 na poslední commit) posunuta na novější verzi.
235 * *True merge*. Pokud nenastane jedna z předchozích situací, jedná se
236 o skutečné sloučení. Výsledek je nějaká kombinace obou verzí.
238 Pokud došlo v případě *True merge* ke změně stejného místa v kódu v
239 obou větvích, dojde k tzv. *konfliktu*, který musí být vyřešen ručně.
241 1. Zkuste provést sloučení své větve *master* s *origin/master*
243 git merge origin/master
244 Výsledkem bude *Fast forward* a uvidíme jaké soubory byly změněny:
246 git merge origin/master
247 Updating 0ebd30c..a99dc51
250 contrib/Makefile.am | 9 +-
252 Toto je velmi častá operace a proto lze operace `fetch` a `merge`
253 nahradit jedním příkazem
256 2. Vždy, když člověk pracuje na nějaké netriviální změně, je užitečné,
257 založit si na to samostatnou větev. Dnešním úkolem bude sloučit
258 větev *osp/only-directories* s větví *master* a protože to není
259 triviální založte si na to novou větev:
261 git checkout -b homework
262 3. Pokud provedete sloučení
264 git merge osp/only-directories
265 Výsledek bude vypadat pravděpodobně takto:
267 Auto-merging doc/man/mc.1.in
268 Auto-merging src/filemanager/find.c
269 CONFLICT (content): Merge conflict in src/filemanager/find.c
270 Automatic merge failed; fix conflicts and then commit the result.
272 Vidíme, že se automaticky povedlo sloučit změny v souboru
273 `doc/mc.1.in`, ale při slučování změn ve `src/find.c` už takové
274 štěstí nemáme a výsledkem je konflikt.
280 Konflikt lze řešit několika způsoby (viz také `git merge --help`):
282 * Vzdáme to a vrátíme se k verzi před slučováním
286 * Konflikt vyřešíme a oznámíme to gitu příkazy `git add` a `git
287 commit` (jak nám git napovídá v hláškách)
289 V průběhu řešení konfliktu je užitečné používat příkaz `git status`,
290 abychom zjistili, co je ještě potřeba vyřešit. V našem případě vypadá
294 You have unmerged paths.
295 (fix conflicts and run "git commit")
297 Changes to be committed:
299 modified: doc/man/mc.1.in
302 (use "git add <file>..." to mark resolution)
304 both modified: src/filemanager/find.c
306 Konflikt lze řešit následujícími způsoby:
308 * Soubor s konfliktem otevřeme v textovém editoru a najdeme sekvence
309 `<<<<<<<<`, `=========` a `>>>>>>>>`, kterými jsou označené
310 jednotlivé konfliktní oblasti. Tato místa musíme opravit tak, aby
311 kód dával smysl a poté zmíněné sekvence znaků smažeme.
312 * Použijeme příkaz `git mergetool`, který spouští grafický nástroj
313 (např. [kdiff3][kdiff3] nebo `meld`), který nám se slučováním
316 `kdiff3` vedle sebe zobrazuje 3 různé verze projektu: poslední
317 společnou verzi (base), verzi z větve před slučováním (local) a
318 verzi ze slučované větve (remote), tj. té uvedené jako parametr
319 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 řádek, které mají v levém sloupci `?` – tj. konfliktů.
326 Při řešení konfliktů nám může pomoct i příkaz `gitk --merge`, který
327 zobrazí pouze commity, které modifikovaly konfliktní soubory.
328 Jednoduše tam můžeme zjistit, které změny konflikt způsobily.
330 Git obsahuje poměrně jednoduchý algoritmus (v porovnání s jinými
331 verzovacími systémy) pro slučování různých verzí souborů. Některým
332 studentům se to nelíbí a stěžují si. Přečtěte si
333 [názor Linuse Torvaldse][mergealg], proč tomu tak je.
335 [mergealg]:http://stackoverflow.com/questions/612580/how-does-git-solve-the-merging-problem/612747#612747
337 [kdiff3]:http://kdiff3.sourceforge.net/
342 K tomu, aby výsledky vaší práce na open source projektech byly snadno
343 dostupné pro ostatní je užitečné založit si vlastní repozitář, odkud
344 si budou moct ostatní vaše změny stáhnout.
346 1. Zaregistrujte se na [GitHubu](https://github.com/). V závislosti na
347 tom, jakým protokolem chcete k repozitářům na GitHubu přistupovat
348 si nastavte buď [password caching][passcache] (pro HTTP) nebo
349 [SSH klíče][sshkeys] (pro SSH).
351 [passcache]:https://help.github.com/articles/set-up-git#password-caching
352 [sshkeys]:https://help.github.com/articles/generating-ssh-keys
354 2. Vytvoření repozitáře na GitHubu. Můžete založit buď
355 [nový projekt][new] nebo udělat tzv. "fork" existujícího projektu.
357 Pro účely tohoto cvičení si [založte fork][forkmc]
358 [Midnight commanderu][osp-mc]. Do takto vytvořeného repozitáře pak
359 nahrajete vypracovaný úkol z dnešního cvičení.
361 3. Do svého repozitáře na GitHubu nahrajete větev z lokálního
362 repozitáře některým z těchto způsobů:
364 git remote add github https://github.com/<mujlogin>/mc.git
365 git push github HEAD:homework
368 git push https://github.com/<mujlogin>/mc.git homework
371 git push ssh://git@github.com/<mujlogin>/mc.git homework
373 [new]:https://github.com/new
374 [osp-mc]:https://github.com/CTU-OSP/mc
375 [forkmc]:https://github.com/CTU-OSP/mc/fork
381 Proveďte sloučení větve `ssh://git@rtime.felk.cvut.cz/osp/mc
382 only-directories` s aktuální vývojovou větví Midnight Commanderu
383 (`origin/master`). Výsledek musí jít zkompilovat a musí obsahovat
384 funkcionalitu přidanou do větve `only-directories`. Výsledek uložte do
385 [vámi vytvořeného repozitáře (forku) na GitHubu][forkmc] do větve