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