924 строки
51 KiB
Markdown
924 строки
51 KiB
Markdown
# Система контроля версий Git
|
||
|
||
# Цель работы
|
||
|
||
1. Знать понятия и компоненты систем контроля версий (СКВ),
|
||
порядок и приемы работы с ними.
|
||
|
||
2. Уметь участвовать в командной разработке, используя конкретную СКВ — Git,
|
||
а также типовой web-интерфейс Gitea.
|
||
|
||
# Выполнение работы и составление отчета
|
||
|
||
Необходимо выполнить все действия без пропусков.
|
||
Читайте пункт задания до конца перед тем, как выполнять его.
|
||
Для удобства по тексту отмечены пункты,
|
||
способ выполнения которых нужно придумать **самостоятельно.**
|
||
|
||
Нужно читать и осмысливать текст, который печатается в ответ на команды.
|
||
Если допущена ошибка, нужно повторить команду правильно.
|
||
|
||
Отчет должен содержать все введенные команды, ответы системы, пояснения,
|
||
что было сделано между пунктами (например, отредактирован файл), а также то,
|
||
что требуется добавить в отчет по тексту задания.
|
||
Пример первых пунктов отчета:
|
||
[txt](assets/report.txt), [Markdown](assets/report.md).
|
||
|
||
Весь текст, который был в терминале, в отчете должен быть как текст,
|
||
а не как скриншоты терминала. Скриншоты `gitk` и web UI не нужны.
|
||
|
||
Отчет нужно закоммитить в репозитарий, который создается в ходе ЛР,
|
||
как текстовый файл `README.txt` (простой текст)
|
||
или как `README.md` в формате Markdown.
|
||
Это лучше сделать одним последним коммитом после выполнения работы.
|
||
|
||
|
||
## Вход в терминал и создание структуры каталогов
|
||
|
||
Большая часть работы будет выполняться в терминале (командной строке).
|
||
Для Windows вместе с Git поставляется программа Git Bash: эмулятор терминала
|
||
Linux. Ее можно запустить из контекстного меню любого каталога пунктом
|
||
*Git Bash Here* или из меню «Пуск».
|
||
На OS X и в Linux достаточно открыть обычный терминал.
|
||
|
||
**Самостоятельно.**
|
||
Создайте на рабочем столе каталог `lab02` для данной ЛР
|
||
и запустите в нем Git Bash.
|
||
|
||
**Внимание.**
|
||
Даже если работа выполняется в компьютерном классе,
|
||
создавайте каталог на рабочем столе, а не на сетевом диске,
|
||
так как git не сможет нормально с ним работать.
|
||
|
||
В терминале откроется *приглашение (prompt)* примерно такого вида:
|
||
|
||
```
|
||
user@mpei-dc-win7 MINGW32 /c/Users/user/Desktop/lab02
|
||
$
|
||
```
|
||
|
||
Здесь важен рабочий каталог `/c/Users/user/Desktop/lab02`
|
||
и символ `$` — начало ввода команд.
|
||
|
||
Просмотреть файлы в рабочем каталоге можно командой `ls`.
|
||
В каталоге `lab02` пусто, поэтому `ls` ничего не выведет.
|
||
|
||
В ходе работы будем имитировать проект с двумя участниками: Алисой и Бобом.
|
||
Компьютеры Алисы и Боба имитируют папки `lab02/alice` и `lab02/bob`:
|
||
|
||
``` sh
|
||
mkdir alice
|
||
mkdir bob
|
||
```
|
||
|
||
Переход между каталогами делается командой `cd`.
|
||
Перейдем «на компьютер Алисы» — в каталог `alice`:
|
||
|
||
``` sh
|
||
cd alice
|
||
```
|
||
|
||
**Самостоятельно.**
|
||
Создайте здесь каталог `project` и перейдите в него.
|
||
|
||
Перейти на уровень выше можно командой `cd ..`
|
||
(две точки в конце, после `cd` пробел).
|
||
|
||
**Самостоятельно.**
|
||
Перейдите из каталога проекта вверх, затем вернитесь в каталог `project`.
|
||
|
||
Все команды нужно заносить в отчет.
|
||
Текст в Git Bash копируется при выделении, ничего нажимать не нужно.
|
||
Скопированное можно вставить в любую другую программу *Ctrl+V,* как обычно.
|
||
Для вставки в сам Git Bash из буфера обмена
|
||
нажмите правую кнопку мыши или *Shift+Insert.*
|
||
|
||
## Инициализация репозитария и настройка Git
|
||
|
||
Инициализируем репозитарий в текущем каталоге (`project`):
|
||
|
||
``` sh
|
||
git init
|
||
```
|
||
|
||
К приглашению командной строки добавилось `(master)`: имя текущий ветви Git.
|
||
|
||
В новых версиях Git с настройками по умолчанию печатает длинное сообщение-подсказку,
|
||
строки которого начинаются с `hint:`.
|
||
Суть сообщения в том, что долгое время ветка по умолчанию называлась `master`,
|
||
но по известным причинам разработчики Git планируют изменить это,
|
||
поэтому сразу предлагается поменять имя ветки и дается команда для этого.
|
||
|
||
**Самостоятельно.**
|
||
Поменяйте имя ветки на `main` командой `git branch -m main`.
|
||
Если у вас старая версия Git (подсказка не появлялась),
|
||
то делайте это не сейчас, а после создания первого коммита.
|
||
|
||
**Примечание.**
|
||
Многие проекты продолжают успешно использовать `master`,
|
||
чтобы не менять сложившиеся процессы.
|
||
Мы предлагаем использовать `main`, во-первых, чтобы попробовать лишнюю команду,
|
||
во-вторых, потому что такой станет ветка по умолчанию в будущем.
|
||
|
||
Git хранит свои данные в каталоге `.git` в той папке, где сделано `git init`.
|
||
Ее можно увидеть командой `ls -A`.
|
||
Каталог является скрытым, поэтому в «Проводнике» его по умолчанию не видно.
|
||
Заходить в `.git` и что-либо делать там не нужно.
|
||
Если удалить этот каталог, репозитарий будет безвозвратно утерян.
|
||
|
||
Git хранит три набора настроек:
|
||
|
||
* Системные — для всех пользователей компьютера.
|
||
|
||
* Пользовательские — для данного пользователя системы (`user` в примере).
|
||
На практике чаще всего пользуются ими. Хранятся в профиле пользователя.
|
||
|
||
* Локальные — для отдельного репозитария, хранятся в нем же.
|
||
Используются, если нужно работать с проектами от разного имени
|
||
(примеры: личные и корпоративные проекты отдельно, Алиса и Боб в случае ЛР).
|
||
|
||
Локальные настройки перекрывают пользовательские.
|
||
Пользовательские настройки перекрывают системные.
|
||
Настройки Git сами не находятся под контролем версий,
|
||
они специфичны для конкретного компьютера или конкретной копии репозитария.
|
||
|
||
Настроим репозитарий Алисы, чтобы коммиты были от ее имени:
|
||
|
||
``` sh
|
||
git config user.name 'Alice (IvanovII)'
|
||
git config user.email 'alice@example.com'
|
||
```
|
||
|
||
**Самостоятельно.** Укажите данные пользователя, как в примере.
|
||
Вместо `IvanovII` используйте свое имя и инициалы латиницей.
|
||
Используйте свой университетский адрес почты.
|
||
Git сам по себе не отправляет писем на этот адрес.
|
||
|
||
Кавычки должны быть парными.
|
||
Они могут быть одинарными (`'`) или двойными (`"`).
|
||
Если нарушить парность кавычек или нажать *Enter,* не закрыв кавычку,
|
||
ввод команды продолжится на следующей строке. В этом случае можно прервать
|
||
выполнение команды, нажав *Ctrl + C,* затем ввести команду правильно.
|
||
|
||
|
||
## Создание коммитов
|
||
|
||
Запустите CodeBlocks и создайте проект в репозитарии Алисы:
|
||
|
||
*Project title:* `project`\
|
||
*Folder to create project in:* `C:\Users\user\Desktop\lab02\alice`\
|
||
*Project filename:* `project.cbp`\
|
||
*Resulting filename:* `C:\Users\user\Desktop\lab02\alice\project\project.cbp`
|
||
|
||
**Внимание.**
|
||
По умолчанию CodeBlocks предложит создать подкаталог `project` внутри `project`.
|
||
Это неправильно. Убедитесь, что пути такие, как показано выше ↑.
|
||
|
||
Соберите проект.
|
||
|
||
На этом этапе должна быть следующая структура файлов и каталогов:
|
||
|
||
``` text
|
||
lab02
|
||
├── alice
|
||
│ └── project <--------- текущий рабочий каталог
|
||
│ ├── .git <--------- создан командой "git init"
|
||
│ ├── bin <--------- создан CodeBlocks при сборке
|
||
│ ├── obj <--------- (то же самое)
|
||
│ ├── main.cpp <-- код программы
|
||
│ └── project.cbp <-- файл проекта
|
||
└── bob
|
||
```
|
||
|
||
### Занесение файлов под контроль версий
|
||
|
||
Вернувшись в Git Bash, просмотрим состояние рабочей копии:
|
||
|
||
``` sh
|
||
git status
|
||
```
|
||
|
||
**В отчете** нужно пояснить, что означает каждая строка вывода этой команды.
|
||
|
||
Добавим файл `main.cpp` в индекс, то есть в набор изменений, который войдет
|
||
в очередной коммит. Так как этот файл ранее не отслеживался Git,
|
||
то говорят, что файл «заносят под Git» или «начинают отслеживать».
|
||
|
||
``` sh
|
||
git add main.cpp
|
||
```
|
||
|
||
На Windows может отобразиться такое сообщение:
|
||
|
||
``` text
|
||
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` и коротким сообщением:
|
||
|
||
``` sh
|
||
git commit -m 'code: заготовка программы'
|
||
```
|
||
|
||
### Составление сообщений к коммитам
|
||
|
||
На практике важно, чтобы описания коммитов были информативными: в будущем
|
||
по ним быстро читают историю проекта, ищут коммиты по ключевым словам.
|
||
Заголовок (первая строка) должен быть коротким (желательно до 50 символов)
|
||
и описывать суть изменений, потому что только он показывается в списке
|
||
коммитов. Часто в заголовок включают тему (к какой части проекта относится
|
||
коммит) или номер задачи в системе отслеживания ошибок:
|
||
|
||
* `code: заготовка программы` — изменен код (а не документация, например)
|
||
* `build: update CMake version` — коммит относится к сборке
|
||
* `обрабатывает пустой массив | fixes #1234` — исправляет ошибку № 1234
|
||
* `timer: учет високосных лет #4321` — доработка таймера по задаче № 4321
|
||
|
||
Временным незаконченным коммитам иногда приписывают `WIP:` (work in progress).
|
||
|
||
Из заголовка должно быть ясно, что в целом сделано и зачем (почему, для чего).
|
||
Обычно нет смысла писать, какие именно файлы и функции были изменены, потому
|
||
что это можно просмотреть в самом коммите. После заголовка через пустую строку
|
||
может идти расширенное пояснение (тело), иногда очень длинное.
|
||
|
||
Как и для исходного кода, главный критерий — понятность и единообразие. Можно
|
||
писать на русском или английском, в совершенной форме или в повелительном
|
||
наклонении — но одинаково во всех коммитах.
|
||
|
||
**Самостоятельно.**
|
||
Добавьте файл `project.cbp` в индекс и сделайте коммит с ним, тема — `build`.
|
||
Сообщение после темы придумайте по смыслу изменений, например, для этого
|
||
коммита подошло бы «добавлен файл проекта» или «add project file».
|
||
|
||
### Создание коммитов с изменениями
|
||
|
||
Заменим тело функции `main()` на ввод двух чисел:
|
||
|
||
``` cpp
|
||
cout << "Enter A and B: ";
|
||
int a, b;
|
||
cin >> a >> b;
|
||
```
|
||
|
||
**Самостоятельно.**
|
||
Просмотрите состояние репозитария (`git status`). В отчете поясните различия
|
||
между случаем, когда добавлялся новый файл, и когда изменился существующий.
|
||
|
||
Чтобы закоммитить изменения, есть три способа, описанных ниже. Обратите
|
||
внимание: Git «видит» состояние файлов на диске, поэтому после добавления
|
||
изменений нужно сохранять файл в CodeBlocks. Желательно также собирать
|
||
программу после изменений.
|
||
|
||
**Способ 1.**
|
||
Сначала выбрать файлы, изменения которых должны войти в коммит, затем сделать
|
||
коммит:
|
||
|
||
``` sh
|
||
git add main.cpp
|
||
git commit -m "..."
|
||
```
|
||
|
||
Этот способ удобен, если изменения присутствуют не только в тех файлах, которые
|
||
коммитятся. Например, если работа над кодом уже закончена, а документация еще
|
||
не дописана и коммитить ее не нужно.
|
||
|
||
**Самостоятельно.**
|
||
Добавьте в программу вывод суммы `a` и `b`.
|
||
|
||
**Способ 2.**
|
||
Добавить в индекс все изменения, затем сделать коммит:
|
||
|
||
``` sh
|
||
git add -u
|
||
git commit -m "..."
|
||
```
|
||
|
||
Способ удобен, если изменено много файлов. После `git add -u`, которая
|
||
добавляет в индекс измененные файлы, можно командой `git add <файл>` добавить
|
||
в индекс новые файлы.
|
||
|
||
**Самостоятельно.**
|
||
Добавить в программу вывод разности `a` и `b`.
|
||
|
||
**Внимание.**
|
||
Код доработок должен быть составлен в точности так:
|
||
|
||
``` cpp
|
||
cout << "A + B = " << a + b << '\n'
|
||
<< "A - B = " << a - b << '\n';
|
||
```
|
||
|
||
Дальнейшие дополнения тоже должны продолжать одну большую инструкцию вывода,
|
||
а не быть отдельными. Это нужно для того, чтобы в последующих пунктах
|
||
можно было наблюдать некоторые примечательные ситуации.
|
||
|
||
**Способ 3.**
|
||
Добавить все изменения в индекс и сделать коммит в один шаг:
|
||
|
||
``` sh
|
||
git commit -a -m "..."
|
||
```
|
||
|
||
Способ полностью эквивалентен предыдущему и удобен, если коммит меняет
|
||
только существующие файлы.
|
||
|
||
## Игнорирование файлов
|
||
|
||
Можно заметить, что в выводе команды `git status` все время присутствуют
|
||
каталоги `bin/` и `obj/`. Они содержат бинарные файлы (целевой `*.exe`
|
||
и промежуточные), которые являются производными от исходного кода, уже
|
||
находящегося под контролем версий. Является грубой ошибкой заносить
|
||
под контроль версий продукты сборки. То же самое относится к файлам,
|
||
в которых некоторые среды сохраняют, например, состояние редактора: открытые
|
||
файлы и расположение окон одного члена команды не нужны в общем хранилище.
|
||
|
||
Укажем Git игнорировать присутствие каталога `bin`. Для этого создадим
|
||
в CodeBlocks новый файл *(File → New... → Empty)* и запишем в него строку:
|
||
|
||
``` text
|
||
/bin
|
||
```
|
||
|
||
Косая черта в начале означает путь от корня репозитария (каталога `project`),
|
||
без нее игнорировался бы файл или каталог `bin` в любой подпапке. Сохраним
|
||
файл в корне репозитарий под именем `.gitignore`, именно с точкой в начале.
|
||
|
||
Каждое правило игнорирования пишется на отдельной строке `.gitignore`.
|
||
|
||
Выполнив `git status`, можно видеть, что каталог `bin` не отображается.
|
||
|
||
**Самостоятельно.**
|
||
Если вы работаете в CodeBlocks,
|
||
занесите в список игнорируемых каталоги `bin` и `obj` и файлы `*.layout`.
|
||
Убедитесь, что это удалось.
|
||
|
||
Если вы работаете в другой среде, то обеспечьте,
|
||
чтобы под контролем версий не было бинарных файлов
|
||
(в Visual Studio Code бинарный файл обычно будет `main` или `main.exe`)
|
||
и лишних файлов для своей среды.
|
||
[Типовые `.gitignore` для разных сред.](https://github.com/github/gitignore)
|
||
|
||
Файл `.gitignore` может и обычно должен находиться под контролем версий.
|
||
|
||
**Самостоятельно.**
|
||
Создайте коммит с `.gitignore`, тема — `git`.
|
||
|
||
|
||
## Просмотр истории
|
||
|
||
### Работа с журналом репозитария
|
||
|
||
Журнал репозитария показывает команда `git log`.
|
||
|
||
Если лог изменений длинный, `git log` показывает текст с прокруткой.
|
||
Чтобы выйти из этого режима, нажмите `q`.
|
||
|
||
У команды `git log` много опций, например:
|
||
|
||
* `git log --stat` показывает файлы, измененные в коммитах.
|
||
* `git log --oneline --decorate` показывает коммиты компактно (`--oneline`),
|
||
а также показывает ссылки, концы веток и тэги (`--decorate`).
|
||
* `git log --oneline --decorate --all --graph` делает то же для всех веток (`--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>` может быть хэшем коммита, именем ветви или выражением,
|
||
которое задает, на сколько от них отступить в истории.
|
||
|
||
Просмотрим последний коммит тремя эквивалентными способами:
|
||
|
||
1. `git show HEAD` (текущий)
|
||
2. `git show main` (по имени ветви)
|
||
3. `git show d2e8af` (по хэшу нужного коммита)
|
||
|
||
Для просмотра предыдущего коммита можно либо записать его хэш,
|
||
либо указать, что от последнего нужно отступить на один коммит: `HEAD~1`.
|
||
|
||
**Самостоятельно.**
|
||
Просмотрите предпоследний коммит **(в отчете** зафиксируйте результат единожды)
|
||
тремя способами.
|
||
|
||
|
||
### Просмотр изменений
|
||
|
||
Внесем изменения в `main.cpp`: добавим печать произведения чисел,
|
||
но не станем пока делать коммит.
|
||
|
||
Просмотрим изменения в рабочей копии:
|
||
|
||
``` sh
|
||
git diff
|
||
```
|
||
|
||
**В отчете** необходимо пояснить все компоненты отображаемого патча.
|
||
|
||
Первый аргумент команды `git diff` включает показ изменений от указанного
|
||
коммита до последнего, включая изменения в рабочей копии:
|
||
|
||
``` sh
|
||
git diff HEAD~2
|
||
```
|
||
|
||
С двумя аргументами команда показывает разницу между указанными коммитами,
|
||
например, так можно исключить изменения в рабочей копии из вывода предыдущей
|
||
команды:
|
||
|
||
``` sh
|
||
git diff HEAD~2 HEAD
|
||
```
|
||
|
||
**Самостоятельно.**
|
||
Просмотрите изменения между самым первым коммитом и коммитом, добавляющим
|
||
вывод разности.
|
||
|
||
|
||
### Использование GUI
|
||
|
||
Просмотр истории — одна из операций в СКВ, которую иногда удобнее выполнять
|
||
из графической среды. Вместе с Git поставляется графическая оболочка gitk
|
||
(Git GUI), которую можно вызвать пунктом *Git GUI Here* в контекстном меню
|
||
папки проекта. Эта оболочка очень примитивная, на практике пользуются
|
||
[более мощными](https://git-scm.com/downloads/guis) или встроенными в среду
|
||
разработки.
|
||
|
||
Просмотр истории в gitk делается из меню *Repository → Visualize All Branch
|
||
History.* Можно выбирать коммит для просмотра из списка; смотреть
|
||
как изменения *(Diff),* так и версии файлов *(Old version, New version);*
|
||
искать коммиты.
|
||
|
||
**В отчет** ничего заносить не нужно.
|
||
|
||
## Откат изменений
|
||
|
||
**Самостоятельно.**
|
||
Закоммитьте изменения в рабочей копии (вывод произведения).
|
||
|
||
Предположим, необходимо отменить (откатить) этот коммит, то есть вернуться
|
||
к предыдущему. Для этого воспользуемся командной `git reset`:
|
||
|
||
``` sh
|
||
git reset --hard HEAD~1
|
||
```
|
||
|
||
Здесь `HEAD~1` указывает на коммит, к которому нужно откатить состояние рабочей
|
||
копии, а ключ `--hard` означает, что нужно привести рабочую копию точно
|
||
к состоянию выбранного коммита.
|
||
|
||
CodeBlocks (и другие среды) могут при этом показать предупреждение, что файл
|
||
на диске был изменен, и предложить загрузить его заново. Следует согласиться.
|
||
|
||
Добавим над функцией `main()` комментарий:
|
||
|
||
``` cpp
|
||
// you may type whatever you want
|
||
```
|
||
|
||
Уберем изменения в `main.cpp` другим способом — откатив этот файл к состоянию
|
||
в последнем коммите (`HEAD`):
|
||
|
||
``` sh
|
||
git checkout HEAD -- main.cpp
|
||
```
|
||
|
||
Второй способ необходим, чтобы откатывать отдельные файлы. Аргумент `HEAD`
|
||
необязателен, но вместо него можно указать не последний, а любой другой
|
||
коммит. Это полезно, если нужно восстановить состояние одного файла таким,
|
||
какое оно было в известный момент.
|
||
|
||
## Обмен кодом через удаленное хранилище
|
||
|
||
### Регистрация на сервере
|
||
|
||
Зарегистрируйтесь на [Git УИТ](http://uit.mpei.ru/git)
|
||
под именем вида `KozliukDA` (своя фамилия и инициалы, как в почте МЭИ).
|
||
Пароль придумайте самостоятельно.
|
||
|
||
### Доступ к серверу
|
||
|
||
При работе из лаборатории проблем к доступом к серверу не будет.
|
||
При работе не из лаборатории см. [приложение](#annex-access),
|
||
как решать возникающие проблемы.
|
||
|
||
### Настройка SSH
|
||
|
||
Отправлять изменения в удаленный репозитарий обычно могут не все.
|
||
Так, доступ на запись к репозитариям по умолчанию есть только
|
||
у создателя (пользователя, зарегистрированного на предыдущем шаге).
|
||
Загрузка комитов (доступ на чтение) из публичных репозитариев разрешена всем.
|
||
|
||
Сервер должен выяснить, что клиент, представившийся определенным пользователем,
|
||
действительно им является (провести *аутентификацию*).
|
||
Клиент git взаимодействует с сервером по протоколу SSH (secure shell),
|
||
который использует для аутентификации пары ключей:
|
||
открытый (public, публичный) и закрытый (private, приватный) ключ.
|
||
Конкретный открытый ключ связан с конкретным закрытым.
|
||
Сначала открытый ключ загружается на сервер через web-интерфейс.
|
||
Затем любой клиент, который обладает соответствующим закрытым ключом,
|
||
может доказать это серверу.
|
||
У одного пользователя может быть несколько пар ключей,
|
||
например, для рабочего и домашнего компьютера,
|
||
тогда на сервер загружаются два открытых ключа.
|
||
|
||
**Внимание.**
|
||
Закрытый ключ является таким же секретом, как пароль пользователя.
|
||
Любой, кто получит закрытый ключ, сможет вносить на сервер изменения
|
||
от имени вашего пользователя.
|
||
Закрытый ключ нельзя давать никому, открытый ключ можно давать свободно.
|
||
|
||
Создать пару ключей:
|
||
|
||
``` sh
|
||
ssh-keygen
|
||
```
|
||
|
||
По умолчанию закрытый ключ записывается в файл `/home/user/.ssh/id_ed25519`,
|
||
можно оставить это значение по умолчанию (нажать *Enter*).
|
||
Далее нужно ввести пароль, которым будет защищен ключ, и повторить его.
|
||
|
||
Пример вывода команды:
|
||
|
||
`Generating public/private rsa key pair.
|
||
Enter file in which to save the key (/home/user/.ssh/id_ed25519):` *(Enter)*\
|
||
`Enter passphrase (empty for no passphrase): ` *(ввод не отображается)*\
|
||
`Enter same passphrase again: ` *(ввод не отображается)*\
|
||
`Your identification has been saved in /home/user/.ssh/id_ed25519
|
||
Your public key has been saved in /home/user/.ssh/id_ed25519.pub`\
|
||
*(Далее следуют уникальные для каждого ключа строки.)*
|
||
|
||
Вводить пароль каждый раз, когда используется ключ, неудобно.
|
||
Используют программу-агент, которая работает в фоне и предоставляет ключи
|
||
другим программам, в том числе git.
|
||
Пароль требуется тогда вводить один раз — при загрузке ключа в агент.
|
||
|
||
Запустить агент:
|
||
|
||
``` sh
|
||
eval $(ssh-agent -s)
|
||
```
|
||
|
||
Загрузить ключ (потребуется ввести пароль):
|
||
|
||
``` sh
|
||
ssh-add
|
||
```
|
||
|
||
По умолчанию `ssh-add` загружает `~/.ssh/id_ed25519`, для загрузки других ключей,
|
||
если это нужно, можно передавать ей путь к файлу ключа явно.
|
||
|
||
Отобразить открытый ключ можно командой:
|
||
|
||
``` sh
|
||
cat ~/.ssh/id_ed25519.pub
|
||
```
|
||
|
||
**Самостоятельно.**
|
||
Скопировать открытый ключ (текст) и добавить в список открытых ключей
|
||
своей учетной записи. Это делается в настройках (меню пользователя
|
||
в правом верхнем углу, пункт *Settings*), раздел *SSH/GPG keys,*
|
||
панель *Manage SSH keys,* кнопка *Add key.*
|
||
|
||
Если работа выполняется в компьютерном классе, закрытый ключ будет утерян
|
||
после выхода из учетной записи (или выключении компьютера). Проще всего
|
||
дома или на следующем занятии создать новый ключ и добавить его на сервер.
|
||
На практике ключи не уничтожают (кроме случаев, когда их украли),
|
||
а переносят как файлы, например, при переустановке системы.
|
||
Из проводника Windows файл закрытого ключа
|
||
виден как `C:\Users\User\.ssh\id_ed25519`,
|
||
если понадобится его скопировать.
|
||
|
||
### Отправка проекта на сервер
|
||
|
||
Создайте репозитарий под названием `cs-lab02`.
|
||
Для этого нужно нажать на знак «плюс» справа вверху и выбрать *New Repository.*
|
||
|
||
* В поле *Repository Name* нужно ввести `cs-lab02`.
|
||
* Галочку *Make repository private* нужно снять,
|
||
чтобы репозитарий был виден всем пользователям.
|
||
* Остальные настройки нужно оставить по умолчанию.
|
||
|
||
После создания пустого репозитария будет показана страница с инструкциями,
|
||
как настроить связь с удаленным хранилищем:
|
||
|
||
1. В разделе *Quick setup* нужно выбрать вариант SSH.
|
||
2. В разделе *…or push an existing repository from the command line*
|
||
даны команды, которые необходимо выполнить.
|
||
|
||
При первом соединении с сервером будет выведен запрос:
|
||
|
||
```
|
||
The authenticity of host 'uit.mpei.ru (10.1.6.13)' can't be established.
|
||
ED25519 key fingerprint is SHA256:...
|
||
This key is not known by any other names.
|
||
Are you sure you want to continue connecting (yes/no/fingerprint)?
|
||
```
|
||
|
||
Нужно ввести `yes` и нажать Enter.
|
||
|
||
Обновите страницу и убедитесь, что файлы проекта видны в web-интерфейсе.
|
||
Любой файл можно просмотреть в бразуере.
|
||
По ссылке *Commits* можно просматривать коммиты.
|
||
|
||
### Получение проекта с сервера
|
||
|
||
Предположим, к разработке проекта присоединяется Боб.
|
||
Откройте новый терминал Git Bash в каталоге `bob`.
|
||
Клонируйте проект:
|
||
|
||
``` sh
|
||
git clone <адрес> <каталог>
|
||
```
|
||
|
||
На место `<адреса>` нужно подставить адрес, который использовался в команде
|
||
`git remote add` (его можно всегда отобразить командой `git remote -v`).
|
||
Каталог — название папки для проекта: используйте `project`, если не указывать,
|
||
это было бы название репозитария (`cs-lab02`). Угловых скобок в команде быть
|
||
не должно!
|
||
|
||
Перейдите в каталог проекта «на машине Боба» (здесь и далее это означает
|
||
работу во втором терминале и над файлами в `bob/project`):
|
||
|
||
``` sh
|
||
cd project
|
||
```
|
||
|
||
**Самостоятельно.**
|
||
«На машине Боба» настройте Git (`git config`) аналогично тому, как это
|
||
делалось для Алисы в начале лабораторной работы.
|
||
|
||
|
||
|
||
## Совместная работа над проектом без конфликтов правок
|
||
|
||
«На машине Боба» добавьте в программу печать произведения чисел и сделайте
|
||
коммит. Просмотрите последний коммит и убедитесь, что он сделан от имени
|
||
Боба.
|
||
|
||
Отправьте коммит на сервер.
|
||
|
||
``` sh
|
||
git push
|
||
```
|
||
|
||
**Примечание.**
|
||
Поскольку работа от лица Алисы и Боба ведется одним локальным пользователем,
|
||
используется один и тот же ключ SSH, поэтому Бобу не требуется отдельной
|
||
учетной записи на сервере и отдельного ключа SSH.
|
||
На практике у каждого разработчика, разумеется, своя учетная запись и ключ.
|
||
|
||
Обновите страницу в web-интерфейсе и убедитесь, что коммит попал в удаленный
|
||
репозитарий. Обратите внимание, что авторство коммитов записано в самих
|
||
коммитах, оно не зависит от пользователя системы.
|
||
|
||
«На машине Алисы» (то есть в первом терминале, в каталоге `alice/project`)
|
||
выполните загрузку изменений:
|
||
|
||
``` sh
|
||
git fetch
|
||
```
|
||
|
||
Убедитесь, что в рабочей копии изменений еще не произошло.
|
||
|
||
Просмотрите историю всех веток:
|
||
|
||
``` sh
|
||
git log --oneline --decorate --all --graph
|
||
```
|
||
|
||
Как можно видеть, ветка `main` отстает на один коммит от ветки
|
||
`origin/main` (версии ветки `main` из удаленного репозитария под названием
|
||
`origin`, то есть на сервере).
|
||
|
||
Продвиньте ветку `main` к скачанной версии:
|
||
|
||
``` sh
|
||
git pull --ff-only
|
||
```
|
||
|
||
Убедитесь, что рабочая копия проекта «у Алисы» соответствует версии «у Боба».
|
||
|
||
Команда `git pull` автоматически делает `git fetch`, поэтому можно было бы
|
||
применять только ее, но важно понимать, что получение изменений в Git
|
||
двухфазное: загрузка новой части истории и синхронизация положения веток.
|
||
|
||
**Самостоятельно.**
|
||
«От имени Алисы» добавьте в программу печать деления, сделайте коммит,
|
||
отправьте его на сервер и получите новую версию «на машине Боба».
|
||
Иначе говоря, повторите шаги выше, поменяв местами роли Алисы и Боба.
|
||
|
||
## Разрешение конфликтов правок при совместной работе
|
||
|
||
Предположим, Алиса решает добавить в программу печать максимума из чисел,
|
||
а Боб — минимума.
|
||
|
||
**Внимание.** Код вывода в программе перед выполнением дальнейшего должен
|
||
иметь следующий вид:
|
||
|
||
``` cpp
|
||
cout << "A + B = " << a + b << '\n'
|
||
<< "A - B = " << a - b << '\n'
|
||
<< "A * B = " << a * b << '\n'
|
||
<< "A / B = " << a / b << '\n';
|
||
```
|
||
|
||
В противном случае код нужно привести в соответствие отдельным коммитом
|
||
и синхронизировать состояние «у Алисы» и «у Боба».
|
||
|
||
«На машине Алисы» дополните программу печатью максимума, сделайте коммит
|
||
и отправьте его на сервер.
|
||
|
||
«На машине Боба» дополните программу печатью минимума, сделайте коммит
|
||
и попытайтесь отправить его на сервер. Как можно видеть, удаленный
|
||
репозитарий не принимает изменений: коммит Боба основан не на последнем
|
||
существующем коммите.
|
||
|
||
«От лица Боба» загрузите коммиты из удаленного хранилища и отобразите
|
||
историю всех веток — результат нужно представить **в отчете.**
|
||
|
||
Можно видеть, что ветка `main` локального репозитария
|
||
разошлась с веткой `origin/main`, то есть с веткой `main` на сервере.
|
||
Бобу нужно переместить свой коммит поверх коммита Алисы,
|
||
то есть поверх `origin/main`:
|
||
|
||
``` sh
|
||
git rebase origin/main
|
||
```
|
||
|
||
Однако эта команда завершается с ошибкой, сообщающей о конфликте в `main.cpp`.
|
||
Просмотрите состояние хранилища и поясните **в отчете.**
|
||
|
||
«На машине Боба» в CodeBlocks место конфликта будет отмечено прямо в коде.
|
||
Необходимо **самостоятельно:**
|
||
|
||
1. Удалить метки конфликта: `<<<< ...`, `... >>>>` и `=====`.
|
||
2. Отредактировать код так, чтобы он включал и правки Алисы, и правки Боба.
|
||
3. Убедиться, что программа компилируется и работает.
|
||
|
||
После того, как конфликт разрешен, нужно добавить файл в индекс и продолжить
|
||
прерванную операцию `rebase`:
|
||
|
||
``` sh
|
||
git add main.cpp
|
||
git rebase --continue
|
||
```
|
||
|
||
Убедитесь, что история хранилища теперь имеет желаемый вид (зафиксировав
|
||
это **в отчете)** и отправьте изменения на сервер.
|
||
|
||
|
||
## Использование веток
|
||
|
||
Предположим, пока Боб синхронизировал изменения, Алиса решила изменить
|
||
тип чисел с целых на действительные. Предполагая, что это займет время,
|
||
Алиса ведет работу в отдельной ветке. На момент начала работы репозитарий
|
||
Алисы *не* синхронизирован с сервером, то есть последний коммит добавляет
|
||
печать максимума. Все действия ведутся «на машине Алисы».
|
||
|
||
Создайте ветку `double`:
|
||
|
||
``` sh
|
||
git branch double
|
||
```
|
||
|
||
Переключитесь на нее:
|
||
|
||
``` sh
|
||
git checkout double
|
||
```
|
||
|
||
**Примечание.** Создание ветки и переключение на нее можно делать одной
|
||
командой: `git checkout -b double`. Этой команде можно передать
|
||
аргумент-ссылку на коммит, где создать ветку.
|
||
|
||
Можно заметить, что текущая ветка в приглашении терминала изменилась.
|
||
|
||
Замените тип переменных `a` и `b` на `double` и сделайте коммит.
|
||
|
||
Переключитесь на ветку `main`:
|
||
|
||
``` sh
|
||
git checkout main
|
||
```
|
||
|
||
**Самостоятельно.**
|
||
Синхронизируйте ветку `main` «на машине Алисы» с сервером.
|
||
Просмотрите историю всех веток и занесите результат **в отчет.**
|
||
|
||
Слейте ветку `double` в `main`:
|
||
|
||
``` sh
|
||
git merge double
|
||
```
|
||
|
||
В результате слияния образуется специальный новый коммит (merge commit),
|
||
к которому Git предлагает написать сообщение в редакторе. Строки,
|
||
начинающиеся с октоторпа («решетки», `#`), в сообщение не войдут.
|
||
|
||
Отправьте изменения на сервер.
|
||
|
||
Просмотрите и занесите **в отчет** историю всех веток репозитария.
|
||
|
||
### Редактор Vim
|
||
|
||
Vim — продвинутый текстовый редактор. Он не является частью Git, но популярен
|
||
в системах семейства \*nix, поэтому предлагается по умолчанию. Не обязательно
|
||
его использовать, но нужно знать минимум для обращения с ним.
|
||
|
||
После запуска Vim находится в так называемом *нормальном режиме.*
|
||
Чтобы начать вводить текст, нужно перейти *в режим вставки,*
|
||
нажав `i` (одну клавишу).
|
||
В режиме вставки можно набирать текст обычным образом.
|
||
Вернуться в нормальный режим можно нажатием *Escape.*
|
||
Находясь в нормальном режиме, можно сохранить сообщение и выйти из Vim
|
||
нажатием `ZZ` (две заглавные Z, то есть *Shift+Z* два раза).
|
||
|
||
Если вместо *Shift+Z* нажать *Ctrl+Z,* Vim будет приостановлен, а коммит
|
||
останется незавершенным. В этом случае нужно вернуться в Vim командой `fg`
|
||
в терминале.
|
||
|
||
Чтобы писать длинные сообщения, но не использовать Vim, можно указать
|
||
другой редактор (например, примитивный `nano`):
|
||
|
||
``` sh
|
||
EDITOR=nano git merge double
|
||
```
|
||
|
||
# Формат защиты
|
||
|
||
Защита состоит из ответов на теоретические вопросы по лекции
|
||
и выполнения задания, рассчитанного на 10 минут.
|
||
|
||
Пример задания:
|
||
|
||
1. Создать новый репозитарий.
|
||
2. Закоммитить файл `task.txt` с цифрами от 0 до 9 на отдельных строках.
|
||
3. Удалить строки с цифрами от 5 до 8, закоммитить.
|
||
4. Просмотреть предпоследний коммит.
|
||
5. Создать ветку `task` от предыдущего коммита.
|
||
6. Переключиться на ветку `task`.
|
||
7. Добавить в начало файла строки с буквами `a`, `b`, `c`, закоммитить.
|
||
8. Переключиться на ветку `master`.
|
||
9. Добавить в начало файла строки с буквами `d`, `e`, `f`, закоммитить.
|
||
10. Слить ветку `task` в `master`, разрешив конфликт так, чтобы буквы шли
|
||
по порядку.
|
||
|
||
|
||
# Приложение. Решение проблем с доступом к серверу {#annex-access}
|
||
|
||
Git может взаимодействовать с сервером по протоколу SSH или HTTP.
|
||
В интерфейсе Gitea и других подобных систем
|
||
ссылку для доступа по нужному протоколу можно получить через web-интерфейс:
|
||
надо списком файлов репозитария есть переключатель между HTTP и SSH.
|
||
|
||
К серверу кафедры доступ *извне сети МЭИ* есть только по HTTP.
|
||
Кроме того, в некоторых сетях SSH запрещен (не работает):
|
||
в сети общежитий МЭИ, в некоторых мобильных сетях.
|
||
Симптом того, что доступа по SSH нет — сообщение операции с сервером:
|
||
|
||
```
|
||
ssh: connect to host uit.mpei.ru port 22: Connection timed out
|
||
```
|
||
|
||
В таких сетях придется в командах `git clone ...`, `git remote add origin ...`
|
||
использовать адрес для HTTP.
|
||
Если команда `git remote add origin ...` уже была выполнена с адресом для SSH,
|
||
можно сменить его на HTTP командой `git remote set-url origin ...`.
|
||
Когда используется HTTP, при каждой загрузке данных на сервер или с сервера
|
||
Git будет запрашивать имя пользователя и пароль.
|
||
|
||
На практике пользуются в основном SSH.
|
||
К серверу кафедры можно подключиться по SSH, работая извне сети МЭИ,
|
||
если [настроить VPN МЭИ][vpn].
|
||
При включенном VPN работа с сервером по SSH извне сети МЭИ
|
||
не отличается от работы из лаборатории.
|
||
Рекомендуется пойти этим путем, хотя он и сложнее, чтобы отработать навык,
|
||
который пригодится при реальной удаленной работе.
|
||
|
||
[vpn]: https://mpei.ru/Structure/uchchast/icc/Documents/access_services_ICN.pdf
|