Як зрозуміти та вирішити конфлікти в 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.