Спојити или мерџовати — то је операција спајања две гране у Git-у, која обједињује измене из једне гране у другу. У савременом развоју, мерџ је стандардни начин интеграције feature гране у главну грану пројекта. Према GitHub Octoverse 2024, дневно се изврши преко 15 милиона мерџева. Merge — кључни механизам колаборативног рада који омогућава обједињавање рада више програмера у један производ.
Главно
Мерџ у Git-у — операција спајања две или више историја развоја у једну. Када програмер споји грану, Git аутоматски проналази заједничког претка (base commit) и креира нови комит спајања који укључује измене из обе гране. Three-way merge — стандардни алгоритам који пореди три стања: заједнички предак, прва грана и друга грана.
Процес мерџа почиње командом git merge. Git одређује тачку разилажења грана и секвенцијално примењује измене из изворне гране на циљну. Ако измене нису у конфликту, Git извршава fast-forward или креира merge commit у зависности од подешавања. Fast-forward — сценарио у којем се циљна грана једноставно помера на комитове изворне гране.
# Пређи на циљну грану и споји
git checkout main
git merge feature/payment-module
# Споји са експлицитним no-fast-forward
git merge --no-ff feature/payment-module
# Откажи спајање ако су конфликти превише сложени
git merge --abort
Заставица --no-ff (no fast-forward) присилно креира merge commit чак и када је fast-forward могућ. Ово чува информацију да су измене направљене у одвојеној грани. Многи тимови преферирају управо овај приступ за очување гранања историје у експлицитном облику.
У Git-у постоје три главне стратегије спајања грана, од којих је свака погодна за одређени сценарио. Избор стратегије зависи од културе тима и захтева за чистоћом историје пројекта.
| Стратегија | Резултат | Када применити |
|---|---|---|
| Standard merge | merge commit + цела историја | тимови који цене пуну историју |
| Squash merge | један комит, историја сажета | feature гране са мноштвом малих комитова |
| Rebase merge | линеарна историја, без merge commit | личне feature гране, пре креирања PR-а |
Standard merge креира merge commit са два родитеља. Цела историја се чува, али граф гранања постаје сложенији. Squash merge обједињује све комитове feature гране у један и примењује га на циљну грану — историја постаје линеарна и чиста, али се губи информација о међукорацима.
Rebase, иако није потпуни мерџ, постиже исти резултат — измене из једне гране се преносе на другу. Разлика је у томе што се историја преписује: комитови feature гране се поново креирају изнад последњег комита циљне гране. Ово даје идеално линеарну историју, али захтева force push при слању.
Конфликт при мерџу настаје када су у две гране измењени исти редови фајла. Git не може аутоматски да одреди коју верзију да задржи и захтева интервенцију програмера. Конфликти се приказују у фајловима у виду посебних маркера: <<<<<<<, =======, >>>>>>>.
Процес решавања конфликта укључује неколико корака. Прво, програмер отвара конфликтни фајл и ручно бира потребне измене. Важно је не само изабрати једну од верзија, већ разумети логику обе измене и донети исправну одлуку. Након уређивања фајла, маркери конфликта се уклањају, а измене се додају у staging area путем git add.
# Прикажи листу конфликтних фајлова
git status
# Покрени mergetool (нпр. VS Code, IntelliJ)
git mergetool
# Након решавања свих конфликата
git add .
git merge --continue
# Или у потпуности откажи спајање
git merge --abort
Коришћење визуелних алата за мерџ значајно убрзава решавање конфликата. VS Code, IntelliJ IDEA и GitKraken пружају интерфејсе са три панела: тренутна грана, долазећа грана и резултат. Алат git mergetool аутоматски отвара подешени едитор за сваки конфликтни фајл.
Најбољи начин да се избегну сложени конфликти је редовна синхронизација feature гране са главном граном. Ако програмер спаја main у своју грану једном дневно, конфликти ће бити мали и лако решиви. Акумулација измена током недеље гарантује сложене конфликте са високим ризиком од грешака.
Rebase и merge — два начина обједињавања измена, а избор између њих често изазива расправе у тимовима. Rebase преноси комитове из једне гране на другу, преписујући историју. Merge креира нови комит спајања, чувајући историју гранања. Сваки приступ има своје предности и ограничења.
Rebase је прикладан када програмер ради у својој локалној feature грани и жели да добије чисту линеарну историју пре креирања Pull Request-а. Након rebase-а, сви комитови се поређају секвенцијално, без непотребних merge commit-ова. Међутим, rebase захтева force push и није примењив на гранама на којима ради више људи истовремено.
Златно правило Git-а: не користи rebase на комитовима који су већ послати у заједнички репозиторијум. Ово гарантује да историја у заједничкој грани остаје непромењена, а други програмери неће наићи на дуплиране или изгубљене комитове. За интеграцију feature гране у главну користи merge кроз Pull Request.
Правилан процес мерџа — основа стабилног развоја. У савременом тимском раду, мерџ се извршава не преко конзоле, већ кроз Pull Request на GitHub-у или Merge Request у GitLab-у. PR пролази код-ревију, аутоматске CI провере и тек онда се спаја у главну грану.
Прва пракса — спајај само након продаска свих провера. CI pipeline треба да састави пројекат, покрене тестове и провери квалитет кода. Ако макар једна провера није прошла, мерџ се блокира. Савремене платформе (GitHub, GitLab) имају уграђену заштиту: branch protection rules аутоматски блокирају мерџ при паду CI-а.
Друга пракса — никада не спајај покварени код. Пре мерџа, програмер мора да се увери да његове измене не ломе билд и не регресирају постојећу функционалност. За то постоје аутоматски тестови и code review.
Трећа пракса — чисти feature гране након мерџа. Грана која је већ спојена мора бити обрисана. Ово спречава забуну и затрпавање репозиторијума. GitHub аутоматски предлаже брисање гране након мерџа PR-а, а подешавања репозиторијума се могу конфигурисати за аутоматско брисање.
Често постављана питања
Мерџ (merge) — спајање две гране Git-а у једну. Измене из једне гране се преносе у другу кроз троструко спајање (three-way merge). Резултат се фиксира у новом комиту спајања који има два родитељска комита. Merge commit чува информацију о томе које гране су спојене.
Merge креира нови комит спајања, чувајући историју гранања. Rebase преписује историју, преносећи комитове преко друге гране без креирања merge commit-а. Rebase даје линеарну историју, али захтева force push. Merge је сигурнији за заједничке гране, rebase је бољи за личне.
Отворите конфликтни фајл, пронађите маркере <<<<<<<, ======= и >>>>>>>, изаберите потребне измене и уклоните маркере. Додајте фајл кроз git add и завршите мерџ кроз git merge --continue. Користите git mergetool за визуелно решавање у VS Code или IntelliJ IDEA.
Pull Request (или Merge Request) је обавезан при спајању feature гране у главну грану пројекта. PR пролази код-ревију колега и аутоматске CI провере. Ово је стандард савременог развоја. Директан push у main грану је забрањен у већини пројеката.
Squash merge обједињује све комитове feature гране у један пре спајања. Ово даје чисту историју главне гране без међукорака. Користите squash merge када feature грана садржи много помоћних комитова (wip, fixes) и није потребно чувати све међукораке у историји.
Закључци
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође