Cvičení: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14.

Cílem tohoto cvičení je ukázat klíčovou vlastnost Gitu: větve. Je to mocný nástroj pro každodenní kódování softwaru vyvíjeného v týmu. Ale hodí se i pro jednočlenné týmy.

Dnešní software je málokdy tvořen jedinou osobou: je to týmová činnost a členové týmu potřebují spolupracovat na vývoji. Na tomto cvičení si ukážeme, co Git nabízí v tomto směru pro týmy v podstatě libovolné velikosti. Také se podíváme, jak je GitLab propojený s Gitem a jaké nástroje nabízí pro manažerskou část práce.

Obvykle týmová práce na sdíleném kódu znamená, že potřebujete, aby:

  • práce každého člena týmu byla oddělená,
  • ale bylo možné jednoduše výsledky jednotlivých členů kombinovat (alespoň v okamžiku, kdy jsou části skutečně nezávislé)

Tyto vlastnosti Git nabízí pomocí větví (branches). Ve skutečnosti je koncept větví mnohem silnější a hodí se i pro projekty s jedním vývojářem. V tomto cvičení se podíváme, jak se používají.

Než začneme …

Mnoho věcí, které zde zmíníme, už znáte z předchozích cvičení. To je v pořádku a je to naschvál: teď se je pokusíme představit najednou jako ucelený obrázek toho, co Git umí a jak se s ním pravděpodobně setkáte ve svých budoucích zaměstnáních.

Pokud už větve z Gitu znáte, asi si všimnete, že spousty věcí zjednodušujeme. Formálně jsou větve ukazatele na uzly acyklického grafu comittů atd., ale to skutečně není podstatné pro pochopení tohoto textu (a do detailů se tím zabývá NSWI154).

Až budete procházet příklady, vždy je provádějte v daném pořadí neboť jinak přestanou věci dávat smysl a neuvidíte to, co bychom chtěli, abyste viděli.

Průběžný příklad

Vytvořte fork projektu teaching/nswi177/2023/common/group-sum.

Fork je kompletní kopie repozitáře původního projektu. Při forkování se stanete vlastníkem projektu a můžete cokoliv měnit, aniž by se to dotklo původního projektu.

Projekt student-LOGIN, kam odesíláte hodnocené úlohy, neforkujte.

Doporučujeme, abyste změnili viditelnost vašeho forku na privátní.

V průběhu celého cvičení budeme pracovat s příkladem v tomto repozitáři. Nezapomeňte naklonovat svůj fork (nikoli původní repozitář). Budete v něm provádět poměrně hodně změn.

Tento příklad se pokusí napodobit práci v týmu – kdykoli budeme mluvit o nějaké funkci (nebo chybě), představte si, že pracujete ve velkém týmu a funkce/chyby nejsou jednořádkové opravy, ale několikadenní práce jednotlivých členů týmu.

Podívejte se na implementaci uvnitř souboru group_sum.py. Později uvidíme, že soubor group_sum.py by měl být v adresáři src a projekt by měl být správně nakonfigurován pro Pythoní program, ale zatím zůstaneme u jednodušší varianty.

Implementace je mírně rozšířenou verzí našeho předchozího skriptu, který uměl sčítat celá čísla na základě obsahu řádku.

Především používá modul argparse pro lepší parsování parametrů příkazového řádku. To nám umožnilo rozšířit funkci o použití vlastních oddělovačů a dalších drobnosti.

Vzhledem k tomu, že celý nástroj je poměrně malý, je výpočetní jádro programu zakrslík v porovnání s konfigurací parseru voleb na příkazové řádce. Tohoto kódu se však nebojte. Parser parametrů v Pythonu je poměrně užitečný modul, který s minimálním úsilím udělá programy v Pythonu mnohem čitelnějšími (především z hlediska jejich spuštění).

Issues v GitLabu

Všimněte si, že skript se ukončí s výjimkou, pokud vstupní soubor neexistuje (místo vypsání nějaké hezké chybové zprávy).

To rozhodně není uživatelsky přívětivé. Pojďme to napravit.

Lidé však (nemá smysl to zastírat) málokdy mají čas řešit problém okamžitě. Proto je docela užitečné poznamenat si všechny nevyřešené problémy v projektu, aby se na ně nezapomnělo.

Abychom si udrželi přehled o nevyřešených problém v našich projektech, je dobré je zaznamenávat. V postranní liště každého Gitlabového projektu je odkaz na Issues: pokud na něj kliknete, uvidíte, že The Issue Tracker is the place to add things that need to be improved or solved in a project. To je to, co potřebujeme ;-).

Vytvořte nový Issue popisující problém.

Každá issue má nadpis – něco jako předmět v e-mailu –, který by měl shrnout, co je špatně nebo co se musí udělat. Také musíte dodat popis – v některých případech si můžete myslet, že nadpis Chyba když chybí soubor úplně stačí. Nestačí! Vždy přidejte příklad a postup, jak problém zreprodukovat.

Tohle platí i v případě, že hlásíte problémy se zadáním apod. nám :-).

Vraťme se k našemu příkladu. Chyby se často houfují. Problémů s kódem je více. Co se stane při následujícím vstupu:

alpha   45
charlie 32
alpha   HELLO

Jejda, další výjimka.

Vytvořte proto ve svém projektu další issue. Opět: použijte popisný nadpis, uveďte smysluplný popis. Opravdu. Použijte to jako cvičení pro hodnocený úkol.

Zobrazte si seznam svých issue. Každý issue by měl mít vedle sebe číslo, na které se můžeme později odkázat.

Něco navíc

Mnohé projekty mají dokonce šablony, aby uživatele navedly, které všechny informace jsou potřeba. My je používat nebudeme, ale pro velké projekty jsou užitečné. GitLab je umí také.

Všimněte si odkazu Markdown, které obsahuje nápovědu pro formátování pomocí Markdownu: znalost ` a ``` je prostě nutnost pro každého programátora :-).

Pro další čtení si do vyhledávače zadejte jak napsat dobrý bugreport (how to write a good bug report) a alespoň jeden článek si přečtěte. Stojí to za to. Doopravdy.

Větve v Gitu (pojďme opravit chyby)

Git má koncept větví, které reprezentují řadu commitů. Zatím byla tato řada lineární – každý commit (kromě úplně prvního a posledního, pochopitelně) měl jeden předchozí a jeden následující commit. Jejich pořadí bylo určeno tím, jak commity vznikaly v čase.

Větve v Gitu vám umožní tuto linearitu rozbít – commit může mít více následníků: práce se rozdělí a každá větev má svou vlastní cestu: do programu přidáváme nové funkce. Commit také může mít dva rodiče: provádíme tzv. merge dvou větví (slití, spojení).

Typický příklad je práce v týmu. Alice a Bob pracují oba na stejném projektu a sdílí jeden repozitář. Alice pracuje na funkci A, Bob na funkci B. Oba začali na stejném commitu (řekněme hned po vydání poslední verze), ale jejich práce se rozchází: každý pracuje na svém úkolu a přidává své funkce. Jakmile jsou se svojí prací spokojeni, provedou tzv. merge – tj. spojí svoje verze dohromady a získají tak verzi, které obsahuje funkce A i B.

Následující obrázek ukazuje příklad jednoduchých větví (v MSIMu), ale i komplikovanější situaci ve středně velkém open-source projektu (HelenOS).

Ve skutečnosti jste už s Gitovými větvemi pracovali, aniž byste o tom věděli. Když uděláte clone repozitáře, vytvoříte přesnou kopii stavu na serveru. Když jste přidali nové commity lokálně, objevili se vlastně na větvi, která je odlišná od větve na serveru. Pak push tyto větve zase spojil. A pull pracuje v opačném směru: přitáhne nové commity ze serveru a připojí je k vaší místní větvi. Ale zatím tyto větve nikdy nedivergovaly – jedna větev byla vždy prefixem té druhé. Takže spojování nevyžadovalo žádnou extra činnost a bylo pro vás transparentní.

Pokud jste někdy zapomněli git pull (nebo git push), je pravděpodobné, že Git/GitLab již vytvořil nějaké větve a merge za vás. Nyní to uděláme záměrně :-).

Commit zprávy (už zase)

Přečtěte si článek How to Write a Git Commit Message od Chrise Beams, pokud jste ho ještě nečetli. A pokud jste ho už četli, klidně si ho přečtěte znovu.

Fázi, kdy Git byl nepřítel, máme teď už za sebou a můžeme tedy pracovat na zlepšení našich návyků.

Od teď začněte psát rozumné commit zprávy. Jsou součástí vývoje a jsou téměř tak důležité jako kód, na který odkazují. Hlavně ve velkých týmech.

Začneme odečítat body za zprávy jako “Nahran novy soubor” nebo podobné nesmysly, které nic neříkají. Pojďme začít praktikovat dobré návyky co nejdřív.

Pokud máte problém pojmenovat commit, asi se v něm děje moc věcí najednou. Je to stejné jako s funkcemi (ano, už zase!): pokud je nedokážete popsat jednou větou, něco je špatně.

Feature branches (větve pro nové funkce)

Aby se kód udržel v rozumném stavu, mnoho projektů má jednoduché pravidlo: commitujte co nejčastěji, ale kód v hlavní větvi (tohle jméno můžete změnit, ale obvykle se potkáte s názvy jako master nebo main) musí být vždy v pořádku (ve smyslu, že všechny testy prochází).

Pokud pracujete na nové funkci, vytvořte si novou větev. Pracujte v této větvi a spojte ji s masterem jen, když bude funkce hotová. Pro některé projekty je povinnou částí před vlastním mergem recenze kódu (code review) nebo dokonce zátěžové testy (load testing).

Má to skvělou výhodu: když někdo začne novou větev, můžete si být celkem jistý, že začíná s funkčním kódem.

Podle projektu se můžete setkat i s větvemi jako development, kde mergování nevyžaduje code review, nebo s větví production, jež označuje kód, který bude distribuován (často automaticky) zákazníkům.

Doporučujeme přečíst A Simple Git Branch Workflow (dev.to), pokud chcete vědět více detailů o tom, jak tento přístup funguje.

Budeme se tím ale řídit i v tomto cvičení. Pro každou novou funkci (nebo opravu chyby) budeme mít novou větev a do hlavní větve ji zamergujeme až po otestování.

Vytváření nových větví v Gitu

Začneme opravou problému s vyhozenou výjimkou. Vytvoříme pro ni větev a přepneme se na ni.

Větší týmy mají často zavedené konvence pro pojmenování větví. Zjednodušme si to a používejme issue/N-jmeno pro větve, které mají opravit problém v issue s číslem N.

Pro vytvoření větve použijeme příkaz git branch.

git branch issue/1-hide-traceback

K vytvoření jedinečného názvu větve používáme id issue (abychom zabránili případným kolizím s ostatními vývojáři), ale také připojujeme krátké shrnutí, abychom my sami věděli, na čem pracujeme.

Tento příkaz neudělá žádnou viditelnou změnu. Pouze označí aktuální (poslední) commit jako výchozí bod pro novou větev.

Abychom skutečně přepnuli na novou větev, musíme provést příkaz

git checkout issue/1-hide-traceback

V tuto chvíli nemá přepnutí žádný viditelný efekt – obě větve master a issue/1-hide-traceback odkazují na stejný stav souborů.

Místo toho si také můžete zapamatovat jediný příkaz, který vytvoří větev a přepne na ni.

git checkout -b issue/1-hide-traceback

Občas vyplavou na povrch detaily z implementace Gitu. V případě větví nejde obecně vytvořit větve issue a issue/1 v ten samý okamžik.

Obvykle jsou konvence pro pojmenování větví apod. zdokumentovány v nějaké vývojářské dokumentaci.

Nyní opravte tento problém. Solution.

Commitněte změnu.

Propojení commitů s issue a git commit --amend

Git je při práci s commity poměrně pružný. Pokud zjistíte, že chcete změnit poslední commit, můžete soubory přidat pomocí git add a poté zavolat git commit --amend. Otevře se vám textový editor s již vyplněnou commit message, kterou můžete změnit.

Nikdy nevolejte --amend na commit, který jste již odeslali na server. Tento commit by již mohl být naklonován někým jiným a věci by se mohly rozbít (ve skutečnosti by bylo možné věci opravit, protože commit by se v podstatě choval jako větev, ale není to něco, co byste asi chtěli dělat).

Pokud jste také změnili již odeslaný (pushed) commit, museli byste provést forced push, abyste commit na serveru přepsali. U mnoha projektů to na větvi master není vůbec možné. Proto se raději vyhněte změnám již nahraných commitů.

Technicky vzato, git commit --amend vytvoří nový commit místo původního. To má několik drobných následků, z nichž nejdůležitější je, že historie před a po změně jsou z pohledu Gitu odlišné. To znamená, že pokud jste již odeslali původní commit, neměli byste jej měnit, protože bude obtížné odeslat ten nový (protože nerozšiřuje historii na serveru).

Použijte tuto funkci a přidejte k poslednímu commitu fixes #1. Jakmile tuto verzi odešlete na GitLab, stanou se dvě věci. Zaprvé, issue bude obsahovat odkaz na commit a na #1 ve zprávě o commitu bude možné kliknout a otevřít zmíněnou issue.

Protože náš commit tento problém opravil, přidali jsme speciální klíčové slovo fixes do zprávy, aby se issue automaticky uzavřela (existuje spousta vzorů pro uzavírání issues).

Issue bude uzavřena, jakmile bude commit začleněn do větve master. To dává velký smysl: problém může být opraven, ale dokud není kód ve větvi master, program stále obsahuje chybu (vzpomeňte si, že ve větvi master je obvykle kód, který je dodáván zákazníkovi).

Všimněte si, že to slouží ke dvěma účelům – šetříme čas (nemusíme vůbec přepínat do prohlížeče) a poskytujeme také cenný odkaz na to, který commit byl skutečně zodpovědný za opravu chyby. Všimněte si, že issue na GitLabu zatím není označena jako opravená (vyřešená), protože jsme do zatím na GitLab nic nenahráli.

V projektu byste teď neměly být mít žádné necommitnuté změny. Přepneme se zpět do větve master. Zkontrolujte, zda skript (po přepnutí) neobsahuje vaši opravu. Hint.

Zkontrolovali jste to? Dobře, můžeme pokračovat.

Všimněte si, že pokud máte skript otevřený v textovém editoru, měl by vás upozornit na změnu souboru na disku. Pokud tomu tak není, načtěte soubor (reload) znovu ručně.

Poznámka ke git add --patch (nebo git add -p)

Někdy se můžete setkat se situací, kdy v jednom souboru změníte dvě věci, které spolu nesouvisejí. Například, když opravíte funkční chybu, ale zároveň si všimnete překlepu v komentáři. V takové situaci byste změnu měli rozdělit do dvou commitů.

Pokud jsou změny ve dvou různých souborech, je to snadné: jednoduše zavoláte git add na první soubor, git commit a pak git add na druhý soubor.

Pokud jsou změny ve stejném souboru, je možné použít git add -p (pro --patch), kdy Git interaktivně zobrazí každý hunk – v podstatě jednu změnu – a zeptá se, zda má být git add-ován, nebo ne.

Pomocí git add -p můžete snadno rozdělit své commity a také si zkontrolovat, co jste udělali, aniž byste museli v editoru provádět nějaké šílené un-do/re-do.

Všimněte si však, že možnosti systému Git jsou omezené, pokud jde o posuzování toho, co je změna. Někdy, když jsou změny blízko sebe (tj. na stejném řádku), Git je nebude považovat za dva různé hunky. Budete nuceni ručně upravit hunk (e) podle pokynů poskytovaných systémem Git.

Nahrání (push) nové větve

Přepněte zpět na větev issue/1-hide-traceback a odešlete ji na GitLab. Pokud spustíte git push (jak jste byli zvyklí), bude si Git stěžovat, že aktuální větev nemá žádnou větev v upstreamu. Znamená to (víceméně), že tuto větev nahráváte poprvé a Git se chce ujistit, jak má větev na serveru pojmenovat.

Příjemné je, že Git vám nabídne příkaz, který můžete spustit, abyste zajistili nahrání (push) větve.

git push --set-upstream origin issue/1-hide-traceback

Odkaz, který vám GitLab poslal zpět, prozatím ignorujte.

Znovu otevřete projekt v prohlížeči. Zkontrolujte, zda vaše issue nyní obsahuje odkaz na commit, který ji zmiňuje; a na domovské stránce projektu můžete vybrat, která větev se má zobrazit.

Cvičení

Vyzkoušejte si to sami.

Cvičení I

Nyní vyřešíme druhý problém (když data obsahují neceločíselné hodnoty). Vytvořte novou větev, problém vyřešte a opravu commitněte.

Větev zatím nepushujte.

Několik otázek a věcí k zamyšlení:

  • Proč musíte nejprve přepnout na master? Jak by vypadalo větvení, kdybyste větvili z issue/1-hide-traceback? Proč je to špatně?
  • K vyřešení issue stačí, když prostě řádek přeskočíte (ale upozorněte na něj uživatele!)
  • Nezapomeňte do zprávy uvést closes #2 (nebo nějaký podobný text).
Solution.

Cvičení II: hot-fix

Předpokládejme, že jste si právě všimli překlepu v souboru README.md (najděte slovo valeus).

Tohle chceme opravit rovnou a uděláme to (ale jen pro tentokrát) přímo ve větvi master.

Často se tomu říká “hot-fix”: něco, co potřebujete opravit co nejdříve a kde porušení obvyklých zvyklostí týkajících se větvení, revize kódu, testování atd. je spíš na obtíž (i když to opravdu záleží na týmu a produktu, na kterém pracujete).

Přepněte se tedy do větve master (opravu problému č. 2 jste již commitnuli, že?), opravte překlepy a commitněte.

Nahrajte vaše změny z hlavní větve.

Graf commitů

V prohlížeči otevřete stránku Repository -> Graph (z projektu). Měly by vám graficky zobrazit vaše větve.

Vedle větve master byste měli vidět novou větev issue/1-hide-traceback, která vychází ze stejného commitu.

Grafické zobrazení je dobrým pomocníkem, pokud se ztratíte ve složitém modelu větvení a nejste si jisti, zda by některé změny měly být v konkrétní větvi viditelné, nebo ne.

Účelem není vytvářet složité grafy, i když někdy mohou být docela divoké.

Pro git log můžete také použít parametr --graph, abyste získali grafické zobrazení v terminálu.

Merge requests (žádosti o začlenění)

Žádosti o začlenění (merge requests) jsou pokročilou vychytávkou GitLabu pro větší týmy. Ve velkých týmech je code review vyžadován vždy před tím, než je nějaký kód odeslán do větve master.

Code review obvykle znamená, že zkušený vývojář si prohlédne váš kód, okomentuje ho a může po vás chtít další úpravy. Představte si pod tím přejmenování funkcí, použití jiných datových struktur nebo opravy dokumentace. Cokoliv od skutečných funkčních chyb po styl zdrojáku.

Merge requesty se hodí přesně na tohle. Než dojde k vlastnímu začlenění (tj. než vaše změny odejdou do hlavní vývojové větve), můžete otevřít tzv. žádost (merge request).

Merge request se hodně podobá issue: ve skutečnosti jsou oba formuláře velmi podobné. To je proto, že Issues popisují známý problém (nebo žádost o novou funkci), zatímco merge requesty popisují, jak byl problém vyřešen. Jak říká GitLab, merge requests are a place to propose changes you’ve made to a project and discuss those changes with others.

Je dobrým zvykem zmínit, které issue daný merge request uzavře (nebo se kterými souvisí).

Opět, pro formátování lze použít Markdown.

Ve velkých týmech budou ostatní vývojáři komentovat váš kód právě během merge requestu a také budou spuštěny automatické testy nad vaším kódem (což se naučíte v některým z dalších cvičení).

Merge requesty mohou dávat smysl i pro osobní projekty: umožní vývojáři rychle zkontrolovat, že je vše v pořádku (třeba, že byly commitnuty všechny soubory apod.).

Pokračování průběžného příkladu

Přepněte se na větev druhé issue a odešlete ji také na GitLab. Budete muset znovu použít přepínač --set-upstream.

Ujistěte se, že v commit zprávě této větve je uvedeno closes #2. Pokud ne, pomůže vám git commit --amend.

Všimněte si, že po push by se měl zobrazit text informující o možnosti otevřít merge request s odkazem.

Otevřete si tento odkaz v prohlížeči.

Všimněte si, že tzv. merge request (česky asi Žádost o sloučení) ještě nebyl odeslán. Název a popis jsou předvyplněny a vypadají podobně jako formulář, který jsme viděli u issues.

Vytvořte nyní merge request.

Překontrolujte cíl merge requestu. Musí to být větev ve vašem repozitáři, nikoliv repozitář, ze kterého jste se forknuli.

Udělejme nyní merge daného merge requestu (je tam na to velké tlačítko).

Zachovejte výchozí nastavení a proveďte merge (tj. ne rebase ani squash).

Po uzavření merge requestu bychom měli vidět nový commit ve větvi master.

Můžete se také znovu podívat na graf commitů a zjistit, jak commity vypadají po merge.

Zkontrolujte nyní issues u svého projektu a všimněte si, že druhá issue by měla být již uzavřena. Můžete také zkontrolovat podrobnosti u issue a všimnout si, jak je commit pěkně propojen s issue.

Zpět v místním klonu repozitáře: nezapomeňte si stáhnout (pull) nejnovější změny z masteru (GitLab vytvořil commit pouze na serveru). Hint.

Mergování z příkazové řádky

Nyní zmergujeme první issue přímo z příkazového řádku, aniž bychom museli otevírat merge request. Protože merge request je vždy vázán na nějakou větev, můžete také vždy mergovat i na příkazovém řádku.

Všimněte si opět dvojího přístupu, který je v Linuxu všudypřítomný: můžete používat pěkné grafické uživatelské rozhraní, ale také plně automatizovatelné rozhraní příkazového řádku.

Nejprve se musíme ujistit, že se nacházíme ve větvi, do které chceme mergovat. Obvykle je to hlavní větev.

Samotný merge je vskutku jednoduchý.

# musíme být na hlavní větvi (git checkout master)
git merge issue/1-hide-traceback

A je hotovo. Znovu odešlete větev master a zkontrolujte graf commitů. Všimněte si, že merge je vlastně jen commit, který má jako rodiče dva různé commity (předchozí verze). Většina voleb jak tak v obou dílčích příkazech (tj. commit a merge) podobná.

Zobrazení seznamu větví a mazání větví

Abychom viděli seznam větví, stačí spustit následující příkaz.

git branch

Někdy je užitečné zobrazit si všechny větve včetně těch na vzdáleném serveru (vizte též dále) přidáním -a.

Když je větev zmergovaná, můžeme ji odstranit, abychom udržovali seznam kratší.

git branch -d issue/1-hide-traceback
Odstraněním větve se neodstraní její již zmergované commity.

Odstraněním větve se vlastně je odstraní štítek, který uváděl, že konkrétní commit patří do určité větve. Proto Git nepožaduje potvrzení při použití -d, protože neodstraňujete žádný skutečný kód ani žádné commity. Pokud však větev ještě není zmergována, Git ji odmítne odstranit (s chybovým hlášením, že větev není not fully merged, a s nápovědou, abyste použili velké -D, pokud ji skutečně chcete smazat).

Začleňování změn z upstreamu (udržování vaší větve aktuální)

Feature branch vám umožní pracovat na nové funkci bez zásahů do hlavní větve. Ale práce v hlavní větvi dál pokračuje a vy potřebujete udržet vaší novou větev aktuální.

To je velmi obvyklá úloha. Nechcete přijít o důležité aktualizace, které se dějí v masteru. Často může přeskočení takových aktualizací výrazně zkomplikovat mergování zpátky. Podle velikosti a aktivity projektu může dávat smysl mergovat změny z hlavní větve každý týden nebo dokonce každý den.

Udržování vaší větve aktuální se obvykle označuje jako mergování z upsreamu, protože se tím odkazuje na rodičovský projekt (větev).

Uvidíte, že se to vlastně vůbec neliší od jiného mergování. Je to jen o určení správného směru (tj. změny do hlavní větve nebo změny v hlavní větve do vaší feature větve).

Git vám s tímto procesem vždy pomůže a často to bude zcela automatizovaná záležitost.

Budeme simulovat, že práce v tzv. upstream projektu (tj. v repozitáři, ze kterého jste forknuli) pokračuje, a vy chcete, aby váš repozitář (váš fork) byl aktuální.

V Gitu je toto vše možné a (možná překvapivě) je velmi malý rozdíl mezi tím, zda mergujete svou vlastní (lokální) větev nebo změny někoho jiného, kdo pracuje v úplně jiném forku.

Pro mergování změn z jiného repozitáře, než je ten výchozí (např. jiný projekt na GitLabu), musíme nastavit tzv. remotes.

remote je pojmem, který říká, že váš místní klon ví i o jiných forkách a může vám říct, zda se liší. Opět se jedná o docela zjednodušený pohled na věc, ale je dostačující pro první seznámení s Git remotes. Obvykle očekáváte, že remotes mají společného předka, tj. počáteční commit je ve všech remotech stejný.

Chcete-li zobrazit své remotes, spusťte (v místním klonu svého forku repozitáře s příkladem) následující příkazy

git remote

Pravděpodobně by vypsal pouze origin. To je výchozí remote: když provedete git pull nebo git push, použije se origin. Tudíž jste již používali remotes, i když jste o tom nevěděli ;-).

Stejně jako prakticky u všeho v systému Git lze konfigurovat i výchozí remote. Podrobnosti najdete v příručce pod heslem tracking branch.

Spuštění s -v (pro verbose) vypíše konkrétní adresy URL, na kterých se nachází vzdálený server. Ve skutečnosti nyní pravděpodobně uvidíte dva remotes: jeden pro push, druhý pro fetch (pull). Dokonce můžete Git nakonfigurovat tak, aby stahoval z jiného projektu, než do kterého nahráváte. Pro nás to však v tuto chvíli není příliš užitečné.

Chcete-li zjistit ještě více podrobností, zkuste git remote show origin.

Přidání dalšího remote

Přidejme do našeho repozitáře nový remote. Ten bude odkazovat na jiný projekt, abychom mohli porovnat naše změny se změnami v něm (opět zjednodušený pohled na věc).

git remote add upstream gitolite3@linux.ms.mff.cuni.cz:lab07-group-sum-ng.git

Ale počkat. Tohle není repozitář GitLabu! To je ale v pořádku. Přidáme remote, který žije někde jinde. Budou sdílet stejnou historii Gitu a vše bude fungovat.

Výše uvedený příkaz přidal remote s názvem upstream, který ukazuje na zadanou adresu. Všimněte si, že příkaz nic nevypsal.

Znovu spusťte git remote. Co se změnilo?

Všimněte si, že náš repozitář obsahuje příponu -ng pro novou generaci, tj. (tak trochu) simulujeme, že původní projekt, ze kterého jste forknuli, je zastaralý, ale někdo jiný ho převzal a pokračuje ve vývoji jinde.

Práce s remotes

Přidáním remote ještě nedošlo k výměně dat. Gitu musíte říct, aby vše provedl, nic se neděje automaticky. Všimněte si, že pokud se někdy setkáte s jiným verzovacím systémem, bude vám používání systému Git připadat velmi nízkoúrovňové a možná i zdlouhavé. Je to daň za jeho efektivitu a flexibilitu.

Pojďme stáhnout změny z našeho nového remote.

git fetch upstream

Mělo by se zobrazit typické shrnutí jako při klonování/stahování změn v systému Git. Tentokrát odkazuje na data z upstreamového repozitáře.

Ve vaší pracovní kopii (tj. v adresáři s vaším projektem) se však nic nezměnilo. To je v pořádku, žádali jsme pouze o stažení změn, nikoli o jejich uplatnění.

Spusťte však git branch a git branch --all, abyste zjistili, ke kterým větvím máte nyní přístup.

Všimněte si, že přidáním remote nezačne žádná komunikace se vzdáleným serverem, Git pouze zapíše konfiguraci. Až git fetch pak skutečně načetl změny ze vzdáleného serveru. Bez git fetch bychom neměli žádné informace o skutečném kódu dostupném na daném vzdáleném serveru.

Porovnávání větví (a jejich mergování)

Nyní prozkoumáme, jak se liší nově přidaný remote.

Začněme zobrazením commitů na remote:

git log remotes/upstream/tests

Jak vidíte, git log může zobrazit commity pouze v určité větvi (ano, remotes/... je ve skutečnosti název větve: koneckonců jste ho viděli v git branch --all). A funguje také na souborech (např. git log -- README.md). Je to vskutku mocný příkaz.

Ve skutečnosti by fungoval i git log README.md: dokud je Git schopen rozlišovat mezi názvy souborů a větví. Protože se názvy souborů obvykle liší od názvů větví, bude to ve většině případů fungovat i bez explicitního oddělovače --.

Chtěli jsme se ale podívat, jak se kód liší. To je vlastně ještě důležitější: chcete vidět, jaké změny v kódu byly provedeny a zda bude vůbec možné je mergovat.

git diff remotes/upstream/tests

Měla by se zobrazit záplata, která zobrazí, že nově přidaný remote se liší pouze v jednom: byly přidány automatizované testy.

Vypadají docela dobře – takže je chceme mít i v našem projektu.

Mergněme tedy vzdálenou větev:

git merge remotes/upstream/tests

Protože nedochází ke konfliktům (tj. obě větve – master a remotes/upstream/tests – změnily různé soubory), mergování by mělo být automaticky dokončeno.

Zkontrolujte adresář projektu: je v něm soubor tests.bats?

Všimněte si, že (commit) zprávu o mergování můžete změnit pomocí --amend.

Řešení konfliktů

Stejným způsobem se připravte na mergování (tj. ještě nespouštějte git merge) s upstream/hotfix.

Jak jste si pravděpodobně všimli, druhá větev obsahuje opravu překlepu. Ale to už jste opravili (pokud ne, opravte to před mergováním!).

Merge povede k takzvanému konfliktu: dva vývojáři upravili stejný souboru a provedli své individuální úpravy. To musíme vyřešit ručně.

To je zcela běžné a není třeba se toho bát. Git vám dokáže hodně pomoci – pokud dojde ke změnám v různých částech souboru, dokáže je bez problémů sloučit. Když ale obě větve změní stejné řádky, je na vás, abyste to vyřešili. To je zcela přirozené a byli byste překvapeni, kolikrát je Git schopen sloučit věci automaticky.

Dost bylo teorie, nyní spusťte příkaz merge:

git merge remotes/upstream/hotfix

Toto mergování skončí chybou a Git vás bude informovat o konfliktu.

Zkontrolujte výstup příkazu merge. Všimněte si, jak se vám Git snaží poradit, co lze udělat…

Spusťte také git status a prozkoumejte jeho výstup.

Nyní přichází složitější část celého postupu: je třeba vyřešit konflikt.

V našem případě je to poměrně jednoduché. U složitého softwaru může být řešení konfliktu velmi složitou operací, protože je třeba nejprve zkontrolovat několik míst a změny mentálně zkombinovat. Mít automatizované testy může pomoci, ale analytické myšlení je určitě výhodou.

Jakmile konflikt vyřešíte, musíte zavolat git add (jako při normálním commitu - mergování je koneckonců stále jen commit), abyste konflikt označili jako vyřešený (resolved).

git add README.md

Chcete-li mergování dokončit, spusťte příkaz git commit jako při běžném commitu.

Nezapomeňte změny nahrát na server.

Jak by nyní vypadalo grafické znázornění commitů v GitLabu?

Před otevřením stránky Graphs v GitLabu si ji zkuste načrtnout na papír.

I když je systém Git často schopen provést mergování sám, tak za to, že výsledek dává smysl, vždy odpovídá uživatel.

Je snadné si představit situaci, kdy dva vývojáři změní různé části programu (aniž by cokoli rozbili), ale kombinovaná změna nebude fungovat.

Dobré testy v těchto situacích určitě pomůžou.

Zcela nesouvisející témata :-)

Následující příklady berte jako exkurzi, že skripty nejsou jen o nudných textových souborech, ale že jejich možnosti jsou nekonečné.

VLC

VLC je populární multimediální přehrávač dostupný pro mnoho platforem. Nebudeme vás nudit grafickým uživatelským rozhraním, ale místo toho si ukážeme několik zajímavých možností příkazového řádku. Mohou se hodit pro specifické případy, jako je třeba kioskový režim (např. na výstavě).

Následující přepínače příkazového řádku přehrají zadané video, ale rozdělí ho do čtyř různých oken. Možná užitečné, pokud máte více monitorů s velmi tenkými okraji.

vlc --video-splitter wall --wall-cols 2 --wall-rows 2 --wall-element-aspect 4:3 video.mpg

VLC lze použít také ke streamování videa po síti. Za předpokladu, že máte v aktuálním adresáři soubory 1.mp4, 2.mp4 a 3.mp4, připravte následující soubor (pojmenujte jej vod.vlc):

new channel1 vod
setup channel1 input 1.mp4
setup channel1 enabled

new channel2 vod
setup channel2 input 2.mp4
setup channel2 enabled

new channel3 vod
setup channel3 input 3.mp4
setup channel3 enabled

V každém bloku je nastavena jedna konfigurace videa on-demand (tj. klient požádá o konkrétní video a server VLC ho poskytne).

Dále spustíme VLC v příkazovém řádku v režimu serveru, aby naslouchal připojení RTSP na portu 5554.

cvlc --vlm-conf vod.vlc  --rtsp-tcp --rtsp-port 5554

Vybrané video nyní můžeme přehrát následujícím příkazem.

vlc rtsp://127.0.0.1:5554/channel3

Výše uvedené předpokládá, že video přehráváte na stejném počítači. První příkaz lze rozšířit pomocí --rtsp-host, aby mohl poslouchat na jiném rozhraní a streamovat video po síti (a druhý příkaz spustit na jiném počítači).

youtube-dl

youtube-dl je nástroj pro stahování videí, který dokáže vyhledávat a stahovat videa z různých webových stránek. Jako nástroj příkazového řádku jej lze použít ve skriptech nebo ke stahování videí pro pozdější prohlížení (tj. stahování s rychlým připojením a pozdější přehrání).

Jednou z podporovaných stránek je OpenClassroom Standfordské univerzity. Zde jsou některá videa ve formátu FLV, ale youtube-dl dokáže video převést jednoduchým přidáním --recode-video mp4:

youtube-dl --recode-video mp4 "http://openclassroom.stanford.edu/MainFolder/VideoPage.php?course=IntroToAlgorithms&video=CS161L1P1&speed=100"

Aktualizace: vizte, prosím, issue #405.

youtube-dl podporuje mnoho dalších webů: stahování z mnoha z nich je výslovně zakázáno jejich podmínkami používání (pro mnohé je použití podobných nástrojů na hranici tzv. fair use policy).

ffmpeg

Dalším zajímavým nástrojem je ffmpeg, který je obecným konvertorem videoformátů. Kromě triviální konverze mezi různými formáty umí i spoustu dalších efektů.

Jako obvykle: výhoda rozhraní příkazového řádku je podstatná pro hromadné konverze, kde není nutná interakce uživatele.

Následující příkaz například převede zvuk na AAC, přičemž video zůstane zachováno bez jakýchkoli úprav. Používali jsme ho pro videa ze cvičení, aby fungovaly i ve Firefoxu.

ffmpeg -i "input_file.mp4" -c:v copy -c:a aac "output_file.mp4"

Dokáže toho však mnohem víc. Nejprve si stáhneme následující klipy a uložme je jako 1.mp4, 2.mp4 a 3.mp4.

https://www.videvo.net/video/airport-departure-board/6001/
https://www.videvo.net/video/swiss-aircraft-taking-off/4061/
https://www.videvo.net/video/airplane-window-view/5176/

Následující příkaz pak vloží tyto klipy vedle sebe do jednoho videa. Používá více -i k načtení více vstupních souborů a pak pomocí složitého filtru poskládá videa vedle sebe. Příkaz -t se používá k omezení převodu pouze na prvních 10 sekund.

Proměnné se používají pouze k uložení informací o dané pipeline. V příkazu f_rescale změníme měřítko všech videí. Proměnná [0] označuje první vstupní soubor (první v poli vstupních souborů), [v0] je uživatelský identifikátor pro pojmenování výsledku filtru. V f_stack použijeme filtr xstack s danou specifikací rozvržení – pokud vás zajímají podrobnosti o polohování pomocí 0_0|w0_0|w0_h1, podívejte se na tuto stránku.

f_rescale='[0]scale=-1:360[v0];[1]scale=-1:180[v1];[2]scale=-1:180[v2]'
f_stack='[v0][v1][v2]xstack=inputs=3:layout=0_0|w0_0|w0_h1[v]'

ffmpeg -i 1.mp4 -i 2.mp4 -i 3.mp4 -filter_complex "$f_rescale;$f_stack" -map "[v]" -t 10 -vsync 2 output.mp4

Pokud potřebujete výše uvedené provést pro jedno video, bude jistě jednodušší použít interaktivní editor. Pokud však potřebujete zpracovat více videí se stejnou nebo podobnou konfigurací, může být lepší volbou ffmpeg.

Úlohy před cvičením (deadline: začátek vašeho cvičení, týden 27. března - 31. března)

Následující úlohy musí být vyřešeny a odevzdány před příchodem na vaše cvičení. Pokud máte cvičení ve středu v 10.40, soubory musí být nahrány do vašeho projektu (repozitáře) na GitLabu nejpozději ve středu v 10.39.

Pro virtuální cvičení je deadline úterý 9:00 (každý týden, vždy ráno, bez ohledu na možné státní svátky apod.).

Všechny úlohy (pokud není explicitně uvedeno jinak) musí být odevzdány přes váš repozitář na úkoly. Pro většinu úloh existují automatické testy, které vám mohou pomoci zkontrolovat úplnost vašeho řešení (zde je popsáno jak číst jejich výsledky).

Některé úlohy jsou nahrány do jiné větve než je master.

Testy se to snaží kontrolovat, ale ne vždy je to možné a proto některé na větvi master selžou i v případě, že je všechno v pořádku.

07/avg.py (60 bodů, skupina git)

Zkopírujte následující kód do souboru 07/avg.py ve vašem projektu do větve master.

#!/usr/bin/env python3

import sys

def main():
    values = list(map(int, sys.argv[1:]))
    avg = sum(values) / len(values)
    print(avg)

if __name__ == "__main__":
    main()

Skript vypočítá průměr čísel zadaných na příkazovém řádku, např. při spuštění ./avg.py 1 2 6 vypíše 3,0.

Vytvořte větev v Gitu s názvem lab-07/avg, kde opravíte kód tak, aby fungoval i v případě, že nejsou zadány žádné argumenty (program v takovém případě vypíše 0).

Váš repozitář musí obsahovat alespoň dva commity tohoto souboru: jeden importující aktuální řešení a jeden (ve větvi lab-07/avg) opravující problém.

Automatizované testy také kontrolují, zda jsou spuštěny na správné větvi.

Nezapomeňte nahrát lab-07/avg do Gitlabu a zkontrolovat výstup testu.

07/issue.txt (40 bodů, skupina git)

Vytvořte Issue s názvem 07/issue ve svém projektu, která představuje tento úkol.

Uzavřete tuto issue z commitu, který přidává text ‘SOLVED’ do souboru 07/issue.txt.

Oprava může být provedena v jakékoli větvi, ale nakonec musí být zamergována do větve master, aby byla issue skutečně opravena.

Automatické testy nemají přístup k vašemu seznamu Issues.

Ujistěte se, že jste odkázali na správnou issue, my to pak zkontrolujeme při spuštění testů mimo Gitlab.

Úlohy po cvičení (deadline: 16. dubna)

Očekáváme, že následující úlohy vyřešíte po cvičení, tj. poté, co budete mít zpětnou vazbu k vašim řešením úloh před cvičením.

Všechny úlohy (pokud není explicitně uvedeno jinak) musí být odevzdány přes váš repozitář na úkoly. Pro většinu úloh existují automatické testy, které vám mohou pomoci zkontrolovat úplnost vašeho řešení (zde je popsáno jak číst jejich výsledky).

07/UPSTREAM.md (30 bodů, skupina git)

Možná jste si všimli, že repozitář s úlohami byl ve skutečnosti forkem jiného. (To bylo z technických důvodů, protože to zjednodušuje vytváření projektů.)

Ale také to znamená, že můžete mergovat změny od nás. Rodičovský projekt teď obsahuje soubor 07/UPSTREAM.md s nezajímavým obsahem.

Přimergujte tento soubor do vašeho repozitáře. Nedělejte rebase ani squash, udělejte, prosím, normální merge.

Soubor nekopírujte, využijte Git pro začlenění (merge) změny.

Testy mohou po určité době začít selhávat (GitLab klonuje pouze nedávnou historii).

To je v pořádku: důležité je, že test v určitém okamžiku proběhl v pořádku.

07/group_sum.py (70 bodů, skupina git)

V tomto úkolu znovu projdete přípravu na cvičení, jen tentokrát ve vašem repozitáři s úlohami.

Pro tuto úlohu nejsou žádné automatické testy.
Prosím, postupujte přesně podle pokynů, aby se ve vašem projektu objevily všechny části řešení.

Nad souborem group_sum.py z repozitáře s příklady proveďte následující.

Import (1)

Naimportujte skript a vytvořte commit s původní verzí ve větvi master.

Tato verze bude obsahovat všechny problémy, které jsme zmínili během cvičení.

Issue (2)

Vytvořte dva issue pro dvě velké chyby v programu.

Jednu issue pro nevypisování pěkné chybové zprávy, když soubor nelze otevřít.

A druhou pro přeskočení řádku s neplatnými údaji.

Větve (3)

Počínaje commitem z bodu (1) vytvořte v repozitáři s úlohami dvě větve navázané na vaše issues.

Pojmenujte větve jako issue/N-popis, kde N musí odkazovat na dříve vytvořené issues (2).

Oprava problému (4)

Opravte každý problém z bodu (2) ve větvích vytvořených v bodě (3).

Issue uzavřete prostřednictvím commit zpráv.

Obě větve nahrajte na GitLab, abychom je našli i tam.

Mergování oprav

Větve vytvořené v bodech (3) a (4) zamergujte do větve master (tj. vytvoříte dva merge commity) a odešlete je do Gitlabu.

AKTUALIZACE: mergování první větve může vyústit v tzv. fast-forward merge, tj. není vytvořen skutečný commit (se dvěma rodiči). Pro tuto úlohu je to v pořádku.

Pokud chcete, můžete také vytvořit merge request. V takovém případě neodstraňujte původní větve!

Učební výstupy

Učební výstupy podávají zhuštěný souhrn základních konceptů a dovedností, které byste měli umět vysvětlit a/nebo použít po každém cvičení. Také obsahují absolutní minimum, které je potřebné pro pochopení navazujících cvičení (a dalších předmětů).

Znalosti konceptů

Znalost konceptů znamená, že rozumíte významu a kontextu daného tématu a jste schopni témata zasadit do většího rámce. Takže, jste schopni …

  • vysvětlit rozdíl mezi forkem projektu a klonem (pracovní kopií) repozitáře

  • vysvětlit, co je větev v Gitu

  • vysvětlit, co je feature branch

  • vysvětlit, co je mergování větví v Gitu

  • vysvětlit, co je to merge (pull) request a kdy je užitečný

  • vysvětlit, co je to Git remote

  • vysvětlit, kdy může dojít ke konfliktu při slučování (merge) v Gitu a jak jej lze vyřešit

  • vysvětlit, co se obvykle míní tzv. upstream repozitářem (projektem)

Praktické dovednosti

Praktické dovednosti se obvykle týkají použití daných programů pro vyřešení různých úloh. Takže, dokážete …

  • vytvořit větev v Gitu lokálně pomocí příkazu git branch

  • nahrát novou větev v Gitu na vzdálený server

  • vytvořit merge request (žádost o začlenění) z feature větve v GitLabu

  • přepínat mezi větvemi (git checkout)

  • sloučit (merge) lokální větve Gitu pomocí git merge

  • řešit konflikty při mergování

  • nastavit Git remotes

  • používat aliasy Gitu pro efektivnější práci

  • volitelné: používání prográmku youtube-dl

  • volitelné: používání VLC z příkazové řádky

Seznam změn na této stránce

  • 2023-03-25: Přidány úlohy po cvičení.

  • 2023-03-27: Vysvětlení fast-forward u hodnocení úlohy po cvičení.

  • 2023-03-27: Varování ohledně pojmenování větví.