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