воскресенье, 20 января 2019 г.

Скотт Чакон, Бен Страуб. Git для профессионального программиста. Подробное описание самой популярной системы контроля версий


Давно подумывал освоить какую-нибудь систему контроля версий исходного кода. Git - одна из наиболее популярных сейчас систем. Для меня важно, что реализована она на языке Си. Довольно популярна сейчас и другая распределённая система контроля версий - Mercurial. По свойствам они очень похожи, однако Mercurial реализован на языке Python. В отличие от централизованных систем контроля версий, распределённые системы позволяют разным разработчикам делать форки чужих проектов с особой лёгкостью. Каждый из форков является вполне самостоятельным, но его авторы могут обмениваться друг с другом фрагментами кода, применимыми к их вариантам кода. Конечно, всё то же самое можно делать и с централизованными системами, но особенности их реализации таковы, что делать форки чужих проектов и обмениваться кодом в централизованных системах значительно тяжелее.

Изначально мотивацией для освоения Git у меня было желание представить свои разработки в виде, удобном для просмотра через интернет. Поэтому сначала я захотел подобрать для себя подходящее веб-приложение для управления репозиториями. Попробовал настроить GitBlit, написанный на Java, но в нём меня не устроило отсутствие локализации интерфейса на русский язык. Затем настроил Gogs, написанный на Go, где такая локализация была. Начинал осваивать Git без книги. Создал репозитории программ, научился добавлять коммиты, создавать ветки и осуществлять слияния веток. Но захотелось изучить вопрос немного глубже, поэтому я решил прочитать книгу.

До этого много раз в книжных магазинах попадалась эта книга. Оригинал книги распространяется бесплатно в электронном виде - ProGit. Имеется перевод книги, который делают энтузиасты. Его можно найти по ссылке ProGit на русском. Несмотря на то, что имеется бесплатно доступная электронная версия, мне удобнее читать с бумаги.

Купил бумажную книгу, прочитал и порадовался как глубине изложенного материала, так и очень высокому качеству перевода. В тексте книги мне не попадались ни предложения с мутным смыслом, ни рассогласованной терминологии, ни ошибок в ссылках на другие разделы, чего было довольно много, например, в книге IPv6. Администрирование сетей. Хочется выразить благодарность за качество перевода Рузмайкиной И., о которой я не нашёл никакой информации, даже имени, кроме списка переведённых книг. Ошибки мне попадались только на иллюстрациях, но скорее всего они присутствуют и в оригинале книги. Например, в книге имеется такая вот иллюстрация, на которой красным цветом я обозначил исправления, соответствующие описанию:


Читал я довольно долго. Материал непростой, а иногда и не особо интересный. Например, я читал без особого интереса главы про сайт GitHub, а также главы про Subversion, Perforce и приложения с графическим интерфейсом. Поэтому в перерывах между чтениями этой книги успел прочитать ещё три других.

Небольшим субъективным недостатком я посчитал то, что в книге не описана одна из моих любимых операций - git add с опцией -e. Эта опция позволяет редактировать патч, превращающий старую версию файла в новую. При этом можно удалить часть изменений из патча, а часть поменять. После выхода из редактора в коммит будут добавлены только те изменения, которые остались. Остальные изменения не попадут в коммит, но и никуда не пропадут - их можно будет добавить в следующий коммит целиком командой git add или очередную часть командой git add с опцией -e. Часто бывает так, что редактируя файл, я обнаруживаю в нём какие-то мелкие ошибки, закомментированные фрагменты кода. В таком случае я обычно тут же исправляю обнаруженные ошибки и удаляю устаревший код, но эти изменения логически не связаны с основными изменениями, которые я вношу в файл. Поэтому такие мелкие правки я выделяю в отдельные коммиты.

Вторая моя любимая операция - git rebase с опцией -i, описана достаточно подробно. После подготовки серии коммитов я могу переставить их местами или объединить в один коммит. Объединение коммитов бывает особенно полезно, когда первоначальная правка кода оказывается неправильной или не полной. В последующих правках код может быть исправлен, поэтому перед отправкой коммитов в публичный репозиторий неплохо объединить такие изменения в один коммит, чтобы коммит сразу оказался бы верным. Человеку, который будет читать журналы изменений, не придётся переживать о том, что коммит явно содержит ошибку и проверять, была ли исправлена эта ошибка в последней версии кода.

Мне особо полезной показалась глава 10, рассказывающая о внутреннем устройстве системы. Поначалу я осваивал высокоуровневые возможности системы и некоторые особенности системы приводили меня в замешательство. Например, однажды я переименовал каталог при помощи команды git mv, а потом добавил в репозиторий ещё один каталог, внутри которого был файл, совпадающий с тем, который был в первом каталоге. После создания коммита я обнаружил, что оригинальный файл переместился не в переименованный каталог, как ожидалось, а во вновь добавленный каталог. При этом новый файл возник в переименованном каталоге, а не во вновь добавленном. Дело в том, что git не записывает в коммите действия, приведшие к изменению файлов. git оперирует снимками файлов, относящихся к каждому коммиту.

Каждый коммит, за исключением начального, ссылается на предыдущий или на несколько предыдущих, если произошло слияние веток. Каждый коммит ссылается на дерево файлов. Дерево файлов ссылается на двоичные объекты и поддеревья. Двоичный объект представляет собой содержимое определённой версии определённого файла. Если файл не меняется в рамках коммита, то в следующем коммите создаётся ссылка на уже имеющийся двоичный объект. Если файл изменён, то в репозитории создаётся новый двоичный объект с новым содержимым, а очередной коммит содержит в одном из поддеревьев ссылку на вновь добавленный объект. Это не самый оптимальный способ хранения данных внутри репозитория, поэтому время от времени git упаковывает объекты в pack-файл, копируя туда последнюю версию каждого файла, а все предыдущие файлы записывает в виде отличий от следующей версии. Но сути такая упаковка не меняет: в git в принципе невозможно точно указать, какой файл и как был переименован. Если один файл исчез в одном снимке, а другой появился в следующем снимке, то система пытается сравнить их между собой и если обнаруживает достаточно общих фрагментов, то считает что файл был переименован. В моём случае проблему с неправильным определением переименования файла можно было бы решить, если этот коммит разделить на два: в первом происходило бы только переименование каталога, а во втором - добавление нового каталога.

Исходя из этих соображений, я бы порекомендовал начинать чтение книги именно с 10 главы, т.к. все остальные главы понять будет гораздо проще, зная на какие внутренние возможности системы опирается та или иная высокоуровневая операция.

В конце книги имеется приложение, в котором приведена краткая сводка основных высокоуровневых операций со ссылками на разделы, где эти операции описываются более подробно.

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

Комментариев нет:

Отправить комментарий