uklizená, lineární historie Git
jednou z věcí, která je v mnoha projektech založených na Git přehlížena, je hodnota historie lineárního odevzdání. Ve skutečnosti mnoho nástrojů a pokynů odrazuje pracovní postupy Git, které se zaměřují na lineární historii. Považuji to za smutné, protože uklizená historie je velmi cenná a existuje přímočarý pracovní postup, který zajišťuje lineární historii.
Lineární vs nelineární historie
lineární historie je jednoduše historie Git, ve které všechny commity přicházejí po sobě. Tedy. nenajdete žádné sloučení poboček s nezávislou historií odevzdání.
proč chcete lineární historii?
kromě toho, že je uklizená a logická, lineární historie se hodí, když:
- při pohledu na historii. Nelineární historie může být velmi obtížné sledovat-někdy do té míry, že historie je prostě nepochopitelná.
- zpětné sledování změn. Například: “představili jste funkci a před nebo po opravě chyby B?”.
- sledování chyb. Git má velmi elegantní funkci zvanou git bisect, kterou lze použít k rychlému nalezení, které odevzdání zavedlo chybu nebo regresi. S nelineární historií se však git bisect stává obtížným nebo dokonce nemožným.
- vrácení změn. Řekněme, že jste našli odevzdání, které způsobilo regresi, nebo chcete odstranit funkci, která neměla vyjít v konkrétním vydání. S trochou štěstí (pokud se váš kód příliš nezměnil) můžete jednoduše vrátit nežádoucí odevzdání pomocí git revert. Pokud však máte nelineární historii, možná se spoustou fúzí mezi větvemi, bude to výrazně těžší.
pravděpodobně existuje další hrstka situací, ve kterých je lineární historie velmi cenná, v závislosti na tom, jak používáte Git.
bod je: čím méně lineární je vaše historie, tím méně cenná je.
příčiny nelineární historie
Stručně řečeno, každé odevzdání merge je potenciálním zdrojem nelineární historie. Existují však různé druhy odevzdání sloučení.
sloučit z větve téma do master
když jste hotovi s větví téma a chcete ji integrovat do master, běžnou metodou je sloučení větve téma do master. Možná něco v duchu:
git checkout mastergit pullgit merge --no-ff my-topic-branch
dobrou vlastností této metody je, že uchováváte informace o tom, které commity byly součástí vaší větve téma (alternativou by bylo vynechat “- no-ff”, což by Gitu umožnilo provést rychlý posun vpřed místo sloučení, v takovém případě nemusí být tak jasné, která z revizí skutečně patřila vaší větvi téma).
problém se sloučením do master vzniká, když je vaše větev tématu založena na starém mistrovi místo nejnovějšího tipu master. V tomto případě nevyhnutelně získáte nelineární historii.
zda to bude běžný problém nebo ne, do značné míry závisí na tom, jak aktivní je repozitář Git, kolik vývojářů pracuje současně atd.
sloučit z master do větve téma
někdy chcete aktualizovat větev tak, aby odpovídala nejnovějšímu master (např. na master jsou některé nové funkce, které se chcete dostat do větve téma, nebo zjistíte, že nemůžete sloučit větev téma do master, protože existují konflikty).
běžnou metodou, kterou někteří dokonce doporučují, je sloučení špičky master do větve tématu. Toto je hlavní zdroj nelineární historie!
řešení: Rebase!
git rebase je velmi užitečná funkce, kterou byste měli použít, pokud chcete lineární historii. Někteří považují koncept rebasingu za trapný, ale je to opravdu docela jednoduché: přehrajte změny (odevzdání) ve své větvi nad novou revizi.
například můžete pomocí git rebase změnit kořen větve tématu ze starého mistra na špičku nejnovějšího mistra. Za předpokladu, že máte odhlášenou větev tématu, můžete to udělat:
git fetch origingit rebase origin/master
Všimněte si, že odpovídající operací sloučení by bylo sloučení špičky master do větve téma (jak je znázorněno na předchozím obrázku). Výsledný obsah souborů ve větvi téma by byl stejný, bez ohledu na to, zda provedete rebase nebo sloučení. Historie je však jiná (lineární vs nelineární!).
to zní dobře a dobře. Existuje však několik upozornění s rebase, které byste měli vědět.
upozornění 1: Rebase vytvoří nové commity
Rebasing větve skutečně vytvoří nové commity. Nové commity budou mít jiné SHA: s než staré commity. Obvykle to není problém, ale dostanete se do potíží, pokud znovu založíte větev, která existuje mimo místní úložiště (např. pokud vaše větev již existuje v origin).
pokud chcete posunout rebasovanou větev do vzdáleného repozitáře, který již obsahuje stejnou větev (ale se starou historií),:
-
git push --force-with-lease
), protože Git vám nedovolí posunout novou historii do existující větve jinak. To účinně nahrazuje historii pro danou vzdálenou větev novou historií. - možná způsobí, že někdo jiný bude velmi nešťastný, protože jejich místní verze dané větve již neodpovídá větvi na dálkovém ovladači, což může vést k nejrůznějším problémům.
obecně se vyhněte přepisování historie větve na dálkovém ovladači (jedinou výjimkou je přepis historie větve, která je pod kontrolou kódu-v závislosti na tom, jak funguje váš systém kontroly kódu-ale to je jiná diskuse).
pokud potřebujete znovu založit větev, která je sdílena s ostatními na dálkovém ovladači, jednoduchým pracovním postupem je vytvořit novou větev, kterou znovu založíte, namísto opětovného spuštění původní větve. Za předpokladu, že jste zkontrolovali my-topic-branch
, můžete to udělat:
git checkout -b my-topic-branch-2git fetch origingit rebase origin/mastergit push -u origin my-topic-branch-2
…a pak řekněte lidem pracujícím na my-topic-branch
, aby místo toho pokračovali v práci na my-topic-branch-2
. Stará větev je pak v podstatě zastaralá a neměla by být sloučena zpět do master.
upozornění 2: Řešení konfliktů v rebase může být více práce než ve sloučení
pokud dojde ke konfliktu v operaci sloučení, vyřešíte všechny konflikty jako součást tohoto odevzdání sloučení.
v operaci rebase však můžete potenciálně získat konflikt pro každé odevzdání ve větvi, kterou rebasujete.
ve skutečnosti mnohokrát zjistíte, že pokud dojde ke konfliktu v odevzdání, narazíte na související (velmi podobné) konflikty v následujících revizích ve vaší větvi, jednoduše proto, že commity ve větvi tématu mají tendenci být příbuzné(např. úprava stejných částí kódu).
nejlepší způsob, jak minimalizovat konflikty je sledovat, co se děje v master, a vyhnout se nechat téma větev běžet příliš dlouho bez rebasing. Řešení malých konfliktů vpředu každou chvíli je snazší, než je všechny zvládnout V jednom velkém šťastném konfliktním nepořádku na konci.
některá varování pro uživatele GitHub
GitHub je skvělý v mnoha věcech. Je to fantastické pro Git hosting a má skvělé webové rozhraní s procházením kódu, pěknou funkcí Markdown, podstatou atd.
Pull requests má na druhé straně několik funkcí, které aktivně maří lineární historii Git. Bylo by velmi vítané, kdyby GitHub tyto problémy skutečně vyřešil, ale do té doby byste si měli být vědomi nedostatků:
“Merge pull request” umožňuje nelineární sloučení do master
může být lákavé stisknout zelené, přátelské tlačítko “Merge pull request” pro sloučení větve tématu do master. Zejména proto, že čte “tato větev je aktuální se základní větví. Sloučení lze provést automaticky”.
co GitHub opravdu říká, je, že větev může být sloučena, aby zvládla bez konfliktů. Nekontroluje, zda je větev pull request založena na nejnovějším master.
jinými slovy, pokud chcete lineární historii, musíte se ujistit, že větev pull request je rebasována na nejnovější master sami. Pokud vím, žádné takové informace nejsou dostupné přes webové rozhraní Githubu (pokud nepoužíváte ” chráněné větve – – viz níže), takže je musíte udělat z místního klienta Git.
i když je požadavek pull správně sestaven, neexistuje žádná záruka, že operace sloučení ve webovém rozhraní GitHub bude atomová (tj. někdo může před provedením operace sloučení tlačit změny na master-a GitHub si nebude stěžovat).
takže opravdu, jediný způsob, jak se ujistit, že vaše větve jsou správně rebased na vrcholu nejnovější master je provést operaci sloučení lokálně a tlačit výsledný master ručně. Něco v duchu:
git checkout mastergit pullgit checkout my-pullrequest-branchgit rebase mastergit checkout mastergit merge --no-ff my-pullrequest-branchgit push origin master
pokud máte smůlu a někdo dokáže tlačit změny, aby zvládl mezi vašimi operacemi pull a push, vaše operace push bude odepřena. To je však dobrá věc, protože zaručuje, že vaše operace je atomová. Jen git reset --hard origin/master
a opakujte výše uvedené kroky, dokud neprojde.
Poznámka: respektujte pokyny projektu w.r. t.kontrola a testování kódu. Např. Pokud používáte automatické testy (sestavení, statická analýza, testy jednotek,…) jako součást požadavku pull, měli byste pravděpodobně znovu odeslat rebased větev (buď pomocí git push-f, nebo otevřením nového PR), spíše než jen aktualizovat hlavní větev ručně.
funkce chráněných větví podporuje sloučení z master
pokud ve svém projektu GitHub používáte chráněné větve a kontroly stavu, skutečně získáte ochranu před sloučením větve pull request do master, pokud není založena na nejnovějším master (myslím, že důvodem je, že kontroly stavu provedené na PR větvi by měly být platné i po sloučení do master).
Nicméně … pokud větev pull request není založena na nejnovějším master, zobrazí se vám přátelské tlačítko s názvem “aktualizovat větev” s textem ” tato větev je zastaralá se základní větví. Sloučit nejnovější změny z master do této větve”.
v tomto okamžiku je nejlepší možností lokálně rebasovat větev a vynutit ji na požadavek pull. Za předpokladu, že jste se my-pullrequest-branch
odhlásili, proveďte:
git fetch origingit rebase origin/mastergit push -f
bohužel požadavky na GitHub pull nehrají dobře se silovými tlaky, takže některé informace o kontrole kódu se mohou v procesu ztratit. Pokud to není přijatelné, zvažte vytvoření nového pull requestu (tzn. posunutí rebasované větve na novou vzdálenou větev a vytvoření pull requestu z nové větve).
závěr
pokud vám záleží na lineární historii:
- Rebase téma větev na vrcholu nejnovější master před sloučením do master.
- nespojujte master do větve tématu. Místo toho Rebase.
- když sdílíte větev tématu s ostatními, vytvořte novou větev, kdykoli ji potřebujete znovu sestavit(
my-topic-branch-2
,my-topic-branch-3
, …). - pokud používáte požadavky na GitHub pull, mějte na paměti:
- tlačítko” Sloučit ” nezaručuje, že větev PR je založena na nejnovějším master. V případě potřeby ručně Rebase.
- pokud používáte chráněné větve s kontrolami stavu, nikdy nestiskněte tlačítko” Aktualizovat větev”. Rebase ručně místo.
pokud se příliš nestaráte o lineární historii Git-šťastné sloučení!
seznam přání pro GitHub
pro vás jemné lidi pracující na GitHub: přidejte prosím podporu pro rebase workflow v pull requests(to by mohlo být opt – in možnosti úložiště).
- Přidejte možnost Zakázat tlačítko sloučení / operaci v požadavcích na pull, pokud větev není založena na nejnovějším master (to by nemělo vyžadovat použití kontrol stavu).
- Přidat tlačítko “Rebase onto latest master”. V mnoha případech by to měla být operace bez konfliktů, kterou lze snadno provést prostřednictvím webového rozhraní.
- zachovat historii požadavků pull (commits, comments,…) po rebase / force push.
Leave a Reply