Git - Kapitola 4. Sdílení vývoje s ostatními

Získávání aktualizací pomocí git pull / Zasílání patchů do projektu / Importování patchů do projektu / Veřejné git repositáře / Vytvoření veřejného repositáře / Exportování git repositáře přes git protokol /  Exportování git repositáře přes http / Tlačení změn do veřejného repositáře / Co dělat když push selže / Vytvoření sdíleného repositáře / Povolení procházení repositáře webovým prohlížečem / Příklady / Údržba tématických větví pro maintainera Linuxového subsystému

Obsah

Získávání aktualizací pomocí git pull
Zasílání patchů do projektu
Importování patchů do projektu
Veřejné git repositáře
Vytvoření veřejného repositáře
Exportování git repositáře přes git protokol
Exportování git repositáře přes http
Tlačení změn do veřejného repositáře
Co dělat když push selže
Vytvoření sdíleného repositáře
Povolení procházení repositáře webovým prohlížečem
Příklady
Údržba tématických větví pro maintainera Linuxového subsystému

Získávání aktualizací pomocí git pull

Po naklonování repositáře a provedení několika vlastních změn se můžete rozhodnout podívat se na změny provedené v původním repositáři a doplnit je do vaší práce.

Už jsme vidělit jak udržovat remote-tracking větve aktuální pomocí příkazu git-fetch(1),a jak sloučit dvě větve. Takže změny z původního repositáře můžete do master větve sloučit příkazem:

$ git fetch
$ git merge origin/master

Nicméně, příkaz git-pull(1) poskytuje způsob jak toto provést v jediném kroku:

$ git pull origin master

Ve skutečnosti, pokud máte načtenu větev "master", potom příkaz "git pull" standardně merguje změny z HEAD větve původního repositáře. Takže výše uvedeného lze často docílit prostě příkazem

$ git pull

Obecněji, větev vytvořená ze vzdálené větve bude standardně načítat data z této vzdálené větve. Podívejte se na detaily o volbách branch.<name>.remote a branch.<name>.merge v manuálové stránce příkazu git-config(1), a diskusi o volbě  —track v manuálové stránce příkazu git-checkout(1) kde se dozvíte jak tyto defaultní hodnoty měnit.

Navíc, abyste museli psát co nejméně, příkaz "git pull" vám také pomáhá vytvářením výchozího popisu commitu, dokumentujícího větev a repositář ze kterého jste načítala.

(Ale všimněte si že žádný takový commit nebude vytvořen v případě fast forward; namísto toho bude vaše větev pouze aktualizována na nejnovější commit z upstream větve.)

Příkazu git pull můžete také předat "." namísto "vzdáleného" repositáře, a v tomto případě pouze přimerguje větev z aktuálního repositáře; takže příkazy

$ git pull . branch
$ git merge branch

jsou přihližně ekvivalentní. První je ve skutečnosti velmi široce používán.

Zasílání patchů do projektu

Pokud máte pouze několik málo změn, nejjednodušší způsob jak je odeslání do projektu může být prosté zaslání e-mailem:

Nejdříve, použijte příkaz git-format-patch(1); například:

$ git format-patch origin

Který vyprodukuje očíslovanou posloupnost souborů v aktuálním adresáři, jeden pro každý patch v aktuální větvi ale ne v origin/HEAD.

Poté můžete tyto patche načíst do vašeho mailového klienta a poslat je ručně. Nicméně, pokud jich máte mnoho k oeslání, můžete raději využít skript git-send-email(1) pro automatizaci tohoto procesu. Nejdříve se ale podívejte do mailing listu vašeho projektu a zjistěte v jaké formě chtějí patche dostávat.

Importování patchů do projektu

Git také poskytuje nástroj git-am(1) (am znamená "apply mailbox") pro importování e-mailem doručované posloupnosti patchů. Prostě uložte všechny zprávy obsahující patche, v daném pořadí, do jediného mailbox souboru, řekněme "patches.mbox", a potom spusťte

$ git am -3 patches.mbox

Git aplikuje všechny patche v daném pořadí; pokud najde jakékoliv konflikty, zastaví se a vy můžete konflikty opravit tak jak bylo popsáno v kapitole "Resolving a merge". (Volba "-3" říká gitu aby provedl merge; pokud dáváte přednost tomu aby git prostě přerušil činnost a ponechal váš pracovní strom a index nedotknutý, můžete tuto volbu vynechat.)

Jakmile je index aktualizován i výsledky po vyřešení konfliktů, namísto vytváření nového commitu prostě spusťte

$ git am --resolved

a git pro vás vytvoří commit a bude potračovat v aplikaci zbývajících patchů z mailboxu.

Konečným výsledkem bude posloupnost commitů, jeden pro každý patch v původním mailboxu, s informací o autorovi a popisem commitu načtenými ze zprávy obsahující jednotlivé části.

Veřejné git repositáře

Dalším způsobem jak zasílat změny do projektu je říci maintainerovi (osobě zodpovědné za repositár) daného projektu aby si změny načetl z vašeho repositáře pomocí příkazu git-pull(1). V sekci "Načítání aktualizací příkazem git pull" jsme tento příkaz popsali jako způsob jak načítat změny z "main" repositáře, ale stejně dobře funguje i opačným směrem.

Pokud vy i maintainer máte oba účty na stejném stroji, potom můžete jednoduše přetáhnout změny ze svých repositářů přímo; příkazy které jako argumenty přijímají URL repositářů přijmou také název lokálního adresáře:

$ git clone /path/to/repository
$ git pull /path/to/other/repository

nebo ssh URL:

$ git clone ssh://yourhost/~you/repository

Pro projekt s několika málo vývojáři, nebo pro synchronizaci několika privátních repositářů, to může být plně postačující.

Nicméně, obvyklejším způsobem je udržovt oddělený veřejný repositář (obvykle na samostatném stroji) ze kterého si ostatní mohou načítat (pull) změny. To je obvykle pohodlnější, a umožňuje vám to jasně oddělit rozdělanou vlastní práci od veřejně viditelného obsahu.

Svou každodenní práci budete ukládat do soukromého repositáře, ale změny budete ze soukromého repositáře pravidelně "tlačit" (push) do veřejného repositáře, čímž ostatním vývojářům umožníte jejich načtení. Takže tok změn, v situaci kdy je jeden vývojář s veřejným repositářem, vypadá takto:

                          vy tlačíte
váš osobní repositář ------------------> váš veřejný repositář
      ^                                             |
      |                                             |
      | vy načítáte                                 | oni načítají
      |                                             |
      |                                             |
      |                       oni tlačí             V
jejich veřejný repositář <------------------- jejich repositář

Postup podrobněji vysvětlíme v následujících odstavcích,

Vytvoření veřejného repositáře

Předpokládejme že váš osobní repositář je v adresáři ~/proj. Nejdříve vytvoříme klon repositáře a řekneme příkazu  git daemon že ho chceme zveřejnit:

$ git clone --bare ~/proj proj.git
$ touch proj.git/git-daemon-export-ok

Výsledný adresář proj.git obsahuje "prostý" git repositář - je to pouze obsah ".git" adresáře, bez všech souborů okolo.

Dále, zkopírujte proj.git na server kde plánujete hostovat veřejný repositář. Můžete použít příkazy scp, rsync nebo cokoliv je nejpohodlnější.

Exportování git repositáře přes git protokol

Toto je preferovaná metoda.

Pokud server spravuje někdo jiný, měl by vám sdělit do kterého adresáře repositář umístět, a na jakém git:// URL bude dostupný. Poté můžete přeskočit na odstavec "Tlačení změn do veřejného repositáře" níže.

Jinak vše co musíte udělat je spuštění git-daemon(1);který bude poslouchat na portu 9418. Standardně umožní přístup do libovolného adresáře vypadajícího jako .git adresář a obsahujícího kouzelný soubor git-daemon-export-ok. Předání cest adresářů jako argumentů příkazu git daemon dále omezí exportované adresáře na dané cesty.

Můžete také spustit příkaz git daemon jako inetd službu, detaily najdete v manuálové stránce příkazu git-daemon(1) (zejména se podívejte na odstavec s příklady).

Exportování git repositáře přes http

Git protokol poskytuje lepší výkon a spolehlivost, ale na serveru s nastaveným web serverem, nastavení http exportů může být znatelně jednodušší.

Vše co musíte udělat je umístit nově vytvořený git adresář do adresáře dostupného přes webový server, a provést několik málo nastavení tak aby weboví klienti dostali potřebné doplňující informace:

$ mv proj.git /home/you/public_html/proj.git
$ cd proj.git
$ git --bare update-server-info
$ mv hooks/post-update.sample hooks/post-update

(Pro vysvětlení posledních dvou řádek se podívejte do git-update-server-info(1) a githooks(5).)

Oznamte URL proj.git. Všichni ostatní poté budou schopni klonovat nebo načítat z daného URL, například z příkazové řádky takto:

$ git clone http://yourserver.com/~you/proj.git

(Poněkud sofistikovanější nastavení pomocí WebDAV, umožňující také "pushing" přes http, najdete v manuálové stránce příkazu setup-git-server-over-http).

Tlačení změn do veřejného repositáře

Všimněte si že dvě techniky popsané výše (exportování přes http nebo git) umožňuje maintainterům načítat vaše poslední změny, ale neposkytují možnost zápisu, který potřebujete pro aktualizaci veřejného repositáře posledními změnami z vašich soukromých repositářů.

Nejjednodušší způsob jak toho dosáhnout je použití příkazů git-push(1) a ssh; pro aktualizaci vzdálené větve pojmenované "master" posledními změnami z vaší "master" větve, spusťte:

$ git push ssh://yourserver.com/~you/proj.git master:master

nebo pouze

$ git push ssh://yourserver.com/~you/proj.git master

Stejně jako git fetch, i příkaz git push si bude stěžovat pokud nedojde k fast forward; v následující sekci najdete detaily o řešení této situace.

Všimněte si že cílem "push" je obvykle "bare" repositář. Můžete také pushovat do repositáře který má načtený pracovní strom, ale pracovní strom nebude pushem upraven. To může vést k neočekávaným výsledkům pokud větev do které pushujete je aktuálně načtenou větví!

Stejně jako v případě příkazu git fetch, můžete také nastavit konfigurační volby které vám ušetří psaní; takže například, po

$ cat >>.git/config <<EOF
[remote "public-repo"]
        url = ssh://yourserver.com/~you/proj.git
EOF

byste výše uvedený push měli být schopni provést pouze příkazem

$ git push public-repo master

Přečtěte si podrobnosti o remote.<name>.url, branch.<name>.remote, a remote.<name>.push v manuálové stránce příkazu git-config(1).

Co dělat když push selže

Pokud by výsledkem pushe nebyl fast forward vzdálené větve, potom push selže s chybovou hláškou

error: remote 'refs/heads/master' is not an ancestor of
 local  'refs/heads/master'.
 Maybe you are not up-to-date and need to pull first?
error: failed to push to 'ssh://yourserver.com/~you/proj.git'

To se může stát, například pokud:

Příkaz git push můžete donutit vykonat update, a to připojením znaku plus na začátek názvu větve:

$ git push ssh://yourserver.com/~you/proj.git +master

Kdykoliv je za normálních okolností modifikován vrchol veřejného repositáře, je modifikován tak že nově odkazuje na potomka původního commitu. Tím že v této vynutíte push, uvedenou konvenci porušíte. (Podívejte se do odstavce nazývaného “Problémy s přepisem historie”.)

Nicméně, toto je obvyklá praxe pro lidi kteří potřebují jednoduchý způsob jak publikovat rozpracované série patchů, a je to akceptovatelný kompromis pokud varujete ostatní vývojáře že větev budete spravovat tímto způsobem.

Je také možné že push takto selže pokud právo pushovat do daného adresáře mají i další lidé. V tom případě je správným řešením zkusit znovu push poté co nejdříve aktualizujete svou práci: buď příkazem pull nebo příkazem fetch následovaným rebase; více najdete v následujícím odstavci a manuálové stránce k příkazu gitcvs-migration(7).

Vytvoření sdíleného repositáře

Další možností jak spolupracovat je použití modelu běžně používaného v CVS, kde několik vývojářů se speciálními právy všichni zapisují a čtou z jediného sdíleného repositáře. Návod jak toto nastavit najdete v manuálové stránce gitcvs-migration(7).

Nicméně, ačkoliv na podpoře sdílených repositářů v gitu není nic špatného, tento mód fungování obecně není doporučován, jednoduše proto že způsob spolupráce který git podporuje - výměnou patchů a načítáním z veřejných repositářů - má tolik výhod nad centrálním sdíleným repositářem:

  • Schopnost gitu rychle importovat a mergovat patche umožňuje jedinému maintainerovi zpracovávat i velké počty příchozích změn. A když to přeroste únosnou mez, příkaz git pull nabízí maintainerovi jednoduchý způsob jak tuto činnost delegovat jiným maintainerům, a přitom umožňuje volitelné review příchozích změn.
  • Protože repositář každého vývojáře má stejnou kompletní kopii projektu, žádný repositář není "speciální" a pro jiného vývojáře je triviální převzít správu projektu, buď po vzájemné dohodě nebo, proto že původní maintainer je nedosažitelný nebo je s ním obtížné pracovat.
  • Absence centrální skupiny "commiterů" znamená že je třeba méně formálních rozhodnutí kdo je "členem" a kdo ne.

Povolení procházení repositáře webovým prohlížečem

CGI skript gitweb poskytuje uživatelům jednoduchý způsob procházení souborů a historie vašeho projektu bez nutnosti instalovat git; podrobnosti o nastavení najdete v souboru gitweb/INSTALL ve zdrojových kódech gitu.

Příklady

Údržba tématických větví pro maintainera Linuxového subsystému

Tento odstavec popisuje jak Tony Luck používá git ve své roli maintainera IA64 architektury pro Linuxové jádro.

Používá dvě veřejné větve:

  • Větev "test" do které jsou patche začleňovány nejdříve, takže jsou zveřejněny před integrováním se zbývajícím vývojem. Tento strom je dostupný pro Andrewa aby do -mm mohl vložit cokoliv bude chtít.
  • Strom "release" do kterého jsou přesouvány patche pro finální testování, a slouží jaké pro jejich odesílání dále Linusovi (zasláním "please pull" žádosti).

Používá také několik dočasných větví ("topic branches"), přičemž každá z nich obsahuje skupinu logicky souvisejících patchů.

Pro podobné nastavení nejdříve vytvořte svůj pracovní strom naklonováním Linusova veřejného stromu:

$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/\
linux-2.6.git work
$ cd work

Linusův strom bude uložen v remote větvi nazvané origin/master, a lze z ní aktualizovat příkazem git-fetch(1); můžete také sledovat další veřejné stromy tak že pomocí příkazu git-remote(1) vytvoříte "remote" větev a příkazem git-fetch(1) je budete udržovat aktuální; viz. také  Kapitola 1, Repositáře a větve.

Nyní vytvořte větve ve kterých budete pracovat; ty začínají na současném vrcholu větve origin/master, a měly by být nastaveny (pomocí volby —track příkazu git-branch(1)) tak aby standardně načítaly změny z Linusovy větve.

$ git branch --track test origin/master
$ git branch --track release origin/master

Jednoduše je lze aktualizovat příkazem git-pull(1).

$ git checkout test && git pull
$ git checkout release && git pull

Důležitá poznámka! Pokud máte v těchto větvích jakékoliv lokální změny, potom tento merge v historii vytvoří commit objekt (bez lokálních změn git jednoduše provede "fast forward" merge). Mnoho lidí nemá v oblibě "šum" který to způsobuje v Linuxové historii, takže byste se tomu v "release" větvi měli důsledně vyhýbat, protože tyto rušivé commity se stanou součástí trvalé historie až Linuse požádáte o načtení změn z release větve.

A few configuration variables (see git-config(1)) can make it easy to push both branches to your public tree. (See the section called “Setting up a public repository”.)

Několik konfiguračních proměnných (viz. git-config(1)) může zjednodušit načítání změn z obou větví do vašeho veřejného stromu. (Podrobnosti najdete v odstavci nazvaném “Vytvoření veřejného repositáře”.)

$ cat >> .git/config <<EOF
[remote "mytree"]
url =  master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git
push = release
push = test
EOF

Potom můžete načítat změny z test a release stromů pomocí příkazu git-push(1):

$ git push mytree

nebo pushovat pouze z větve test nebo release pomocí:

$ git push mytree test

nebo

$ git push mytree release

Nyní aplikujeme několik patchů z komunity. Vymysleme nějaké pěkné krátké jméno pro větev do které umístíme tento patch (nebo skupinu patchů), a vytvořme novou větev z vrcholu Linusovy větve:

$ git checkout -b speed-up-spinlocks origin

Nyní aplikujme patch(e), spustíme několik testů, a commitneme změny. Pokud má patch několik částí, potom byste každou měli do větve aplikovat jako samostatný commit.

$ ... patch ... test  ... commit [ ... patch ... test ... commit ]*

Až budete spokojeni s výsledkem změny, můžete ji načíst (pull) do větve "test" čímž ji připravíte na zveřejnění:

$ git checkout test && git pull . speed-up-spinlocks

Je nepravděpodobné že byste narazili na nějaké konflikty ... ale mohli byste pokud jste se trochu loudali a načetli jste nové verze z nadřazené (upstream) větve.

O něco později, po uplynutí doby dostatečné na otestování, můžete stejnou větev načíst do "release" stromu tak aby je bylo možno natlačit do upstream větve. Právě na tomto místě nejlépe oceníte to že každý patch (nebo sérii souvisejících patchů) držíte v samostatné větvi. Znamená to totiž že patche mohou být do "release" větve tlačeny v libovolném pořadí.

$ git checkout release && git pull . speed-up-spinlocks

Po chvíli budete mít několik větví, a i přes dobře zvolená jméno pro každou z nich nejspíše zapomenete k čemu vlastně byly, nebo v jakém jsou stavu. Pro připomenutí toho jaké změny v dané větvi jsou, použijte:

$ git log linux..branchname | git shortlog

Pokud vás zajímá zda již byla daná větev přimergována do testovací nebo release větve, použijte:

$ git log test..branchname

nebo

$ git log release..branchname

(Pokud větev zatím zmergována nebyla, uvidíte vypsáno několik položek. Pokud zmergovány byly, nevypíší příkazy nic.)

Jakmile patch dokončí velký cyklus (přesun z testovací do release větve, načtení Linusem a konečně zpět do vaší lokální "origin/master" větve), není už větev vyhrazená pro danou změnu potřeba. To zjistíte tak že výstup příkazu

$ git log origin..branchname

je prázdný. V tuto chvíli může být větev smazána:

$ git branch -d branchname

Některé změny jsou tak triviální že pro ně není třeba vytvářet samostatnou větev a potom ji mergovat do testovací i release větve. Takové změny stačí aplikovat přímo do "release" větve, a následně je přimergovat zpět do testovací větve.

Pro vytvoření diffstat a shortlog shrnutí změn pro přiložení do "please pull" požadavku zasílaného Linusovi můžete použít příkaz:

$ git diff --stat origin..release

a

$ git log -p origin..release | git shortlog

Tady je několik skriptů které toto ještě dále zjednodušují.

==== update script ====
# Update a branch in my GIT tree.  If the branch to be updated
# is origin, then pull from kernel.org.  Otherwise merge
# origin/master branch into test|release branch

case "$1" in
test|release)
    git checkout $1 && git pull . origin
        ;;
origin)
    before=$(git rev-parse refs/remotes/origin/master)
    git fetch origin
    after=$(git rev-parse refs/remotes/origin/master)
    if [ $before != $after ]
    then
        git log $before..$after | git shortlog
    fi
    ;;
*)
    echo "Usage: $0 origin|test|release" 1>&2
    exit 1
    ;;
esac
==== merge script ====
# Merge a branch into either the test or release branch

pname=$0

usage()
{
    echo "Usage: $pname branch test|release" 1>&2
    exit 1
}

git show-ref -q --verify -- refs/heads/"$1" || {
    echo "Can't see branch <$1>" 1>&2
    usage
}

case "$2" in
test|release)
    if [ $(git log $2..$1 | wc -c) -eq 0 ]
    then
        echo $1 already merged into $2 1>&2
        exit 1
    fi
    git checkout $2 && git pull . $1
    ;;
*)
    usage
    ;;
esac
==== status script ====
# report on status of my ia64 GIT tree

gb=$(tput setab 2)
rb=$(tput setab 1)
restore=$(tput setab 9)

if [ `git rev-list test..release | wc -c` -gt 0 ]
then
    echo $rb Warning: commits in release that are not in test $restore
    git log test..release
fi

for branch in `git show-ref --heads | sed 's|^.*/||'`
do
    if [ $branch = test -o $branch = release ]
    then
        continue
    fi

    echo -n $gb ======= $branch ====== $restore " "
    status=
    for ref in test release origin/master
    do
        if [ `git rev-list $ref..$branch | wc -c` -gt 0 ]
        then
            status=$status${ref:0:1}
        fi
    done
    case $status in
    trl)
        echo $rb Need to pull into test $restore
        ;;
    rl)
        echo "In test"
        ;;
    l)
        echo "Waiting for linus"
        ;;
    "")
        echo $rb All done $restore
        ;;
    *)
        echo $rb "<$status>" $restore
        ;;
    esac
    git log origin/master..$branch | git shortlog
done

 

Komentáře

K tomuto článku zatím žádné komentáře neexistují (nebo čekají na schválení).

Nový komentář

Všechny komentáře podléhají schválení - mezi odesláním komentáře a jeho zobrazením na této stránce tedy může být prodleva. Vyplníte-li e-mailovou adresu, budete o schválení či neschválení komentáře informováni.

V titulku ani v textu nejsou povoleny HTML tagy - budou automaticky odstraněny. Odstavec ukončíte prázdným řádkem.

(nepovinné)