Як вирішити конфлікти злиття в Git

Чи є хороший спосіб пояснити, як вирішувати конфлікти злиття в Git?

4274
02 окт. заданий Spoike 02 Жовтня. 2008-10-02 14:31 '08 о 14:31 2008-10-02 14:31
@ 37 відповідей
  • 1
  • 2

Спробуйте: git mergetool

Він відкриває графічний інтерфейс, який проходить через кожен конфлікт, і ви можете вибрати, як об'єднати. Іноді це вимагає трохи ручного редагування згодом, але зазвичай цього достатньо само по собі. Це набагато краще, ніж робити все вручну.

Згідно з коментарем @JoshGlover:

Команда не обов'язково відкриває графічний інтерфейс, якщо ви його не встановите. Запуск git mergetool для мене привів до використання vimdiff . Ви можете встановити один з наступних інструментів, щоб використовувати його замість того, щоб: meld , opendiff , kdiff3 , tkdiff , xxdiff , tortoisemerge , gvimdiff , diffuse , ecmerge , p4merge , araxis , vimdiff , emerge .

Нижче наведено приклад процедури використання vimdiff для вирішення конфліктів злиття. За цим посиланням

Крок 1: Запустіть наступні команди в вашому терміналі

 git config merge.tool vimdiff git config merge.conflictstyle diff3 git config mergetool.prompt false 

Це встановить vimdiff як інструмент злиття за замовчуванням.

Крок 2: Запустіть наступну команду в терміналі

 git mergetool 

Крок 3: Ви побачите дисплей vimdiff в наступному форматі

  +----------------------+ | | | | |LOCAL |BASE |REMOTE | | | | | +----------------------+ | MERGED | | | +----------------------+ 

Ці 4 види

LOCAL - це файл з поточної гілки

BASE - загальний предок, як файл виглядав до обох змін

REMOTE - файл, який ви поєднуєте в свою гілку

MERGED - результат злиття, це те, що зберігається в репо

Ви можете переміщатися між цими уявленнями, використовуючи ctrl+w Ви можете безпосередньо перейти до подання MERGED, використовуючи ctrl+w і j .

Більше інформації про навігації vimdiff тут і тут

Крок 4 Ви можете редагувати MERGED наступним чином

Якщо ви хочете отримати зміни від REMOTE

 :diffg RE 

Якщо ви хочете отримувати зміни від BASE

 :diffg BA 

Якщо ви хочете отримувати зміни від LOCAL

 :diffg LO 

Крок 5 Зберегти, вийти, зафіксувати і очистити

:wqa зберегти і вийти з vi

git commit -m "message"

git clean Видаліть зайві файли (наприклад, * .orig), створені інструментом diff.

2535
02 окт. відповідь дан Peter Burns 02 Жовтня. 2008-10-02 20:50 '08 о 20:50 2008-10-02 20:50

Тут можливий прецедент, зверху:

Ви збираєтеся внести деякі зміни, але, на жаль, ви не в курсі:

 git fetch origin git pull origin master From ssh://gitosis@example.com:22/projectname * branch master -> FETCH_HEAD Updating a030c3a..ee25213 error: Entry 'filename.c' not uptodate. Cannot merge. 

Отже, ви оновлюєтеся і спробуйте ще раз, але маєте конфлікт:

 git add filename.c git commit -m "made some wild and crazy changes" git pull origin master From ssh://gitosis@example.com:22/projectname * branch master -> FETCH_HEAD Auto-merging filename.c CONFLICT (content): Merge conflict in filename.c Automatic merge failed; fix conflicts and then commit the result. 

Отже, ви вирішили поглянути на зміни:

border=0
 git mergetool 

О, я, о, мій, висхідний потік змінив деякі речі, але просто щоб використовувати мої зміни ... ні ... їх зміни ...

 git checkout --ours filename.c git checkout --theirs filename.c git add filename.c git commit -m "using theirs" 

І потім ми спробуємо останній раз

 git pull origin master From ssh://gitosis@example.com:22/projectname * branch master -> FETCH_HEAD Already up-to-date. 

Та да!

1625
04 авг. відповідь дан CoolAJ86 04 Серпня. 2010-08-04 20:04 '10 о 20:04 2010-08-04 20:04

Я знаходжу, що інструменти злиття рідко допомагають мені зрозуміти конфлікт або дозвіл. Я зазвичай більш успішно дивлюся маркери конфліктів в текстовому редакторі і використовуючи git журнал в якості доповнення.

Ось кілька порад:

Порада одна

Найкраще, що я знайшов, це використовувати стиль конфлікту злиття "diff3":

git config merge.conflictstyle diff3

Це створює такі маркери конфліктів, як це:

 <<<<<<< Changes made on the branch that is being merged into. In most cases, this is the branch that I have currently checked out (ie HEAD). ||||||| The common ancestor version. ======= Changes made on the branch that is being merged in. This is often a feature/topic branch. >>>>>>> merged into <<<<<<< Changes made on the branch that is being merged into. In most cases, this is the branch that I have currently checked out (ie HEAD). ||||||| The common ancestor version. ======= Changes made on the branch that is being merged in. This is often a feature/topic branch. >>>>>>> checked out <<<<<<< Changes made on the branch that is being merged into. In most cases, this is the branch that I have currently checked out (ie HEAD). ||||||| The common ancestor version. ======= Changes made on the branch that is being merged in. This is often a feature/topic branch. >>>>>>> merged in <<<<<<< Changes made on the branch that is being merged into. In most cases, this is the branch that I have currently checked out (ie HEAD). ||||||| The common ancestor version. ======= Changes made on the branch that is being merged in. This is often a feature/topic branch. >>>>>>> 

Середня частина - то, як виглядав загальний предок. Це корисно, тому що ви можете порівняти його з верхньої та нижньої версіями, щоб краще зрозуміти, що було змінено в кожній гілці, що дає вам більш повне уявлення про те, яка мета кожного зміни.

Якщо конфлікт складається всього лише з кількох рядків, це, як правило, робить конфлікт дуже очевидним. (Знання того, як виправити конфлікт, сильно відрізняється: вам потрібно знати, над чим працюють інші люди. Якщо ви в замішанні, найкраще просто зателефонувати цій людині в вашу кімнату, щоб вони могли бачити, що ви шукаєте в.)

Якщо конфлікт довше, я вирізаю і вставляю кожен з трьох розділів в три окремих файлу, таких як "мій", "загальний" і "їх".

Потім я можу запустити наступні команди, щоб побачити дві ситуації, які викликали конфлікт:

 diff common mine diff common theirs 

Це не те ж саме, що використовувати інструмент злиття, так як інструмент злиття буде включати всі неконфліктний diff-ханки. Я вважаю, що це відволікає.

Рада два

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

 git log --merge -p <name of file> 

Це показує все коммітов, які стосувалися цього файлу між загальним предком і двома головами, які ви поєднуєте. (Таким чином, він не включає в себе коммітов, які вже існують в обох гілках до злиття.) Це допомагає ігнорувати відмінності, які явно не є фактором у вашому поточному конфлікті.

Рада три

Перевірте свої зміни за допомогою автоматизованих інструментів.

Якщо у вас є автоматичні тести, запустіть їх. Якщо у вас є lint , запустіть його. Якщо це будівельний проект, тоді його необхідно побудувати до того, як ви зробите і т.д. У всіх випадках вам потрібно трохи пройти тестування, щоб переконатися, що ваші зміни нічого не зламали. (Чорт, навіть злиття без конфліктів може порушити робочий код.)

Рада чотири

Плануйте заздалегідь; спілкуватися з колегами.

Планування вперед і усвідомлення того, що працюють інші, може допомогти запобігти конфліктам злиття і / або допомогти вирішити їх раніше - в той час як деталі все ще актуальні.

Наприклад, якщо ви знаєте, що ви і інша людина працюють над різними рефакторингом, які будуть впливати на один і той же набір файлів, вам слід поговорити один з одним завчасно і краще зрозуміти, які типи змін кожен з вас робить. Ви можете заощадити чимало часу і зусиль, якщо проводити заплановані зміни по черзі, а не паралельно.

Для великих рефакторингов, які перетинають великий фрагмент коду, ви повинні серйозно розглянути можливість роботи послідовно: кожен перестає працювати над цією областю коду, а одна людина виконує повний рефакторинг.

Якщо ви не можете працювати послідовно (через тимчасове тиску, можливо), то спілкування про очікуваних конфліктах злиття, по крайней мере, допоможе вам вирішити проблеми раніше, поки деталі ще свіжі. Наприклад, якщо співробітник здійснює руйнівну серію коммітов протягом одного тижня, ви можете захотіти об'єднати / переупакувати цю гілку один або два рази на день на цьому тижні. Таким чином, якщо ви знайдете конфлікти злиття / переадресації, ви можете вирішити їх швидше, ніж якщо ви почекаєте кілька тижнів, щоб об'єднати всі разом в один великий шматок.

Рада п'ять

Якщо ви не впевнені в злитті, не змушуйте його.

Злиття може відчуватися переважною, особливо коли є багато конфліктуючих файлів, а маркери конфліктів охоплюють сотні рядків. Часто при оцінці програмних проектів ми не включаємо достатньо часу для надбавок, таких як обробка божевільного злиття, тому він відчуває себе як реальний опір, щоб витратити кілька годин на аналіз кожного конфлікту.

У довгостроковій перспективі планування вперед і усвідомлення того, що працюють інші, є кращими інструментами для прогнозування конфліктів злиття і підготовки до правильного їх вирішення за менший час.

696
29 сент. відповідь дан Mark E. Haase 29 сент. 2011-09-29 00:08 '11 в 0:08 2011-09-29 00:08
  • Визначте, які файли знаходяться в конфлікті (Git повинен сказати вам це).

  • Відкрийте кожен файл і вивчіть відмінності; Git демаркірует їх. Сподіваюся, буде очевидно, яку версію кожного блоку зберегти. Можливо, вам доведеться обговорити це з іншими розробниками, які вчинили код.

  • Як тільки ви дозволили конфлікт в файлі git add the_file .

  • Як тільки ви дозволите конфлікти all, виконайте git rebase --continue або будь-яку команду Git сказав, коли закінчите.

326
02 окт. відповідь дан davetron5000 02 Жовтня. 2008-10-02 15:41 '08 о 15:41 2008-10-02 15:41

Перевірте відповіді в питанні Скасування злиття в Git , особливо Відповідь Чарльза Бейлі , який показує, як переглядати різні версії файлу з проблемами, наприклад

100
03 окт. відповідь дан Pat Notz 03 Жовтня. 2008-10-03 18:15 '08 о 18:15 2008-10-03 18:15

Злиття конфліктів відбувається, коли зміни вносяться в файл одночасно. Ось як це можна вирішити.

git CLI

Ось прості кроки, які потрібно зробити, коли ви потрапляєте в конфліктну стан:

  • Зверніть увагу на список конфліктуючих файлів: git status (в розділі Unmerged paths ).
  • Вирішіть конфлікти окремо для кожного файлу одним з наступних підходів:

    • Використовуйте GUI для вирішення конфліктів: git mergetool (найпростіший спосіб).

    • Щоб прийняти віддалену / іншу версію, використовуйте: git checkout --theirs path/file . Це відхилить будь-які локальні зміни, які ви зробили для цього файлу.

    • Щоб прийняти локальну / нашу версію, використовуйте: git checkout --ours path/file

      Однак ви повинні бути обережні, оскільки віддалені зміни з якихось причин були усунені.

      Пов'язано: Яке точне значення "ours" і "їх" в git?

    • Відредагуйте конфліктуючі файли вручну і знайдіть блок коду між <<<<< / >>>>> , потім виберіть версію зверху чи знизу ===== . Дивіться: Як конфлікти представлені .

    • Конфлікти шляхів і імен файлів можуть бути вирішені за допомогою git add / git rm .

  • Нарешті, перегляньте файли, готові для фіксації, використовуючи: git status .

    Якщо у вас все ще є файли під Unmerged paths , і ви дозволили конфлікт вручну, то нехай Git знає, що ви його вирішили: git add path/file .

  • Якщо все конфлікти були успішно вирішені, скопіюйте зміни: git commit -a і натисніть на віддалений, як зазвичай.

Див. Також: Вирішення конфлікту злиття з командного рядка в GitHub

DiffMerge

Я успішно використовував DiffMerge , який може візуально порівнювати і об'єднувати файли в Windows, macOS і Linux / Unix.

Він графічно може відображати зміни між 3-ма файлами і дозволяє автоматичне злиття (коли це безпечно) і повний контроль над редагуванням результуючого файлу.

2019

05 авг. відповідь дан kenorb 05 Серпня. 2015-08-05 17:29 '15 о 17:29 2015-08-05 17:29

Якщо ви робите часті дрібні коммітов, почніть з перегляду коментарів коммітов за допомогою git log --merge . Потім git diff покаже вам конфлікти.

Для конфліктів, які пов'язані з кількома рядками, легше побачити, що відбувається в зовнішньому GUI-інструменті. Мені подобається opendiff - Git також підтримує vimdiff, gvimdiff, kdiff3, tkdiff, meld, xxdiff, emerge з коробки, і ви можете встановити інші: git config merge.tool "your.tool" встановить ваш обраний інструмент, а потім git mergetool після невдалого злиття покаже вам відмінності в контексті.

Кожен раз, коли ви редагуєте файл для вирішення конфлікту, git add filename оновлює індекс, і ваш diff більше не буде його показувати. Коли всі конфлікти обробляються і їх файли були git add -ed, git commit завершить злиття.

75
02 окт. відповідь дан Paul 02 Жовтня. 2008-10-02 19:11 '08 о 19:11 2008-10-02 19:11

Див. Як конфлікти представлені або в Git, документації git merge , щоб зрозуміти, що таке маркери конфліктів злиття.

Крім того, в розділі Як вирішити конфлікти пояснюється, як розв'язувати конфлікти:

Після перегляду конфлікту ви можете зробити дві речі:

  • Вирішите не зливатися. Єдині очисники, які вам потрібні, - це reset індексний файл для фіксації HEAD для зворотного перетворення 2. і для очищення робочих сільських змін, зроблених в 2. і 3; git merge --abort можна використовувати для цього.

  • Дозволити конфлікти. Git відзначатиме конфлікти в робочому дереві. Відредагуйте файли в формі і git add їх в індекс. Використовуйте git commit , щоб запечатати угоду.

Ви можете працювати в конфлікті за допомогою декількох інструментів:

  • Використовуйте mergetool. git mergetool , щоб запустити графічний mergetool, який буде працювати через злиття.

  • Подивіться на відмінності. git diff відображає тристоронній diff, виділяючи зміни як з версій HEAD , так і MERGE_HEAD .

  • Подивіться на відмінності між кожною гілкою. git log --merge -p <path> буде показувати diff спочатку для версії HEAD , а потім версії MERGE_HEAD .

  • Подивіться на оригінали. git show :1:filename показує загального предка, git show :2:filename показує версію HEAD , а git show :3:filename показує версію MERGE_HEAD .

Ви також можете прочитати про марку конфлікту злиття і про те, як їх вирішити в розділі Pro Git Основне злиття конфлікти .

43
14 июля '13 в 21:34 2013-07-14 21:34 відповідь дан user456814 14 липня '13 о 21:34 2013-07-14 21:34

Для Emacs користувачі, які хочуть вирішити конфлікти злиття напів-вручну:

 git diff --name-status --diff-filter=U 

відображає всі файли, що вимагають вирішення конфліктів.

Відкрийте кожен з цих файлів один за іншим або все відразу:

 emacs $(git diff --name-only --diff-filter=U) 

При відвідуванні буфера, що вимагає редагування в Emacs, введіть

 ALT+x vc-resolve-conflicts 

Це відкриє три буфера (мій, їх і вихідний буфер). Перейдіть, натиснувши "n" (наступна область), "p" (область передбачення). Натисніть "a" і "b", щоб скопіювати мою або свою область у вихідний буфер відповідно. І / або відредагувати вихідний буфер безпосередньо.

Закінчивши: натисніть "q". Emacs запитує вас, чи хочете ви зберегти цей буфер: так. По завершенні буфера відзначте його як дозволене при запуску від заклинателя:

 git add FILENAME 

Закінчивши роботу з усіма типами буферів

 git commit 

щоб завершити злиття.

36
23 февр. відповідь дан eci 23 февр. 2013-02-23 02:04 '13 в 2:04 2013-02-23 2:04

Я або хочу свою або їх версію повністю, або хочу переглянути окремі зміни і прийняти рішення для кожного з них.

Повністю прийміть мою або їх версію:

Прийміть мою версію (локальну, нашу):

 git checkout --ours -- <filename> git add <filename> # Marks conflict as resolved git commit -m "merged bla bla" # An "empty" commit 

Прийміть їх версію (віддалену, їх):

 git checkout --theirs -- <filename> git add <filename> git commit -m "merged bla bla" 

Якщо ви хочете зробити для всіх конфліктних файлів, запустіть:

 git merge --strategy-option ours 

або ж

 git merge --strategy-option theirs 

Перегляньте всі зміни та прийміть їх індивідуально

  1. git mergetool
  2. Перегляньте зміни та прийміть будь-яку версію для кожного з них.
  3. git add <filename>
  4. git commit -m "merged bla bla"

За замовчуванням mergetool працює в командному рядку. Як використовувати командний рядок mergetool має бути окремим питанням.

Ви також можете встановити візуальний інструмент для цього, наприклад, meld і запустити

 git mergetool -t meld 

Буде відкрита локальна (наша), "базова" або "об'єднана" версія (поточний результат злиття) і віддалена (їх). Збережіть об'єднану версію, коли закінчите, знову запустіть git mergetool -t meld поки не отримаєте "Немає необхідності об'єднувати файли", потім перейдіть до кроку 3. і 4.

28
29 сент. відповідь дан Noidea 29 сент. 2016-09-29 16:02 '16 о 16:02 2016-09-29 16:02

Будь ласка, виконайте наступні кроки, щоб виправити конфлікти злиття в Git:

  1. Перевірте статус Git: статус Git

  2. Отримайте набір патчів: git fetch (перевірте правильний патч з вашого коммітов Git)

  3. Витягти локальну гілку (в моєму прикладі це temp1): git checkout -b temp1

  4. Вийміть недавнє вміст з master: git pull --rebase origin master

  5. Запустіть mergetool, перевірте конфлікти і виправте їх ... і перевірте зміни в віддаленій гілці з вашої поточної гілкою: git mergetool

  6. Перевірте статус знову: git status

  7. Видаліть непотрібні файли, локально створені за допомогою mergetool, зазвичай mergetool створює додатковий файл з розширенням * .orig. Будь ласка, видаліть цей файл, так як це просто дублікат, виправте зміни локально і додайте правильну версію ваших файлів. git add #your_changed_correct_files

  8. Перевірте статус знову: git status

  9. Зафіксуйте зміни в одному і тому ж ідентифікатор (це дозволяє уникнути нового окремого набору виправлень): git commit --amend

  10. Push в гілку master: git push (в ваше сховище Git)

28
16 апр. відповідь дан Chhabilal 16 Квітня. 2015-04-16 10:02 '15 в 10:02 2015-04-16 10:02

Простіше кажучи, якщо ви добре знаєте, що зміни в одному з репозиторіїв не важливі, і хочете вирішити всі зміни на користь іншого, використовуйте:

 git checkout . --ours 

дозволити зміни на користь вашого сховища, або

 git checkout . --theirs 

дозволити зміни на користь іншого або основного сховища.

Або ж вам доведеться використовувати інструмент злиття GUI для покрокового перегляду файлів, скажімо, інструмент злиття p4merge , або написати будь-яке ім'я, яке ви вже встановили

 git mergetool -t p4merge 

і після завершення файлу вам доведеться зберегти і закрити, щоб відкрити наступний.

27
26 янв. відповідь дан Mohamed Selim 26 Січня. 2016-01-26 20:42 '16 о 20:42 2016-01-26 20:42

Ви можете виправити конфлікти злиття декількома способами, як інші деталізували.

Я думаю, що реальний ключ - це знання того, як зміни течуть з локальними і віддаленими репозиторіями. Ключем до цього є розуміння гілок відстеження. Я виявив, що я думаю про гілці відстеження як "відсутньої частини в середині" між мною, моїм локальним, фактичним файловим каталогом і віддаленим, який визначається як походження.

Я особисто звик до двох речей, щоб допомогти уникнути цього.

замість:

 git add . git commit -m"some msg" 

Що має два недоліки -

a) Всі нові / змінені файли додаються і можуть містити деякі небажані зміни.
б) Ви не можете спочатку переглянути список файлів.

Отже, замість цього:

 git add file,file2,file3... git commit # Then type the files in the editor and save-quit. 

Таким чином, ви більш навмисно обговорюєте, які файли додаються, і ви також можете переглянути список і подумати трохи більше, використовуючи редактор для повідомлення. Я вважаю, що це також покращує мої повідомлення про фіксацію, коли я використовую повноекранний редактор, а не параметр -m .

[Оновлення - з часом я перемкнув більше на:

 git status # Make sure I know whats going on git add . git commit # Then use the editor 

]

Також (і більш доречно для вашої ситуації), я намагаюся уникати:

 git pull 

або

 git pull origin master.