Git
Chapters ▾ 2nd Edition

5.2 Дистрибуирани Гит - Како се даје допринос пројекту

Како се даје допринос пројекту

Главни проблем код описивања начина на који се даје допринос пројекту је то што постоји велики број варијација на који се то може учинити. Пошто је програм Гит веома флексибилан, људи могу да раде на много начина, па је проблематично описати како треба да дате допринос — сваки пројекат је помало другачији. Неки од фактора који су укључени у то је број људи који активно дају доприносе, изабрани процес рада, приступ вашим комитовима, и евентуална метода за спољне доприносе.

Први фактор је број људи који активно учествују — колико корисника активно даје допринос коду овог пројекта и колико често. У многим случајевима, имаћете два или три програмера са неколико комитова на дан, или можда и мање за не тако активне пројекте. За веће компаније или пројекте, број програмера би могао да пређе неколико хиљада, са стотинама или хиљадама комитова на дан. Ово је важно јер са више програмера можете да наиђете на више проблема, јер кôд неће увек моћи да се глатко споји. Промене које поднесете би могле да застаре или буду прегажене радом који је неко спојио док сте ви радили, или док су ваше промене чекале да се одобре или примене. Како да одржите свој кôд константно актуелним, а комитове валидним?

Следећи фактор је процес рада који се примењује у пројекту. Да ли је централизован, тако да сваки програмер има једнак приступ за писање у главној линији кода? Да ли пројекат има одржаваоца или руководиоца интеграцијом који проверава све закрпе? Да ли се све закрпе прегледају и одобравају? Да ли сте ви укључени у тај процес? Да ли је примењен систем са поручницима, и да ли морате прво њима да пошаљете свој рад?

Следећи фактор је ваш комит приступ. Неопходни процес рада за давање доприноса пројекту је много другачији ако имате приступ писања пројекту него ако га немате. Ако немате приступ писања, како се одлучује да ли ће ваш рад бити прихваћен у пројекат? Да ли уопште постоји таква политика? Колико ствари мењате у једном тренутку? Колико често дајете допринос?

Сва ова питања могу да утичу на то колико ефикасно дајете допринос пројекту и какви процеси рада су пожељни или доступни вама. Покрићемо аспекте свих ових фактора у низу случајева коришћења, почевши од једноставних ка компликованијим; требало би да на основу ових примера будете у стању да конструишете одређене процесе рада који су вам потребни у пракси.

Смернице за комитове

Пре него што пређемо на одређене случајеве коришћења, ево неких кратких напомена о комит порукама. Поседовање добре смернице за креирање комитова и њено поштовање ће у великој мери учинити рад са програмом Гит и сарадњу са осталима много једноставнијом. Гит пројекат обезбеђује документ који излаже бројне добре савете за креирање комитова које треба употребити за слање закрпи — можете да их прочитате у изворном коду програма Гит у фајлу Documentation/SubmittingPatches.

У првом реду, не желите да рад који пошаљете поседује грешке у вези празних карактера. (whitespace errors). Програм Гит вам обезбеђује једноставан начин да ово проверите — пре него што комитујете, извршите git diff --check, што ће вам идентификовати и приказати евентуалне грешке у вези празних карактера.

Излаз команде `git diff --check`
Слика 56. Излаз команде git diff --check.

Ако ову команду покренете пре комитовања, видећете има ли грешака у вези празних карактера које бисте комитовали и које би сметале другим програмерима.

Затим, потрудите се да сваки комит буде логички издвојен скуп промена. Ако можете, пробајте да промене учините лако сварљивим — немојте да кодирате цео викенд радећи на пет различитих проблема, па да их онда у понедељак све стрпате у један огроман комит. Чак и ако не комитујете током викенда, употребите стејџ у понедељак тако да поделите свој рад на барем један комит по проблему који сте решавали, са корисним комит порукама. Ако неке промене модификују исти фајл, пробајте да употребите git add --patch да парцијално стејџујете фајлове (детаљније је обрађено у Интерактивно стејџовање). Снимак пројекта на врху гране је исти било да комитујете једном или пет пута, само је потребно да у неком тренутку све промене буду додате, зато се трудите да олакшате посао својим колегама програмерима када буду морали да прегледају ваше промене.

Овај приступ такође олакшава да се касније неки скуп измена издвоји или да се врати на неко од пређашњих стања. Поновно исписивање историје описује бројне корисне Гит трикове за преписивање историје и интерактивно стејџовање фајлова — употребите ове алате за изградњу чисте и јасне историје пре него што свој рад пошаљете другима.

Последња ствар коју треба да имате на уму је комит порука. Стицање навике да креирате квалитетне комит поруке учиниће коришћење програма Гит и сарадњу са другима много лакшом. Као опште правило, поруке треба да почну једном линијом која није дужа од око 50 карактера и која концизно описује скуп промена, за којом следи празна линија, а затим детаљније објашњење. Гит пројекат захтева да детаљније објашњење садржи и мотивацију за промену и да искаже измену понашања који њена примена уводи у односу на претходно понашање — ову смерницу је добро пратити. Такође је добра идеја да комит поруку пишете у императиву: „Fix bug” а не „Fixed bug” или „Fixes bug”. Другим речима, издајте наредбе. Ево шаблона који можете да следите, то је донекле измењена верзија коју је оригинално написао Тим Поуп:

Capitalized, short (50 chars or less) summary

More detailed explanatory text, if necessary.  Wrap it to about 72
characters or so.  In some contexts, the first line is treated as the
subject of an email and the rest of the text as the body.  The blank
line separating the summary from the body is critical (unless you omit
the body entirely); tools like rebase will confuse you if you run the
two together.

Write your commit message in the imperative: "Fix bug" and not "Fixed bug"
or "Fixes bug."  This convention matches up with commit messages generated
by commands like git merge and git revert.

Further paragraphs come after blank lines.

- Bullet points are okay, too

- Typically a hyphen or asterisk is used for the bullet, followed by a
  single space, with blank lines in between, but conventions vary here

- Use a hanging indent

Ако све ваше комит поруке прате овај узор, ствари ће бити много једноставније и за вас и за програмере са којима радите. Гит пројекат има добро форматиране комит поруке — покушајте тамо да покренете git log --no-merges и погледајте како изгледа лепо форматирана историја комитова у пројекту.

Белешка
Радите како вам кажемо, а не како ми радимо.

У циљу краћег изражавања, многи промери у овој књизи немају лепо форматиране комит поруке; уместо тога просто користимо -m опцију уз команду git commit.

Укратко, радите како вам кажемо, а не како ми радимо.

Мали приватни тим

Најједноставнија поставка на коју ћете вероватно наићи је приватни пројекат са једним или два друга програмера. У овом контексту „приватни” значи да је затвореног кода — није доступан остатку света. Ви и сви остали програмери имате дозволу да гурате промене на репозиторијум.

У оваквом окружењу, можете да следите процес рада сличан оном који се користи када употребљавате Subversion или неки други централизован систем. И даље добијате предности због ствари као што су комитовање ван мреже и знатно једноставније гранање и спајање, али процес рада може да буде веома сличан; основна разлика је то што се у време комитовања спајања раде на страни клијента уместо на страни сервера. Хајде да видимо како би ствар могла да изгледа када два програмера почну заједно да раде на дељеном репозиторијуму. Први програмер, Џон, клонира репозиторијум, направи измену и комитује локално. У овим примерима су поруке протокола замењене са …​ како би се донекле скратиле.

# Џонова машина
Cloning into 'simplegit'...
...
$ cd simplegit/
$ vim lib/simplegit.rb
$ git commit -am 'Remove invalid default value'
[master 738ee87] Remove invalid default value
 1 files changed, 1 insertions(+), 1 deletions(-)

Други програмер, Џесика, ради исту ствар — клонира репозиторијум и комитује промену:

# Џесикина машина
$ git clone jessica@githost:simplegit.git
Cloning into 'simplegit'...
...
$ cd simplegit/
$ vim TODO
$ git commit -am 'Add reset task'
[master fbff5bc] Add reset task
 1 files changed, 1 insertions(+), 0 deletions(-)

Сада Џесика гура свој рад на сервер и то функционише како треба:

# Џесикина машина
$ git push origin master
...
To jessica@githost:simplegit.git
   1edee6b..fbff5bc  master -> master

Последња линија претходног исписа приказује корисну поруку коју враћа операција гурања. Основни формат је <старареф>..<новареф> изреф → уреф, где старареф значи стара референца, новареф значи нова референца, изреф је име локалне референце која се гура и уреф је име удаљене референце која се ажурира. Даље у дискусији ћете видети испис сличан овом, тако да ће вам поседовање основне идеје значења помоћи да разумете разна стања у којима могу да се нађу репозиторијуми. Више детаља можете пронаћи у документацији команде git-push.

Даље у овом примеру, мало касније Џон прави неке измене које комитује у свој локални репозиторијум и покушава да их гурне на исти сервер:

# Џонова машина
$ git push origin master
To john@githost:simplegit.git
 ! [rejected]        master -> master (non-fast forward)
error: failed to push some refs to 'john@githost:simplegit.git'

У овом случају Џону није дозвољено да гурне своје промене јер је већ раније Џесика гурнула своје. Ово је посебно важно разумети ако сте навикли на Subversion, јер ћете приметити да њих двоје нису уређивали исти фајл. Мада Subversion аутоматски на серверу врши спајање ако су уређивани различити фајлови, u програму Git прво морате да спојите комитове локално. Другим речима, Џон најпре мора да преузме Џесикине узводне промене и да их споји са својим локалним репозиторијумом пре него што му буде дозвољено да гурне своје.

Као први корак, Џон преузима Џесикин рад (ово само преузима Џесикин узводни рад, још увек га не спаја са Џоновим радом):

$ git fetch origin
...
From john@githost:simplegit
 + 049d078...fbff5bc master     -> origin/master

У овом тренутку би Џонов локални репозиторијум требало да изгледа некако овако:

Џонова разграната историја
Слика 57. Џонова разграната историја

Сада Џон може да споји преузети Џесикин рад са својим локалним радом:

$ git merge origin/master
Merge made by the 'recursive' strategy.
 TODO |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

Све док спајање пролази глатко, Џонова ажурирана историја комитова ће сада изгледати овако:

Џонов репозиторијум после спајања `origin/master`
Слика 58. Џонов репозиторијум после спајања origin/master

Сада би Џон могао да тестира овај нови кôд како би потврдио да Џесикин рад уопште не утиче на његов, па ако све изгледа у реду, онда да напокон гурне свој ново спојени рад на сервер:

$ git push origin master
...
To john@githost:simplegit.git
   fbff5bc..72bbc59  master -> master

Konačno, Džonova istorija komitova ће izgledaти ovako:

Џонова историја комитова после гурања на `origin` сервер
Слика 59. Џонова историја комитова после гурања на origin сервер

У међувремену, Џесика је креирала је тематску грану issue54 и направила три комита на тој грани. Још увек није преузела Џонове промене, тако да њена историја комитова изгледа овако:

Џесикина тематска грана
Слика 60. Џесикина тематска грана

Џесика одједном схвата да је Џон гурнуо нов рад на сервер и жели да га погледа, тако да са сервера може да преузме сав садржај који још увек нема:

# Џесикина машина
$ git fetch origin
...
From jessica@githost:simplegit
   fbff5bc..72bbc59  master     -> origin/master

Ово повлачи рад који је Џон у међувремену гурнуо. Сада Џесикина историја изгледа овако:

Џесикина историја после преузимања Џонових промена
Слика 61. Џесикина историја после преузимања Џонових промена

Џесика мисли да је њена тематска грана спремна, али жели да сазна који део Џоновог преузетог посла треба да споји у свој рад тако да би могла да гурне промене. Извршава команду git log да сазна:

$ git log --no-merges issue54..origin/master
commit 738ee872852dfaa9d6634e0dea7a324040193016
Author: John Smith <jsmith@example.com>
Date:   Fri May 29 16:01:27 2009 -0700

   Remove invalid default value

Синтакса issue54..origin/master је филтер за лог који од програма Гит тражи да покаже само листу комитова који су на другој грани (у овом случају origin/master) а нису на првој грани (у овом случају issue54). Детаљније ћемо обрадити ову синтаксу у Опсези комитова.

У горњем излазу видимо да постоји само један комит који је Џон направио, а који Џесика није спојила у свој локални рад. Ако споји origin/master, то је једини комит који ће променити њен локални рад.

Сада Џесика може да споји своју тематску грану у своју master грану, да споји Џонов рад (origin/master) у своју master грану, па да онда поново гурне промене на сервер.

Најпре (након што је комитовала сав рад на својој issue54 тематској грани), Џесика скаче назад на своју master грану у циљу припреме за интеграцију свега што је урађено:

$ git checkout master
Switched to branch 'master'
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.

Џесика може прво да споји било origin/master или issue54 — обе се налазе узводно, па редослед спајања није важан. Крајњи снимак би требало да буде идентичан без обзира на то који редослед изабере; само ће историја бити донекле другачија. Бира да прво споји issue54 грану:

$ git merge issue54
Updating fbff5bc..4af4298
Fast forward
 README           |    1 +
 lib/simplegit.rb |    6 +++++-
 2 files changed, 6 insertions(+), 1 deletions(-)

Не јављају се проблеми; као што се види, радило се о једноставном спајању премотавањем унапред. Џесика сада довршава процес локалног спајања тако што спаја Џонов рад који раније преузела и који се налази у origin/master грани:

$ git merge origin/master
Auto-merging lib/simplegit.rb
Merge made by the 'recursive' strategy.
 lib/simplegit.rb |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

Sve se чисто spaja i сада Džesikina istorija izgleda ovako:

Џесикина историја након спајања Џонових промена
Слика 62. Џесикина историја након спајања Џонових промена

Сада је origin/master доступна из Џесикине master гране, па би требало да је стању успешно да гурне промене (под претпоставком да Џон у међувремену није гурнуо још промена):

$ git push origin master
...
To jessica@githost:simplegit.git
   72bbc59..8059c15  master -> master

Сваки програмер је неколико пута комитовао и успешно спојио рад оног другог у свој.

Џесикина историја након гурања свих промена назад на сервер
Слика 63. Џесикина историја након гурања свих промена назад на сервер

То је један од најједноставнијих процеса рада. Радите неко време (у општем случају на тематској грани), па спојите тај рад у master грану када буде спреман да се интегрише. Када желите да поделите тај рад, преузмете и спојите у сопствену master грану из origin/master ако се променила, па на крају гурнете master грану на сервер. Уопштени низ акција изгледа некако овако:

Уопштени низ догађаја за једноставан процес рада са више програмера
Слика 64. Уопштени низ догађаја за једноставан процес рада са више програмера

Приватни тим са руководством

У овом наредном сценарију сазнаћете улоге дориносилаца у већој приватној групи. Научићете како да радите у окружењу у којем мале групе сарађују на могућностима, па неко други интегрише те тимске доприносе.

Рецимо да Џон и Џесика раде заједно на једној могућности (нека се то зове „featureA”), док Џесика и трећи програмер Џоси раде на другој (рецимо „featureB”). У овом случају компанија користи неку врсту процеса рада са руководством за интеграцију у којем рад појединачних група интегришу само одређени инжењери и само они могу да ажурирају master грану главног репозиторијума. У овом сценарију, сав рад се обавља на тимски базираним гранама и касније га повлаче интегратори.

Хајде да пратимо Џесикин процес рада док ради на своје две могућности, односно док паралелно сарађује са два различита програмера у овом окружењу. Под претпоставком да већ има клониран репозиторијум, она одлучује да прво ради на featureA. Прави нову грану за могућност и ради нешто на њој:

# Џесикина машина
$ git checkout -b featureA
Switched to a new branch 'featureA'
$ vim lib/simplegit.rb
$ git commit -am 'Add limit to log function'
[featureA 3300904] Add limit to log function
 1 files changed, 1 insertions(+), 1 deletions(-)

У овом тренутку је потребно да подели свој рад са Џоном, па гура комитове са своје featureA гране на сервер. Џесика нема дозволу да гурне на master грану — то могу само интегратори — па мора да гурне на неку другу грану како би сарађивала са Џоном:

$ git push -u origin featureA
...
To jessica@githost:simplegit.git
 * [new branch]      featureA -> featureA

Џесика шаље мејл Џону у којем му каже да је гурнула неке ствари на грану која се зове featureA и да он сада може да погледа то. Док чека на повратну информацију од Џона, Џесика одлучује да почне посао на featureB са Џоси. За почетак, почиње нову грану за могућност и базира је на master грани са севера:

# Џесикина машина
$ git fetch origin
$ git checkout -b featureB origin/master
Switched to a new branch 'featureB'

Sada Džesika pravi nekoliko komitova na grani featureB:

$ vim lib/simplegit.rb
$ git commit -am 'Make ls-tree function recursive'
[featureB e5b0fdc] Make ls-tree function recursive
 1 files changed, 1 insertions(+), 1 deletions(-)
$ vim lib/simplegit.rb
$ git commit -am 'Add ls-files'
[featureB 8512791] Add ls-files
 1 files changed, 5 insertions(+), 0 deletions(-)

Сада Џесикин репозиторијум изгледа овако:

Џесикина иницијална историја комитова
Слика 65. Џесикина иницијална историја комитова

Спремна је да гурне свој рад, али добија мејл од Џоси да је грана са неким почетним радом на „featureB” већ гурнута на сервер под именом featureBee. Џесика најпре мора да споји те промене са сопственим пре него што буде у стању да гурне свој рад на сервер. Она преузима Џосине промене са git fetch:

$ git fetch origin
...
From jessica@githost:simplegit
 * [new branch]      featureBee -> origin/featureBee

Под претпоставком да је Џесика још увек на својој одјављеној грани featureB, сада може да споји Џосин рад са својим командом git merge:

$ git merge origin/featureBee
Auto-merging lib/simplegit.rb
Merge made by the 'recursive' strategy.
 lib/simplegit.rb |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

У овом тренутку Џесика жели да гурне сав овај спојени „featureB” рад назад на сервер, али не жели просто да гурне своју featureB грану. Пошто је Џоси већ започела узводну featureBee грану, Џесика жели да гурне у ту грану што чини са:

$ git push -u origin featureB:featureBee
...
To jessica@githost:simplegit.git
   fba9af8..cd685d1  featureB -> featureBee

Ово се зове рефспек (refspec). За детаљнију дискусију о Гит рефспековима и разним стварима које можете да урадите помоћу њих, погледајте Рефспек. Приметите и -u заставицу; ово је скраћено од --set-upstream, што конфигурише гране за касније лакше гурање и повлачење.

Одједном Џесика добија мејл од Џона који јој каже да је гурнуо неке промене на featureA грану на којој сарађују и моли је да их погледа. Она поново извршава git fetch да би преузела те промене:

$ git fetch origin
...
From jessica@githost:simplegit
   3300904..aad881d  featureA   -> origin/featureA

Џесика може да погледа лог Џоновог новог рада поређењем садржаја управо преузете featureA гране са својом локалном копијом те исте гране:

$ git log featureA..origin/featureA
commit aad881d154acdaeb2b6b18ea0e827ed8a6d671e6
Author: John Smith <jsmith@example.com>
Date:   Fri May 29 19:57:33 2009 -0700

    Increase log output to 30 from 25

Ако јој се допадне оно што види, спојиће Џонов нови рад у своју локалну featureA грану командом:

$ git checkout featureA
Switched to branch 'featureA'
$ git merge origin/featureA
Updating 3300904..aad881d
Fast forward
 lib/simplegit.rb |   10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)

На крају, Џесика пожели да направи неколико малих измена овог коначно спојеног садржаја, може слободно да их уради, па да их комитује у своју локалну featureA грану и гурне крајњи резултат назад на сервер:

$ git commit -am 'Add small tweak to merged content'
[featureA 774b3ed] Add small tweak to merged content
 1 files changed, 1 insertions(+), 1 deletions(-)
$ git push
...
To jessica@githost:simplegit.git
   3300904..774b3ed  featureA -> featureA

Џесикина историја комитова сада изгледа отприлике овако:

Џесикина историја након комитовања на грану могућности
Слика 66. Џесикина историја након комитовања на грану могућности

У једном тренутку, Џесика, Џоси и Џон обавештавају интеграторе да су гране featureA и featureBee на серверу спремне за интеграцију у главну линију. Када интегратори споје ове гране у главну линију, преузимање ће довлачити нови спојни комит, па ће историја изгледати овако некако:

Џесикина историја након спајања њених двеју тематских грана
Слика 67. Џесикина историја након спајања њених двеју тематских грана

Многе групе прелазе на Гит управо због ове могућности да више тимова ради паралелно, спајајући различите линије рада при крају процеса. Могућност програма Гит да мање подгрупе тима сарађују преко удаљених грана без потребе да у рад укључују или да успоравају читав тим је веома корисна. Процес рада који сте овде видели изгледа отприлике овако:

Основни процес рада оваквог приватног тима са руководством
Слика 68. Основни процес рада оваквог приватног тима са руководством

Рачвани јавни пројекат

Давање доприноса јавним пројектима се ради на мало другачији начин. Пошто немате дозволу да директно мењате гране на пројекту, свој рад морате на неки други начин да проследите одржаваоцима пројекта. Први пример описује давање доприноса пројекту путем рачвања на Гит хостовима који подржавају једноставно рачвање. Ово подржавају многи хостинг сајтови (укључујући GitHub, BitBucket, repo.or.cz, и остале), многи одржаваоци пројекта очекују овакав начин давања доприноса. Наредни одељак описује пројекте који закрпе доприноса радије примају имејлом.

Најпре ћете пожелети да клонирате главни репозиторијум, направите тематску грану за закрпу или низ закрпа које планирате да поднесете, па та ту радите свој посао. Процес у суштини изгледа овако:

$ git clone <url>
$ cd project
$ git checkout -b featureA
  ... рад ...
$ git commit
  ... рад ...
$ git commit
Белешка

Можда ћете хтети да искористите rebase -i и тако згњечите сав свој рад у један једини комит, или да преуредите рад у комитовима тако да одржавалац једноставније може прегледати закрпу — погледајте Поновно исписивање историје за више информација о интерактивном ребазирању.

Када је ваш рад на грани обављен и спремни сте да га проследите одржаваоцима, пређите на оригиналну страницу пројекта и кликните на „Fork” дугме; тако правите своју личну рачву пројекта у коју можете да уписујете. Онда треба да додате овај нови URL репозиторијума као нови удаљени вашег пројекта, назовимо га у овом случају myfork:

$ git remote add myfork <url>

Онда треба да гурнете свој рад на тај репозиторијум. Најлакше је да тематску грану на којој радите гурнете на свој рачвани репозиторијум, а не да тај рад спајате са својом master граном, па да онда то гурнете. Разлог за то је што у случају да се ваш рад не прихвати или се из њега нешто селективно узме (cherry-picked), не морате да премотавате уназад своју master грану (о Гит операцији cherry-pick се детаљније говори у Процеси рада са ребазирањем и одабиром (cherry-picking)). Ако одржаваоци изврше merge, rebase или cherry-pick вашег рада, у сваком случају ћете вратити назад тако што ћете повући са њиховог репозиторијума.

Свој рад свакако можете гурнути са:

$ git push -u myfork featureA

Када ваш рад гурнете на свој рачвани репозиторијум, треба да обавестите одржаваоце оригиналног пројекта да имате рад који бисте желели да споје. Ово се често назива захтевом за повлачење (pull request) и обично га генеришете или преко веб сајта — GitHub има свој сопствени „Pull Request” механизам захтева за повлачење који ћемо обрадити у GitHub — или можете да покренете команду git request-pull и ручно пошаљете њен излаз на имејл одржаваоца пројекта.

Команда git request-pull узима основну грану у коју желите да повучете своју тематску грану и URL Гит репозиторијума са ког желите да се повуче, па штампа сажетак свих промена које тражите да се повуку. На пример, ако Џесика жели да Џону пошаље захтев за повлачење, а урадила је два комита на тематској грани коју је управо гурнула, може да изврши следеће:

$ git request-pull origin/master myfork
The following changes since commit 1edee6b1d61823a2de3b09c160d7080b8d1b3a40:
Jessica Smith (1):
        Create new function

are available in the git repository at:

  git://githost/simplegit.git featureA

Jessica Smith (2):
      Add limit to log function
      Increase log output to 30 from 25

 lib/simplegit.rb |   10 +++++++++-
 1 files changed, 9 insertions(+), 1 deletions(-)

Овај излаз може да се пошаље одржаваоцу — он му говори одакле је рад разгранат, резимира комитове и каже му одакле да повуче овај рад.

На пројекту који ви не одржавате, у општем случају је лакше да имате грану као што је master која увек прати origin/master и да свој посао радите на тематским гранама које лако можете да одбаците ако буду одбијене. Ако се у међувремену врх главног репозиторијума помери, па ваши комитови више не могу чисто да се споје, чињеница да је тематски рад изолован у тематске гране вам омогућава да лакше ребазирате свој рад. На пример, ако желите да пошаљете другу тему рада на пројекат, немојте настављати да радите на тематској грани коју сте управо гурнули узводно — почните од master гране главног репозиторијума:

$ git checkout -b featureB origin/master
  ... рад ...
$ git commit
$ git push myfork featureB
$ git request-pull origin/master myfork
  ... слање генерисаног захтева за повлачење имејлом одржаваоцу ...
$ git fetch origin

Сада је свака од тема садржана унутар силоса — слично као ред закрпа — које можете да ребазирате, измените и пишете преко њих а да се теме не мешају међусобно и да не зависе једна од друге, овако:

Инцијална историја комитова са радом `featureB`
Слика 69. Инцијална историја комитова са радом featureB

Рецимо да је одржавалац пројекта повукао гомилу других закрпа и пробао вашу прву грану, али она се више не спаја без проблема. У том случају можете пробати да ребазирате ту грану на врх origin/master гране, да решите конфликте за одржаваоца, и онда поново пошаљете своје промене:

$ git checkout featureA
$ git rebase origin/master
$ git push -f myfork featureA

Ово поново исписује вашу историје тако да сада изгледа као Историја комитова после рада featureA.

Историја комитова после рада `featureA`
Слика 70. Историја комитова после рада featureA

Пошто сте ребазирали грану, да бисте на серверу могли заменити грану featureA комитом који није њен потомак, у команди push морате да наведете -f. Други начин би могао бити да овај нови рад гурнете на другу грану на серверу (која се рецимо зове featureAv2).

Погледајмо још један могући сценарио: одржавалац је погледао ваш рад у другој грани и свиђа му се концепт, али би волео да направите промену у неком детаљу имплементације. Искористићете ову прилику и да преместите рад тако да буде базиран на тренутној master грани пројекта. Почећете нову грану базирану на тренутној origin/master грани, ту ћете да згњечите featureB промене, решићете евентуалне конфликте, направити измену имплементације и онда гурнути то као нову грану:

$ git checkout -b featureBv2 origin/master
$ git merge --squash featureB
  ... измена имплементације ...
$ git commit
$ git push myfork featureBv2

Опција --squash узима сав рад на спојеној грани и сабија га у један скуп промена, правећи тако стање репозиторијума као да се догодило право спајање, а да заправо не креира комит спајања. Ово значи да ће ваш будући комит имати само једног родитеља и дозвољава вам да уведете све промене са друге гране па да онда направите још промена пре него што забележите нови комит. Опција --no-commit такође може да буде корисна да одложите комит спајања у случају подразумеваног процеса спајања.

Сада можете да обавестите одржаваоце да сте направили захтеване измене и да могу да их пронађу у вашој featureBv2 грани.

Историја комитова након рада на `featureBv2`
Слика 71. Историја комитова након рада на featureBv2

Јавни пројекат преко имејла

Многи пројекти имају утврђене процедуре за прихватање закрпи — мораћете да проверите одређена правила за сваки пројекат, јер ће се разликовати. Пошто постоји неколико старијих већих пројеката који прихватају закрпе преко мејлинг листе програмера, сада ћемо прећи тај пример.

Процес рада је сличан као у претходном случају — правите тематске гране за сваки низ закрпа на којем радите. Разлика је у томе како их шаљете пројекту. Уместо да рачвате пројекат и гурате на своју личну верзију за коју имате право уписа, генеришете имејл верзије сваког низа комитова и шаљете их на мејлинг листу програмера.

$ git checkout -b topicA
  ... рад ...
$ git commit
  ... рад ...
$ git commit

Сада имате два комита која желите да пошаљете на мејлинг листу. Извршавате git format-patch да генеришете mbox форматиране фајлове које можете да шаљете на листу — команда претвара сваки комит у имејл поруку у којој прва линија комит поруке постаје тема имејла, а остатак комит поруке и закрпа коју тај комит постаје тело имејла. Добра ствар у вези с овиме је то што примењивање закрпе из имејла који је генерисала команда format-patch исправно очувава све информације о комиту.

$ git format-patch -M origin/master
0001-add-limit-to-log-function.patch
0002-increase-log-output-to-30-from-25.patch

Команда format-patch исписује имена фајлова закрпа које креира. Заставица -M налаже програму Гит да тражи фајлове којима је промењено име. Фајлови на крају изгледају овако:

$ cat 0001-add-limit-to-log-function.patch
From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith <jessica@example.com>
Date: Sun, 6 Apr 2008 10:17:23 -0700
Subject: [PATCH 1/2] add limit to log function

Limit log functionality to the first 20

---
 lib/simplegit.rb |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index 76f47bc..f9815f1 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -14,7 +14,7 @@ class SimpleGit
   end

   def log(treeish = 'master')
-    command("git log #{treeish}")
+    command("git log -n 20 #{treeish}")
   end

   def ls_tree(treeish = 'master')
--
2.1.0

Ове фајлове са закрпама можете и да уређујете тако да додате више информација за мејлинг листу које не желите да се појаве у комит поруци. Ако додате текст између --- линије и почетка закрпе (линија diff --git), онда ће програмери моћи да га прочитају, али процес примењивања закрпе га игнорише.

Да бисте ово послали на листу, можете или да налепите фајл у свој имејл програм или да га пошаљете програмом командне линије. Налепљивање текста често ствара проблеме са форматирањем, поготово са „паметнијим” клијентима који не задржавају карактере прелом линије и остале карактере празног простора. Срећом, програм Гит поседује алат који ће вам помоћи да IMAP протоколом шаљете добро форматиране закрпе, што би могло да вам буде лакше. Показаћемо како да пошаљете закрпу преко сервиса Gmail, што је имејл агент који најбоље познајемо; можете да прочитате детаљне инструкције за многе имејл програме на крају већ поменутог фајла Documentation/SubmittingPatches у изворном коду програма Гит.

Прво, треба да подесите imap одељак у ~/.gitconfig фајлу. Сваку вредност можете посебно да подесите низом git config команди, или можете ручно да их додате, али на крају би ваш config фајл требало да изгледа отприлике овако:

[imap]
  folder = "[Gmail]/Drafts"
  host = imaps://imap.gmail.com
  user = user@gmail.com
  pass = YX]8g76G_2^sFbd
  port = 993
  sslverify = false

Ако ваш IMAP сервер не користи SSL, последње две линије вероватно нису неопходне и host вредност ће уместо imaps:// бити imap://. Кад је то постављено, командом git imap-send можете да поставите низове закрпа у Drafts фолдер наведеног IMAP сервера:

$ cat *.patch |git imap-send
Resolving imap.gmail.com... ok
Connecting to [74.125.142.109]:993... ok
Logging in...
sending 2 messages
100% (2/2) done

Сада би требало да можете да одете у Drafts фолдер, промените To поље у мејлинг листу на коју шаљете закрпу и евентуално ставите у CC одржаваоца или особу које је одговорна за тај одељак, па да пошаљете имејл.

Закрпе можете да шаљете и преко SMTP сервера. Као и раније, можете да подесите посебно сваку вредност низом git config команди, или да их ручно додате у sendmail одељак ~/.gitconfig фајла:

[sendemail]
  smtpencryption = tls
  smtpserver = smtp.gmail.com
  smtpuser = user@gmail.com
  smtpserverport = 587

Када ово урадите, командом git send-email можете да пошаљете своје закрпе:

$ git send-email *.patch
0001-add-limit-to-log-function.patch
0002-increase-log-output-to-30-from-25.patch
Who should the emails appear to be from? [Jessica Smith <jessica@example.com>]
Emails will be sent from: Jessica Smith <jessica@example.com>
Who should the emails be sent to? jessica@example.com
Message-ID to be used as In-Reply-To for the first email? y

Програм Гит затим избаци гомилу лог информација које изгледају некако овако за сваку закрпу коју шаљете:

(mbox) Adding cc: Jessica Smith <jessica@example.com> from
  \line 'From: Jessica Smith <jessica@example.com>'
OK. Log says:
Sendmail: /usr/sbin/sendmail -i jessica@example.com
From: Jessica Smith <jessica@example.com>
To: jessica@example.com
Subject: [PATCH 1/2] Add limit to log function
Date: Sat, 30 May 2009 13:29:15 -0700
Message-Id: <1243715356-61726-1-git-send-email-jessica@example.com>
X-Mailer: git-send-email 1.6.2.rc1.20.g8c5b.dirty
In-Reply-To: <y>
References: <y>

Result: OK
Савет

За помоћ око конфигурисања вашег система и имејла, још савета и трикова, као и за сендбокс у који можете имејлом да пошаљете пробну закрпу, посетите git-send-email.io.

Резиме

Овим одељком смо покрили неколико процеса рада и говорили о разликама рада када сте део малог тима на пројектима затвореног кода у односу на давање доприноса великом јавном пројекту. Сада знате како да проверите да ли постоје грешке празних карактера пре него што комитујете и можете да пишете одличне комит поруке. Научили сте како да форматирате закрпе и како да их имејлом пошаљете на програмерску мејлинг листу. Такође смо покрили и рад са спајањима у контексту различитих процеса рада. Сада сте добро припремљени да дајете допринос било ком пројекту.

У наставку ћете научити како да направите другу страну новчића: да одржавате Гит пројекат. Научићете како да будете благонаклони диктатор или руководилац интеграцијом.

scroll-to-top