]> rtime.felk.cvut.cz Git - edu/osp-wiki.git/blob - cviceni/4.mdwn
Merge branch 'master' into uloha-server
[edu/osp-wiki.git] / cviceni / 4.mdwn
1 [[!meta title="Verzovací systém Git"]]
2
3 [[!toc levels=2]]
4
5 Cíl
6 ===
7
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:
11
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
16 * řešení konfliktů.
17
18 [git]:http://git-scm.com/
19
20 Úvod
21 ====
22
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.
33
34 [gitsurvey]:https://www.survs.com/results/QPESOB10/ME8UTHXM4M
35
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*".
39
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ů:
44
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
48   log`.
49
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
53   repozitáři.
54
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`.
57   
58 Dále se vám určitě bude hodit dokumentace:
59
60     git help <příkaz>
61     git <příkaz> --help
62   
63 [tools]:http://git-scm.com/docs/git-tools
64
65 Postup
66 ======
67
68 1. Základní nastavení. Aby vaše commity obsahovaly správné údaje o
69    vás, nastavte si jméno a email:
70
71        git config --global user.name "Your Name Comes Here"
72        git config --global user.email you@yourdomain.example.com
73
74
75
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.
78    
79        cd ~/mc
80
81 Lokální větve
82 -------------
83
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
86    máme zdrojové kódy:
87
88        git status
89    Dostaneme něco jako:
90    
91        # On branch master
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)
95        #
96        #        modified:   lib/keybind.h
97        #        modified:   src/filemanager/midnight.c
98        #
99        # Untracked files:
100        #   (use "git add <file>..." to include in what will be committed)
101        #
102        #        misc/ext.d/doc.sh
103        #    ...
104        no changes added to commit (use "git add" and/or "git commit -a")
105    Vidíme, že pracujeme na větvi *master* a vzhledem k verzi uložené v
106    repozitáři máme několik změněných souborů.
107
108 [mc]:http://www.midnight-commander.org/
109
110 3. Vytvoříme větev *cviceni1* a přepneme se na ni:
111
112        git checkout -b cviceni1
113
114    Nyní už by měl `git status` ukazovat že pracujeme s větví
115    *cviceni1*.
116
117 4. Nyní provedeme *commit*:
118
119        git commit -a -m 'Zmeny z prvniho cviceni'
120         
121    Parametrem `-a` říkáme, že "commitujeme" vše (all) a `-m` udává
122    komentář ke commitu (message). Kdybychom `-m` vynechali, git spustí
123    editor a nechá nás napsat zprávu v něm.   
124
125    > *Poznámka:* Pokud vám nevyhovuje výchozí editor (většinou `vi`),
126    > nastavte si, že chcete používat jiný editor (např. `pico`):
127    > 
128    >     git config --global core.editor pico
129
130 5. Nyní se přepneme zpět na větev *master*:
131
132        git checkout master
133    V adresáři se vám teď objeví verze bez vašich úprav z 1. cvičení.
134
135 6. Seznam větví v našem repozitáři zjistíme příkazem
136
137        git branch
138    Aktuální větev je označena hvězdičkou `*`.
139    
140 Práce s více vzdálenými repozitáři
141 ----------------------------------
142
143 Doposud jsme pracovali pouze s jedním vzdáleným repozitářem. Mezi velké
144 výhody gitu (a ostatních distribuovaných verzovacích systémů) patří
145 schopnost pracovat s více vzdálenými repozitáři.
146
147 1. Které vzdálené repozitáře máme nakonfigurované zjistíme pomocí
148    
149        git remote -v
150
151    Vidíme, že máme nakonfigurovaný repozitář s názvem *origin* a jeho
152    URL.
153
154 1. Pokud chceme nějaký vzdálený repozitář používat často,
155    vyplatí se ho pojmenovat krátkým jménem (v příkladu níže *osp*),
156    abychom nemuseli pořád psát dlouhé URL:
157    
158        git remote add osp ssh://git@rtime.felk.cvut.cz/osp/mc
159
160    V tomto repozitáři je uloženo zadání dnešní úlohy. Abyste se k němu
161    dostali, musí server znát váš veřejný SSH klíč. Jak toho docílit
162    najdete na [[samostatné stránce|rtime-git-ssh-key]]. Přístup k
163    repozitářům na serveru rtime budete potřebovat i v písemce. **Proto
164    vám důrazně doporučujeme zprovoznit přístup už na tomto cvičení,
165    abyste při písemce neztráceli čas.**
166         
167 2. Nyní můžeme stáhnout obsah právě přidaného repozitáře:
168
169        git fetch osp
170    V případě úspěchu bude výstup vypadat následovně:
171
172        remote: Counting objects: 34, done.
173        remote: Compressing objects: 100% (17/17), done.
174        remote: Total 20 (delta 17), reused 6 (delta 3)
175        Unpacking objects: 100% (20/20), done.
176        From ssh://rtime.felk.cvut.cz/osp/mc
177         * [new branch]      master     -> osp/master
178         * [new branch]      only-directories -> osp/only-directories
179         * [new branch]      only-directories-old -> osp/only-directories-old
180
181 3. Příkaz nám vypíše, že v repozitáři byly tři nové větve. Všechny
182    větve ze vzdálených repozitářů vypíšeme příkazem
183    
184        git branch -r
185    Zjednodušeně řečeno, jediný rozdíl mezi lokální a vzdálenou větví
186    je v tom, že jméno vzdálené větve má prefix `<remote>/` (v našem
187    případě `origin/` nebo `osp/`).
188
189 4. Nyní nás zajímá co je ve větvích, které jsme právě stáhli:
190
191        git log osp/only-directories ^master
192            git log master..osp/only-directories
193    Tyto dva příkazy jsou ekvivalentní a vypisují commity, které jsou
194    ve větvi *osp/only-directories* a zároveň nejsou (^) ve větvi *master*.
195    
196    Chcete-li vidět i změny v kódu (patch) použijte jeden
197    z následujících příkazů:
198
199        git log -p osp/only-directories ^master
200        gitk osp/only-directories ^master
201
202    Výše uvedené příkazy zobrazují změny provedené jednotlivými
203    commity. Pokud nás zajímá celková změna bez rozdělení na jednotlivé
204    committy, pomůže nám příkaz `git diff`:
205
206        git diff master..osp/only-directories
207        git diff master osp/only-directories
208
209    První příkaz zobrazí změny, které obsahuje verze *only-directories* a
210    nejsou ve větvi master. Druhý příkaz zobrazí kompletní rozdíl, tj.
211    i změny, které jsou ve větvi *master* a ne v *only-directories*.
212
213 6. Podobně můžeme postupovat i s původním repozitářem. Jméno *origin*
214    je výchozí a proto ho nemusíme zadávat:
215
216        git fetch
217    Co přibylo ve větvi *master* od prvního cvičení zjistíme příkazem:
218    
219        git log master..origin/master
220
221 Slučování větví (merge)    
222 -----------------------
223
224 Operace slučující dvě a více větví do jedné se nazývá *merge*. V Gitu
225 můžou při slučování nastat tři situace:
226
227 * *Already up-to-date* je situace, kdy už je větev, kterou chceme
228   sloučit, dosažitelná z aktuální větve (už byla sloučena v
229   minulosti). Při sloučení nedojde k žádné změně.
230 * *Fast-forward* je opak předchozí situace, tj. aktuální větev je
231   dosažitelná ze slučované větve. To odpovídá situaci, kdy
232   aktualizujeme na novější verzi. Při sloučení je větev (tj. ukazatel
233   na poslední commit) posunuta na novější verzi.
234 * *True merge*. Pokud nenastane jedna z předchozích situací, jedná se
235   o skutečné sloučení. Výsledek je nějaká kombinace obou verzí.
236   
237 Pokud došlo v případě *True merge* ke změně stejného místa v kódu v
238 obou větvích, dojde k tzv. *konfliktu*, který musí být vyřešen ručně.
239
240 1. Zkuste provést sloučení své větve *master* s *origin/master*
241
242        git merge origin/master
243    Výsledkem bude *Fast forward* a uvidíme jaké soubory byly změněny:
244
245        git merge origin/master
246        Updating 0ebd30c..a99dc51
247        Fast forward
248         configure.ac                  |    1 +
249         contrib/Makefile.am           |    9 +-
250         ...
251    Toto je velmi častá operace a proto lze operace `fetch` a `merge`
252    nahradit jedním příkazem
253
254        git pull
255 2. Vždy, když člověk pracuje na nějaké netriviální změně, je užitečné,
256    založit si na to samostatnou větev. Dnešním úkolem bude sloučit
257    větev *osp/only-directories* s větví *master* a protože to není
258    triviální založte si na to novou větev:
259    
260        git checkout -b homework
261 3. Pokud provedete sloučení
262
263        git merge osp/only-directories
264    Výsledek bude vypadat pravděpodobně takto:
265
266        Auto-merging doc/man/mc.1.in
267            Auto-merging src/filemanager/find.c
268            CONFLICT (content): Merge conflict in src/filemanager/find.c
269            Automatic merge failed; fix conflicts and then commit the result. 
270
271    Vidíme, že se automaticky povedlo sloučit změny v souboru
272    `doc/mc.1.in`, ale při slučování změn ve `src/find.c` už takové
273    štěstí nemáme a výsledkem je konflikt.
274         
275
276 Řešení konfliktů
277 ----------------
278
279 Konflikt lze řešit několika způsoby (viz také `git merge --help`):
280
281 * Vzdáme to a vrátíme se k verzi před slučováním
282
283        git reset --hard
284         
285 * Konflikt vyřešíme a oznámíme to gitu příkazy `git add` a `git
286   commit` (jak nám git napovídá v hláškách)
287
288 V průběhu řešení konfliktu je užitečné používat příkaz `git status`,
289 abychom zjistili, co je ještě potřeba vyřešit. V našem případě vypadá
290 výstup zhruba takto:
291    
292     # On branch homework
293     # Changes to be committed:
294     #
295     #   modified:   doc/man/mc.1.in
296     #
297     # Unmerged paths:
298     #   (use "git add/rm <file>..." as appropriate to mark resolution)
299     #
300     #   both modified:      src/filemanager/find.c
301
302 Konflikt lze řešit následujícími způsoby:
303
304 * Soubor s konfliktem otevřeme v textovém editoru a najdeme sekvence
305   `<<<<<<<<`, `=========` a `>>>>>>>>`, kterými jsou označené
306   jednotlivé konfliktní oblasti. Tato místa musíme opravit tak, aby
307   kód dával smysl a poté zmíněné sekvence znaků smažeme.
308 * Použijeme příkaz `git mergetool`, který spouští grafický nástroj
309   (např. [kdiff3][kdiff3]), který nám se slučováním pomůže.
310   
311   `kdiff3` vedle sebe zobrazuje 3 různé verze projektu: poslední
312   společná verze (base), verzi z větve před slučováním (local) a verzi
313   ze slučované větve (remote), tj. té uvedené jako parametr v příkazu
314   `git merge`.
315   
316   Ve spodní části obrazovky je pak vidět výsledek slučování, který
317   můžeme měnit buď přímou editací a nebo výběrem jednotlivých verzí
318   pomocí tlačítek A, B a C. V tomto okně je potřeba zbavit se všech
319   řádek, které mají v levém sloupci `?` – tj. konfliktů.
320
321 Při řešení konfliktů nám může pomoct i příkaz `gitk --merge`, který
322 zobrazí pouze commity, které modifikovaly konfliktní soubory.
323 Jednoduše tam můžeme zjistit, které změny konflikt způsobily.
324
325 Git obsahuje poměrně jednoduchý algoritmus (v porovnání s jinými
326 verzovacími systémy) pro slučování různých verzí souborů. Některým
327 studentům se to nelíbí a stěžují si. Přečtěte si
328 [názor Linuse Torvaldse][mergealg], proč tomu tak je.
329
330 [mergealg]:http://stackoverflow.com/questions/612580/how-does-git-solve-the-merging-problem/612747#612747
331
332 [kdiff3]:http://kdiff3.sourceforge.net/
333
334 Repozitář na GitHubu
335 --------------------
336
337 K tomu, aby výsledky vaší práce na open source projektech byly snadno
338 dostupné pro ostatní je užitečné založit si vlastní repozitář, odkud
339 si budou moct ostatní vaše změny stáhnout.
340
341 1. Zaregistrujte se na [GitHubu](https://github.com/). V závislosti na
342    tom, jakým protokolem chcete k repozitářům na GitHubu přistupovat
343    si nastavte buď [password caching][passcache] (pro HTTP) nebo
344    [SSH klíče][sshkeys] (pro SSH).
345
346 [passcache]:https://help.github.com/articles/set-up-git#password-caching   
347 [sshkeys]:https://help.github.com/articles/generating-ssh-keys
348
349 2. Vytvoření repozitáře na GitHubu. Můžete založit buď
350    [nový projekt][new] nebo udělat tzv. "fork" existujícího projektu.
351    
352    Pro účely tohoto cvičení si [založte fork][forkmc]
353    [Midnight commanderu][osp-mc]. Do takto vytvořeného repozitáře pak
354    nahrajete úkol z dnešního cvičení.
355    
356 3. Do svého repozitáře na GitHubu nahrajete větev z lokálního
357    repozitáře některým z těchto způsobů:
358
359        git remote add github https://github.com/<mujlogin>/mc.git
360        git push github HEAD:homework
361    nebo
362
363        git push https://github.com/<mujlogin>/mc.git homework
364    nebo
365
366        git push ssh://git@github.com/<mujlogin>/mc.git homework
367
368 [new]:https://github.com/new
369 [osp-mc]:https://github.com/CTU-OSP/mc
370 [forkmc]:https://github.com/CTU-OSP/mc/fork
371
372
373 Zadání
374 ======
375
376 Proveďte sloučení větve `ssh://git@rtime.felk.cvut.cz/osp/mc
377 only-directories` s aktuální vývojovou větví Midnight Commanderu
378 (`origin/master`). Výsledek musí jít zkompilovat a musí obsahovat
379 funkcionalitu přidanou do větve `only-directories`. Výsledek uložte do
380 [vámi vytvořeného repozitáře (forku) na GitHubu][forkmc] do větve
381 `homework`.