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