Файл: Система управления версиями git и российский сервис хранения исходного кода gitflic.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 06.12.2023
Просмотров: 214
Скачиваний: 7
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
78
Цель любой из стратегий – построить дерево рабочей директории, ко- торое будет записано в merge-commit. Стратегия resolve заключается в вы- полнении следующих пяти шагов.
1. Поиск общего предка. Например, при выполнении явного слияния на рисунке 2.15б общим предком будет коммит ГВ3.
2. Поиск файлов, изменившихся в сливаемых ветках относительно общего предка.
3. Блоки, оставшиеся без изменений, включаются в дерево как неиз- менившиеся.
4. Блоки, изменившиеся только в одной из сливаемых веток, добав- ляются в дерево как изменившиеся.
5. Блоки, изменившиеся в обеих ветках, добавляются в дерево только при условии, если изменения в них одинаковые. Если изменения различные, то возникает ситуация «конфликт», и выполнение команды git merge при- останавливается. Программист, выполняющий слияние, должен или устра- нить конфликт и продолжить слияние, или отменить слияние.
Недостаток стратегии resolve проявляется в случае, если в рабочей ветке продолжается редактирование кода и создание новых коммитов. В этой ситуации через некоторое время появится необходимость выполнить ещё одно слияние. Окажется, что для нового слияния общий предок тот же, что и для предыдущего. В результате придётся повторно устранять кон- фликты. В остальных 4 стратегиях сделаны попытки устранить указанный недостаток, но идеальное решение пока не найдено.
2.22.1. Устранение конфликтов слияния
Рассмотрим на примере, как устраняются файловые конфликты, воз- никающие при использовании стратегии слияния resolve. Возникновение конфликта означает, что имеются две различные версии файла, и Git не знает, какую из них добавить в рабочую директорию merge-commit. Git про- сит программиста, выполняющего слияние, дать ему указания, какую вер- сию включать в рабочую директорию.
Предположим, что в проекте есть файл с именем Test_merge.txt. На рисунке 2.16 показано, как изменялось содержимое этого файла.
79
Рисунок 2.16 – Пример слияния веток с конфликтом
Выполним слияние веток Main_branch и Working_branch:
/c/Learn_git
(Working_branch)
$ git checkout 'Main_branch'
Switched to branch 'Main_branch'
/c/Learn_git
(Main_branch)
$ git merge 'Working_branch'
Auto-merging Test_Merge.txt
CONFLICT (content): Merge conflict in Test_Merge.txt
Automatic merge failed; fix conflicts and then commit the result.
В строке “CONFLICT (content): Merge conflict in Test_Merge.txt”Git сообщил, что возник конфликт в содержимом (content) файла
Test_Merge.txt.
В строке “Automatic merge failed; fix conflicts and then commit the re- sult.” Git сообшил, что автоматически устранить конфликт не удалось, и просит устранить конфликт и создать коммит.
В сложившейся ситуации возможны три варианта действий:
1. Отредактировать конфликтующие файлы, добившись того, чтобы они были одинаковыми.
2. Выбрать один из двух файлов.
3. Прервать слияние веток, выполнив команду git merge –abort.
Рассмотрим первые два варианта.
Ручное редактирование конфликтующих файлов. Файл Test_Merge.
80 txt в текущей рабочей директории выглядит следующим образом:
Начало редактирования
<<<<<<< HEAD
Продолжение редактирования
=======
Продолжение редактирования
Окончание редактирования
>>>>>>> Working_branch
Git разметил текст, чтобы помочь в устранении конфликта. Весь текст, находящийся до строчки <<<<<<< HEAD, одинаковый в конфликтующих файлах.
Текст между строчками <<<<<<< HEAD и ======= есть только в файле, находящемся в целевой ветке (HEAD). В нашем случае это ветка
Main_branch.
Текст между строчками ======= и >>>>>>> Working_branch есть только в файле, находящемся в рабочей ветке. В нашем случае это ветка
Working_branch.
Для устранения конфликта необходимо отредактировать конфликту- ющий файл, находящийся в текущей директории. Не обязательно оставлять в файле текст, который поместил в него Git. Можно ввести новый текст, ни- как не связанный с конфликтующими файлами. Обязательно нужно удалить разметку <<<<<<< HEAD, =======, >>>>>>> Working_branch.
После окончания редактирования и сохранения файла конфликт счита- ется устранённым, и можно завершить слияние командой git merge –continue.
В сложных случаях устранению конфликта может помочь наличие в редактируемом файле общего предка. Чтобы Git добавлял общего предка в заготовку для устранения конфликта, необходимо изменить настройки Git, выполнив команду: git config –global merge.conflictstyle diff3
Остановим предыдущее слияние, выполнив команду git merge –abort.
И снова запустим процесс слияния командой git merge ‘Working_branch’.
Сообщение о том, что выполнить слияние невозможно, не изменится. Изме- нится содержание файла Test_Merge.txt. Теперь оно выглядит следующим образом:
<<<<<<< HEAD
Начало редактирования
Продолжение редактирования
||||||| e83aaf9
Начало редактирования
=======
Начало редактирования
Продолжение редактирования
81
Окончание редактирования
>>>>>>> Working_branch
Текст между строчкой ||||||| e83aaf9 и ======= представляет собой об- щего предка. Цифры e83aaf9 – это хеш-значение коммита, в котором нахо- дится общий предок конфликтующих файлов. Из сравнения общего предка с конфликтующими файлами видно, что:
− в файл Test_Merge.txt, находящийся в целевой ветке (HEAD), была добавлена строчка «Продолжение редактирования»;
− в файл Test_Merge.txt, находящийся в рабочей ветке
(Working_branch), были добавлены две строчки «Продолжение редактиро- вания» и «Окончание редактирования».
Изменим файл Test_Merge.txt так, чтобы он содержал текст, который должен быть добавлен в merge-commit. Например, так:
Начало редактирования
Продолжение редактирования (добавлено в целевой ветке)
Окончание редактирования (добавлено в рабочей ветке).
Добавим изменённый файл Test_Merge.txt в индекс (git add
Test_Merge.txt) и продолжим процесс слияния, выполнив команду git merge –
continue. В процессе выполнения последней команды автоматически откро- ется редактор для того, чтобы можно было ввести комментарий к merge- commit. Результат слияния:
$ git add Test_Merge.txt
/c/Learn_git
(Main_branch|MERGING)
$ git merge --continue
[Main_branch 2e9bb1b] Merge branch 'Working_branch' into Main_branch
Слияние прошло успешно. Был создан merge-commit с хеш-значением
2e9bb1b, находящийся в ветке Main_branch. В объекте merge-commit содер- жится следующая информация:
/c/Learn_git
(Main_branch)
$ git cat-file -p 2e9bb1b tree cc7737e7d54a2bb5b64bee090ab0b48a09f2a776 parent a4649d09a31c61b7a4d382f0d7ab27fd61b27249 parent 93bc4e1c6548223a5d6b40c3f11de56b7f1837d2 author Git_Book
Merge branch 'Working_branch' into Main_branch
Существенным является то, что merge-commit содержит ссылки на два родителя (две строчки parent).
Выбор одного из двух конфликтующих файлов. Для разрешения
82 конфликта путём выбора одного из двух файлов предусмотрены две ко- манды:
− git checkout –ours <имя файла>. При выполнении этой команды в текущую рабочую директорию будет скопирован конфликтующий файл из целевой ветки. Он заменит тот, который Git подготовил для ручного устра- нения конфликта;
− git checkout –their <имя файла>. При выполнении этой команды в текущую рабочую директорию будет скопирован конфликтующий файл из рабочей ветки. Он заменит тот, который Git подготовил для ручного устра- нения конфликта.
После выбора файла его необходимо добавить в индекс (git add <имя файла>) и продолжить выполнение слияния (git mege –continue).
2.23. Перенос начала ветки, команда git rebase
Перед тем, как продолжить, необходимо определить, что такое начало ветки и что такое конец ветки. Начало ветки – это коммит, самый близкий к ветке, из которой «растёт» текущая. Конец ветки – это последний коммит в текущей ветке.
Обычно при структуре дерева коммитов, соответствующей рисунку
2.14а, выполняется неявное слияние, а при структуре дерева коммитов, соответствующей рисунку 2.14б, выполняется явное слияние. Однако, бы- вают случаи, когда при структуре дерева коммитов, изображённой на ри- сунке 2.17, необходимо добавить в целевую ветку коммиты ВМ1 и ВМ2.
Выполнить такую операцию невозможно ни с помощью явного слияния, ни с помощью неявного слияния.
83
Рисунок 2.17 – Структура дерева коммитов до преобразования
Для решения поставленной задачи Git предлагает команду git rebase, которая может изменить структуру дерева коммитов, как показано на ри- сунке 2.18.
Рисунок 2.18 – Структура дерева коммитов после преобразования
После такого преобразования появляется возможность выполнить не- явное слияние, перенеся указатель целевой ветки на коммит ВМ2-Н. В ре- зультате коммиты ВМ1-Н и ВМ2-М окажутся включёнными в целевую ветку. У коммитов ВМ1-Н, ВМ2-Н хеш-значения не совпадают с хеш-зна- чениями коммитов ВМ1, ВМ2, однако изменение состояния рабочей дирек- тории и индекса описываются так же, как в дереве на рисунке 2.17.
Формат команды git rebase:
git rebase <ключи> <целевая ветка>
Команда git rebase выполняется, когда текущей веткой является рабочая.
84
Возможные значения ключей:
--abort – используется при возникновении конфликтов для прерыва- ния выполнения команды git rebase;
--continue – используется для продолжения выполнения команды git rebase после устранения конфликта;
--skip – позволяет пропустить текущий коммит при устранении кон- фликтов.
В процессе преобразования дерева коммитов командой git rebase вы- полняются следующие шаги:
1. Определяется база преобразования, для чего производится поиск общего коммита двух веток (на рисунке 2.17 это коммит ГВ3).
2. Вычисляется разница между предпоследним коммитом целевой ветки и первым коммитом рабочей ветки: d1 = ВМ1 – ГВ3.
3. Добавляется разница d1 к коммиту ГВ4, в результате получается коммит ВМ1-Н.
4. Для каждой пары коммитов в рабочей ветке вычисляется разница и добавляется к последнему преобразованному коммиту. Для примера (ри- сунок 2.17) будут выполнены следующие преобразования: d2 = ВМ2 – ВМ1;
ВМ2-Н = ВМ1-Н + d2.
5. После преобразования всех коммитов указатель рабочей ветки пе- реносится на последний из преобразованных коммитов.
6. Создаётся указатель ORIG_HEAD, указывающий на последний коммит в оригинальной рабочей ветке.
Для примера (рисунок 2.17) результат преобразования, выполненного командой git rebase, показан на рисунке 2.19.
Когда в процессе добавления разницы возникает конфликт, выполне- ние преобразования приостанавливается до тех пор, пока конфликт не будет устранён. Устранение конфликта происходит так, как описано в разделе
2.22.1.
85
Рисунок 2.19 – Результат выполнения команды rebase
Если в процессе анализа конфликтной ситуации оказывается, что файлы из текущего коммита можно не включать в рабочую директорию
(они полностью замещаются файлами из следующего коммита), то текущее преобразование можно пропустить, выполнив команду git rebase –skip.
Прервать выполнение команды git rebase возможно только в случае её приостановки для устранения конфликта. Прерывание осуществляется ко- мандой git rebase –break. Однако даже после завершения преобразования возможно вернуть дерево коммитов в состояние, в котором оно находилось до начала преобразования. Для этого используется указатель ORIG_HEAD и команда: git reset –hard ORIG_HEAD
2.24. Копирование коммита, команда git cherry-pick
В процессе работы над проектом может возникнуть ситуация, когда необходимо скопировать коммит из одной ветки в другую. Например, два разработчика создают части программного модуля, ведя работу в разных ветках. Одному из них необходимо получить код своего коллеги, в котором
86 описывается общий интерфейс. При этом сливать ветки пока рано. Или дру- гой пример. В результате ошибочных действий был потерян важный ком- мит. Его хеш-значение известно. Исправить ситуацию может копирование
«потерянного» коммита в текущую ветку.
Команда, позволяющая копировать коммиты, называется git cherry-pick.
1 2 3 4 5 6 7 8 9 ... 12
Формат команды git cherry-pick:
a) git cherry-pick <ключи> <хеш-значение коммита, подлежащего ко- пированию> b) git cherry-pick <ключи> <хеш-значение первого коммита> <хеш- значение последнего коммита>
В случае а будет скопирован один коммит. В случае b будет скопиро- вана последовательность коммитов.
Возможные значения ключей:
--edit – позволяет отредактировать комментарий к коммиту;
--no-commit – в рабочую директорию будут скопированы различия между её текущим содержимым и коммитом, указанным в команде, новый коммит создан не будет;
--abort – используется только при устранении конфликтов для преры- вания выполнения команды;
--continue – используется только при устранении конфликтов для про- должения выполнения команды после устранения конфликта.
Команда git cherry-pick копирует указанный коммит в текущую дирек- торию. Работает она следующим образом. Вычисляется разница между те- кущим состоянием рабочей директории и рабочей директорией, зафиксиро- ванной в переносимом коммите. Разница добавляется в текущую рабочую директорию. Если возникает конфликт, выполнение команды останавлива- ется до устранения конфликта. После добавления разницы создаётся новый коммит в текущей ветке.
87
2.25. Работа с внешним репозиторием
Git является распределённой системой контроля версий. Это значит, что каждый пользователь Git хранит свои репозитории отдельно и незави- симо от других пользователей. Хранить репозитории возможно или на ло- кальном компьютере, или в сетевом хранилище, называемом внешним ре- позиторием. Использование внешнего репозитория удобно при коллектив- ной работе, для обмена данными между участниками проекта или, когда ин- дивидуальный разработчик использует несколько компьютеров для выпол- нения работы.
Для работы с Git не требуется постоянная связь с внешним репозиторием.
Рабочая директория всегда находится на локальном компьютере. В процессе ра- боты коммиты тоже сохраняются на локальном компьютере. И только, когда разработчик готов поделиться с коллегами результатом, или когда индивиду- альному разработчику необходимо сменить рабочее место, коммиты копиру- ются во внешний репозиторий. Внешний репозиторий обычно защищён паро- лем, и им может пользоваться ограниченный круг разработчиков. Исключение составляют open-source проекты, для которых внешний репозиторий паролем не защищают, но ограничивают права на запись коммитов.
В качестве внешнего репозитория возможно использовать любое фай- ловое хранилище. Если файловое хранилище не подготовлено для хранения репозиториев, не имеет специальных инструментов для создания репозито- риев, просмотра их содержимого и подобных операций, то пользоваться им неудобно. Разработчикам программного обеспечения повезло, для них со- здан ряд внешних репозиториев с удобным интерфейсом и набором инстру- ментов для коллективной работы. Изложение материала в настоящем учеб- ном пособии базируется на внешнем репозитории GitFlic (GitFlic, 2022).
Нужно иметь в виду, что команды Git, предназначенные для работы с внеш- ним репозиторием, не зависят от репозитория, поэтому изложенный мате- риал остаётся универсальным.
2.25.1. Подключение внешнего репозитория, группа команд git
remote
О сайте GitFic будем думать, как об оболочке, внутри которой хранится множество репозиториев. Единицей хранения в GitFlic является проект.