Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Git

Глава 6 из книги "Архитектура приложений с открытым исходным кодом", том 2.
Оригинал: Git
Автор: Susan Potter
Перевод: А.Панин

6.3. Архитектура системы контроля версий

Сейчас наступил отличный момент для того, чтобы сделать шаг назад и рассмотреть альтернативные Git реализации систем контроля версий. Понимание их отличий позволит исследовать принятые в ходе разработки Git архитектурные решения.

К системе контроля версий обычно предъявляются три ключевых функциональных требования, а именно:
  • Хранение данных
  • Отслеживание изменений содержимого (хранение истории изменений, включающей метаданные слияния)
  • Распространение данных и истории изменений между разработчиками

Примечание: Третье требование из списка выше не является функциональным требованием для всех систем контроля версий.

Хранение данных

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

Наборы изменений на основе отличий файлов инкапсулируют отличия между двумя версиями данных произвольного типа, дополняя их некоторым количеством метаданных. Представление данных в форме ациклического ориентированного графа подразумевает использование объектов, формирующих иерархию, которая зеркалирует содержимое дерева файловой системы в виде копии добавляемых данных (при этом не изменяющиеся объекты в рамках дерева используются повторно тогда, когда это возможно). Git хранит данные в виде ациклического ориентированного графа, использующего различные типы объектов. В разделе "База данных объектов", расположенном ниже этой главе, описываются различные типы объектов, которые могут формировать ациклические ориентированные графы в репозитории Git.

История операций внесения изменений и слияния

В области отслеживания изменений репозитория в большинстве систем контроля версий используется один из следующих подходов:
  • Создание линейного представления истории
  • Использование направленного ациклического графа для хранения данных истории

Для хранения данных истории в Git как и раньше используются направленные ациклические графы. Каждая операция изменения данных (commit) создает метаданные, указывающие на ее предшествующие этапы; операция изменения данных в Git может не иметь или иметь множество (теоретически неограниченное количество) родительских операций изменения данных. Например, первая операция изменения данных в репозитории Git не будет иметь родительских операций, а результат трехстороннего слияния ветвей будет иметь три родительских операции.

Другим ключевым отличием между системой Git и системой Subversion с ее линейным представлением истории предыдущих операций является возможность непосредственного выделения ветвей, в рамках которых будет производиться запись истории большинства операций объединения.

Пример представления данных истории в форме направленного ациклического графа в Git
Рисунок 6.1: Пример представления данных истории в форме направленного ациклического графа в Git

Git в полной мере поддерживает возможности выделения ветвей, используя при этом направленные ациклические графы для хранения данных. История изменений файла непосредственно связывается со структурой директорий (с помощью вершин, представляющих директории) до корневой директории, которая впоследствии связывается с ветвью операции изменения данных. Эта ветвь операции изменения данных, в свою очередь, может иметь одного или нескольких родителей. Это обстоятельство наделяет Git двумя возможностями, которые позволяют нам более четко ориентироваться в истории и данных, чем это возможно при использовании семейства систем контроля версий, созданных на основе RCS, а именно:
  • В момент, когда узел данных (т.е., файлов или директорий) в графе ссылается на те же данные (использует тот же хэш SHA в Git), что и данные другой операции изменения данных, два узла будут гарантированно содержать идентичные данные, позволяя Git эффективно осуществлять дедупликацию.
  • При объединении двух ветвей мы объединяем содержимое двух узлов направленного ациклического графа. Направленный ациклический граф позволяет Git "эффективно" (в сравнении с семейством систем контроля версий на основе RCS) устанавливать стандартные предшествующие узлы.

Распространение данных

Системы контроля версий осуществляют распространение данных рабочей копии между участниками процесса разработки проекта одним их трех способов:
  • Исключительно локально: метод применим для систем контроля версий, к которым не предъявляется третьего функционального требования из представленного выше списка.
  • С использованием центрального сервера: в данном случае все измененные данные должны передаваться через один определенный репозиторий для занесения данных об их изменениях в историю.
  • С использованием распределенной модели: в данном случае репозитории всегда будут открыты для участников процесса разработки, которые смогут "поместить" данные в них, но операции изменения данных могут также осуществляться локально и в этом случае данные будут помещены в репозиторий позднее, что позволяет выполнять работу в условиях отсутствия соединения с сетью.

Для демонстрации преимуществ и недостатков каждого из основных архитектурных решений, мы рассмотрим репозиторий системы Subversion и репозиторий системы Git (на сервере), содержащие идентичные данные (т.е., HEAD-ветвь, указывающая на рабочую ветвь репозитория Git будет содержать те же данные, что и последняя ревизия из поддиректории trunk репозитория Subversion). Разработчик по имени Alex создал локальную копию данных репозитория Subversion и произвел клонирование данных для создания локального репозитория Git.

Представим, что Alex изменяет файл размером в 1 MB в локальной копии данных из репозитория Subversion, после чего отправляет изменения в репозиторий. Локальная копия отражает последние модификации и локальные метаданные претерпевают изменения. В ходе инициированного Alex процесса добавления данных в централизованный репозиторий Subversion генерируется файл различий между предыдущим экземпляром файла и измененным файлом, после чего этот файл различий сохраняется в репозитории.

В подобной ситуации Git ведет себя диаметрально противоположно. В момент, когда Alex производит точно такую же модификацию эквивалентного файла в локальном клонированном репозитории, изменения сначала будут записаны только локально, после чего Alex сможет "поместить" ожидающие в локальном репозитории изменения в публичный репозиторий, сделав свою работу доступной для других участников процесса разработки проекта. Изменения данных сохраняются идентично в каждом репозитории Git, где они присутствуют. При изменении данных в локальном репозитории (в самом простом случае), локальный репозиторий Git создаст новый объект, представляющий измененный файл (со всеми данными файла внутри). Для каждой директории, находящейся выше измененного файла в дереве директорий (а также для корневой директории репозитория), новый объект дерева будет создан с новым идентификатором. Направленный ациклический граф создается в направлении от недавно созданного объекта корневой директории, указывающего на объекты данных (при этом повторно используются существующие ссылки на объекты данных в том случае, если содержимое файлов не было изменено в ходе данной операции изменения данных), а также указывающего на недавно созданный объект данных в месте, где был расположен предыдущий объект данных в предыдущей иерархии. (Объект данных (blob) представляет файл, хранимый в репозитории.)

В этот момент измененные данные все еще находятся в локальном клонированном Alex репозитории на его локальном устройстве хранения данных. Когда Alex "поместит" данные в репозиторий Git с публичным доступом, измененные данные будут отправлены в этот репозиторий. После того, как на стороне публичного репозитория будет проведена проверка того, могут ли измененные данные быть добавлены в ветвь, в публичном репозитории будут сохранены те же самые объекты, что были ранее созданы в локальном репозитории Git.

На самом деле в сценарии работы репозитория Git присутствует гораздо большее количество взаимодействующих систем, которые находятся на заднем плане и требуют от пользователя подтверждения намерения передачи изменений удаленному репозиторию вне зависимости от локальных механизмов отслеживания изменений. Однако, уровни усложнения архитектуры позволяют команде разработчиков получить в свое распоряжение более гибкую в отношении процессов разработки и публикации систему, как было описано выше в разделе "Начало развития проекта Git".

В сценарии использования системы Subversion участник процесса разработки не должен помнить о необходимости помещения данных в публичный удаленный репозиторий, когда данные готовы для обзора другими участниками. В момент, когда небольшая модификация файла большого размера отправляется в центральный репозиторий Subversion, хранение файлов различий гораздо эффективнее хранения всего содержимого файла каждой версии. Однако, как мы увидим позднее, существует обходной путь, который используется системой Git для решения этой проблемы.

6.4. Тулкит

На сегодняшний день экосистема проекта Git включает множество инструментов с интерфейсом командной строки и другими пользовательскими интерфейсами для работы под управлением множества операционных систем (включая ОС Windows, поддержка которой изначально была неполной). Большинство этих инструментов разработано с использованием основного тулкита Git.

Из-за того, что разработка Git на начальных этапах осуществлялась Linus Torvalds и ввиду связи последнего с сообществом разработчиков Linux, она осуществлялась в соответствии с философией создания тулкитов в большей степени в соответствии с традицией создания инструментов с интерфейсом командной строки для ОС семейства Unix.

Тулкит Git разделен на две части: вспомогательные (plumbing) и основные (porcelain) функции. Категория вспомогательных функций состоит из низкоуровневых команд, позволяющих осуществлять основные операции, связанные с отслеживанием состояния данных и манипуляциями с направленными ациклическими графами (DAG). Набор основных функций меньше и содержит команды git, которые скорее всего понадобятся большинству пользователей системы Git для управления репозиториями и осуществления обмена данными между репозиториями в ходе совместной работы.

Хотя архитектура тулкита и позволила реализовать достаточное количество команд для предоставления доступа к большинству функций разработчикам сценариев, разработчики приложений жаловались на отсутствие пригодной для связывания библиотеки Git. Так как исполняемый файл Git вызывает функцию die() код не является реентерантным, поэтому графические и веб-интерфейсы, а также длительно работающие службы должны вызывать функции fork/exec для запуска бинарного файла Git, что может замедлить работу приложения.

Для исправления ситуации, с которой столкнулись разработчики приложений, была проведена работа; если вас интересуют подробности, обратитесь к разделу "Что дальше?".


Следующий раздел: Репозиторий, данные индексирования и рабочие области