Skip to content

Latest commit

 

History

History
150 lines (128 loc) · 16.3 KB

lab2.md

File metadata and controls

150 lines (128 loc) · 16.3 KB
title author fontsize geometry paper lang template
Лабораторная работа 2
А.В. Родионов
14pt
margin=2cm
a4
russian
default.html

Ветвление

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

Реализация веток в Git

Каждый коммит в Git представляет собой набор изменений и одну или более ссылок на предыдущие коммиты, от которых отсчитываются собственно изменения. Каждый коммит уникально идентифицируется контрольной суммой изменений (хэшем), вычисленной по алгоритму SHA-1, благодаря чему его всегда можно найти в репозитории и привести рабочую директорию проекта в состояние, которое ему соответствует. Для этого Git находит все родительские коммиты вплоть до самого первого и последовательно их применяет к исходному состоянию проекта. Ветка в Git представляет собой именованный указатель на коммит. Физически, это файл, у которого есть имя, совпадающее с названием ветки, а содержимое которого представляет собой хэш коммита. Ветка занимает мало места на диске, их можно создавать в практически неограниченном количестве, а так же переименовывать или удалять, не рискуя потерять какой-либо коммит из репозитория. При инициализации пустого репозитория автоматически создается ветка с названием master. Как правило, master отслеживает стабильную линию развития продукта, а новые возможности добавляются в побочных ветках, одну из которых традиционно называют develop.

Для того, чтобы отслеживать, на какой ветке в данный момент времени находится проект, Git поддерживает ветку-указатель HEAD. Она совпадает с последним коммитом текущей ветки проекта. При добавлении очередного коммита, его хэш сохраняется как в ветке, так и в указателе HEAD. Команда git checkout, если ей передать имя ветки или первые цифры хэша коммита, переводит рабочую директорию проекта в соответствующее состояние и устанавливает соответствующим образом HEAD. Если HEAD указывает не на ветку, а на произвольный коммит, такое состояние проекта называется "безголовым" (headless). Изменения в таком состоянии будут также продвигать HEAD, но не будут связаны ни с одной из веток. Из этого состояния можно вернуться в одну из имеющихся веток, тогда коммиты будут "забыты", либо создать новую ветку от последнего "безголового" коммита. При необходимости, можно вернуться к "забытому" коммиту, для этого нужно найти его хэш с помощью команды git reflog или git log -g, затем перейти на него командой git checkout <хэш> или создать для него новую ветку.

Создание веток

Ветка создается путем копирования текущего указателя HEAD с присвоением копии нового имени. В любом состоянии проекта можно создать ветку командой git branch <имя ветки>. Дополнительным параметром может быть хэш или иной идентификатор коммита, в том числе имя другой ветки. В этом случае ветка будет иметь свое начало от заданного во втором параметре коммита или ветки. Команда git branch создает новую ветку, но не переносит на нее HEAD. Чтобы перейти на новую ветку и начать добавлять в нее коммиты, нужно выполнить git checkout <имя новой ветки>. Так как создание новой ветки и переход в нее являются достаточно частой комбинацией действий, команда git checkout -b <имя ветки> позволяет создать новую ветку и сразу переключиться на нее. Если ветка с заданным именем уже существует, команда завершится ошибкой. В любой момент имеющиеся в репозитории ветки можно просмотреть командой git branch без параметров.

Слияние веток

В определенный момент работа, проделанная над побочной веткой, должна быть перенесена в основную. Например, если изменения велись в ветке develop, а необходимо добавить их в ветку master, нужно перейти в последнюю командой git checkout master, а затем выполнить слияние командой git merge develop. Если, во время работы над develop, изменения в master не вносились, слияние представляет собой просто перенос указателя ветки master на последний коммит в develop. Такое слияние называется перемоткой вперед (fast forward) и не образует дополнительного коммита. В сложных проектах, для того, чтобы отслеживать последствия слияний, имеет смысл даже для перемоток вперед создавать новы коммит. Для этого можно использовать команду git merge с флагом --no-ff, например git merge develop --no-ff. Для такого слияния коммит будет создан и автоматически отмечен записью в журнале. Ветка, которая имеет не только родительские коммиты, но и своим последним коммитом вливается в историю другой ветки, называется "слитой" (merged). Такую ветку можно безболезненно удалить, ее коммиты не будут утеряны, т.к. их история продолжается в другой ветке. Историю коммитов, которые были получены в результате слияний, можно просмотреть командой git log --graph.

Удаление веток

Слитые ветки можно удалить командой git branch -d <имя ветки>. После этого она не будет отображаться в выводе команды git branch и ветку с таким же именем можно ответвить уже от другого коммита. При попытке удалить неслитую ветку, Git выдает предупреждение о возможной потере ссылки на неслитые коммиты. Если это действительно необходимо, и неслитые коммиты можно "забыть", ветку можно удалить принудительно командой git branch -D <имя ветки>. Чтобы просмотреть список веток, которые уже были слиты с текущей, можно ввести команду git branch --merged. Ветки из этого списка безопасны для удаления из репозитория. Список веток, в которых есть неслитые коммиты можно получить командой git branch --no-merged.

Разрешение конфликтов

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

<<<<<<< HEAD
    Изменения из текущей ветки
=======
    Изменения из сливаемой ветки
>>>>>>> имя_сливаемой_ветки

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

Для облегчения работы с конфликтами Git предоставляет команду git mergetool. При ее выполнении на для всех файлов с конфликтами запускается предварительно сконфигурированная утилита. В частности для Linux и Windows бесплатно распространяется программа p4merge из пакета Perforce Visual Client. После ее установки необходимо внести изменения в конфигурацию Git следующим образом:

git config --global merge.tool p4merge
git config --global mergetool.p4merge.cmd <путь к p4merge> '$BASE $LOCAL $REMOTE $MERGED'
git config --global mergetool.p4merge.trustExitCode false

Аналогично можно сконфигурировать p4merge для просмотра изменений командой git difftool:

git config --global diff.tool p4merge
git config --global difftool.p4merge.cmd <путь к p4merge> '$LOCAL $REMOTE'

Задание для самостоятельной работы

  • Для работы используйте репозиторий, созданный в предыдущей лабораторной работе
  • Ответвите от ветки master ветку develop и перейдите на нее
  • Создайте в ней несколько коммитов и перейдите обратно в ветку master
  • Слейте в нее ветку develop в режиме fast-forward
  • Проделайте то же самое, но с созданием коммита при слиянии
  • Удалите ветку develop
  • Переведите проект в "безголовое" состояние путем перехода на один из прошлых коммитов
  • Добавьте к нему новые коммиты, затем покиньте их, вернувшись в ветку master
  • Найдите потерянные коммиты и создайте для них отдельную ветку, затем слейте ее с master и удалите
  • Создайте ветку feature и добавьте в нее и в ветку master несколько параллельных коммитов
  • Выполните безконфликтное слияние веток и убедитесь с помощью git log --graph, что был создан соответствующий коммит
  • Создайте конфликтующие изменения в ветках feature и master
  • Выполните слияние и просмотрите файлы с неслитыми изменениями
  • Сконфигурируйте программу для команды git mergetool и с ее помощью разрешите конфликты в пользу ветки master или feature
  • Выполните коммит с разрешением конфликтов и просмотрите историю командой git log --graph