§ 7. Репозитории кода

Введение

01 До появления системы контроля версий код ядра Linux разрабатывался через электронную почту: разработчик загружал себе на компьютер последнюю версию кода, которая регулярно публиковалась на сайте, делал необходимые изменения и отправлял эти изменения в виде патча на публичный адрес электронной почты. Сообщение получали все подписчики рассылки, оставляли свои комментарии и правки, а затем финальная версия патча объединялась с основной версией кода. Для автоматизации данного процесса Линусом Торвальдсом была придумана система контроля версий Git. Эта система на данный момент является наиболее популярной: ее используют как маленьких, так и в больших проектах. Сложно представить себе разработку чего-либо без такой системы: по сути без этого инструмента невозможно эффективно вести командную разработку.

Принцип работы

02 Ключевым понятием Git является мгновенная копия репозитория кода (англ. snapshot). Каждый раз, когда программист фиксирует изменения, создается новая мгновенная копия, которой присваивается уникальных хэш всех изменений. Если какие-то файлы не были изменены в этой копии, тогда на них ставятся ссылки из предыдущих копий. Таким образом, репозиторий Git является хранилищем копий исходного кода, соответствующим различным версиям программы, а между этими копиями установлены древовидные связи, которые помогают отследить историю изменений. Это позволяет говорить о Git, как о файловой системе, в которой каждое изменение приводит к созданию новой корневой директории с обвновленным список файлов и содержимым файлов, а это, в свою очередь, напоминает то, как люди, которые не знают о существовании систем контроля версий, работают с несколькими версиями документов.

03 Репозиторий Git создается с помощью команды git init. Эта команда создает директорию .git и записывает в нее настройки по умолчанию. В этой директории будут храниться все файлы проекта в формате, который понятен системе Git, в текущей же директории будут храниться копии файлов, соответствующие некоторой мгновенной копии из репозиторий. Чтобы добавить измененные файлы в репозиторий, используется команда git add, которая сохраняет изменения внутри директории .git, но не создает новой мгновенной копии. Для того чтобы зафиксировать добавленные изменения и создать новую мгновенную копию, используется команда git commit. Эта команда запускает редактор, в котором описываются сделанные изменения. Таким образом, привычный способ написания кода не меняется, нужно лишь зафиксировать изменения после окончания работы.

КомандаОписание
git initсоздание нового репозитория в текущей директории
git statusтекущее состояние мгновенной копии репозитория
git addдобавить новый или измененный файл в репозиторий
git add --allдобавить все новые и измененные файлы в репозиторий
git rm --cachedудалить добавленный файл из репозитория (копия файла в текущей директории не удаляется)
git rmудалить добавленный файл из репозитория (копия файла в текущей директории также удаляется)
git commitзафиксировать изменения путем создания новой мгновенной копии
git logвывести список мгновенных копий
Основные команды Git.

Ветки

04 На основе одной мгновенной копии можно создать несколько новых, которые будут обновляться независимо друг от друга. Эту возможность используют для параллельной разработки кода командой программистов. Каждый раз когда в проект нужно добавить новую возможсноть, создается отдельная ветка из основной с помощью команды git checkout -b и в этой ветке записываются все изменения. Когда возможность полностью реализована эту ветку сливают с основной с помощью команды git merge.

КомандаОписание
git checkout -bсоздать новую ветку из текущей мгновенной копии
git checkoutпереключиться на существующую ветку
git branch -dудалить локальную ветку
git branchвывести список веток
git mergeслить ветку в текущую
Команды для работы с ветками.
git checkout master       # переключиться на основную ветку
git checkout -b feature-x # создать новую ветку для реализации возможности X
...                       # внести изменения в ветку с помощью git add, git commit
git checkout master       # перключиться обратно на основую ветку
git merge feature-x       # слить изменения из ветки feture-x в ветку master
Типичный рабочий процесс в Git.

Синхронизация

05 Git является распределенной системой; это означает, что разработка кода может вестись асинхронно разными программистами. Каждый из них работает с локальной копией репозитория, которую можно синхронизировать с копией на сервере с помощью команд. Создание локальной копии делается с помощью команды git clone. Здесь в качестве источника можно указать любую поддерживаемую ссылку на репозиторий: локальную директорию, директорию на сервере, доступ к которому осуществляется по SSH, и ссылку HTTP/HTTPS. После первичного копирования новые изменения с сервера можно получать с помощью команды git pull, а отправлять с помощью команды git push.

06 В результате распределенной разработки может возникнуть ситуация, когда в основной ветке появились изменения, после того как из нее была создана новая ветка. Если попытаться в этой ситуации слить новую ветку с основной, то возникнет конфликт. Для того чтобы этого избежать, можно перебазировать новую ветку на обновленную основную: как будто новая ветка была создана из последней версии основной. Это делается с помощью команды git rebase.

КомандаОписание
git cloneскопировать репозиторий из другого места: локальная директория, HTTP-ссылка
git pullскопировать изменения из другого репозитория (локальная директория, HTTP-ссылка)
git pushскопировать изменения в другой репозиторий (локальная директория, HTTP-ссылка)
git rebaseперебазировать текущую ветку на указанную
git fetch --allполучить изменения с сервера, но не обновлять локальные ветки
Команды для синхронизации веток и репозиториев.
git checkout master       # переключиться на основную ветку
git checkout -b feature-x # создать новую ветку для реализации возможности X
...                       # внести изменения в ветку с помощью git add, git commit
...                       # кто-то вносит изменения в master на сервере
git checkout master       # перключиться обратно на основую ветку
git pull                  # получить последние изменения текущей ветки с сервера
git checkout feature-x    # переключиться на ветку feature-x
git rebase master         # перебазировать ветку feature-x на последнюю версию ветки master
git checkout master       # перключиться обратно на основую ветку
git merge feature-x       # слить изменения из ветки feture-x в ветку master
Порядок действий при распределенной разработке.

Имена

07 Все имена в Git так или иначе связаны с хэшем (уникальным идентификатором) мгновенной копии. Название ветки автоматически транслируется в индентификатор последней мгновенной копии в этой ветке. Ключевое слово HEAD также транслируется в идентификатор последней мгновенной копии. Чтобы обратиться к предыдущей копии, используют HEAD~1, где единица обозначает, на сколько копии назад сдвинуться. Все имена относятся только к локальной копии репозитория. Если же требуется обратиться к копии на сервере, то надо либо обновить локальный репозиторий, чтобы получить не необходимую мгновенную копию, либо использовать команду git fetch --all, чтобы получить последние изменения с сервера, но не обновлять локальные ветки. После git fetch к именам, полученным с сервера, можно обратиться, указав имя сервера в виде префикса: origin/master. Это возможно, поскольку все мгновенные копии хранятся внутри директории .git единообразно, а ветки и идентификаторы являются всего лишь ссылаются на них.

КомандаОписание
HEADпоследняя мгновенная копия в текущей ветке
HEAD~1предыдущая мгновенная копия в текущей ветке
masterпоследняя мгновенная копия в локальной ветке master
origin/masterпоследняя мгновенная копия в ветке master сервера origin, полученная с этого сервера с помощью git fetch
Имена мгновенных копий.

Заключение

08 Репозитории хранят все предыдущие версии кода, которые могут использоваться разными способами. Если командой разработчиков поддерживается сразу несколько версий программы, то их удобно обновлять в разных ветках кода (устраняющие уязвимости патчи, как правило, добавляются во все поддерживаемые версии программы). При анализе кода на уязвимости также используется история изменений, чтобы понять, какие версии кода подвержены найденной уязвимости. Наконец, историю изменений можно использовать, чтобы понять, какая часть файла поменялась и вызвала ошибку во время запуска программы, а также для быстрого отката изменений.

Задания

Первый репозиторий1 балл

09 Создайте репозиторий, добавьте в него текстовый файл с содержимым "Hello world!" и зафиксируйте изменения. Проверьте себя с помощью команды git log.

Ветки1 балл

10 Создайте в репозитории из первого задания новую ветку и добавьте в него новый файл с произвольным содержимым. Зафиксируйте изменения и слейте новую ветку с основной. Проверьте себя с помощью команды git log.

Синхронизация1 балл

11 В отдельной директории вне текущего репозитория создайте новый репозиторий с помощью команды git init --bare. Он будет выполнять роль репозитория на сервере, хоть и находится на той же машине. Отправьте в этот репозиторий изменения из предыдущего задания с помощью команды git push. Затем склонируйте этот репозиторий в еще одну директорию и проверьте с помощью команды git log, что вся история изменений сохранилась.

Исправление конфликтов2 балла

12 В репозитории из второго задания создайте новую ветку, измените в ней один из файлов, зафиксируйте изменения и отправьте на сервер. Затем в склонированном репозитории из третьего задания создайте новую ветку с другим именем, измените в ней тот же файл, зафиксируйте изменения и отправьте на сервер. Теперь слейте обе ветки в основную с помощью git merge и исправьте конфликтующие изменения вручную.

Видео

Запись лекции 16.10.2021.