MyTetra Share
Делитесь знаниями!
Git: Разрешение конфликтов объединений
07.08.2018
11:45
Текстовые метки: git, merge, слияние, объединение, разрешение конфликтов
Раздел: Компьютер - Программирование - Системы контроля версий (VCS) - Git

Разрешение конфликтов объединений

Если слияние не произошло автоматически, git помещает индекс и рабочий катвалог в специальное состояние содержащее всю необходимую информацию чтобы помочь разрешить конфликт слияния. Файлы с конфликтами помечены в индексе специально, так, что до тех пор, пока вы решите проблему и не обновите индекс, совершить git-commit(1) не удастся:


$ git commit
file.txt: needs merge file.txt
#необходимо объединить file.txt


Кроме того, в git-status(1) будут перечислены эти файлы, как “unmerged” (необъединённые), а файлы с конфликтами будет помечены маркерами конфликта, например:


<<<<<<< HEAD:file.txt
Hello world
=======
Goodbye
>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt


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

$ git add file.txt
$ git commit


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


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

7.1. Получение помощи при разрешения конфликтов слияния


Все изменения, которые git смог объединить автоматически уже добавлены в файл индекса, так что git-diff(1) показывает только конфликты. Используется особый синтаксис:

$ git diff
diff --cc file.txt
index 802992c,2b60207..0000000
--- a/file.txt
+++ b/file.txt
@@@ -1,1 -1,1 +1,5 @@@
++<<<<<<< HEAD:file.txt
+Hello world
++=======
+ Goodbye
++>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt


Напомним, что комммит, который будут зафиксирован после того, как мы урегулировали конфликт будет иметь двух родителей, а не одного: один из родителей будет HEAD – вершина текущей ветки; а другой будут вершиной другой ветви, значение которой временно сохраняется в переменной MERGE_HEAD. В ходе слияния, индекс содержит три версии каждого файла.
Каждая из этих трех «стадий файла» представляет различные версии файла:

$ git show :1:file.txt # файл в общий предке обеих ветвей
$ git show :2:file.txt # Версия HEAD, но и в том числе любое
# неконфликтное изменение из MERGE_HEAD
$ git show :3:file.txt # версия с MERGE_HEAD, но и в том числе любое
# не конфликтующее изменение от HEAD.


Начиная со стадии 2 и стадии 3 версии были обновлены с неконфликтными изменениями, и остающиеся разлияия между ними являются существенной для анализа слияния, и которую можно получить командой git-diff(1) для из индекса для показа этих оставшихся конфликтов.


Команда git diff показывает различия между версией рабочего каталога file.txt и версиями стадии 2 и стадии 3.
Таким образом заменяя в каждой строке один "+" или “-", он теперь использует две колонки: первая колонка используется для различия между первым родителем и рабочий каталогом, а вторая отмечает различия между вторым родителем и рабочий каталогом. (См. “COMBINED DIFF FORMAT” раздела git-diff-files(1) для деталей формата.)


После разрешения конфликта обычным способом (но до обновления индекса), сравнения это будет выглядеть следующим образом:

$ git diff
diff --cc file.txt
index 802992c,2b60207..0000000
a/file.txt
+++ b/file.txt
@@@ -1,1 -1,1 +1,1 @@@
– Hello world
-Goodbye
++Goodbye world


Здесь видно, что наше версия решение исключить “Hello world” от первого родителя, удалить “Goodbye” от второго родителя, и добавить: “Goodbye world”, которое ранее отсутствовало в обоих предках.


В некоторых особые опции diff позволяют находить различия рабочего каталога от любого из этих этапов:

$ git diff -1 file.txt # различий с этапом 1
$ git diff --base file.txt # как выше
$ git diff -2 file.txt # различий c этапом 2
$ git diff --ours file.txt # как выше
$ git diff -3 file.txt # различий c этапом 3
$ git diff --theirs file.txt # как выше.


Командами git-log(1) и gitk[1] также обеспечивается помощь при слиянии:

$ git log --merge
$ gitk --merge


Они будут показывать все коммиты, которые существуют только в HEAD и в MERGE_HEAD, и которых касаются необъеденяемый файл.


Вы также можете использовать git-mergetool(1) которая позволяет объединить необъеденённые файлы с помощью внешних средств, таких как emacs или kdiff3.


Каждый раз, когда вы урегулировать конфликты в файле и обновляя индекс:

$ git add file.txt


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

8. Отмена слияния

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

$ git reset --hard HEAD


Или, если вы уже зафиксировали слияние коммитом, что вы хотите его отменить,


$ git reset --hard ORIG_HEAD


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

9. Быстрое слияние вперёд

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


Однако, если текущая ветка является потомком другой и каждый коммит всегда один и всегда содержат коммит предыдущего, то можно просто выполнив команду git`ом “fast forward” (Быстрое движение вперёд) вершина текущей ветки продвинется вперед до точки вершины объединения в новую ветвь, без каких-либо новых созданий коммитов.

10. Исправление ошибок

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

$ git reset --hard HEAD


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


  1. Вы можете создавать новые коммит, в котором отменить всё, что вы сделали в старом коммите. Это правильный вариант, если ваши ошибки уже были преданы гласности.
  2. Ни в коем случае не делайте этого, если вы уже сделали историю гласной; git обычно не ожидает изменения истоии проекта, и не может корректно выполнять частые слияния с веткой, у которой имело изменение истории. Напрример, от удаляемого вами коммита кто-то уже мог создать отдельную ветку разработки и такая ветка не сможет быть в дальнейшем корректно слита с вашим проектом.

10.1. Исправление ошибки новым коммитом

Создание нового коммита, который отменяет ранее сделанные исправления, очень легко, просто дайте команду git-revert(1) с ссылкой на плохой коммит; например, вернуться к последниме коммиту:

$ git revert HEAD


Это позволит создать новыЙ коммит, который “откатывает” изменения HEAD. Вам будет предоставлена возможность отредактировать сообщение для совершения нового коммита.


Вы также можете вернуть последние изменения, например, предпоследние:

$ git revert HEAD^


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


Если последние изменения пересекаются с изменениями будет восстановления, Вам будет предложено устранить конфликты вручную, как и в случае удалени конфликтов a merge .

10.2. Исправление ошибки путём переписывания истории

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


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

$ git commit --amend


который заменит старый коммит новым путём включения ваших изменений, и давая вам возможность изменить вначале старое сообщение коммита.
Опять же, вы никогда не должны делать это с коммитом который уже был объединён в другую ветвь, в этом случае лучше использовать git-revert(1).
Кроме того, можно заменить более старые коммиты в истории, но это более сложная тема и мы оставим её его для другой главы.


Так же в этом разделе:
 
MyTetra Share v.0.52
Яндекс индекс цитирования