Как избавиться от конфликтов git

Содержание
  1. Git для начинающих. Урок 10. Конфликты и их разрешение
  2. Видеоурок
  3. Конспект урока
  4. Работа с конфликтами в командной строке
  5. Как работать с конфликтами в PhpStorm
  6. Мердж мастера в свою ветку
  7. Для информации
  8. Что могу посоветовать
  9. Конфликты слияния в Git
  10. Общие сведения о конфликтах слияния
  11. Типы конфликтов слияния
  12. Git прерывает работу в самом начале слияния
  13. Git прерывает работу во время слияния
  14. Создание конфликта слияния
  15. Выявление конфликтов слияния
  16. Разрешение конфликтов слияния с помощью командной строки
  17. Команды Git, с помощью которых можно разрешить конфликты слияния
  18. Общие инструменты
  19. Инструменты для случаев, когда Git прерывает работу в самом начале слияния
  20. Инструменты для случаев, когда конфликты Git возникают во время слияния
  21. Резюме
  22. 7.8 Инструменты Git — Продвинутое слияние
  23. Продвинутое слияние
  24. Конфликты слияния
  25. Прерывание слияния
  26. Игнорирование пробельных символов
  27. Ручное слияние файлов
  28. Использование команды checkout в конфликтах
  29. История при слиянии
  30. Комбинированный формат изменений
  31. Отмена слияний
  32. Исправление ссылок
  33. Отмена коммита
  34. Другие типы слияний
  35. Выбор «нашей» или «их» версий
  36. Слияние поддеревьев

Git для начинающих. Урок 10.
Конфликты и их разрешение

Видеоурок

Конспект урока

Краткое содержание урока, основные инструкции для командной строки, полезные ссылки и советы.

Работа с конфликтами в командной строке

До этого момента мы или работали в одиночку, или с коллегой над разными участками кода. То есть не пересекались с другими программистами. На практике такое бывает редко, и случается, что приходится править те же участки кода, что и наши коллеги.

Посмотрим на практике

У нас есть блок учеников, он уже в мастере и выглядит так

Нам нужно доработать этот блок, мы создаем новую ветку students-redesign и изменяем этот блок. Мы хотим добавить классы к тегам ul и li. Получается так

Закоммитим эти изменения и будем готовы залить в мастер. Но пока мы работали, нашему коллеге поручили добавить в этот же блок нового ученика. Эту задачу коллега уже сделал и залил в мастер. У него (и в мастере) код блока учеников теперь выглядит так

То есть просто добавил еще один тег li. Конечно, наших изменений он не видел.

Таким образом два человека правили код в одном и том же месте.

Мы переключаемся в мастер, пулимся, подтягиваем его коммит и пытаемся слить ее в мастер. И видим такое

Здесь git говорит, что обнаружен конфликт. То есть в текущей ветки (master) и в той, которую мы пытаемся вмерджить (students-redesign) есть изменения в одних и тех же участках кода. Нужно посмотреть, в чем заключается конфликт. Открываем файл index.html и видим

Угловые скобки ограничивают зону конфликта — те участки, где git видит изменения.

Блок между >>>>>> students-redesign — то, что в ветке students-redesign.

Чтобы не запутаться, git дает подсказки:
— HEAD — это текущая ветка,
— students-redesign — ветка, которую мы сливаем в текущую.

Наша задача — собрать эти 2 участка в одно целое. Добавить и изменения в ветку students-redesign, и те, что уже приехали в мастер. Это называется разрешить конфликт или разрезолвить конфликт.

Мы помним, что наши изменения в ветке students-redesign это добавленные классы students (ul) и students__fio (li). Значит, их нужно оставить. А изменения в мастере — это новый ученик, новый тег li, его тоже нужно оставить, но дописать к li класс students__fio, чтобы теги были с одинаковыми классами

Мы оставили новые изменения и учли то, что приехало в мастер. Угловые скобки и значки === тоже нужно убрать — в итоге останется правильный html.

Последнее, что останется сделать, это соощить git, что конфликты разрешены. Для этого нужно закоммтить изменения так, мы привыкли, и не забыть запушить. То есть, коммит с фиксом конфликта — это самый обычный коммит.

Как работать с конфликтами в PhpStorm

PhpStorm упрощает работу с конфликтами. Когда мы мерджим ветку и возникает конфликт, PhpStorm показывает окно, в котором выведен список всех файлов, в которых обнаружены конфликты. Кнопка Merge. открывает еще одно окно, разделенное на 3 части. В левой части показываются изменения в текущей ветке, куда мы мерджим. В правой части — изменения из ветки, которую мы мерджим в нашу. А в центре нужно собрать готовый код.

Такое визуальное разделение гораздо проще, чем анализировать сплошной текст с угловыми скобками и разделителями ===

Когда мы разрешим все конфликты, PhpStorm предложит нам нажать кнопку Apply, чтобы закончить мердж. Мы ее нажмем и IDE создаст мердж-коммит без необходимости коммитить вручную, как мы это делали в командной строке.

Мердж мастера в свою ветку

Происходит точно так же, как и нашей ветки в мастер. Конфликты резолвим аналогично. При этом когда придет время сливать ветку в мастер, разрешать конфликты больше не придется, потому что мы уже создали коммит с фиксом конфликта.

Для информации

  • конфликты в коде — это не чрезвычайная ситуация, это нормально
  • если вы работаете один, возможно, конфликты происходить не будут или будут достаточно редки
  • конфликты будут гораздо чаще, если вы работаете в команде
  • разрешение конфликтов бывает долгим, иногда очень долгим

Когда я начинал изучать git, конфликты для меня были самой большой проблемой. Каждый раз, когда мерджил ветку в мастер, я со страхом ждал, будет конфликт или нет. Реакция была примерно такая

Возможно, вам тоже сейчас кажется, что это сложная тема. Но со временем начинаешь больше понимать, почему возникают конфликты, когда они возникают и как их разрешать. Конфликты в коде — это вполне рабочая ситуация, это часть работы программиста.

Что могу посоветовать

  • пользуйтесь визуальными инструментами для разрешения конфликтов. PhpStorm в этом гораздо удобнее простого редактора и командной строки
  • чаще подтягивайте мастер в свою ветку, то есть держите ветку актуальной. Чем больше устаревает ваша ветка, тем больше шансов, что при мердже окажется много конфликтов
  • не откладывайте разрешение конфликта. Рано или поздно это придется сделать
  • не стесняйтесь звать на помощь. Просто потому, что вы можете не знать детали функционала, который реализовал ваш коллега
  • не забывайте пушить ветку после резолва конфликта. Особенно это важно, если вы работаете с коллегой в одной ветке. Тогда при подтягивании мастера конфликт разрешит только один из вас. Остальные подтянут чистый код
  • пользуйтесь инструментом «волшебная палочка» в PhpStorm очень осторожно (смотрите видео, там это разбирается)

На этом первая часть курса закончена
Спасибо за внимание и до встречи!

Вторая часть курса в процессе. За обновлениями следите здесь или в группе в ВК
Скоро выйдет промежуточный урок — переходный от первой ко второй части.

Источник

Конфликты слияния в Git

Системы контроля версий предназначены для управления дополнениями, вносимыми в проект множеством распределенных авторов (обычно разработчиков). Иногда один и тот же контент могут редактировать сразу несколько разработчиков. Если разработчик A попытается изменить код, который редактирует разработчик B, может произойти конфликт. Для предотвращения конфликтов разработчики работают в отдельных изолированных ветках. Основная задача команды git merge заключается в слиянии отдельных веток и разрешении любых конфликтующих правок.

Общие сведения о конфликтах слияния

Слияние и конфликты являются неотъемлемой частью работы с Git. В других инструментах управления версиями, например SVN, работа с конфликтами может быть дорогой и времязатратной. Git позволяет выполнять слияния очень просто. В большинстве случаев Git самостоятельно решает, как автоматически интегрировать новые изменения.

Обычно конфликты возникают, когда два человека изменяют одни и те же строки в файле или один разработчик удаляет файл, который в это время изменяет другой разработчик. В таких случаях Git не может автоматически определить, какое изменение является правильным. Конфликты затрагивают только того разработчика, который выполняет слияние, остальная часть команды о конфликте не знает. Git помечает файл как конфликтующий и останавливает процесс слияния. В этом случае ответственность за разрешение конфликта несут разработчики.

Типы конфликтов слияния

Конфликт во время слияния может произойти в двух отдельных точках — при запуске и во время процесса слияния. Далее рассмотрим, как разрешать каждый из этих конфликтных сценариев.

Git прерывает работу в самом начале слияния

Выполнение команды слияния прерывается в самом начале, если Git обнаруживает изменения в рабочем каталоге или разделе проиндексированных файлов текущего проекта. Git не может выполнить слияние, поскольку иначе эти ожидающие изменения будут перезаписаны новыми коммитами. Такое случается из-за конфликтов не с другими разработчиками, а с ожидающими локальными изменениями. Локальное состояние необходимо стабилизировать с помощью команд git stash , git checkout , git commit или git reset . Если команда слияния прерывается в самом начале, выдается следующее сообщение об ошибке:

Git прерывает работу во время слияния

Сбой В ПРОЦЕССЕ слияния говорит о наличии конфликта между текущей локальной веткой и веткой, с которой выполняется слияние. Это свидетельствует о конфликте с кодом другого разработчика. Git сделает все возможное, чтобы объединить файлы, но оставит конфликтующие участки, чтобы вы разрешили их вручную. При сбое во время выполнения слияния выдается следующее сообщение об ошибке:

Создание конфликта слияния

Чтобы лучше разобраться в конфликтах слияния, в следующем разделе мы смоделируем конфликт для дальнейшего изучения и разрешения. Для запуска моделируемого примера будет использоваться интерфейс Git c Unix-подобной командной строкой.

С помощью приведенной в этом примере последовательности команд выполняются следующие действия.

  • Создается новый каталог с именем git-merge-test , выполняется переход в этот каталог и инициализация его как нового репозитория Git.
  • Создается новый текстовый файл merge.txt с некоторым содержимым.
  • В репозиторий добавляется файл merge.txt и выполняется коммит.
Читайте также:  Жидкость для прочистки труб канализации крот

Теперь у нас есть новый репозиторий с одной веткой main и непустым файлом merge.txt . Далее создадим новую ветку, которая будет использоваться как конфликтующая при слиянии.

Представленная выше последовательность команд выполняет следующие действия.

  • Создает новую ветку с именем new_branch_to_merge_later и выполняет переход в нее.
  • Перезаписывает содержимое файла merge.txt .
  • Выполняет коммит нового содержимого.

В этой новой ветке new_branch_to_merge_later мы создали коммит, который переопределил содержимое файла merge.txt .

Эта последовательность команд выполняет переключение на ветку main , добавляет содержимое в файл merge.txt и делает коммит. После этого в нашем экспериментальном репозитории находятся два новых коммита, первый — в ветке main , а второй — в ветке new_branch_to_merge_later . Теперь запустим команду git merge new_branch_to_merge_later и посмотрим, что из этого выйдет!

БАХ! 💥 Возник конфликт. Хорошо, что система Git сообщила нам об этом.

Выявление конфликтов слияния

Как мы убедились на выполняемом примере, Git выводит небольшое описательное сообщение о возникновении КОНФЛИКТА. Чтобы получить более глубокое понимание проблемы, можно запустить команду git status .

Вывод команды git status говорит о том, что из-за конфликта не удалось слить пути. Теперь файл merge.text отображается как измененный. Давайте изучим этот файл и посмотрим, что изменилось.

Для просмотра содержимого файла merge.txt воспользуемся командой cat . Видно, что в файле появились новые странные дополнения:

Эти новые строки можно рассматривать как «разделители конфликта». Строка ======= является «центром» конфликта. Все содержимое между этим центром и строкой находится в текущей ветке main, на которую ссылается указатель HEAD . А все содержимое между центром и строкой >>>>>>> new_branch_to_merge_later является содержимым ветки для слияния.

Разрешение конфликтов слияния с помощью командной строки

Самый простой способ разрешить конфликт — отредактировать конфликтующий файл. Откройте файл merge.txt в привычном редакторе. В нашем примере просто удалим все разделители конфликта. Измененное содержимое файла merge.txt будет выглядеть следующим образом:

После редактирования файла выполните команду git add merge.txt , чтобы добавить новое объединенное содержимое в раздел проиндексированных файлов. Для завершения слияния создайте новый коммит, выполнив следующую команду:

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

Команды Git, с помощью которых можно разрешить конфликты слияния

Общие инструменты

Команда status часто используется во время работы с Git и помогает идентифицировать конфликтующие во время слияния файлы.

При передаче аргумента —merge для команды git log будет создан журнал со списком конфликтов коммитов между ветками, для которых выполняется слияние.

Команда diff помогает найти различия между состояниями репозитория/файлов. Она полезна для выявления и предупреждения конфликтов слияния.

Инструменты для случаев, когда Git прерывает работу в самом начале слияния

Команда checkout может использоваться для отмены изменений в файлах или для изменения веток.

Команда reset может использоваться для отмены изменений в рабочем каталоге или в разделе проиндексированных файлов.

Инструменты для случаев, когда конфликты Git возникают во время слияния

При выполнении команды git merge с опцией —abort процесс слияния будет прерван, а ветка вернется к состоянию, в котором она находилась до начала слияния.

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

Резюме

Конфликты слияния могут пугать. К счастью, Git предлагает мощные инструменты их поиска и разрешения. Большую часть слияний система Git способна обрабатывать самостоятельно с помощью функций автоматического слияния. Конфликт возникает, когда в двух ветках была изменена одна и та же строка в файле или когда некий файл удален в одной ветке и отредактирован в другой. Как правило, конфликты возникают при работе в команде.

Существует множество способов разрешения конфликтов слияния. В этой статье мы рассмотрели немалое количество инструментов командной строки, которые предоставляет Git. Более подробную информацию об этих инструментах см. на отдельных страницах для команд git log , git reset , git status , git checkout и git reset . Помимо этого многие сторонние инструменты также предлагают оптимизированные функции, поддерживающие работу с конфликтами слияния.

Готовы попробовать ветвление?

Ознакомьтесь с этим интерактивным обучающим руководством.

Источник

7.8 Инструменты Git — Продвинутое слияние

Продвинутое слияние

Обычно выполнять слияния в Git довольно легко. Git упрощает повторные слияния с одной и той же веткой, таким образом, позволяя вам иметь очень долго живущую ветку, и вы можете сохранять ее всё это время в актуальном состоянии, часто разрешая маленькие конфликты, а не доводить дело до одного большого конфликта по завершению всех изменений.

Однако, иногда всё же будут возникать сложные конфликты. В отличие от других систем управления версиями, Git не пытается быть слишком умным при разрешении конфликтов слияния. Философия Git заключается в том, чтобы быть умным, когда слияние разрешается однозначно, но если возникает конфликт, он не пытается сумничать и разрешить его автоматически. Поэтому, если вы слишком долго откладываете слияние двух быстрорастущих веток, вы можете столкнуться с некоторыми проблемами.

В этом разделе мы рассмотрим некоторые из возможных проблем и инструменты, которые предоставляет Git, чтобы помочь вам справиться с этими более сложными ситуациями. Мы также рассмотрим некоторые другие нестандартные типы слияний, которые вы можете выполнять, и вы узнаете как можно откатить уже выполненные слияния.

Конфликты слияния

Мы рассказали некоторые основы разрешения конфликтов слияния в Основные конфликты слияния, для работы с более сложными конфликтами Git предоставляет несколько инструментов, которые помогут вам понять, что произошло и как лучше обойтись с конфликтом.

Во-первых, если есть возможность, перед слиянием, в котором может возникнуть конфликт, позаботьтесь о том, чтобы ваша рабочая копия была без локальных изменений. Если у вас есть несохранённые наработки, либо припрячьте их, либо сохраните их во временной ветке. Таким образом, вы сможете легко отменить любые изменения, которые сделаете в рабочем каталоге. Если при выполнении слияния вы не сохраните сделанные изменения, то некоторые из описанных ниже приёмов могут привести к утрате этих наработок.

Давайте рассмотрим очень простой пример. Допустим, у нас есть файл с исходниками на Ruby, выводящими на экран строку ‘hello world’.

В нашем репозитории, мы создадим новую ветку по имени whitespace и выполним замену всех окончаний строк в стиле Unix на окончания строк в стиле DOS. Фактически, изменения будут внесены в каждую строку, но изменятся только пробельные символы. Затем мы заменим строку «hello world» на «hello mundo».

Теперь мы переключимся обратно на ветку master и добавим к функции некоторую документацию.

Теперь мы попытаемся слить в текущую ветку whitespace и в результате получим конфликты, так как изменились пробельные символы.

Прерывание слияния

В данный момент у нас есть несколько вариантов дальнейших действий. Во-первых, давайте рассмотрим как выйти из этой ситуации. Если вы, возможно, не были готовы к конфликтам и на самом деле не хотите связываться с ними, вы можете просто отменить попытку слияния, используя команду git merge —abort .

Эта команда пытается откатить ваше состояние до того, что было до запуска слияния. Завершиться неудачно она может только в случаях, если перед запуском слияния у вас были не припрятанные или не зафиксированные изменения в рабочем каталоге, во всех остальных случаях всё будет хорошо.

Если по каким-то причинам вы обнаружили себя в ужасном состоянии и хотите просто начать всё сначала, вы можете также выполнить git reset —hard HEAD (либо вместо HEAD указав то, куда вы хотите откатиться). Но помните, что это откатит все изменения в рабочем каталоге, поэтому удостоверьтесь, что никакие из них вам не нужны.

Игнорирование пробельных символов

В данном конкретном случае конфликты связаны с пробельными символами. Мы знаем это, так как это простой пример, но в реальных ситуациях это также легко определить при изучении конфликта, так как каждая строка в нем будет удалена и добавлена снова. По умолчанию Git считает все эти строки изменёнными и поэтому не может слить файлы.

Стратегии слияния, используемой по умолчанию, можно передать аргументы, и некоторые из них предназначены для соответствующей настройки игнорирования изменений пробельных символов. Если вы видите, что множество конфликтов слияния вызваны пробельными символами, то вы можете прервать слияние и запустить его снова, но на этот раз с опцией -Xignore-all-space или -Xignore-space-change . Первая опция игнорирует изменения в любом количестве существующих пробельных символов, вторая игнорирует вообще все изменения пробельных символов.

Поскольку в этом примере реальные изменения файлов не конфликтуют, то при игнорировании изменений пробельных символов всё сольётся хорошо.

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

Ручное слияние файлов

Хотя Git довольно хорошо обрабатывает пробельные символы, с другими типами изменений он не может справиться автоматически, но существуют другие варианты исправления. Например, представим, что Git не умеет обрабатывать изменения пробельных символов и нам нужно сделать это вручную.

То что нам действительно нужно — это перед выполнением самого слияния прогнать сливаемый файл через программу dos2unix . Как мы будем делать это?

Во-первых, мы перейдём в состояние конфликта слияния. Затем нам необходимо получить копии нашей версии файла, их версии файла (из ветки, которую мы сливаем) и общей версии (от которой ответвились первые две). Затем мы исправим либо их версию, либо нашу и повторим слияние только для этого файла.

Читайте также:  1600 крысы лд50 хитозан

Получить эти три версии файла, на самом деле, довольно легко. Git хранит все эти версии в индексе в разных «состояниях», каждое из которых имеет ассоциированный с ним номер. Состояние 1 — это общий предок, состояние 2 — ваша версия и состояния 3 взято из MERGE_HEAD — версия, которую вы сливаете («их» версия).

Вы можете извлечь копию каждой из этих версий конфликтующего файла с помощью команды git show и специального синтаксиса.

Если вы хотите что-то более суровое, то можете также воспользоваться служебной командой ls-files -u для получения SHA-1 хешей для каждого из этих файлов.

Выражение :1:hello.rb является просто сокращением для поиска такого SHA-1 хеша.

Теперь, когда в нашем рабочем каталоге присутствует содержимое всех трёх состояний, мы можем вручную исправить их, чтобы устранить проблемы с пробельными символами и повторно выполнить слияние с помощью малоизвестной команды git merge-file , которая делает именно это.

Теперь у нас есть корректно слитый файл. На самом деле, данный способ лучше, чем использование опции ignore-all-space , так как в его рамках вместо игнорирования изменений пробельных символов перед слиянием выполняется корректное исправление таких изменений. При слиянии с ignore-all-space мы в результате получим несколько строк с окончаниями в стиле DOS, то есть в одном файле смешаются разные стили окончания строк.

Если перед коммитом изменений вы хотите посмотреть какие в действительности были различия между состояниями, то можете воспользоваться командой git diff , сравнивающей содержимое вашего рабочего каталога, которое будет зафиксировано как результат слияния, с любым из трёх состояний. Давайте посмотрим на все эти сравнения.

Чтобы сравнить результат слияния с тем, что было в вашей ветке до слияния, или другими словами увидеть, что привнесло данное слияние, вы можете выполнить git diff —ours

Итак, здесь мы можем легко увидеть что же произошло с нашей веткой, какие изменения в действительности внесло слияние в данный файл — изменение только одной строки.

Если вы хотите узнать чем результат слияния отличается от сливаемой ветки, то можете выполнить команду git diff —theirs . В этом и следующем примере мы используем опцию -w для того, чтобы не учитывать изменения в пробельных символах, так как мы сравниваем результат с тем, что есть в Git, а не с нашим исправленным файлом hello.theirs.rb .

И, наконец, вы можете узнать как изменился файл по сравнению сразу с обеими ветками с помощью команды git diff —base .

В данный момент мы можем использовать команду git clean для того, чтобы удалить не нужные более дополнительные файлы, созданные нами для выполнения слияния.

Использование команды checkout в конфликтах

Возможно, нас по каким-то причинам не устраивает необходимость выполнения слияния в текущий момент, или мы не можем хорошо исправить конфликт и нам необходимо больше информации.

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

У нас есть три уникальных коммита, которые присутствуют только в ветке master и три других, которые присутствуют в ветке mundo . Если мы попытаемся слить ветку mundo , то получим конфликт.

Мы хотели бы увидеть в чем состоит данный конфликт. Если мы откроем конфликтующий файл, то увидим нечто подобное:

В обеих сливаемых ветках в этот файл было добавлено содержимое, но в некоторых коммитах изменялись одни и те же строки, что и привело к конфликту.

Давайте рассмотрим несколько находящихся в вашем распоряжении инструментов, которые позволяют определить как возник этот конфликт. Возможно, не понятно как именно вы должны исправить конфликт и вам требуется больше информации.

Полезным в данном случае инструментом является команда git checkout с опцией —conflict . Она заново выкачает файл и заменит маркеры конфликта. Это может быть полезно, если вы хотите восстановить маркеры конфликта и попробовать разрешить его снова.

В качестве значения опции —conflict вы можете указывать diff3 или merge (последнее значение используется по умолчанию). Если вы укажете diff3 , Git будет использовать немного другую версию маркеров конфликта — помимо «нашей» и «их» версий файлов будет также отображена «базовая» версия, и таким образом вы получите больше информации.

После того, как вы выполните эту команду, файл будет выглядеть так:

Если вам нравится такой формат вывода, то вы можете использовать его по умолчанию для будущих конфликтов слияния, установив параметру merge.conflictstyle значение diff3 .

Команде git checkout также можно передать опции —ours и —theirs , которые позволяют действительно быстро выбрать одну из версий файлов, не выполняя слияния совсем.

Это может быть действительно полезным при возникновении конфликтов в бинарных файлах (в этом случае вы можете просто выбрать одну из версий), или при необходимости слить из другой ветки только некоторые файлы (в этом случае вы можете выполнить слияние, а затем перед коммитом переключить нужные файлы на требуемые версии).

История при слиянии

Другой полезный инструмент при разрешении конфликтов слияния — это команда git log . Она поможет вам получить информацию о том, что могло привести к возникновению конфликтов. Временами может быть очень полезным просмотреть историю, чтобы понять почему в двух ветках разработки изменялась одна и та же область кода.

Для получения полного списка всех уникальных коммитов, которые были сделаны в любой из сливаемых веток, мы можем использовать синтаксис «трёх точек», который мы изучили в Три точки.

Это список всех шести коммитов, включённых в слияние, с указанием также ветки разработки, в которой находится каждый из коммитов.

Мы также можем сократить его, попросив предоставить нам более специализированную информацию. Если мы добавим опцию —merge к команде git log , то она покажет нам только те коммиты, в которых изменялся конфликтующий в данный момент файл.

Если вы выполните эту команду с опцией -p , то получите только список изменений файла, на котором возник конфликт. Это может быть действительно полезным для быстрого получения информации, которая необходима, чтобы понять почему что-либо конфликтует и как наиболее правильно это разрешить.

Комбинированный формат изменений

Так как Git добавляет в индекс все успешные результаты слияния, то при вызове git diff в состоянии конфликта слияния будет отображено только то, что сейчас конфликтует. Это может быть полезно, так как вы сможете увидеть какие ещё конфликты нужно разрешить.

Если вы выполните git diff сразу после конфликта слияния, то получите информацию в довольно своеобразном формате.

Такой формат называется «комбинированным» («Combined Diff»), для каждого различия в нем содержится два раздела с информацией. В первом разделе отображены различия строки (добавлена она или удалена) между «вашей» веткой и содержимым вашего рабочего каталога, а во втором разделе содержится то же самое, но между «их» веткой и рабочим каталогом.

Таким образом, в данном примере вы можете увидеть строки и >>>>>>> в файле в вашем рабочем каталоге, хотя они отсутствовали в сливаемых ветках. Это вполне оправдано, потому что, добавляя их, инструмент слияния предоставляет вам дополнительную информацию, но предполагается, что мы удалим их.

Если мы разрешим конфликт и снова выполним команду git diff , то получим ту же информацию, но в немного более полезном представлении.

В этом выводе указано, что строка «hola world» при слиянии присутствовала в «нашей» ветке, но отсутствовала в рабочей копии, строка «hello mundo» была в «их» ветке, но не в рабочей копии, и, наконец, «hola mundo» не была ни в одной из сливаемых веток, но сейчас присутствует в рабочей копии. Это бывает полезно просмотреть перед коммитом разрешения конфликта.

Такую же информацию вы можете получить и после выполнения слияния с помощью команды git log , узнав таким образом как был разрешён конфликт. Git выводит информацию в таком формате, если вы выполните git show для коммита слияния или вызовете команду git log -p с опцией —cc (без неё данная команда не показывает изменения для коммитов слияния).

Отмена слияний

Теперь когда вы знаете как создать коммит слияния, вы можете сделать его по ошибке. Одна из замечательных вещей в работе с Git — это то, что ошибки совершать не страшно, так как есть возможность исправить их (и в большинстве случаев сделать это просто).

Коммит слияния не исключение. Допустим, вы начали работать в тематической ветке, случайно слили ее в master , и теперь ваша история коммитов выглядит следующим образом:

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

Исправление ссылок

Если нежелаемый коммит слияния существует только в вашем локальном репозитории, то простейшее и лучшее решение состоит в перемещении веток так, чтобы они указывали туда куда вам нужно. В большинстве случаев, если вы после случайного git merge выполните команду git reset —hard HEAD

, то указатели веток восстановятся так, что будут выглядеть следующим образом:

Мы рассматривали команду reset ранее в Раскрытие тайн reset, поэтому вам должно быть не сложно разобраться с тем, что здесь происходит. Здесь небольшое напоминание: reset —hard обычно выполняет три шага:

Перемещает ветку, на которую указывает HEAD. В данном случае мы хотим переместить master туда, где она была до коммита слияния ( C6 ).

Читайте также:  Как избавится от яиц клопов

Приводит индекс к такому же виду что и HEAD.

Приводит рабочий каталог к такому же виду, что и индекс.

Недостаток этого подхода состоит в изменении истории, что может привести к проблемам в случае совместно используемого репозитория. Загляните в Опасности перемещения, чтобы узнать что именно может произойти; кратко говоря, если у других людей уже есть какие-то из изменяемых вами коммитов, вы должны отказаться от использования reset . Этот подход также не будет работать, если после слияния уже был сделан хотя бы один коммит; перемещение ссылки фактически приведёт к потере этих изменений.

Отмена коммита

Если перемещение указателей ветки вам не подходит, Git предоставляет возможность сделать новый коммит, который откатывает все изменения, сделанные в другом. Git называет эту операцию «восстановлением» («revert»), в данном примере вы можете вызвать её следующим образом:

Опция -m 1 указывает какой родитель является «основной веткой» и должен быть сохранен. Когда вы выполняете слияние в HEAD ( git merge topic ), новый коммит будет иметь двух родителей: первый из них HEAD ( C6 ), а второй — вершина ветки, которую сливают с текущей ( C4 ). В данном случае, мы хотим отменить все изменения, внесённые слиянием родителя #2 ( C4 ), и сохранить при этом всё содержимое из родителя #1 ( C6 ).

История с коммитом восстановления (отменой коммита слияния) выглядит следующим образом:

Новый коммит ^M имеет точно такое же содержимое как C6 , таким образом, начиная с нее всё выглядит так, как будто слияние никогда не выполнялось, за тем лишь исключением, что «теперь уже не слитые» коммиты всё также присутствуют в истории HEAD . Git придет в замешательство, если вы вновь попытаетесь слить topic в ветку master :

В ветке topic нет ничего, что ещё недоступно из ветки master . Плохо, что в случае добавления новых наработок в topic , при повторении слияния Git добавит только те изменения, которые были сделаны после отмены слияния:

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

В этом примере, M и ^M отменены. В коммите ^^M , фактически, сливаются изменения из C3 и C4 , а в C8 — изменения из C7 , таким образом, ветка topic полностью слита.

Другие типы слияний

До этого момента мы рассматривали типичные слияния двух веток, которые обычно выполняются с использованием стратегии слияния, называемой «рекурсивной». Но существуют и другие типы слияния веток. Давайте кратко рассмотрим некоторые из них.

Выбор «нашей» или «их» версий

Во-первых, существует ещё один полезный приём, который мы можем использовать в обычном «рекурсивном» режиме слияния. Мы уже видели опции ignore-all-space и ignore-space-change , которые передаются с префиксом -X , но мы можем также попросить Git при возникновении конфликта использовать ту или иную версию файлов.

По умолчанию, когда Git при слиянии веток замечает конфликт, он добавляет в код маркеры конфликта, отмечает файл как конфликтующий и позволяет вам разрешить его. Если же вместо ручного разрешения конфликта вы хотите, чтобы Git просто использовал какую-то определённую версию файла, а другую игнорировал, то вы можете передать команде merge одну из двух опций -Xours или -Xtheirs .

В этом случае Git не будет добавлять маркеры конфликта. Все неконфликтующие изменения он сольёт, а для конфликтующих он целиком возьмёт ту версию, которую вы указали (это относится и к бинарным файлам).

Если мы вернёмся к примеру «hello world», который использовали раньше, то увидим, что попытка слияния в нашу ветку приведёт к конфликту.

Однако, если мы выполним слияние с опцией -Xours или -Xtheirs , конфликта не будет.

В этом случае, вместо добавления в файл маркеров конфликта с «hello mundo» в качестве одной версии и с «hola world» в качестве другой, Git просто выберет «hola world». Однако, все другие неконфликтующие изменения будут слиты успешно.

Такая же опция может быть передана команде git merge-file , которую мы обсуждали ранее, то есть для слияния отдельных файлов можно использовать команду git merge-file —ours .

На случай если вам нужно нечто подобное, но вы хотите, чтобы Git даже не пытался сливать изменения из другой версии, существует более суровый вариант — стратегия слияния «ours». Важно отметить, что это не то же самое что опция «ours» рекурсивной стратегии слияния.

Фактически, эта стратегия выполнит ненастоящее слияние. Она создаст новый коммит слияния, у которой родителями будут обе ветки, но при этом данная стратегия даже не взглянет на ветку, которую вы сливаете. В качестве результата слияния она просто оставляет тот код, который находится в вашей текущей ветке.

Вы можете видеть, что между веткой, в которой мы были, и результатом слияния нет никаких отличий.

Это часто бывает полезно, когда нужно заставить Git считать, что ветка уже слита, а реальное слияние отложить на потом. Для примера предположим, что вы создали ветку release и проделали в ней некоторую работу, которую когда-то впоследствии захотите слить обратно в master . Тем временем в master были сделаны некоторые исправления, которые необходимо перенести также в вашу ветку release . Вы можете слить ветку с исправлениями в release , а затем выполнить merge -s ours этой ветки в master (хотя исправления в ней уже присутствуют), так что позже, когда вы будете снова сливать ветку release , не возникнет конфликтов, связанных с этими исправлениями.

Слияние поддеревьев

Идея слияния поддеревьев состоит в том, что у вас есть два проекта и один из проектов отображается в подкаталог другого. Когда вы выполняете слияние поддеревьев, Git в большинстве случаев способен понять, что одно из них является поддеревом другого и выполнить слияние подходящим способом.

Далее мы рассмотрим пример добавления в существующий проект другого проекта и последующее слияние кода второго проекта в подкаталог первого.

Первым делом мы добавим в наш проект приложение Rack. Мы добавим Rack в наш собственный проект, как удалённый репозиторий, а затем выгрузим его в отдельную ветку.

Таким образом, теперь у нас в ветке rack_branch находится основная ветка проекта Rack, а в ветке master — наш собственный проект. Если вы переключитесь сначала на одну ветку, а затем на другую, то увидите, что они имеют абсолютно разное содержимое:

Может показаться странным, но, на самом деле, ветки в вашем репозитории не обязаны быть ветками одного проекта. Это мало распространено, так как редко бывает полезным, но иметь ветки, имеющие абсолютно разные истории, довольно легко.

В данном примере, мы хотим выгрузить проект Rack в подкаталог нашего основного проекта. В Git мы можем выполнить это с помощью команды git read-tree . Вы узнаете больше о команде read-tree и её друзьях в главе Git изнутри, сейчас же вам достаточно знать, что она считывает содержимое некоторой ветки в ваш текущий индекс и рабочий каталог. Мы просто переключимся обратно на ветку master и выгрузим ветку rack_branch в подкаталог rack ветки master нашего основного проекта:

Когда мы будем выполнять коммит, он будет выглядеть так, как будто все файлы проекта Rack были добавлены в этот подкаталог — например, мы скопировали их из архива. Важно отметить, что слить изменения одной из веток в другую довольно легко. Таким образом, если проект Rack обновился, мы можем получить изменения из его репозитория просто переключившись на соответствующую ветку и выполнив операцию git pull :

Затем мы можем слить эти изменения обратно в нашу ветку master .

Для того, чтобы получить изменения и заполнить сообщение коммита используйте параметр —squash , вместе с опцией -Xsubtree рекурсивной стратегии слияния. Вообще-то, по умолчанию используется именно рекурсивная стратегия слияния, но мы указали и её тоже для пущей ясности.

Все изменения из проекта Rack слиты и подготовлены для локального коммита. Вы также можете поступить наоборот — сделать изменения в подкаталоге rack вашей основной ветки и затем слить их в вашу ветку rack_branch , чтобы позже передать их ответственным за проекты или отправить их в вышестоящий репозиторий проекта Rack.

Таким образом, слияние поддеревьев даёт нам возможность использовать рабочий процесс в некоторой степени похожий на рабочий процесс с подмодулями, но при этом без использования подмодулей (которые мы рассмотрим в Подмодули). Мы можем держать ветки с другими связанными проектами в нашем репозитории и периодически сливать их как поддеревья в наш проект. С одной стороны это удобно, например, тем, что весь код хранится в одном месте. Однако, при этом есть и некоторые недостатки — поддеревья немного сложнее, проще допустить ошибки при повторной интеграции изменений или случайно отправить ветку не в тот репозиторий.

Другая небольшая странность состоит в том, что для получения различий между содержимым подкаталога rack и содержимого ветки rack_branch — для того, чтобы увидеть необходимо ли выполнять слияния между ними — вы не можете использовать обычную команду diff . Вместо этого следует выполнить команду git diff-tree , указав ветку, с которой производится сравнение:

Вот как выглядит процедура сравнения содержимого подкаталога rack с содержимым ветки master на сервере после последнего скачивания изменений:

Источник

Оцените статью
Избавляемся от вредителей