Как использовать vimdiff для разрешения конфликта git merge?

159

Я только что слил ветку в мой мастер в git и получил. Automatic merge failed; fix conflicts and then commit the result.Теперь я побежал git mergetoolи открыл vimdiff с изображением ниже. Я не знаю, как использовать vimdiff. Что означает каждая панель здесь, и как мне действовать, чтобы устранить конфликт слияния?

введите описание изображения здесь

Крутой парень Йо
источник
3
Смотрите эту страницу . Если это то, что вы подразумеваете под «правильным», текущее состояние вашего кода вверху слева.
romainl
@romainl Я все еще запутался после прочтения, что это за ярлыки и как выбрать файл для использования в качестве основной ветви?
Крутой парень Йо
2
@ Дэнни http://www.rosipov.com/blog/use-vimdiff-as-git-mergetool/ - хороший.
ᴠɪɴᴄᴇɴᴛ
Также смотрите: это
Skelliam

Ответы:

142

Все четыре буфера обеспечивают различное представление одного и того же файла. Верхний левый буфер (LOCAL) - это то, как файл выглядел в вашей целевой ветви (во что вы сливаетесь). Верхний правый буфер (REMOTE) - это то, как файл выглядел в вашей исходной ветке (откуда вы объединяетесь). Средний буфер (BASE) является общим предком двух (так что вы можете сравнить, как левая и правая версии отличались друг от друга).

Я могу ошибаться в следующем. Я думаю, что источником конфликта слияния является то, что оба файла изменили одну и ту же часть файла после BASE; LOCAL изменил кавычки с двойных на одинарные, и REMOTE сделал то же самое изменение, но также изменил значение фона с цвета на URL. (Я думаю, что слияние недостаточно умен, чтобы заметить, что все изменения в LOCAL также присутствуют в REMOTE; он просто знает, что LOCAL внес изменения после BASE в тех же местах, что и REMOTE).

В любом случае нижний буфер содержит файл, который вы действительно можете редактировать - тот, который находится в вашем рабочем каталоге. Вы можете вносить любые изменения, которые вам нравятся; vimпоказывает, как он отличается от каждого из видов сверху, которые являются областями, которые автоматическое объединение не может обработать. Вытащите изменения из МЕСТНОГО, если вы не хотите УДАЛЕННЫХ изменений. Извлеките изменения из REMOTE, если вы предпочитаете их локальным изменениям. Потяните с BASE, если считаете, что REMOTE и LOCAL ошибочны. Сделайте что-то совершенно другое, если у вас есть идея получше! В конце концов, изменения, которые вы вносите здесь, действительно будут зафиксированы.

chepner
источник
4
Быстрый вопрос, как мне сохранить в Vim?
Крутой парень Йо
6
:xили :w( :xтоже выходит) плюс «возврат».
Джонатан Леффлер
4
Андерс: есть и другие инструменты слияния, которые вы можете использовать, если не знаете, как их использовать vim.
Чепнер
3
@AndersKitson, поскольку вы работаете в Mac OS X, FileMerge идеален, бесплатен и поставляется с XCode.
Ромен
8
Почему отрицательный голос? Если что-то действительно неверно, пожалуйста, исправьте это или, по крайней мере, укажите на это.
13
91

Ответ @ chepner отличный, я хотел бы добавить некоторые детали вопроса «как мне решить проблему слияния». Если вы посмотрите, как на самом деле использовать vimdiff в этом случае, он пойдет ниже.


Во-первых, обратитесь к опции «прервать все» - если вы не хотите использовать «vimdiff» и хотите прервать слияние: нажмите Esc, затем введите :qa!и нажмите Enter. (см. также Как выйти из редактора Vim? ). Git спросит вас, было ли слияние завершено, ответьте с помощью n.


Если вы хотите использовать vimdiff, вот несколько полезных ярлыков. Предполагается, что вы знаете основы Vim (навигация и вставка / обычный режим):

  • перейти к нижнему буферу (результат слияния): Ctrl-W j
  • перейти к следующему diff с помощью j/k ; или, лучше, использовать ] cи[ c для перехода к следующему и предыдущему разности соответственно
  • использование z o время на сгибе, чтобы открыть его, если хотите увидеть больше контекста
  • для каждого сравнения, согласно ответу @ chepner, вы можете либо получить код из локальной, удаленной или базовой версии, либо отредактировать его и повторить, как считаете нужным
    • чтобы получить его из локальной версии, используйте :diffget LO
    • с пульта: :diffget RE
    • с базы: :diffget BA
    • или, если вы хотите редактировать код самостоятельно, сначала получите версию из локальной / удаленной / базовой, а затем перейдите в режим вставки и отредактируйте остальные.
  • После этого сохраните результат объединения и закройте все окна. :wqa
    • если вы хотите прервать слияние текущего файла и не пометить его как разрешенный, :cquitвместо этого выйдите с помощью : Как отменить внешний git diff?
  • обычно git обнаруживает, что слияние было выполнено, и создает коммит слияния

По-видимому, невозможно добавить как локальные, так и удаленные блоки конфликтов без вставки копий или пользовательских ярлыков: /vi/10534/is-there-a-way-to-take-both- Когда-использование-vim-as-merge-tool - это позор, так как add-add - такой распространенный тип конфликта.

Чтобы vimdiff не просил вас нажимать клавишу ввода при каждом запуске, добавьте в .vimrc:

set shortmess=Ot

как упомянуто в: /vi/771/how-can-i-suppress-the-press-enter-prompt-when-opening-files-in-diff-mode

Вы можете искать в Интернете другие ярлыки vimdiff. Я нашел это полезным: https://gist.github.com/hyamamoto/7783966

Тимур
источник
10
Это должно быть проголосовано x1000 раз и принято как лучший ответ.
Андрей Портной
чтобы быстро перейти к следующему конфликту, просто выполните поиск ===. сделать "/ ===" и войти
Апит Джон Исмаил
См. Этот пост ( stackoverflow.com/questions/51520705/… ), если найдено более одного совпадения с использованием :diffget.
Джейсон
7

Окончательный mergetool, чтобы заменить vimdiff

Это что-то вроде насмешки, но это то, к чему я в конечном итоге присоединился как к vimmer после попытки vimdiff.

Чтобы разрешить конфликт слияния, мне почти всегда нужно видеть:

  • УДАЛЕННЫЙ
  • МЕСТНЫЙ
  • две разницы:
    • diff BASE REMOTE
    • diff BASE LOCAL

затем попытаться соединить их обоих.

В то время как vimdiff показывает BASE, LOCAL и REMOTE на экране:

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

Я не знаю, как сделать так, чтобы это четко показывало те две разницы, которые мне нужны, кроме того, что я смотрел направо, налево, направо, налево, несколько раз.

Более того, LOCAL и REMOTE уже видны в маркерах конфликтов git merge, поэтому я не так уж выиграл от инструмента, который их показывает снова.

Поэтому я вместо этого создал свой собственный крошечный «difftool», который фактически показывает различия, которые я пропустил:

~ / Bin / cirosantilli-mergetool

#!/usr/bin/env bash
BASE="$1"
LOCAL="$2"
REMOTE="$3"
diff --color -u "$BASE" "$LOCAL"
diff --color -u "$BASE" "$REMOTE"
exit 1

GitHub вверх по течению .

И установите его с помощью:

git config --global mergetool.cirosantilli-mergetool.cmd 'cirosantilli-mergetool $BASE $LOCAL $REMOTE'
git config --global mergetool.cirosantilli-mergetool.trustExitCode true
# If you want this to become your default mergetool.
#git config --global merge.tool 'cirosantilli-mergetool'

Теперь, когда вы делаете:

git mergetool -t cirosantilli-mergetool

он показывает две разницы, которые я хочу на терминале, например что-то вместе:

--- ./src/dev/arm/RealView_BASE_15560.py        2019-12-27 13:46:41.967021591 +0000
+++ ./src/dev/arm/RealView_LOCAL_15560.py       2019-12-27 13:46:41.979021479 +0000
@@ -994,7 +994,7 @@                                                              

     def setupBootLoader(self, cur_sys, loc):
         if not cur_sys.boot_loader:                           
-            cur_sys.boot_loader = [ loc('boot_emm.arm64'), loc('boot_emm.arm') ]
+            cur_sys.boot_loader = [ loc('boot.arm64'), loc('boot.arm') ]
         cur_sys.atags_addr = 0x8000000                  
         cur_sys.load_offset = 0x80000000                    

@@ -1054,7 +1054,7 @@                                           
             ]                                                     

     def setupBootLoader(self, cur_sys, loc):
-        cur_sys.boot_loader = [ loc('boot_emm_v2.arm64') ]
+        cur_sys.boot_loader = [ loc('boot_v2.arm64') ]
         super(VExpress_GEM5_V2_Base,self).setupBootLoader(
                 cur_sys, loc)                             

--- ./src/dev/arm/RealView_BASE_15560.py        2019-12-27 13:46:41.967021591 +0000
+++ ./src/dev/arm/RealView_REMOTE_15560.py      2019-12-27 13:46:41.991021366 +0000
@@ -610,10 +610,10 @@           
     def attachIO(self, *args, **kwargs):              
         self._attach_io(self._off_chip_devices(), *args, **kwargs)

-    def setupBootLoader(self, cur_sys, loc):
-        cur_sys.boot_loader = loc('boot.arm') 
-        cur_sys.atags_addr = 0x100                           
-        cur_sys.load_offset = 0       
+    def setupBootLoader(self, cur_sys, boot_loader, atags_addr, load_offset):
+        cur_sys.boot_loader = boot_loader      
+        cur_sys.atags_addr = atags_addr     
+        cur_sys.load_offset = load_offset

Итак, вы можете увидеть здесь две разности, сброшенные в терминал:

  • RealView_BASE_15560.py против RealView_LOCAL_15560.py
  • RealView_BASE_15560.py против RealView_REMOTE_15560.py

Если различия велики, я просто поищу с моими суперспособностями tmux .

Да, вы теряете некоторые ярлыки, которые предоставляет vimdiff, но в общем случае для разрешения конфликтов требуется аккуратная вставка копий из обеих версий, что я могу сделать в обычном сеансе vim с маркерами конфликтов git.

Наблюдение и проверка файлов в то время как vimdiff работы

Прежде чем я сел и автоматизировал мою идеальную установку с cirosantilli-mergetool , я делал то, что мне нужно, для получения двух различий.

Пока git mergetoolработает vimdiff, если есть конфликт с файлом, скажем, скажем main.py, git генерирует файлы для каждой из версий, названных как:

main_BASE_1367.py
main_LOCAL_1367.py
main_REMOTE_1367.py

в том же каталоге, main.pyгде 1367находится PID git mergetool и, следовательно, «случайное» целое число, как указано в: разделе В конфликте git merge, какие файлы BACKUP, BASE, LOCAL и REMOTE генерируются?

Итак, чтобы увидеть git statusнужные мне различия , я сначала нахожу сгенерированные файлы , а затем открываю новые терминалы и делаю vimdiff между парами файлов, которые мне нужны:

vim -d main_BASE_1367.py main_LOCAL_1367.py
vim -d main_BASE_1367.py main_REMOTE_1367.py

Вместе с git mergetool , эта информация помогает МНОГО выяснить, что происходит быстро!

Кроме того, даже когда работает mergetool, вы можете просто открыть файл:

vim main.py

непосредственно и отредактируйте его там, если вы чувствуете, что это будет легче с большим окном редактора.

Перейти непосредственно к конфликтам слияния

Хотя ]cпри переходе к следующей точке различия внутри vimdiff, не всегда возникает конфликт слияния.

Чтобы помочь с этим, я имею в своем ~/.vimrc:

# Git Merge conflict
nnoremap <leader>gm /\v^\<\<\<\<\<\<\< \|\=\=\=\=\=\=\=$\|\>\>\>\>\>\>\> /<cr>

который находит конфликты напрямую.

мерзавец

Возможно, лучшим вариантом будет просто отказаться от использования vimdiff и положиться на обычный vim + git imerge, о котором говорилось в разделе : Как я могу узнать, какие коммиты Git вызывают конфликты? поскольку кривая обучения vimdiff раздражает, и она не выполняет те функции, которые нам нужны больше всего.

Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
1
Upvoted. Я думаю, что упомянул это 9 лет назад в stackoverflow.com/a/3052118/6309 . (см. последнюю часть ответа)
VonC
@VonC да, я думаю, ты выиграл это! XD
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功