Git
Chapters ▾ 2nd Edition

3.1 Гранање у програму Гит - Укратко о гранању

Скоро сваки VCS има неку врсту подршке за гранање. Гранање значи да се одвајате од главне линије развоја програма и да настављате рад без утицаја на ту главну линију. У многим VCS алатима, ово је донекле захтеван процес који укључује прављење нове копије директоријума са изворним кодом, што у случају великих пројеката може да потраје.

Неки људи модел гранања у програму Гит зову првокласном особином и заиста, та могућност издваја програм Гит из мноштва осталих VCS система. Зашто је то тако посебно? Начин на који програм Гит прави гране је невероватно једноставан за обраду, што чини да се операције гранања извршавају скоро тренутно, а скакање с једне на другу грану је углавном подједнако брзо. За разлику од многих других VCS система, програм Гит охрабрује процесе рада који често користе гранање и спајање, чак и неколико пута током једног дана. Разумевањем и овладавањем овом техником добијате моћно и јединствено оруђе које у потпуности може променити начин на који развијате свој производ.

Укратко о гранању

Да бисмо заиста разумели како програм Гит барата гранањем, морамо да се вратимо корак уназад и да истражимо како програм Гит чува податке.

Као што се можда сећате из Шта је Гит?, програм Гит не чува податке као низ скупова промена или разлика, већ као низ снимака.

Када направите комит, program Гит чува комит објекат који садржи показивач на снимак садржаја који сте стејџовали. Овај објекат такође садржи и ауторово име и мејл адресу, поруку која је унесена, као и показиваче на комит или комитове који су директно претходили овом комиту (тј. његовог родитеља или родитеље): нула родитеља за почетни комит, једног родитеља за нормални комит, и више родитеља за комит који је резултат спајања две или више грана.

Да бисмо ово сликовито приказали, претпоставимо да имате директоријум који садржи три фајла и да их све стејџујете, а затим комитујете. Стејџовање фајлова рачуна контролну суму сваког од њих (SHA-1 хеш као што смо поменули у Шта је Гит?), чува ту верзију фајла у Гит репозиторијум (програм Гит то назива блобовима) и додаје ту контролну суму на стејџ:

$ git add README test.rb LICENSE
$ git commit -m 'Initial commit'

Када са git commit направите комит, програм Гит прави контролну суму сваког поддиректоријума (у овом случају, само корени директоријум пројекта) и чува их у Гит репозиторијум као стабло. Програм Гит онда креира комит објекат који има метаподатке и показивач на корен стабла пројекта тако да поново може креирати тај снимак онда када буде био потребан.

Ваш Гит репозиторијум сада садржи пет објеката: три блоба (од којих сваки представља садржај једног од три фајла), једно стабло које садржи листу садржаја директоријума и наводи која имена фајлова се чувају у ком блобу, као и један комит са показивачем на тај корен стабла и све комит метаподатке.

Комит и његово стабло
Слика 9. Комит и његово стабло

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

Комитови и њихови родитељи
Слика 10. Комитови и њихови родитељи

Грана у програму Гит је просто један мали покретни показивач на неки од ових комитова. Подразумевано име гране у програму Гит је master. Када почнете да комитујете, даје вам се master грана која показује на последњи комит који сте направили. Сваки пут када комитујете, показивач master гране се аутоматски креће унапред.

Белешка

„master” грана у програму Гит није посебна грана. Она је потпуно иста као и свака друга грана. Једини разлог због којег скоро сваки репозиторијум има такву грану је то што је команда git init подразумеваним направи, а већина људи нема потребу да је мења.

Грана и њена историја комитова
Слика 11. Грана и њена историја комитова

Прављење нове гране

Шта се дешава када направите нову грану? Па, када то урадите, креира се нови показивач којим се крећете унаоколо. Рецимо да направите нову грану коју ћете назвати testing. То се ради командом git branch:

$ git branch testing

Ово прави нови показивач на исти комит на којем се тренутно налазите.

Две гране које показују на исти низ комитова
Слика 12. Две гране које показују на исти низ комитова

Како програм Гит зна на којој грани се тренутно налазите? Он чува посебан показивач који се зове HEAD. Обратите пажњу на то да је ово много другачије од HEAD концепта у осталим VCS системима на које сте можда навикли, као што су Subversion или CVS. У програму Гит, ово је показивач на локалну грану на којој се тренутно налазите. У овом случају, још увек сте на master грани. Командом git branch сте само направили нову грану — нисте прешли на њу.

`HEAD` показује на грану
Слика 13. HEAD показује на грану

Ово лако можете да видите тако што ћете извршити обичну git log команду која вам приказује на шта показују показивачи грана. Ова опција се зове --decorate.

$ git log --oneline --decorate
f30ab (HEAD -> master, testing) add feature #32 - ability to add new formats to the central interface
34ac2 Fixed bug #1328 - stack overflow under certain conditions
98ca9 Initial commit

Видите да су master и testing гране одмах поред комита f30ab.

Мењање грана

Када желите да пређете на неку постојећу грану, извршавате команду git checkout. Хајде да пређемо на нову грану testing:

$ git checkout testing

Ово помера показивач HEAD тако да показује на грану testing.

`HEAD` показује на тренутну грану
Слика 14. HEAD показује на тренутну грану

Зашто је ово битно? Па, хајде да урадимо још један комит:

$ vim test.rb
$ git commit -a -m 'made a change'
`HEAD` грана се помера унапред када се направи комит
Слика 15. HEAD грана се помера унапред када се направи комит

Ово је занимљиво, јер се сада testing грана померила унапред, али ваша master грана још увек показује на комит на коме сте били када сте извршили git checkout да промените гране. Хајде да се вратимо назад на грану master:

$ git checkout master
Белешка
git log не приказује све гране све време

Ако бисте сада извршили git log, запитали бисте се где нестаде „testing” грана коју сте управо креирали, јер је нема у излазу команде.

Грана није нестала; програм Git једноставно не зна да вас та грана интересује и покушава да вам прикаже оно што мисли да вас интересује. Другим речима, команда git log ће подразумевано да прикаже само историју комитова испод гране коју сте одјавили.

Ако желите да видите историју комитова неке гране, морате експлицитно да је наведете: git log testing. Ако желите да видите све гране, додајте --all у своју git log команду.

`HEAD` се помера када извршите одјављивање
Слика 16. HEAD се помера када извршите одјављивање

Ова команда је урадила две ствари. Померила је показивач HEAD назад на место у грани master и вратила је фајлове у радном директоријуму на снимак на који показује master. Ово такође значи и да ће се промене које правите одсад па надаље одвојити од старије верзије пројекта. Команда у суштини премотава уназад, поништавајући рад у testing грани, како бисте могли да кренете другим путем.

Белешка
Мењање грана мења фајлове у радном директоријуму

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

Хајде да направимо неколико промена и поново комитујемо:

$ vim test.rb
$ git commit -a -m 'made other changes'

Сада се историја вашег пројекта раздвојила (погледајте Раздвојена историја). Направили сте грану, пребацили сте се на њу, урадили нешто у њој, и онда се вратили назад на главну грану и урадили још мало посла. Обе ове промене су изоловане у посебним гранама: можете да скачете с једне на другу напред-назад и да их спојите онда када будете спремни. И све сте то урадили простим командама branch, checkout и commit.

Раздвојена историја
Слика 17. Раздвојена историја

Ово лако можете видети и са git log командом. Ако извршите git log --oneline --decorate --graph --all, исписаће вам се историја комитова, показујући вам где се сада налазе показивачи на гране и како се историја раздвајала.

$ git log --oneline --decorate --graph --all
* c2b9e (HEAD, master) made other changes
| * 87ab2 (testing) made a change
|/
* f30ab add feature #32 - ability to add new formats to the
* 34ac2 fixed bug #1328 - stack overflow under certain conditions
* 98ca9 Initial commit

Пошто је грана у програму Гит заправо једноставан фајл који садржи 40 карактера SHA-1 контролне суме комита на који показује комит, прављење и уништавање грана је јефтино. Креирање нове грану је брзо и једноставно колико и уписивање 41 бајта у фајл (40 карактера и карактер за прелом линије).

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

Хајде да погледамо зашто и ви треба то да радите.

Белешка
Креирање нове гране и истовремени прелаз на њу

Врло је чест случај да желите креирати нову грану и да се одмах пребаците у њу — то можете обавити у једном кораку командом git checkout -b <именовегране>.

Белешка

Почевши од програма Гит верзије 2.23 па надаље уместо git checkout можете употребити git switch за:

  • Пребацивање на постојећу грану: git switch testing-branch.

  • Креирање нове гране и прелазак на њу: git switch -c new-branch. Заставица -c стоји уместо create (креирај), али можете да употребите и комплетну заставицу: --create.

  • Повратак на грану коју сте претходно одјавили: git switch -.