Как да разберем и разрешим конфликти в Git

Ето го, думата, която всеки разработчик мрази да види: конфликт. Just Просто няма начин да заобиколите случайния конфликт на сливане при работа с Git (или други системи за контрол на версиите).

Но когато разговарям с разработчици, често чувам, че има чувство на безпокойство или дискомфорт около темата за сливането на конфликти.

Решаването на конфликти често остава тъмно, загадъчно място: ситуация, при която нещата са силно разбити и не е ясно как да се измъкнем от тях (без да влошаваме нещата).

Въпреки че е вярно, че конфликтите на сливания са неизбежна част от живота на разработчика, дискомфортът в тези ситуации е напълно незадължителен.

Намерението ми с тази статия е да внеса малко яснота по тази тема: как и кога обикновено възникват конфликти, какви всъщност са и как да ги разреша - или да ги отменя.

Когато правилно разберете тези неща, ще можете да се справите с конфликтите на сливане по много по-спокоен и уверен начин. 😍

Как и кога възникват конфликти

Името вече го казва: „конфликти на сливане“ могат да възникнат в процеса на интегриране на фиксирания от различен източник.

Имайте предвид обаче, че „интеграцията“ не се ограничава само до „сливане на клонове“. Това може да се случи и при пребазиране или интерактивно пребазиране, при извършване на череша или изтегляне или дори при повторно прилагане на скривалище.

Всички тези действия извършват някакъв вид интеграция - и точно тогава могат да се случат конфликти на сливане.

Но разбира се, тези действия не водят до конфликт на сливане всеки път (слава Богу!). В идеалния случай трябва да се озовавате в тези ситуации много рядко. Но кога точно възникват конфликти?

Всъщност способностите за сливане на Git са едно от най-големите му предимства: обединяването на клонове работи безпроблемно през повечето време, защото Git обикновено е в състояние да разбере нещата сам.

Но има ситуации, при които са направени противоречиви промени - и когато технологията просто не може да реши кое е правилно или не. Тези ситуации просто изискват решение от човек.

Истинската класика е, когато точно същият ред код беше променен в два фиксации, в два различни клона. Git няма начин да разбере коя промяна предпочитате! 🤔

Има някои други подобни ситуации - например когато файл е бил модифициран в един клон и изтрит в друг - но те са малко по-рядко срещани.

На "Кулата" Git настолен GUI , например, има хубав начин за визуализация на тези видове ситуации:

Как да разберем кога е възникнал конфликт

Не се притеснявайте: Git ще ви каже много ясно, когато се е случил конфликт. 😉  

Първо, той ще ви уведоми незабавно в ситуацията , например когато сливането или пребазирането не успее поради конфликт:

$ git merge develop Auto-merging index.html CONFLICT (content): Merge conflict in index.html CONFLICT (modify/delete): error.html deleted in HEAD and modified in develop. Version develop of error.html left in tree. Automatic merge failed; fix conflicts and then commit the result.

Както можете да видите от горния пример, когато се опитах да извърша сливане, създадох конфликт на сливане - и Git съобщава проблема много ясно и бързо:

  • Възникна конфликт във файла „index.html“.
  • Възникна друг конфликт във файла „error.html“.
  • И накрая, поради конфликтите, операцията за сливане се провали.

Това са ситуациите, в които трябва да разровим кода и да видим какво трябва да се направи.

В малко вероятния случай, че сте пропуснали тези предупредителни съобщения, когато се е случил конфликт, Git допълнително ви информира, когато стартирате git status:

$ git status On branch main You have unmerged paths. (fix conflicts and run "git commit") (use "git merge --abort" to abort the merge) Unmerged paths: (use "git add/rm ..." as appropriate to mark resolution) deleted by us: error.html both modified: index.html

С други думи: не се притеснявайте да не забележите конфликти на сливане. Git се уверява, че не можете да ги пренебрегнете.

Как да отмените конфликт в Git и да започнете отначало

Конфликтите за сливане идват с известна спешност. И с право: ще трябва да се справите с тях, преди да можете да продължите с работата си.

Но въпреки че игнорирането им не е опция, "справянето с конфликти на сливания" не означава непременно, че трябва да ги разрешите. Отмяната им също е възможно!

Това може да си струва да се повтори: винаги имате възможност да отмените конфликт на сливане и да се върнете в състоянието преди. Това е вярно дори когато вече сте започнали да разрешавате конфликтните файлове и сте попаднали в задънена улица.

В тези ситуации е чудесно да имате предвид, че винаги можете да започнете отначало и да се върнете в чисто състояние, преди конфликтът дори да се е случил.

За тази цел повечето команди се предлагат с --abortопция, например git merge --abortи git rebase --abort:

$ git merge --abort $ git status On branch main nothing to commit, working tree clean

Това трябва да ви даде увереността, че наистина не можете да объркате. Винаги можете да прекратите, да се върнете в чисто състояние и да започнете отначало.

Как всъщност изглеждат конфликтите в Git

Сега, сигурни в знанието, че нищо не може да се счупи, нека видим как наистина изглежда конфликтът под капака. Това ще демистифицира тези малки мошеници и в същото време ще ви помогне да загубите уважение към тях и да спечелите увереност в себе си.

Като пример, нека разгледаме съдържанието на (в момента конфликтния) файл "index.html" в редактор:

Git беше достатъчно любезен, за да маркира проблемната област във файла, като го затвори в <<<<<<< HEADи >>>>>>> [other/branch/name]. Съдържанието, което идва след първия маркер, произхожда от текущия ни работещ клон. И накрая, ред с =======символи разделя двете противоречиви промени.

Как да разрешим конфликт в Git

Нашата работа като разработчици сега е да изчистим тези редове: след като приключим, файлът трябва да изглежда точно така, както искаме да изглежда.

Може да се наложи да поговорите със съотборника, който е написал „другите“ промени и да решите кой код всъщност е правилен. Може би е наше, може би е тяхно - или може би смес между двете.

This process - cleaning up the file and making sure it contains what we actually want - doesn't have to involve any magic. You can do this simply by opening your text editor or IDE and starting to making your changes.

Often, however, you'll find that this is not the most efficient way. That's when dedicated tools can save time and effort:

  • Git GUI Tools: Some of the graphical user interfaces for Git can be helpful when solving conflicts. The Tower Git GUI, for example, offers a dedicated "Conflict Wizard" that helps visualize and solve the situation:
  • Dedicated Merge Tools: For more complicated conflicts, it can be great to have a dedicated "Diff & Merge Tool" at hand. You can configure your tool of choice using the "git config" command. (Consult your tool's documentation for detailed instructions.) Then, in case of a conflict, you can invoke it by simply typing git mergetool. As an example, here's a screenshot of "Kaleidoscope" on macOS:

After cleaning up the file - either manually or in a Git GUI or Merge Tool - we have to commit this like any other change:

  • By using git add on the (previously) conflicted file, we inform Git that the conflict has been solved.
  • When all conflicts have been solved and added to the Staging Area, you need to complete the resolution by creating a regular commit.

How to Become More Confident and Productive

Many years ago, when I started using version control, merge conflicts regularly freaked me out: I was afraid that, finally, I had managed to break things for good. 😩

Only when I took the time to truly understand what was going on under the hood was I able to deal with conflicts confidently and efficiently.

The same was true, for example, when dealing with mistakes: only once I learned how to undo mistakes with Git was I able to become more confident and productive in my work.

I highly recommend taking a look at the free "First Aid Kit for Git", a collection of short videos about how to undo and recover from mistakes with Git.

Have fun becoming a better programmer!

About the Author

Tobias Günther is the CEO of Tower, the popular Git desktop client that helps more than 100,000 developers around the world to be more productive with Git.