]> rtime.felk.cvut.cz Git - edu/osp-wiki.git/blob - cviceni/4.mdwn
Update "Řešení konfliktů"
[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/7/wo/GffJpx48NlVaG9rZZGKsI0/0.0.0.7.7.3.3.1.1.5.1.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/docs/git-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        # Changes not staged for commit:
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:   lib/keybind.h
95        #        modified:   src/filemanager/midnight.c
96        #
97        # Untracked files:
98        #   (use "git add <file>..." to include in what will be committed)
99        #
100        #        misc/ext.d/doc.sh
101        #    ...
102        no changes added to commit (use "git add" and/or "git commit -a")
103    Vidíme, že pracujeme na větvi *master* a vzhledem k verzi uložené v
104    repozitáři máme několik změněných souborů.
105
106 [mc]:http://www.midnight-commander.org/
107
108 3. Vytvoříme větev *cviceni1* a přepneme se na ni:
109
110        git checkout -b cviceni1
111
112    Nyní už by měl `git status` ukazovat že pracujeme s větví
113    *cviceni1*.
114
115 4. Nyní provedeme *commit*:
116
117        git commit -a -m 'Zmeny z prvniho cviceni'
118         
119    Parametrem `-a` říkáme, že "commitujeme" vše (all) a `-m` udává
120    komentář ke commitu (message). Kdybychom `-m` vynechali, git spustí
121    editor a nechá nás napsat zprávu v něm.   
122
123    > *Poznámka:* Pokud vám nevyhovuje výchozí editor (většinou `vi`),
124    > nastavte si, že chcete používat jiný editor (např. `pico`):
125    > 
126    >     git config --global core.editor pico
127
128 5. Nyní se přepneme zpět na větev *master*:
129
130        git checkout master
131    V adresáři se vám teď objeví verze bez vašich úprav z 1. cvičení.
132
133 6. Seznam větví v našem repozitáři zjistíme příkazem
134
135        git branch
136    Aktuální větev je označena hvězdičkou `*`.
137    
138 Práce s více vzdálenými repozitáři
139 ----------------------------------
140
141 Doposud jsme pracovali pouze s jedním vzdáleným repozitářem. Mezi velké
142 výhody gitu (a ostatních distribuovaných verzovacích systémů) patří
143 schopnost pracovat s více vzdálenými repozitáři.
144
145 1. Které vzdálené repozitáře máme nakonfigurované zjistíme pomocí
146    
147        git remote -v
148
149    Vidíme, že máme nakonfigurovaný repozitář s názvem *origin* a jeho
150    URL.
151
152 1. Pokud chceme nějaký vzdálený repozitář používat často,
153    vyplatí se ho pojmenovat krátkým jménem (v příkladu níže *osp*),
154    abychom nemuseli pořád psát dlouhé URL:
155    
156        git remote add osp ssh://git@rtime.felk.cvut.cz/osp/mc
157
158    V tomto repozitáři je uloženo zadání dnešní úlohy. Abyste se k němu
159    dostali, musí server znát váš veřejný SSH klíč. Jak toho docílit
160    najdete na [[samostatné stránce|rtime-git-ssh-key]]. Přístup k
161    repozitářům na serveru rtime budete potřebovat i v písemce. **Proto
162    vám důrazně doporučujeme zprovoznit přístup už na tomto cvičení,
163    abyste při písemce neztráceli čas.**
164         
165 2. Nyní můžeme stáhnout obsah právě přidaného repozitáře:
166
167        git fetch osp
168    V případě úspěchu bude výstup vypadat následovně:
169
170        remote: Counting objects: 17, done.
171            remote: Compressing objects: 100% (9/9), done.
172            remote: Total 10 (delta 4), reused 7 (delta 1)
173            Unpacking objects: 100% (10/10), done.
174            From ssh://rtime.felk.cvut.cz/osp/mc
175                 * [new branch]          master     -> osp/master
176                 * [new branch]          only-directories -> osp/only-directories
177
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 jméno vzdálené větve má prefix `<remote>/` (v našem
184    případě `origin/` nebo `osp/`).
185
186 4. Nyní nás zajímá co je ve větvích, které jsme právě stáhli:
187
188        git log osp/only-directories ^master
189            git log master..osp/only-directories
190    Tyto dva příkazy jsou ekvivalentní a vypisují commity, které jsou
191    ve větvi *osp/only-directories* a zároveň nejsou (^) ve větvi *master*.
192    
193    Chcete-li vidět i změny v kódu použijte jeden z následujících příkazů:
194
195        git log -p osp/only-directories ^master
196            gitk osp/only-directories ^master
197
198 6. Podobně můžeme postupovat i s původním repozitářem. Jméno *origin*
199    je výchozí a proto ho nemusíme zadávat:
200
201        git fetch
202    Co přibylo ve větvi *master* od prvního cvičení zjistíme příkazem:
203    
204        git log master..origin/master
205
206 Slučování větví (merge)    
207 -----------------------
208
209 Operace slučující dvě a více větví do jedné se nazývá *merge*. V Gitu
210 Můžou při slučování nastat tři situace:
211
212 * *Already up-to-date* je situace, kdy už je větev, kterou chceme
213   sloučit, dosažitelná z aktuální větve (už byla sloučena v
214   minulosti). Při sloučení nedojde k žádné změně.
215 * *Fast-forward* je opak předchozí situace, tj. aktuální větev je
216   dosažitelná ze slučované větve. To odpovídá situaci, kdy
217   aktualizujeme na novější verzi. Při sloučení je větev (tj. ukazatel
218   na poslední commit) posunuta na novější verzi.
219 * *True merge*. Pokud nenastane jedna z předchozích situací, jedná se
220   o skutečné sloučení. Výsledek je nějaká kombinace obou verzí.
221   
222 Pokud došlo v případě *True merge* ke změně stejného místa v kódu v
223 obou větvích, dojde k tzv. *konfliktu*, který musí být vyřešen ručně.
224
225 1. Zkuste provést sloučení své větve *master* s *origin/master*
226
227        git merge origin/master
228    Výsledkem bude *Fast forward* a uvidíme jaké soubory byly změněny:
229
230        git merge origin/master
231        Updating 0ebd30c..a99dc51
232        Fast forward
233         configure.ac                  |    1 +
234         contrib/Makefile.am           |    9 +-
235         ...
236    Toto je velmi častá operace a proto lze operace `fetch` a `merge`
237    nahradit jedním příkazem
238
239        git pull
240 2. Vždy, když člověk pracuje na nějaké netriviální změně, je užitečné,
241    založit si na to samostatnou větev. Dnešním úkolem bude sloučit
242    větev *osp/only-directories* s větví *master* a protože to není
243    triviální založte si na to novou větev:
244    
245        git checkout -b homework
246 3. Pokud provedete sloučení
247
248        git merge osp/only-directories
249    Výsledek bude vypadat pravděpodobně takto:
250
251        Auto-merging doc/man/mc.1.in
252            Auto-merging src/filemanager/find.c
253            CONFLICT (content): Merge conflict in src/filemanager/find.c
254            Automatic merge failed; fix conflicts and then commit the result. 
255
256    Vidíme, že se automaticky povedlo sloučit změny v souboru
257    `doc/mc.1.in`, ale při slučování změn ve `src/find.c` už takové
258    štěstí nemáme a výsledkem je konflikt.
259         
260
261 Řešení konfliktů
262 ----------------
263
264 Konflikt lze řešit několika způsoby (viz také `git merge --help`):
265
266 * Vzdáme to a vrátíme se k verzi před slučováním
267
268        git reset --hard
269         
270 * Konflikt vyřešíme a oznámíme to gitu příkazy `git add` a `git
271   commit` (jak nám git napovídá v hláškách)
272
273 V průběhu řešení konfliktu je užitečné používat příkaz `git status`,
274 Abychom zjistili, co je ještě potřeba vyřešit. V našem případě vypadá
275 výstup zhruba takto:
276    
277     # On branch homework
278     # Changes to be committed:
279     #
280     #   modified:   doc/man/mc.1.in
281     #
282     # Unmerged paths:
283     #   (use "git add/rm <file>..." as appropriate to mark resolution)
284     #
285     #   both modified:      src/filemanager/find.c
286
287 Konflikt lze řešit následujícími způsoby:
288
289 * Soubor s konfliktem otevřeme v textovém editoru a najdeme sekvence
290   `<<<<<<<<`, `=========` a `>>>>>>>>`, kterými jsou označené
291   jednotlivé konfliktní oblasti. Tato místa musíme opravit tak, aby
292   kód dával smysl a poté zmíněné sekvence znaků smažeme.
293 * Použijeme příkaz `git mergetool`, který spouští grafický nástroj
294   (např. [kdiff3][kdiff3]), který nám se slučováním pomůže.
295   
296   `kdiff3` vedle sebe zobrazuje 3 různé verze projektu: poslední
297   společná verze (base), verzi z větve před slučováním (local) a verzi
298   ze slučované větve (remote), tj. té uvedené jako parametr v příkazu
299   `git merge`.
300   
301   Ve spodní části obrazovky je pak vidět výsledek slučování, který
302   můžeme měnit buď přímou editací a nebo výběrem jednotlivých verzí
303   pomocí tlačítek A, B a C. V tomto okně je potřeba zbavit se všech
304   řádek, které mají v levém sloupci `?` – tj. konfliktů.
305
306 Při řešení konfliktů nám může pomoct i příkaz `gitk --merge`, který
307 zobrazí pouze commity, které modifikovaly konfliktní soubory.
308 Jednoduše tam můžeme zjistit, které změny konflikt způsobily.
309
310 [kdiff3]:http://kdiff3.sourceforge.net/
311
312 Repozitář na repo.or.cz
313 ---------------------------
314
315 K tomu, aby výsledky vaší práce na open source projektech byly snadno
316 Dostupné pro ostatní je užitečné založit si vlastní repozitář, odkud
317 si budou moct ostatní vaše změny stáhnout.
318
319 1. [Zaregistrujte se][reg] na [repo.or.cz][roc]. K repozitáři na
320    repo.or.cz se přistupuje protokolem SSH a autorizace se provádí na
321    základě veřejných klíčů.
322    
323    1. Pokud žádný klíč nemáte, vytvořte si ho příkazem 
324    
325           ssh-keygen
326
327       Příkaz se vás zeptá kam klíč uložit. Výchozí volba vám zpočátku
328       bude stačit. Dále se zeptá na heslo (passphrase) k vašemu
329       privátnímu klíči. Pokud žádné heslo nezadáte, bude se moct
330       kdokoli, kdo se dostane k souboru s vaším klíčem (např.
331       administrátor školních serverů), autorizovat jako vy.
332    
333    2. Pokud jste se rozhodli chránit klíč heslem, je otravné zadávat
334       heslo vždy, když klíč používáte. Naštěstí existuje program
335       `ssh-agent`, který uchovává odheslované klíče v paměti a
336       poskytuje je vždy, když je nějaký oprávněný program potřebuje.
337       Vy tak zadáte heslo jen jednou a to když předáváte klíč
338       ssh-agentovi příkazem
339
340           ssh-add
341
342 [reg]:http://repo.or.cz/reguser.cgi
343 [roc]:http://repo.or.cz/
344
345 2. Vytvoření repozitáře na repo.or.cz.
346
347    Můžete založit buď [nový projekt][new] a nebo udělat tzv.
348    [fork existujícího projektu][forkmc]. Pro oba typy projektů můžete
349    zvolit zda bude repozitář pouze automaticky aktualizovanou kopií
350    jiného repozitáře (*mirror mode*) a nebo zda-li bude možné do něj přímo
351    ukládat nové commity (*push mode*).
352    
353    Pro účely tohoto cvičení si
354    [založte fork][forkmc] [Midnight commanderu][w/osp.git] v *Push módu*, který se
355    bude jmenovat podle vašeho loginu. Do tohoto repozitáře pak
356    nahrajete úkol z dnešního cvičení.
357    
358 3. Do vašeho vzdáleného repozitáře můžete nahrát vaší větev některým z
359    těchto způsobů
360
361        git push ssh://<mujlogin>@repo.or.cz/srv/git/midnight-commander/osp/<mujfork>.git homework
362    nebo 
363
364        git remote add repo-or-cz ssh://<mujlogin>@repo.or.cz/srv/git/midnight-commander/osp/<mujfork>.git
365        git push repo-or-cz homework
366
367 [new]:http://repo.or.cz/regproj.cgi
368 [forkmc]:http://repo.or.cz/regproj.cgi?fork=midnight-commander/osp.git
369
370 Zadání
371 ======
372
373 Proveďte sloučení větve `ssh://git@rtime.felk.cvut.cz/osp/mc
374 only-directories` s aktuální vývojovou větví `origin/master`. Výsledek
375 uložte do [vámi vytvořeného repozitáře (forku) na repo.or.cz][forkmc]
376 do větve `homework`.
377
378 [w/osp.git]:http://repo.or.cz/w/midnight-commander/osp.git