Знать понятия и компоненты систем контроля версий (СКВ), порядок и приемы работы с ними.
Уметь участвовать в командной разработке, используя конкретную СКВ — Git, а также популярный хостинг репозитариев — GitHub.
Выполнять необходимо все действия. Для удобства по тексту отмечены пункты, способ выполнения которых нужно придумать самостоятельно.
Отчет должен содержать все введенные команды (но не их вывод), пояснения, что было сделано между пунктами (например, отредактирован файл), а также то, что требуется добавить в отчет по тексту задания.
Большая часть работы будет выполняться в терминале (командной строке). Для Windows вместе с Git поставляется программа Git Bash: эмулятор терминала Linux. Ее можно запустить из контекстного меню любого каталога пунктом Git Bash Here или из меню «Пуск».
Самостоятельно. Создайте каталог lab02
для данной ЛР (например, в своей сетевой папке) и запустите в нем Git Bash.
В терминале откроется приглашение (prompt) примерно такого вида:
user@mpei-dc-win7 MINGW32 /c/lab02
$
Здесь важен рабочий каталог /c/lab02
и символ $
— начало ввода команд.
Просмотреть файлы в рабочем каталоге можно командой ls
. В каталоге lab02
пусто, поэтому ls
ничего не выведет.
В ходе работы будем имитировать проект с двумя участниками: Алисой и Бобом. Компьютеры Алисы и Боба имитируют папки lab02/alice
и lab02/bob
:
Переход между каталогами делается командой cd
. Перейдем «на компьютер Алисы» — в каталог alice
:
Самостоятельно. Создайте здесь каталог project
и перейдите в него.
Чтобы перейти на уровень выше можно командой cd ..
(две точки в конце).
Самостоятельно. Перейдите из каталога проект вверх, затем снова в каталог project
.
Все команды нужно заносить в отчет. Текст в Git Bash копируется при выделении, ничего нажимать не нужно; скопированное можно вставить в любую другую программу Ctrl+V, как обычно. Для вставки в сам Git Bash нажмите правую кнопку мыши.
Инициализируем репозитарий в текущем каталоге:
К приглашению командной строки добавилось (master)
: имя текущий ветви Git. Ветвь master
используется по умолчанию.
Git хранит три набора настроек:
Системные — для всех пользователей компьютера, как правило, их не меняют.
Пользовательские — для данного пользователя системы (user
в примере). На практике чаще всего пользуются ими. Хранятся в профиле пользователя.
Локальные — для отдельного репозитария, хранятся в нем же. Используются, если нужно работать с проектами от разного имени (примеры: личные и корпоративные проекты отдельно, Алиса и Боб в случае ЛР).
Локальные настройки имеют приоритет перед пользовательскими, а те — перед системными. Настройки Git сами не находятся под контролем версий, они специфичны для конкретного компьютера или конкретной копии репозитария.
Настроим репозитарий Алисы, чтобы коммиты были от ее имени:
В имя пользователя включите свою фамилию и инициалы, как в примере. Технически допустимы любые символы, однако в реальных проектах принята латиница. Почта может быть любой, Git просто указывает ее в коммитах.
Запустите CodeBlocks и создайте проект в репозитарии Алисы. Убедитесь, что не создается ненужных подкаталогов:
Project title: project
Folder to create project in: C:\lab02\alice
Project filename: project.cbp
Resulting filename: C:\lab02\alice\project\project.cbp
Соберите проект.
Вернувшись в Git Bash, просмотрим состояние рабочей копии:
В отчете нужно пояснить, что означает каждая строка вывода этой команды.
Добавим файл main.cpp
в отслеживаемые (в индекс):
На Windows может отобразиться такое сообщение:
warning: LF will be replaced by CRLF in main.cpp
The file will have its original line endings in your working directory
Оно безвредно. Смысл в том, что Git хранит файлы с немного измененном виде, чтобы обеспечивать удобную работу с репозитарием в любой операционной системе.
Самостоятельно. Еще раз просмотрите состояние рабочей копии и поясните в отчете изменения.
Выполним коммит с файлом main.cpp
и коротким сообщением:
На практике важно, чтобы описания коммитов были информативными: в будущем по ним быстро читают историю проекта, ищут коммиты по ключевым словам. Заголовок (первая строка) должен быть коротким (желательно до 50 символов) и описывать суть изменений, потому что только он показывается в списке коммитов. Часто в заголовок включают тему (к какой части проекта относится коммит) или номер задачи в системе отслеживания ошибок:
code: заготовка программы
— изменен код (а не документация, например)build: update CMake version
— коммит относится к сборкеобрабатывает пустой массив | fixes #1234
— исправляет ошибку № 1234timer: учет високосных лет #4321
— доработка таймера по задаче № 4321Временным незаконченным коммитам иногда приписывают WIP:
(work in progress).
Из заголовка должно быть ясно, что в целом сделано и зачем (почему, для чего). Обычно нет смысла писать, какие именно файлы и функции были изменены, потому что это можно просмотреть в самом коммите. После заголовка через пустую строку может идти расширенное пояснение (тело), иногда очень длинное.
Как и для исходного кода, главный критерий — понятность и единообразие. Можно писать на русском или английском, в совершенной форме или в повелительном наклонении — но одинаково во всех коммитах.
Самостоятельно. Добавьте файл project.cbp
в индекс и сделайте коммит с ним, тема — build
.
Заменим тело функции main()
на ввод двух чисел:
Самостоятельно. Просмотрите состояние репозитария. В отчете поясните различия между случаем, когда добавлялся новый файл, и когда изменился существующий.
Чтобы закоммитить изменения, есть три способа:
Сначала выбрать файлы, изменения которых должны войти в коммит, затем сделать коммит:
Этот способ удобен, если измнения присутствуют не только в тех файлах, которые коммитятся. Например, если работа над кодом уже закончена, а документация еще не дописана и коммитить ее не нужно.
Добавить в индекс все изменения, затем сделать коммит:
Способ удобен, если измнено много файлов. После git add -u
, которая добавляет в индекс измененные файлы, можно командой git add <файл>
добавить в индекс новые файлы.
Добавить все изменения в индекс и сделать коммит в один шаг:
Способ полностью эквивалентен предыдущему и удобен, если коммит меняет только существующие файлы.
Самостоятельно. Сделайте коммит с изменениями в main.cpp
первым способом.
Самостоятельно. Сделайте другими способами еще два коммита, меняющих main.cpp
: вывод суммы чисел и вывод разности чисел.
Внимание. Код доработок должен быть составлен в точности так:
Дальнейшие дополнения тоже должны продолжать одну большую инструкцию вывода, а не быть отдельными. Это нужно для того, чтобы в последующих пунктах можно было наблюдать некоторые примечательные ситуации.
Можно заметить, что в выводе команды git status
все время присутствуют каталоги bin/
и obj/
. Они содержат бинарные файлы (целевой *.exe
и промежуточные), которые являются производными от исходного кода, уже находящегося под контролем версий. Является грубой ошибкой заносить под контроль версий продукты сборки. То же самое относится к файлам, в которых некоторые среды сохраняют, например, состояние редактора: открытые файлы и расположение окон одного члена команды не нужны в общем хранилище.
Укажем Git игнорировать присутствие каталога bin
. Для этого создадим в CodeBlocks новый файл (File → New… → Empty) и запишем в него строку:
/bin
Косая черта в начале означает путь от корня репозитария (каталога project
), без нее игнорировался бы файл или каталог bin
в любой подпапке. Сохраним файл в корне репозитарий под именем .gitignore
, именно с точкой в начале.
Каждое правило игнорирования пишется на отдельной строке .gitignore
.
Выполнив git status
, можно видеть, что каталог bin
не отображается.
Самостоятельно. Занесите каталог obj
в список игнорируемых и убедитесь, что это удалось.
Файл .gitignore
может и обычно должен находиться под контролем версий.
Самостоятельно. Создайте коммит с .gitignore
, тема — git
.
Журнал репозитария показывает команда git log
. У нее много опций, например:
git log --stat
показывает файлы, измененные в коммитах;git log --oneline --decorate
показывает коммиты компактно;git log --oneline --decorate --all --graph
делает то же для всех веток.Среди прочего, команда показывает для каждого коммита его хэш, например, d2e8af7ff9c4684d0deb60d3305474bcaf69ce5c
. Некоторые версии команды показывают хэш сокращенно — краткий вариант тоже будет восприниматься командами Git, которые принимают хэш.
Попробуйте каждую из приведенных команд. В отчете подробно опишите, что показывается git log --stat
для последнего коммита.
Коммиты можно фильтровать по разным признакам:
git log -- main.cpp
показывает затрагивающие main.cpp
;git log --grep "code:"
показывает коммиты с code:
в сообщении.Самостоятельно. Найдите сначала коммиты по теме build
, затем коммиты, затрагивающие project.cbp
.
Содержимое отдельных коммитов просматривается командой git show <refspec>
, где <refspec>
может быть хэшем коммита, именем ветви или выражением, которое задает, на сколько от них отступить в истории.
Просмотрим последний коммит тремя эквивалентными способами:
git show HEAD
(текущий)git show master
(по имени ветви)git show d2e8af
(по хэшу нужного коммита)Для просмотра предыдущего коммита можно либо записать его хэш, либо указать, что от последнего нужно отступить на один коммит: HEAD~1
.
Самостоятельно. Просмотрите предпоследний коммит (в отчете зафиксируйте результат единожды) тремя способами.
Внесем изменения в main.cpp
: добавим печать произведения чисел, но не станем пока делать коммит.
Просмотрим изменения в рабочей копии:
В отчете необходимо пояснить все компоненты отображаемого патча.
Первый аргумент команды git diff
включает показ изменений от указанного коммита до последнего, включая изменения в рабочей копии:
С двумя аргументами команда показывает разницу между указанными коммитами, например, так можно исключить изменения в рабочей копии из вывода предыдущей команды:
Самостоятельно. Просмотрите изменения между самым первым коммитом и коммитом, добавляющим вывод разности.
Просмотр истории — одна из операций в СКВ, которую иногда удобнее выполнять из графической среды. Вместе с Git поставляется графическая оболочка gitk (Git GUI), которую можно вызвать пунктом Git GUI Here в контекстном меню папки проекта. Эта оболочка очень примитивная, на практике пользуются более мощными или встроенными в среду разработки.
Просмотр истории в gitk делается из меню Repository → Visualize All Branch History. Можно выбирать коммит для просмотра из списка; смотреть как изменения (Diff), так и версии файлов (Old version, New version); искать коммиты.
В отчет ничего заносить не нужно.
Самостоятельно. Закоммитьте изменения в рабочей копии (вывод произведения).
Предположим, необходимо отменить (откатить) этот коммит, то есть вернуться к предыдущему. Для этого воспользуемся командной git reset
:
Здесь HEAD~1
указывает на коммит, к которому нужно откатить состояние рабочей копии, а ключ --hard
означает, что нужно привести рабочую копию точно к состоянию выбранного коммита.
CodeBlocks (и другие среды) могут при этом показать предупреждение, что файл на диске был изменен, и предложить загрузить его заново. Следует согласиться.
Добавим над функцией main()
комментарий:
Уберем изменения в main.cpp
другим способом — откатив этот файл к состоянию в последнем коммите (HEAD
):
Второй способ необходим, чтобы откатывать отдельные файлы. Аргумент HEAD
необязателен, но вместо него можно указать не последний, а любой другой коммит. Это полезно, если нужно восстановить состояние одного файла таким, какое оно было в известный момент.
Зарегистрируйтесь на GitHub под именем вида KozlyukDA
. При регистрации нужно указывать действующую почту — на нее придет письмо для подтверждения регистрации.
Создайте репозитарий под названием cs-lab02
. Вопреки рекомендациям по ссылке, не нужно добавлять в репозитарий файл README.md
или лицензию.
После создания пустого репозитария будет показана страница с инструкциями, как настроить связь с хранилищем на GitHub:
При взаимодействии с удаленным хранилищем будет запрошено имя пользователя и пароль от GitHub.
Обновите страницу и убедитесь, что проект успешно загружен на GitHub. Любой файл можно просмотреть в бразуере. По ссылке Commits можно просматривать коммиты.
Предположим, к разработке проекта присоединяется Боб. Откройте новый терминал Git Bash в каталоге bob
. Клонируйте проект:
На место <адреса>
нужно подставить адрес, который использовался в команде git remote add
(его можно всегда отобразить командой git remote -v
). Каталог — название папки для проекта: используйте project
, если не указывать, это было бы название репозитария (cs-lab02
).
Перейдите в каталог проекта «на машине Боба» (здесь и далее это означает работу во втором терминале и над файлами в bob/project
):
Самостоятельно. «На машине Боба» настройте Git (git config
) аналогично тому, как это делалось для Алисы в начале лабораторной работы.
«На машине Боба» добавьте в программу печать произведения чисел и сделайте коммит. Просмотрите последний коммит и убедитесь, что он сделан от имени Боба.
Отправьте коммит на GitHub (используйте те же учетные данные, что и ранее):
Обновите страницу GitHub и убедитесь, что коммит попал в удаленный репозитарий. Обратите внимание, что авторство коммитов записано в самих коммитах, оно не зависит от пользователя системы или GitHub.
«На машине Алисы» (то есть в первом терминале, в каталоге alice/project
) выполните загрузку изменений:
Убедитесь, что в рабочей копии изменений еще не произошло.
Просмотрите историю всех веток:
Как можно видеть, ветка master
отстает на один коммит от ветки origin/master
(версии ветки master
из удаленного репозитария под названием origin
, то есть на GitHub).
Продвиньте ветку master
к скачанной версии:
Убедитесь, что рабочая копия проекта «у Алисы» соответствует версии «у Боба».
Команда git pull
автоматически делает git fetch
, поэтому можно было бы применять только ее, но важно понимать, что получение изменений в Git двухфазное: загрузка новой части истории и синхронизация положения веток.
Самостоятельно. «От имени Алисы» добавьте в программу печать деления, сделайте коммит, отправьте его на GitHub и получите новую версию «на машине Боба». Иначе говоря, повторите шаги выше, поменяв местами роли Алисы и Боба.
Предположим, Алиса решает добавить в программу печать максимума из чисел, а Боб — минимума.
Внимание. Код вывода в программе перед выполнением дальнейшего должен иметь следующий вид:
cout << "A + B = " << a + b << '\n'
<< "A - B = " << a - b << '\n'
<< "A * B = " << a * b << '\n'
<< "A / B = " << a / b << '\n';
В противном случае код нужно привести в соответствие отдельным коммитом и синхронизировать состояние «у Алисы» и «у Боба».
«На машине Алисы» дополните программу печатью максимума, сделайте коммит и отправьте его на GitHub.
«На машине Боба» дополните программу печатью минимума, сделайте коммит и попытайтесь отправить его на GitHub. Как можно видеть, удаленный репозитарий не принимает изменений: коммит Боба основан не на последнем существующем коммите.
«От лица Боба» загрузите коммиты из удаленного хранилища и отобразите историю всех веток — результат нужно представить в отчете.
Можно видеть, что ветка master
раздвоилась. Бобу нужно переместить свой коммит поверх коммита Алисы, то есть поверх origin/master
:
Однако эта команда завершается с ошибкой, сообщающей о конфликте в main.cpp
. Просмотрите состояние хранилища и поясните в отчете.
«На машине Боба» в CodeBlocks место конфликта будет отмечено прямо в коде. Необходимо самостоятельно:
<<<< ...
, ... >>>>
и =====
.После того, как конфликт разрешен, нужно добавить файл в индекс и продолжить прерванную операцию rebase
:
Убедитесь, что история хранилища теперь имеет желаемый вид (зафиксировав это в отчете) и отправьте изменения на GitHub.
Предположим, пока Боб синхронизировал изменения, Алиса решила изменить тип чисел с целых на действительные. Предполагая, что это займет время, Алиса ведет работу в отдельной ветке. На момент начала работы репозитарий Алисы не синхронизирован с GitHub, то есть последний коммит добавляет печать максимума. Все действия ведутся «на машине Алисы».
Создайте ветку double
:
Переключитесь на нее:
Примечание. Создание ветки и переключение на нее можно делать одной командой: git checkout -b double
. Этой команде можно передать аргумент-ссылку на коммит, где создать ветку.
Можно заметить, что текущая ветка в приглашении терминала изменилась.
Замените тип переменных a
и b
на double
и сделайте коммит.
Переключитесь на ветку master
:
Самостоятельно. Синхронизируйте ветку master
«на машине Алисы» с GitHub. Просмотрите историю всех веток и занесите результат в отчет.
Слейте ветку double
в master
:
В результате слияния образуется специальный новый коммит (merge commit), к которому Git предлагает написать сообщение в редакторе. Строки, начинающиеся с октоторпа («решетки», #
), в сообщение не войдут.
Просмотрите и занесите в отчет историю всех веток репозитария.
Vim — продвинутый текстовый редактор. Он не является частью Git, но популярен в системах семейства *nix, поэтому предлагается по умолчанию. Не обязательно его использовать, но нужно знать минимум для обращения с ним.
После запуска Vim находится в так называемом нормальном режиме. Чтобы начать вводить текст, нужно перейти в режим вставки, например, нажав i
(одну клавишу). В режиме вставки можно набирать текст обычным образом. Вернуться в нормальный режим можно нажатием Escape. Находясь в нормальном режиме, можно сохранить сообщение и выйти из Vim нажатием ZZ
(две заглавные Z, то есть Shift+Z два раза).
Если вместо Shift+Z нажать Ctrl+Z, Vim будет приостановлен, а коммит останется незавершенным. В этом случае нужно вернуться в Vim командой fg
в терминале.
Чтобы писать длинные сообщения, но не использовать Vim, можно указать другой редактор (например, примитивный nano
):
task.txt
с цифрами от 0 до 9 на отдельных строках.task
от предыдущего коммита.task
.a
, b
, c
, закоммитить.master
.d
, e
, f
, закоммитить.task
в master
, разрешив конфликт так, чтобы буквы шли по порядку.Козлюк Д. А., Мохов А. С., Василькова П. Д. для кафедры Управления и информатики НИУ «МЭИ», 2019 г.