1 [[!meta title="Verzovací systém Git"]]
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:
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.
17 [git]:http://git-scm.com/
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.
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
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*".
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ů:
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
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.
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`.
56 Dále se vám určitě bude hodit dokumentace:
61 [tools]:http://git-scm.com/tools
66 1. Základní nastavení. Aby vaše commity obsahovaly správné údaje o
67 vás, nastavte si jméno a email:
69 git config --global user.name "Your Name Comes Here"
70 git config --global user.email you@yourdomain.example.com
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.
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
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)
95 # modified: po/be-tarask.po
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ů.
103 [mc]:http://www.midnight-commander.org/
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.
110 Naštěstí existuje možnost jak gitu říct, že nás změny v některých
111 souborech nezajímají:
113 git update-index --assume-unchanged $(git ls-files po m4)
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.
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:
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
130 3. Vytvoříme větev *cviceni1* a přepneme se na ni:
132 git checkout -b cviceni1
134 Nyní už by měl `git status` ukazovat že pracujeme s větví
135 *cviceni1* a vidíme pouze námi modifikované soubory.
137 4. Nyní provedeme *commit*:
139 git commit -a -m 'Zmeny z prvniho cviceni'
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.
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`):
148 > git config --global core.editor pico
150 5. Nyní se přepneme zpět na větev *master*:
153 V adresáři se vám teď objeví verze bez vašich úprav z 1. cvičení.
155 6. Seznam větví v našem repozitáři zjistíme příkazem
158 Aktuální větev je označena hvězdičkou `*`.
160 Práce s více vzdálenými repozitáři
161 ----------------------------------
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.
167 1. Které vzdálené repozitáře máme nakonfigurované zjistíme pomocí
171 Vidíme, že máme nakonfigurovaný repozitář s názvem *origin* a jeho
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:
178 git remote add osp ssh://git@rtime.felk.cvut.cz/osp/mc
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.**
187 2. Nyní můžeme stáhnout obsah právě přidaného repozitáře:
190 V případě úspěchu bude výstup vypadat následovně:
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
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
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>/`.
207 4. Nyní nás zajímá co je ve větvích, které jsme právě stáhli:
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*.
214 Chcete-li vidět i změny v kódu použijte jeden z následujících příkazů:
216 git log -p osp/only-directories ^master
217 gitk osp/only-directories ^master
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:
223 Co přibylo ve větvi *master* od prvního cvičení zjistíme příkazem:
225 git log master..origin/master
227 Slučování větví (merge)
228 -----------------------
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:
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í.
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ě.
246 1. Zkuste provést sloučení vaší větve *master* s *origin/master*
248 git merge origin/master
249 Výsledkem bude *Fast forward* a uvidíme jaké soubory byly změněny:
251 git merge origin/master
252 Updating 0ebd30c..a99dc51
255 contrib/Makefile.am | 9 +-
257 Toto je velmi častá operace a proto lze oprace `fetch` a `merge`
258 nahradit jedním příkazem
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:
266 git checkout -b homework
267 3. Pokud provedete sloučení
269 git merge osp/only-directories
270 Výsledek bude vypadat pravděpodobně takto:
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.
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.
285 Konflikt lze řešit několika způsoby (viz také `git merge --help`):
287 * Vzdáme to a vrátíme se k verzi před slučováním
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)
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:
299 # Changes to be committed:
301 # modified: doc/man/mc.1.in
304 # (use "git add/rm <file>..." as appropriate to mark resolution)
306 # both modified: src/filemanager/find.c
308 Konflikt lze řešit následujícími způsoby:
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.
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`.
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
329 [kdiff3]:http://kdiff3.sourceforge.net/
331 Repozitář na repo.or.cz
332 ---------------------------
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.
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íčů.
342 1. Pokud žádný klíč nemáte, vytvořte si ho příkazem
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.
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
361 [reg]:http://repo.or.cz/reguser.cgi
362 [roc]:http://repo.or.cz/
364 2. Vytvoření repozitáře na repo.or.cz.
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*).
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í.
377 3. Do vašeho vzdáleného repozitáře můžete nahrát vaší větev některým z
380 git push ssh://<mujlogin>@repo.or.cz/srv/git/midnight-commander/osp/<mujfork>.git homework
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
386 [new]:http://repo.or.cz/regproj.cgi
387 [forkmc]:http://repo.or.cz/regproj.cgi?fork=midnight-commander/osp.git
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]
397 [w/osp.git]:http://repo.or.cz/w/midnight-commander/osp.git