]> rtime.felk.cvut.cz Git - edu/osp-wiki.git/blob - cviceni/4.mdwn
lectures: minor update of link to LinuxDays 2023 presentation listing.
[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
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ů.
108
109 [mc]:http://www.midnight-commander.org/
110
111 3. Vytvoříme větev *cviceni1* a přepneme se na ni:
112
113        git checkout -b cviceni1
114
115    Nyní už by měl `git status` ukazovat že pracujeme s větví
116    *cviceni1*.
117
118 4. Provedeme *commit*, t.j. uložíme všechny naše změny do lokálního repozitáře:
119
120        git commit -a -m 'Zmeny z prvniho cviceni'
121         
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.   
125
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`):
128    > 
129    >     git config --global core.editor nano
130
131 5. Nyní se přepneme zpět na větev *master*:
132
133        git checkout master
134    V adresáři se vám teď objeví verze bez vašich úprav z 1. cvičení.
135
136 6. Seznam větví v našem repozitáři zjistíme příkazem
137
138        git branch
139    Aktuální větev je označena hvězdičkou `*`.
140    
141 Práce s více vzdálenými repozitáři
142 ----------------------------------
143
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.
147
148 1. Které vzdálené repozitáře máme nakonfigurované zjistíme pomocí
149    
150        git remote -v
151
152    Vidíme, že máme nakonfigurovaný repozitář s názvem *origin* a jeho
153    URL.
154
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:
158    
159        git remote add osp ssh://git@rtime.felk.cvut.cz/osp/mc
160
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.**
167         
168 2. Nyní můžeme stáhnout obsah právě přidaného repozitáře:
169
170        git fetch osp
171    V případě úspěchu bude výstup vypadat následovně:
172
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
181
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
184    
185        git branch -r
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/`).
189
190 4. Nyní nás zajímá co je ve větvích, které jsme právě stáhli:
191
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*.
196    
197    Chcete-li vidět i změny v kódu (patch) použijte jeden
198    z následujících příkazů:
199
200        git log -p osp/only-directories ^master
201        gitk osp/only-directories ^master
202
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`:
206
207        git diff master..osp/only-directories
208        git diff master osp/only-directories
209
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*.
213
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:
216
217        git fetch
218    Co přibylo ve větvi *master* od prvního cvičení zjistíme příkazem:
219    
220        git log master..origin/master
221
222 Slučování větví (merge)    
223 -----------------------
224
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:
227
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í.
237   
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ě.
240
241 1. Zkuste provést sloučení své větve *master* s *origin/master*
242
243        git merge origin/master
244    Výsledkem bude *Fast forward* a uvidíme jaké soubory byly změněny:
245
246        git merge origin/master
247        Updating 0ebd30c..a99dc51
248        Fast forward
249         configure.ac                  |    1 +
250         contrib/Makefile.am           |    9 +-
251         ...
252    Toto je velmi častá operace a proto lze operace `fetch` a `merge`
253    nahradit jedním příkazem
254
255        git pull
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:
260    
261        git checkout -b homework
262 3. Pokud provedete sloučení
263
264        git merge osp/only-directories
265    Výsledek bude vypadat pravděpodobně takto:
266
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. 
271
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.
275         
276
277 Řešení konfliktů
278 ----------------
279
280 Konflikt lze řešit několika způsoby (viz také `git merge --help`):
281
282 * Vzdáme to a vrátíme se k verzi před slučováním
283
284        git merge --abort
285         
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)
288
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á
291 výstup zhruba takto:
292    
293     On branch homework
294     You have unmerged paths.
295       (fix conflicts and run "git commit")
296    
297     Changes to be committed:
298    
299         modified:   doc/man/mc.1.in
300    
301     Unmerged paths:
302       (use "git add <file>..." to mark resolution)
303    
304         both modified:   src/filemanager/find.c
305
306 Konflikt lze řešit následujícími způsoby:
307
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
314   pomůže.
315   
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`.
320   
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ů.
325
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.
329
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.
334
335 [mergealg]:http://stackoverflow.com/questions/612580/how-does-git-solve-the-merging-problem/612747#612747
336
337 [kdiff3]:http://kdiff3.sourceforge.net/
338
339 Repozitář na GitHubu
340 --------------------
341
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.
345
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).
350
351 [passcache]:https://help.github.com/articles/set-up-git#password-caching   
352 [sshkeys]:https://help.github.com/articles/generating-ssh-keys
353
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.
356    
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í.
360    
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ů:
363
364        git remote add github https://github.com/<mujlogin>/mc.git
365        git push github HEAD:homework
366    nebo
367
368        git push https://github.com/<mujlogin>/mc.git homework
369    nebo
370
371        git push ssh://git@github.com/<mujlogin>/mc.git homework
372
373 [new]:https://github.com/new
374 [osp-mc]:https://github.com/CTU-OSP/mc
375 [forkmc]:https://github.com/CTU-OSP/mc/fork
376
377
378 Zadání
379 ======
380
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
386 `homework`.