Магия Git Магия Git iicollaborators
Скачать 309.22 Kb.
|
Глава 8 Раскрываем тайны Мы заглянем под капот и объясним, как Git творит свои чудеса. Я опущу некоторые детали. Для более подробного изучения описаний обратитесь к Руководству пользователя 8.1 Невидимость Как Git может быть таким ненавязчивым? Помимо коммитов время от времени и слияния, вы можете работать так, как будто вы не знали, что контроль версий существует. То есть, пока вам это не нужно, вы его не замечаете, и вот, когда он понадобился вы рады, что Git наблюдал за вами все это время. Другие системы контроля версий, не позволят вам забыть о них. Права доступа к файлам может быть только для чтения, пока вы явно не укажите серверу, какие файлы вы хотите изменить. Центральный сервер может отслеживать кто извлекал какой код и когда. Когда сеть падает вы можете от этого пострадать. Разработчики постоянно борются с виртуальной волокитой и бюрократизмом. Секрет заключается в каталоге .git в вашей рабочей директории. Git хранит историю вашего проекта здесь. Из-за имени, начинающегося с точки каталог не отображается в выводе команды ls. Кроме комманд git push и git pull все остальные операции контроля версий работают в этом каталоге. Вы имеете полный контроль над судьбой ваших файлов потому что Gitу не важно, что вы делаете с ними. Git можно легко восстановить сохраненное в .git состояние в любое время. 8.2 Целостность Большинство людей ассоциируют с криптографией поддержание информации в секрете, но дру- гой не менее важной задачей является поддержание информации в сохранности. Правильное использование криптографических хеш-функций может предотвратить случайные или злонаме- ренные повреждения данных. SHA1 хэш может рассматриваться как уникальный 160-битный идентификационный номер для каждой строки байт, с которой вы сталкиваетесь в вашей жизни. На самом деле гораздо боль- ше: каждая строка байтов, которую любой человек когда-нибудь использует в течение многих жизней. Так как SHA1 хэш сам является последовательностью байтов, мы можем получить хэш строки байтов, содержащей другие хэши. Это простое наблюдение на удивление полезно: смотрите hash Магия Git 33 / 41 chains (цепи хешей). Позднее мы увидим, как Git использует их для эффективного обеспечения целостности данных. Короче говоря, Git хранит ваши данные в подкаталоге ".git/objects", где вместо обычных фай- лов, вы увидите только ID. С помощью идентификаторов как имен файлов, а также нескольких лок-файлов и трюков с временем создания файлов, Git преобразует любую скромную файловую систему в эффективную и надежную базу данных. 8.3 Интеллект Каким образом Git знает, что вы переименовали файл, даже если вы никогда не упоминается тот факт явно? Конечно, вы можете запустить git mv, но это точно так же, как git rm и после git add. Git эвристически находит переименованные файлы и копирует их в последующие версии. В са- мом деле, он может обнаружить, что куски кода были перемещены или скопированы между файлами! Хотя она не может охватить все случаи, это достойная работа, и эта функция всегда улучшается. Если это не работает, попробуйте включить опцию обнаружения копирования и рассмотреть вопрос апгрейда. 8.4 Индексация Для каждого отслеживаемого файла, Git записывает информацию, такую как размер, время со- здания и время последнего изменения в файл, известный как "индекс". Чтобы определить, что файл был изменен, Git сравнивает его текущее состояние с тем, что сохранено в индексе. Если они совпадают, то Git может пропустить перечитывание это файла. Поскольку чтение этой информации значительно быстрее, чем чтение всего файла, то если вы редактировали только несколько файлов, Git может обновить свой индекс почти мгновенно. 8.5 Голые репозитории Вам, возможно, было интересно, какой формат используется в этих онлайн Git репозиториях. Они такие-же хранилища Git, как ваш каталог .git, кроме того что обычно называются proj.- git, и они не имеют рабочую директорию связанную с ними. Большинство команд Git рассчитывают что индекс Git находится в каталоге .git, и не смогут работать на этих голых хранилищах. Исправить это можно, установив переменную окружения GIT_DIR в значение, равное пути к репозиторию, или запустить Git в этом каталоге с опцией --bare. 8.6 Происхождение Git Этот http://lkml.org/lkml/2005/4/6/121 [пост] в Linux Kernel Mailing List описывает цепь событий, которые привели к появлению Git. Весь этот трейд - увлекательные археологические раскопки для историков Git. 8.7 База данных объектов Вот как писать Git-подобной операционной системы с нуля в течение нескольких часов. Магия Git 34 / 41 8.7.1 Blobs Первый волшебный трюк. Выберите имя файла, любое имя файла. В пустой директории: $ echo sweet > YOUR_FILENAME $ git init $ git add . $ find .git/objects -type f Вы увидите .git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d. Откуда я знаю это, не зная имени файла? Это потому, что SHA1 хэш строки: "blob" SP "6" NUL "sweet" LF является aa823728ea7d592acc69b36875a482cdf3fd5c8d, где SP это пробел, NUL является нуле- вым байтом и LF переводом строки. Вы можете проверить это, напечатав: $ echo "blob 6"$'\001'"sweet" | tr '\001' '\000' | sha1sum Кстати, это написано с учетом особенностей оболочки Bash, другие оболочки возможно способ- ны обработать NUL в командной строке, что исключает необходимость использовать костыль с tr. Git является контент-адресуемым: файлы хранятся в независимости от их имени, а по хэшу содержимого, которое мы называем BLOB объект. Мы можем думать о хеше как о уникальном идентификаторе для содержимого файла, так что в некотором смысле мы обращаемся к файлам по их содержимому. Начальный "blob 6" является лишь заголовком, состоящий из типа объекта и его длины в байтах; она упрощает внутренний учет. Таким образом, я могу легко предсказать, что вы увидите. Имя файла не имеет никакого отно- шения: только данные внутри используется для построения BLOB объекта. Вам может быть интересно, что происходит с идентичными файлами. Попробуйте добавить ко- пии с любыми именами файлов вообще. Содержание .git/objects останется тем-же независимо от того, сколько копий вы добавите. Git только хранит данные один раз. Кстати, файлы в директории .git/objects сжимаются с Zlib поэтому вы не сможете просмот- реть их непосредственно. Пропустите их через фильтр http://www.zlib.net/zpipe.c [zpipe-D], или введите: $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d который просто выведет данный объект. 8.7.2 Деревья Но где же имена файлов? Они должны храниться где-то на определенном этапе. Git получает информацию об имени во время коммита: $ git commit # Type some message. $ find .git/objects -type f Теперь вы должны увидеть 3 объекта. На этот раз я не могу сказать вам, какие 2 новые файлы, так как это частично зависит от выбранного имени файла. Допустим вы назвали его "rose". Если это не так, то вы можете переписать историю, чтобы она выглядела как будто вы это сделали: Магия Git 35 / 41 $ git filter-branch --tree-filter 'mv YOUR_FILENAME rose' $ find .git/objects -type f Теперь вы должны увидеть файл .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9, потому что это SHA1 хэш его содержимого: "tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d Проверьте - этот файл действительно содержит указанную выше строку - наберите: $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch С zpipe легко проверить хеш: $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum Проверка хеша сложнее чем через CAT-файла, поскольку его вывод содержит больше, чем сырой несжатый объектный файл. Этот файл является объектом tree: список цепочек, состоящих из типа файла, имени файла, и хэша. В нашем примере это тип файла "100644", это означает что "rose", является обычным файлом и хэш BLOB объект, в котором находится содержимое "rose". Другие возможные типы файлов - исполняемые файлы, символические ссылки или каталоги. В последнем случае, хэш указывает на дереве объектов. Если вы запускали filter-branch, у вас будут старые объекты которые вам больше не нужны. Хотя они будут автоматически выброшены сразу после истечения льготного периода, мы удалим их сейчас, чтобы наш игрушечный пример было легче исследовать: $ rm -r .git/refs/original $ git reflog expire --expire=now --all $ git prune Для реальных проектов, обычно вы должна избегать использовать такие команды, как эта, так как вы разрушаете резервные копии. Если вы хотите чистое хранилище, то обычно лучше сде- лать новый клон. Кроме того, будьте внимательны при непосредственном манипулировании .gi- t: Что делать, если другая команда Git будет запущена в то же время, или внезапного произойдет отключение питания? В общем случае, ссылки должны быть удалены с помощью git update-ref -d, хотя обычно удалить refs/original вручную безопасно. 8.7.3 Коммиты Мы объяснили 2 из 3 объектов. Третий объект - коммит. Его содержимое зависит от сообщения коммита, а также от даты и времени его создания. Для демонстрации того, что мы здесь имеем, мы должны настроить Git немного: $ git commit --amend -m Shakespeare # Change the commit message. $ git filter-branch --env-filter 'export GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" GIT_AUTHOR_NAME="Alice" GIT_AUTHOR_EMAIL="alice@example.com" GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" GIT_COMMITTER_NAME="Bob" GIT_COMMITTER_EMAIL="bob@example.com"' # Rig timestamps and authors. $ find .git/objects -type f Магия Git 36 / 41 Теперь вы должны увидеть .git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187 кото- рый является SHA1 хэшем его содержание: "commit 158" NUL "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF "author Alice "committer Bob LF "Shakespeare" LF Как и раньше, вы можете запустить zpipe или cat-file, чтобы увидить это самостоятельно. Это первый коммит, и поэтому нет родительского коммита, но последующие коммиты всегда будет содержать хотя бы одну строку идентифицирующую родительский коммит. 8.7.4 Неотличимо от магии Там мало сказано. Мы только что открыли секрет мощи Git. Это кажется слишком простым: похоже, что вы могли бы смешать вместе несколько скриптов оболочки и добавить немного кода на C, сделанного в считанные часы. По сути, это точное описание ранних версий Git. Тем не менее, помимо гениальных трюков упаковки, чтобы сэкономить место, и трюков индексации, чтобы сэкономить время, мы теперь знаем, как ловко Git преображает файловую систему в базу данных, идеально подходящую для контроля версий. Например, если какой-то файл объекта базы данных повредила ошибка диска, то его хэш больше не совпадает, предупреждая о проблеме. При хешировании хэшей других объектов, мы сохраня- ем целостность на всех уровнях. Коммит являются атомными, то есть, никогда нельзя закомми- тить лишь часть изменений: мы можем только вычислить хэш коммита и сохранить его в базу данных после того как мы сохраним все соответствующие деревья, блобы и родительские комми- ты. Объектная база данных застрахована от неожиданных прерываний работы с ней таких как перебои в подаче электроэнергии. Мы наносим поражение даже самым хитрым противникам. Пусть кто-то попытается тайно из- менить содержимое файла в древней версии проекта. Чтобы сохранить объектную базу данных согласованной, они также должны изменить хеш соответствующего объекта BLOB, поскольку это теперь другая последовательность байтов. Это означает, что нужно поменять хэш всех де- ревьев, содержащих ссылки на объект этого файла, что в свою очередь изменит хэши коммитов всех объектов с участием таких деревьев, в дополнение к хэшам всех потомков этих коммитов. Это означает, хэш официальной головной ревизии будет отличаться от хеша в этом плохом хра- нилище. По следам несовпадения хэшей мы можем локализовать изуродованный файл, а также коммит, где он впервые был поврежден. Короче говоря, пока 20 байт представляющие последний коммит в безопасности, невозможно изменить репозиторий Git. Как насчет знаменитых черт Git? Создание ветки? Слияние? Теги? Более подробно. Текущая HEAD хранится в файле .git/HEAD, который содержит хэш объекта фиксации. Хэш обновляется во время коммита, а также при выполнении многих других команд. Ветки почти одинаковы: они представляют собой файлы в .git/refs/heads. Тэги тоже: они живут в .git/refs/tags, но они обновляться различными наборами команд. Магия Git 37 / 41 Глава 9 Недостатки Git Есть некоторые проблемы Git, которые я спрятал под сукно. Некоторые из них можно легко ре- шить с помощью сценариев и хуков, некоторые требуют реорганизации или пересмотра проекта, и из нескольких оставшихся неприятности, одну придется терпеть. Или еще лучше, взяться за нее и решить! 9.1 Недостатки SHA1 Со временем, криптографы обнаружать более слабые SHA1. Уже нахождения хэш столкнове- ний является допустимым для хорошо финансируемой организации. За годы, возможно, даже типичный ПК будет иметь достаточную вычислительную мощность, чтобы потихоньку скоррум- пировать Git репозиторий. Надеюсь Git мигрирует к лучшей хэш-функции до того, как дальнейшие исследования разрушат SHA1. 9.2 Microsoft Windows Git на Microsoft Windows может быть накладным: • Cygwin , Linux-среда для Windows, содержит порт Git на Windows - Git на MSys вариант, требующий минимальной рантайм поддержки, хотя для нескольких ком- манд, нужна доработка. 9.3 Несвязанные файлы Если ваш проект очень большой и содержит много не связанных файлов, которые постоянно изменяются, Git могжет оказаться в невыгодном положении больше, чем другие системы, по- скольку отдельные файлы не отслеживаются. Git отслеживает изменения всего проекта, такой подход, как правило, полезен. Решение - разбить проект на части, каждая из которых состоит из связанных файлов. Исполь- зуйте git submodule если вы все еще хотите держать все в одном репозитории. Магия Git 38 / 41 9.4 Кто и что редактировал ? Некоторые системы контроля версий заставить вас отметить файл до редактирования. Хотя это особенно раздражает, когда речь идет о работе с центральным сервером, этот способ имеет два преимущества: 1. Diff'ы быстры, потому что только отмеченные файлы должны быть изучены. 2. Можно обнаружить, кто еще работает с этим файлом, спросив центральный сервер, кто отметил его для редактирования. При наличии соответствующих сценариев, вы можете добиться того же с Git. Это требует со- трудничества со стороны других программистов, которые должны выполнять сценарии при ре- дактировании файла. 9.5 История файлов Поскольку Git записывает изменения всего проекта, то чтобы восстановить историю одного фай- ла требуется больше работы, чем в системах управления версиями, которые позволяют отсле- живать отдельные файлы. Потери, как правило, небольшие, а также стоит иметь в виду, что другие операции являются невероятно эффективными. Например, git checkout быстрее, чем cp -a, и вся дельта проекта сжимаются лучше, чем коллекций на основе дельт файлов. 9.6 Начальное Клонирование Создание клона репозитория дороже обычного чекаута в другие системы управления версиями, особенно когда существует большая история. Первоначальная цена окупается в долгосрочной перспективе, так как большинство операций, затем будут происходить быстро и в автономном режиме. Однако в некоторых ситуациях может быть предпочтительным создание мелких клонов с опцией --depth. Это намного быстрее, но в результате снижается функциональность клона. 9.7 Изменчивые Проекты Git была написан, чтобы быть быстрым по отношению к небольшим изменениям. Люди делают небольшие исправления от версии к версии. Однострочные исправление здесь, новые функции там, исправленные комментарии, и так далее. Но если ваши файлы, радикально отличаются в следующей ревизии, то на каждой фиксации, ваша история обязательно увеличивается на раз- мер всего вашего проекта. Не существует системы управления версиями, которая может решить эту проблему, но пользо- ватели Git пострадают больше, поскольку клонируется вся история. Причины, почему эти изменения бывают настолько велики, должны быть изучены. Возможно, формат файла должен быть изменен. Малые изменения должны быть причиной малых измене- ний в самих файлах. Или, возможно, то, что вам было нужно, это базы данных или система резервного копирова- ния или архивы, а не система контроля версий. Например, контроль версий может быть плохо приспособлен для управления фотографиями периодически получаемых с веб-камеры. Магия Git 39 / 41 Если файлы действительно должны постоянно изменяться, и они действительно должно быть версионироваться, возможность использовать Git централизованным образом. Можно создать мелкой клоны, с небольшой или без истории проекта. Конечно, многие инструменты Git будут недоступны, и исправления должны быть представлены в виде патчей. Вероятно, это штраф, потому как неясно, почему кто-то хочет вести историю дико неустойчиво файлов. Другим примером является проект, зависимый от прошивки, которая принимает форму огром- ного бинарного файла. История этой микропрограммы неинтересна для пользователей, и об- новления сжимаются плохо, так версии микропрограммы могут излишне увеличить размера хранилища. В этом случае исходный код должен храниться в хранилище Git, а бинарные файлы - отдельно. Чтобы сделать жизнь проще, можно было бы распространять скрипт, который используется Git чтобы клонировать код и Rsync или мелкий клон Git для прошивки. 9.8 Глобальный счетчик Некоторые централизованные системы контроля версий, используют положительные числа, ко- торые возрастают, когда происходит новый коммит. Git идентифицирует изменения их хэшем, который лучше во многих обстоятельствах. Но некоторым люди нравится эти целые вокруг. К счастью, легко написать сценарии, чтобы при каждом обновлении центральный репозиторий Git увеличивал целое число, возможно, в теге, и связывает его с хэшем последним коммитом. Каждай клон может поддерживать такой счетчик, но это, вероятно, будет бесполезным, посколь- ку только центральный репозиторий имеет значение для всех. 9.9 Пустые подкаталоги Пустые подкаталоги не могут быть отслежены. Создайте пустой файл, чтобы обойти эту пробле- му. В этом виноват не дизайн Git, а его текущая реализация. Если повезет и пользователи Git уделят больше внимания этой функции, возможно она будет реализована. 9.10 Первоначальный коммит Обычный ученый в области информатики считает от 0, а не от 1. К сожалению, Git с его коммита- ми не присоединиться к этой конвенции. Многие команды плохо работают до первого коммита. Кроме того, некоторые частные случаи должны быть обработаны специально, такие, как rebase веток с различными начальными коммитами. Git'у желательно определение нулевого совершения: как только будет построено хранилище, HEAD будет установлен в строку, состоящую из 20 нулевых байтов. Эта специальный коммит представляет собой пустое дерево, без родителей, предшествовавшие всему в Git репозитории. Затем запустить git log, например, будет показывать пользователю, что коммиты еще не были сделаны вместо выхода с фатальной ошибкой. Аналогично для других инструментов. Каждая первоначальная фиксация - неявный потомок этого нулевого коммита. Например, несвя- занный с какой-либо веткой rebase в целом будет привита на эту цель. В настоящее время приме- няются все, кроме первоначального коммита, в результате чего получается конфликт слияния. Один из способов заключается в использовании git checkout после git commit -C первона- чального коммита, тогда rebase пройдет нормально. Магия Git 40 / 41 К сожалению, в худшем случае, если несколько ветвей с различными начальными коммитами сливаются, то rebase результата требует значительного ручного вмешательства. Магия Git 41 / 41 Глава 10 Приложение А: Перевод этого ру- ководства Клонируйте исходные тексты, затем создайте соответствующую директорию языковой тег IETF : см статья W3C по интернационализации . К примеру, английский язык "en", японский "ja", тра- диционный китайский "zh-Hant". Скопируйте в директорию файлы txt из "en" поддиректории и переведите их. К примеру, для перевода руководства на Klingon , вы можете набрать: $ git clone git://repo.or.cz/gitmagic.git $ cd gitmagic $ mkdir tlh # "tlh" - IETF языковой код для Klingon. $ cd tlh $ cp ../en/intro.txt . $ edit intro.txt # Переведите файл. и так с каждым файлом. Вы можете просмотреть всю вашу работу: $ make LANG=tlh $ firefox book.html Почаще делайте коммиты, потом дайте знать когда ваш перевод готов На GitHub.com есть веб- интерфейс, который позволяет упростить всю работу: сделать форк "gitmagic" проекта, внести ваши изменения, потом попросить меня о слиянии. Я бы хотел чтобы переводы придерживались такой схемы и мои скрипты могли легко создать HTML и PDF версии. Также эта схема позволяет удобно хранить все переводы в официальном репозитории. Но пожалуйста, поступайте как вам удобнее: к примеру, китайский переводчик использовал Google Docs. Я рад тому что ваша работа открывает доступ большему числу людей к моей работе. |