Git: fixup!
Ukážeme si, jak na malinko hežčí historii v gitu :)
Tomuhle článku by měl ideálně předcházet článek o tom, jak si představuji “dokonalý” pullrequest, na ten bohužel teď není čas, ale pro představu by mohl stačit Understanding the GitHub Flow.
Když něco commituji, tak ideálně cílím na commity, které dávají jako celky smysl. Pokud z vás často padají věci typu “refaktoring”, “typo”, “fixed”, “really fixed”, “omg now it’s really fixed”, tak bych se nebál tvrdit, že to úplně ideálně neděláte. Taky se tím občas prohřeším, ale jak se říká v anonymních alkoholicích, prvním krokem je přiznat si, že máme problém.
git commit –amend
Otevřu pullrequest, něco nacommituju, dojde uřvanej Procházka, napíše mi že to mám blbě a pošle mi tam několik gifů. Nezbývá než sednout k IDE a vyřešit komentáře, kdo by se s ním hádal…
No ale chceme tu hezkou historii, že? Takže vyřeším komentáře a protože nechci psát commit “refaktoring” tak to amendnu do předchozího. Čímž jsem vyřešil problém s hezkou historii. Jenže představte si když máte 500+ řádků změn, vyřešíte komentáře, všimnete si něčeho dalšího a opravíte to taky…
A teď nastavávají dvě situace… člověk co má zkontrolovat, že jste vyřešili review, tak buď se zaměří jenom na ty sekce co komentoval poprvé a tedy nemá šanci si všimnou dalších nesouvisejících změn, které tak můžou projít přes review bež povšimnutí, a nebo musí projít úplně celý pullrequest znovu… To asi nebude ideální situace, že?
Závěr tedy je, že nechceme amendovat do pushnutých commitů (dokud nejsou pushnuté, tak to nevadí, protože na ně ještě nedělal nikdo review), když nám dělá na kód někdo review, protože mu přidáváme práci.
git rebase -i master
Další, lepší, řešení je commitovat hala bala a před mergem vždy rebasnout commity na master, vyhezkat commit message a posquashovat bordel. Samozřejmě nutnou podmínkou je, že na to před mergnem nezapomenete :)
Interaktivní rebase je strašně mocná věc. Ovšem nebudu zabíhat do detailů, protože předmětem tohohle článku není vysvětlit interaktivní rebase, to by bylo tak na pět článků.
Zkrátka, když je review hotovo, zkontrolováno a pullrequest čeká už jen na merge, tak větev rebasnu na master, s přepínačem -i
jako interactive :)
git rebase -i master
Větev se mi rebasne a pak se mě git zeptá jak ty moje commity co proti masteru přibyly chci učesat. Otevře mi textovej editor se seznamem těch commitů a na začátku jsou magická klíčová slova.
pick cb422ff Add StockPosition to InventoryItem
pick ceada87 typo
pick 4fd776a refactoring
pick be2340e fixed bug
pick b7be27c cs
pick b4cf8de typo
Mimo jiné tady můžu buď použít squash
nebo fixup
. Protože všechny ty zprávy kromě první jsou bordel, použiju fixup
, který stejně jako squash
commity spojí, ale navíc commit message zahazuje. A protože jsem línej, tak píšu jenom f
.
pick cb422ff Add StockPosition to InventoryItem
f ceada87 typo
f 4fd776a refactoring
f be2340e fixed bug
f b7be27c cs
f b4cf8de typo
“Uložím soubor”, git spustí magii a v historii mi zůstane jenom jeden commit.
3ac0d76 Add StockPosition to InventoryItem
Ten forcepushnu do pullrequestu, počkám jestli si CI nebude stěžovat a můžeme mergovat.
git commit –fixup
Jenže když já jsem línej psát ty commit message! Popravdě, bylo mi to jedno, dokud mi kolega Láďa neukázal, že je nemusím psát, protože existuje --fixup
.
Zkrátka, když commituju a vím že commituju věci relevantní k nějakému konkrétnímu commitu, tak použiju --fixup
# cb422ff Add StockPosition to InventoryItem
git commit --fixup=cb422ff
což mi vygeneruje commit se stejnou zprávou, jakou má commit na který dělám fixup a před něj dá magické klíčové slovo fixup!
cb422ff Add StockPosition to InventoryItem
ceada87 fixup! Add StockPosition to InventoryItem
Což je v důsledku víc psaní, než jenom git commit -m "typo"
, tak si uděláme alias :)
git config --global alias.cif = "!git commit --fixup=HEAD"
A teď už stačí jenom
git cif
Tolik ušetřených znaků! :) Ale to není všechno, teď můžu použít --autosquash
git rebase -i --autosquash master
Ten mi udělá to, že automaticky upraví ten soubor, který se otevře při interactive rebasu, na něco takového
pick cb422ff Add StockPosition to InventoryItem
fixup ceada87 fixup! Add StockPosition to InventoryItem
fixup 4fd776a fixup! fixup! Add StockPosition to InventoryItem
fixup be2340e fixup! fixup! fixup! Add StockPosition to InventoryItem
fixup b7be27c fixup! fixup! fixup! fixup! Add StockPosition to InventoryItem
fixup b4cf8de fixup! fixup! fixup! fixup! fixup! Add StockPosition to InventoryItem
Tady už teď nemusím vůbec nic upravovat, protože je to připravené tak jak jsem to chtěl a po uložení získávám krásné
3ac0d76 Add StockPosition to InventoryItem
Na což si samozřejmě taky uděláme alias
git config --global alias.rbi = "rebase -i --autosquash"
A už stačí jenom
git rbi master
Nejhezčí řešení
Nejspíš jste si taky všimli toho nevzhledného fixup!
spamu…
Ono to v důsledku vůbec ničemu nevadí, protože ty zprávy zahazujeme, ale co kdyby šlo i tohle vyřešit?
No.. abychom se toho zbavili, musíme upravit cif
alias tak, aby nedělal --fixup=HEAD
,
ale místo HEAD
tam dal nejnovější commit, který neobsahuje fixup!
Pomocí git-log --grep
se sice dá filtrovat historie, ale neumí to negaci.
Po chvilce googlení jsem našel celkem pěkné řešení, již klasicky, na StackOverflow.
#! /bin/bash
if (( $# < 1 )); then
echo >&2 "Usage: $0 pattern [<since>..<until>]"
exit 1
fi
pattern=$1
shift
git log --format=%H $@ |
grep -v -f <(git log --format=%H "--grep=$pattern" $@) |
git log -1 --format="%H" --stdin --no-walk
Maličko jsem script upravil, aby vypsal jenom hash nejnovějšího záznamu. Uložíme ho třeba jako ~/bin/git-log-vgrep-most-recent-commit
, přidáme mu executable flag chmod +x ~/bin/git-*
a upravíme cif
alias na
git config --global alias.cif = "!git commit --fixup=$(git-log-vgrep-most-recent-commit 'fixup!')"
A teď při použití git cif
vypadá historie takto
cb422ff Add StockPosition to InventoryItem
ceada87 fixup! Add StockPosition to InventoryItem
4fd776a fixup! Add StockPosition to InventoryItem
be2340e fixup! Add StockPosition to InventoryItem
b7be27c fixup! Add StockPosition to InventoryItem
b4cf8de fixup! Add StockPosition to InventoryItem
Pustím git rbi master
, uložím a dostávám
3ac0d76 Add StockPosition to InventoryItem
Voňavější už to snad ani být nemůže :)
Autor: Filip Procházka