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