title | author | fontsize | geometry | paper | lang | template |
---|---|---|---|---|---|---|
Лабораторная работа 2 |
А.В. Родионов |
14pt |
margin=2cm |
a4 |
russian |
default.html |
В предыдущей лабораторной работе был рассмотрен последовательный процесс сохранения изменений в репозитории 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