V následujícím příspěvku se podíváme na lepší alternativu za git pull
.
Úvod
Dejme tomu, že jste v hlavní větvi (master
) vašeho git repozitáře. Uděláte několik commitů a chcete je hodit na server. Provedete tedy
git push
Pokud vás již však někdo stačil předběhnout a hodil na server své změny, tak dostanete chybové hlášení podobnému tomuto:
To ssh://login@server/git/repo ! [rejected] master -> master (non-fast-forward) error: failed to push some refs to 'ssh://login@server/git/repo' To prevent you from losing history, non-fast-forward updates were rejected Merge the remote changes (e.g. 'git pull') before pushing again. See the 'Note about fast-forwards' section of 'git push --help' for details.
Pokud se v gitu moc neorientujete, tak dáte
git pull
Potom již git push
projde.
No a? V čem je tedy problém?
Když se následně podíváte do logu, tak tam kromě commitů, které jste provedli u sebe, uvidíte další váš commit podobný tomuto:
Merge branch 'master' of ssh://login@server/git/repo
Pokud vaši kolegové v projektu taktéž používají git pull
, tak je pravděpodobné, že takovýchto commitů tam naleznete celou řadu. Říká to, že se provedl merge
(sloučení) hlavní větve (master
) do sebe sama. Podobné commity jsou však v drtivé většině případů nežádoucí a pouze zbytečně znepřehledňují a zaneřáďují commit historii.
Proč a jak tento vůbec commit vznikl?
Když se vám objevilo ono chybové hlášení, tak na serveru byly nové commity, které nemáte stáhnuté lokálně u sebe. Když dáte git pull
, tak git
za vás udělá následující dvě akce: git fetch
a git merge
. Tedy stáhne změny ze serveru (git fetch
) a provede sloučení hlavní větve tak, jak je na serveru (origin/master
) do vaší lokální master
větve. Tím vznikne onen merge commit. Je to podobné, jako když děláte merge sami, akorát zde je to nad master
větví.
Jak se podobným commitům vyhnout?
Možností je více. Já zde zmíním dvě cesty, jak se jim vyhnout.
git pull --rebase=preserve
. Místo toho, abyste dali pouzegit pull
, tak dátegit pull --rebase=preserve
. Ten místo toho, aby za vás udělalgit fetch
agit merge
, tak místomerge
udělárebase
. Pak budete moct provéstgit push
a nevznikne onen zbytečný merge commit. Poznámka: volbapreserve
způsobí, žerebase
zachová lokální merge, pokud jste nějaký provedli a ještě jej nepushnuli (detaily).git up
. Nainstalujte si skriptgit-up
, který vám umožní používatgit up
místogit pull
. Tento příkaz za vás automaticky aktualizuje všechny lokální větve a místomerge
použijerebase
, takže se vyhnete zbytečným commitům. V opačném případě, pokud máte více větví a používátegit pull
, tak je nutné je aktualizovat všechny ručně, což je zdlouhavé.Co se týče nastavení tohoto skriptu, tak používám následující nastavení v
.gitconfig
:[git-up "rebase"] arguments = --preserve-merges log-hook = "echo \"* changes on $1:\"; git log --no-merges --pretty='format:%C(yellow)%h %C(green)%ai %C(bold blue)%an %C(red)%d%C(reset) %s' $1..$2"
Ono první nastavení zachová merge, pokud jste nějaký provedli a po něm dali
git up
. Pokud toto nastaveno nemáte, tak se váš merge změní na rebase a budete se divit, co se stalo. Ono druhé nastavení přehledně vypíše seznam změn, které proběhly od poslední aktualizace. Více viz popis skriptu.
Důležité upozornění
Je třeba vzít do úvahy to, že v obou případech zmíněných výše se používá rebase
, což vám (a především pak vašim kolegům) při nesprávném použití může způsobit bolesti hlavy. Je třeba se dobře seznámit s tím, co to rebase
je a kdy se nesmí používat. Hlavním problémem je, že tento příkaz mění historii. Proto neváhejte a nastudujte si, co tento příkaz dělá. Naučíte se tak podstatnou a důležitou část git
u. Když už něco používáte pro svou práci, tak byste měli chápat, jak to funguje a jak se vyhnout tomu, aby vás to pokousalo. Pokud tedy nevíte, jak rebase
funguje, tak si to nastudujte. Jinak o hodně přijdete.
push -> pull
Drobný leč zásadní překlep:
Když dáte git push, tak git za vás udělá následující dvě akce: git fetch a git merge.
Jinak dobrý tip, díky.
Re: push -> pull
Jejda, díky za upozornění! Opraveno.