§ 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 | вывести список мгновенных копий |
Ветки
04 На основе одной мгновенной копии можно создать несколько новых, которые будут обновляться независимо друг от друга. Эту возможность используют для параллельной разработки кода командой программистов. Каждый раз когда в проект нужно добавить новую возможсноть, создается отдельная ветка из основной с помощью команды git checkout -b
и в этой ветке записываются все изменения. Когда возможность полностью реализована эту ветку сливают с основной с помощью команды git merge
.
Команда | Описание |
---|---|
git checkout -b | создать новую ветку из текущей мгновенной копии |
git checkout | переключиться на существующую ветку |
git branch -d | удалить локальную ветку |
git branch | вывести список веток |
git merge | слить ветку в текущую |
Синхронизация
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 | получить изменения с сервера, но не обновлять локальные ветки |
Имена
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
и исправьте конфликтующие изменения вручную.