-
1. Почетак
- 1.1 О контроли верзије
- 1.2 Кратка историја програма Гит
- 1.3 Шта је Гит?
- 1.4 Командна линија
- 1.5 Инсталирање програма Гит
- 1.6 Подешавања за први пут
- 1.7 Тражење помоћи
- 1.8 Резиме
-
2. Основе програма Гит
- 2.1 Прављење Гит репозиторијума
- 2.2 Снимање промена над репозиторијумом
- 2.3 Преглед историје комитова
- 2.4 Опозив
- 2.5 Рад са удаљеним репозиторијумима
- 2.6 Означавање
- 2.7 Гит алијаси
- 2.8 Резиме
-
3. Гранање у програму Гит
- 3.1 Укратко о гранању
- 3.2 Основе гранања и спајања
- 3.3 Управљање гранама
- 3.4 Процеси рада са гранањем
- 3.5 Удаљене гране
- 3.6 Ребазирање
- 3.7 Резиме
-
4. Гит на серверу
- 4.1 Протоколи
- 4.2 Постављање програма Гит на сервер
- 4.3 Генерисање јавног SSH кључа
- 4.4 Подешавање сервера
- 4.5 Гит демон
- 4.6 Паметан HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Опције за хостовање које нуде трећа лица
- 4.10 Резиме
-
5. Дистрибуирани Гит
-
6. GitHub
-
7. Гит алати
- 7.1 Избор ревизија
- 7.2 Интерактивно стејџовање
- 7.3 Скривање и чишћење
- 7.4 Потписивање вашег рада
- 7.5 Претрага
- 7.6 Поновно исписивање историје
- 7.7 Демистификовани ресет
- 7.8 Напредно спајање
- 7.9 Rerere
- 7.10 Отклањање грешака са програмом Git
- 7.11 Подмодули
- 7.12 Паковање
- 7.13 Замена
- 7.14 Складиште акредитива
- 7.15 Резиме
-
8. Прилагођавање програма Гит
- 8.1 Конфигурисање програма Гит
- 8.2 Гит атрибути
- 8.3 Гит куке
- 8.4 Пример полисе коју спроводи програм Гит
- 8.5 Резиме
-
9. Гит и остали системи
- 9.1 Гит као клијент
- 9.2 Мигрирање на Гит
- 9.3 Резиме
-
10. Гит изнутра
- 10.1 Водовод и порцелан
- 10.2 Гит објекти
- 10.3 Гит референце
- 10.4 Pack фајлови
- 10.5 Рефспек
- 10.6 Протоколи за пренос
- 10.7 Одржавање и опоравак податак
- 10.8 Променљиве окружења
- 10.9 Резиме
-
A1. Додатак А: Програм Гит у другим окружењима
- A1.1 Графички интерфејси
- A1.2 Гит у Visual Studio
- A1.3 Гит у Visual Studio Code
- A1.4 Гит у IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine
- A1.5 Гит у Sublime Text
- A1.6 Гит унутар Bash
- A1.7 Гит у Zsh
- A1.8 Гит у Powershell
- A1.9 Резиме
-
A2. Додатак Б: Уграђивање програма Гит у ваше апликације
- A2.1 Гит из командне линије
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. Додатак В: Гит команде
- A3.1 Подешавање и конфигурација
- A3.2 Набављање и креирање пројеката
- A3.3 Основно снимање
- A3.4 Гранање и спајање
- A3.5 Дељење и ажурирање пројеката
- A3.6 Инспекција и поређење
- A3.7 Отклањање грешака
- A3.8 Крпљење
- A3.9 Имејл
- A3.10 Спољни системи
- A3.11 Администрација
- A3.12 Водоводне команде
3.6 Гранање у програму Гит - Ребазирање
Ребазирање
У програму Гит постоје два основна начина за интеграцију промена из једне гране у другу: merge
и rebase
.
У овом одељку ћете научити шта је ребазирање, како се ради, зашто је то прилично добар алат, као и када треба а када не треба да га користите.
Основно ребазирање
Ако погледате ранији пример из Основе спајања, видећете да сте разгранали свој рад и направили комитове на две различите гране.
Као што смо већ раније показали, најлакши начин да интегришете гране је помоћу команде merge
.
Она ће урадити троструко спајање између два последња снимка са грана (C3
и C4
) и њиховог најсвежијег заједничког претка (C2
), стварајући нови снимак (и комит).
Међутим, постоји још један начин: можете да узмете закрпу промене која је уведена у C4
и да је поново примените преко C3
.
У програму Гит се ово зове ребазирање.
rebase
командом можете да узмете све промене које су комитоване у једну грану и да их поновите у некој другој.
У овом примеру, одјавили бисте грану experiment
, па је затим ребазирали преко master
гране на следећи начин:
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
Ова операција функционише тако што оде на заједничког претка двеју грана (оне на којој се тренутно налазите и оне преко које ребазирате), узима разлику која је створена сваким комитом у грани на којој се налазите, чува те разлике у привремене фајлове, ресетује тренутну грану на исти комит на коме је и грана преко које ребазирате и коначно, редом примењује сваку промену.
C4
преко C3
У овом тренутку можете да се вратите назад на master
грану и да урадите спајање техником премотавања унапред.
$ git checkout master
$ git merge experiment
master
гране унапредСада је снимак на који показује C4'
потпуно исти као и онај на који је показивао C5
у примеру спајања.
Нема разлике у крајњем производу интеграције, али ребазирањем се постиже чистија историја.
Ако истражите лог ребазиране гране, изгледа као линеарна историја: изгледа као да се сав рад одвијао серијски, иако су се ствари заправо одвијале паралелно.
Ово ћете често радити када желите се ваши комитови примене чисто на удаљену грану — можда у пројекту којем желите да дате допринос, али који не одржавате.
У том случају, свој посао бисте радили у једној грани, па када будете спремни да пошаљете своје закрпе главном пројекту, ребазираћете свој рад преко origin/master
.
На овај начин, одржавалац не мора да ради никакав посебан посао око интеграције — само треба да премота унапред или одради чисто примењивање.
Обратите пажњу на то да је завршни снимак на који показује коначни комит, било да је то последњи од ребазираних комитова у случају ребазирања, или коначни комит спајања након спајања, један те исти — само се историја разликује. Ребазирање понавља у некој другој све промене урађене у једној линији рада и то редом којим су прављене, док спајање узима крајње тачке и спаја их.
Интересантнији случајеви ребазирања
Ребазирање може да понови измене и над нечему другом, што није циљна грана ребазирања.
На пример, узмите историју као што је приказана на Историја са тематском граном разгранатом од друге тематске гране.
Разгранали сте тематску грану (server
) да бисте у свој пројекат додали неку функционалност са серверске стране, па направили комит.
Онда сте разгранали и од ње да бисте направили неке промене на клијентској страни (client
), па комитовали неколико пута.
Коначно, вратили сте се на server
грану и направили још неколико комитова.
Претпоставимо да сте одлучили да желите спојити ваше промене на клијентској страни са главном граном како би се објавиле, али желите да одложите промене на серверској страни док их боље не тестирате.
Можете да узмете промене са client
гране које нису на server
грани (C8
и C9
) и да их поновите преко master
гране користећи опцију --onto
команде git rebase
:
$ git rebase --onto master server client
Ово у суштини каже „Провери грану client
, одреди закрпе које су настале након што се одвојила од server
гране, па их онда поново примени у грани client
као да је уместо од server
била одвојена директно од master
гране”.
Мало је сложено, али резултат је одличан.
Сада можете да премотате унапред грану master
(погледајте Премотавање master
гране унапред тако да обухвати промене са гране client
):
$ git checkout master
$ git merge client
master
гране унапред тако да обухвати промене са гране client
Рецимо да сте одлучили да повучете и промене из server
гране.
server
грану можете да ребазирате преко master
гране са git rebase <основна_грана> <тематска_грана>
без потребе да је прво одјавите — ова команда прво одјави тематску грану (у овом случају server
) и примењује пронађене промене на основну грану master
):
$ git rebase master server
Ово понавља рад са server
гране преко master
гране, као што се види на Ребазирање server
гране преко master
гране.
server
гране преко master
гранеЗатим премотате унапред основну грану (master
):
$ git checkout master
$ git merge server
Сада можете да обришете гране client
и server
јер је сав рад обављен на њима интегрисан и више вам неће бити потребне, а историја рада након овог процеса ће изгледати као на Коначна историја комитова:
$ git branch -d client
$ git branch -d server
Опасности ребазирања
Ах, али блаженство ребазирања није без мана, што се може сумирати само једном реченицом:
Не ребазирајте комитове који постоје ван вашег репозиторијума и на којима су људи можда засновали свој рад.
Ако се држите ове смернице, све ће бити у реду. У супротном ће вас људи мрзети, а породица и пријатељи ће вас презирати.
Када нешто ребазирате, ви напуштате постојеће комитове и стварате нове који су им слични, али су ипак другачији.
Ако комитове гурнете негде и остали их повуку, па базирају свој рад над њима, а ви затим поново напишете те комитове са git rebase
и гурнете их поново, ваши сарадници ће морати да поново споје сав свој рад и онда ће настати хаос када пробате да повучете њихов рад назад у свој.
Погледајмо пример који показује како ребазирани рад који сте учинили јавно доступним може изазвати проблеме. Претпоставимо да сте направили клон са централног сервера и онда радили нешто почевши од њега. Историја комитова изгледа овако:
Сада, неко други уради још нешто што укључи и спајање, а затим гурне све на централни сервер. Ви то преузмете и спојите нову удаљену грану са оним што сте урадили, тако да историја изгледа некако овако:
Затим, особа која је гурнула спојен рад одлучи да се врати назад и уместо спајања ребазира оно што је одрадила; изврши git push --force
како би се преписала историја на серверу.
Ви онда преузмете податке са тог сервера, довлачећи нове комитове.
Сада сте обоје у сосу.
Ако извршите git pull
, направићете комит спајања који укључује обе линије историје, и ваш репозиторијум ће изгледати овако:
Ако извршите git log
када ваша историја изгледа овако, видећете два комита који имају истог аутора, време и поруку, што ће унети забуну.
Штавише, ако гурнете ову историју назад на сервер, поново ћете увести све те ребазиране комитове на централни сервер, што ће још више збунити људе.
Прилично је безбедно претпоставити се да други програмер не жели да се C4
и C6
нађу у историји; то је разлог зашто су и радили ребазирање.
Ребазирање када ребазирате
Ако се ипак нађете у оваквој ситуацији, програм Гит има још неке чаролије које вам могу помоћи. Ако неко из тима насилно гурне промене које препишу рад над којем сте ви базирали свој рад, изазов који вам се намеће је да одредите шта је ваше, а шта је та особа преписала.
Испоставља се да поред SHA-1 контролне суме комита, програм Гит рачуна и контролну суму која је базирана само на закрпи која је уведена комитом. Ово се зове „идентификациони број закрпе” (patch-id).
Ако повучете рад који је преписан и ребазирате га преко нових комитова вашег партнера, програм Гит често сам може успешно да одреди шта је јединствено ваше и да примени то назад на врх нове гране.
На пример, ако у претходном сценарију када смо били код Неко гурне ребазиране комитове, напуштајући комитове над којима сте базирали ваш рад уместо спајања извршимо git rebase teamone/master
, програм Гит ће:
-
одредити који рад је јединствен за нашу грану (
C2
,C3
,C4
,C6
иC7
), -
одредити шта нису комитови спајања (
C2
,C3
иC4
), -
одредити шта није било преписано у одредишну грану (само
C2
иC3
, пошто јеC4
иста закрпа као иC4'
) и -
применити те комитове на врх
teamone/master
гране.
Тако да ћемо, уместо резултата који видимо на Поновно спајање истог рада у нови комит спајања, добити нешто што више подсећа на Ребазирање преко насилно гурнутог ребазираног рада.
Ово ће функционисати само ако су C4
и C4'
који је ваш партнер направио скоро идентична закрпа.
У супротном, ребазирање неће моћи да установи да је то дупликат и додаће још једну закрпу која подсећа на C4
(и која вероватно неће моћи чисто да се примени, јер би промене бар донекле већ биле тамо).
Ово можете да упростите и извршавањем git pull --rebase
уместо обичног git pull
.
Или можете то ручно да урадите са git fetch
за којим у овом случају следи git rebase teamone/master`.
Ако користите git pull
и желите да --rebase
буде подразумевана опција, можете да подесите pull.rebase
вредност из конфигурационог фајла на true
са git config --global pull.rebase true
.
Ако само ребазирате комитове који никада нису напустили ваш рачунар, све ће бити у реду. Ако ребазирате комитове који су били гурнути, али нико други није на њима базирао свој рад, такође неће бити проблема. Ако ребазирате комитове који су већ гурнути јавно и могуће је да су људи базирали свој рад на тим комитовима, онда ћете се наћи у фрустрирајућим ситуацијама и бићете мета презира својих сарадника.
Ако ви или партнер у неком тренутку схватите да је овакав след догађаја неопходан, постарајте се да сви остали знају да треба да изврше git pull --rebase
и тако пробају да макар донекле упросте проблем који настаје након ребазирања.
Ребазирање против спајања
Сада када сте видели како функционише ребазирање а како спајање, можда се питате шта је боље. Пре него што дамо одговор на ово, хајде да начинимо корак уназад и попричамо мало о томе шта је заправо историја.
Једна тачка гледишта је да историја комитова вашег репозиторијума представља запис онога што се заправо догодило. То је историјски документ, вредан сам по себи, па не би требало да се преправља. Из овог угла, мењање историје комита је скоро па богохуљење; ви лажете о ономе што се заправо догодило. Шта онда радити када се догоди серија збрканих комитова спајања? Па, ствари су се тако догодиле и репозиторијум треба да сачува то за потомство.
Супротна тачка гледишта је да историја комитова представља причу о томе како је пројекат направљен.
Не бисте објавили прву скицу књиге, па зашто да прикажете свој траљави посао?
Када радите на пројекту, може вам бити потребан запис о свим погрешним корацима које се начинили и свим ћорсокацима у које сте ушли, али када дође време да објавите свету свој рад, пожелећете да прикажете прецизнију причу о томе како се долази од тачке А до тачке Б.
Овај табор користи алате као што је ребазирање и филтер гране да поново испише комитове пре него што се споје у главну грану.
Употребом алата rebase
и filter-branch
они причају причу на начин који је најбољи за будуће читаоце.
Сада, што се тиче питања да ли је боље спајање или ребазирање: надамо се да ћете увидети да ствари нису тако једноставне. Програм Гит је моћан алат, допушта вам да урадите многе ствари са својом историјом, али сваки тим и сваки пројекат је другачији. Сада када знате како обе ове ствари раде, на вама је да одлучите шта је боље за вашу конкретну ситуацију.
А можете да добијете и најбоље из оба света: ребазирајте локалне промене него што их гурнете како бисте пречистили свој рад, али никада немојте да ребазирате било шта што сте негде гурнули.