- •Предисловие
- •Благодарности
- •Лицензия
- •Базовые операции
- •Сохранение состояния
- •Добавление, удаление, переименование
- •Расширенная отмена/Восстановление
- •Возвраты
- •Создание списка изменений
- •Скачивание файлов
- •На острие ножа
- •Публичный доступ
- •Что я наделал?
- •Упражнение
- •Все о клонировании
- •Синхронизация компьютеров
- •Классический контроль исходного кода
- •Создание форка проекта
- •Окончательные бэкапы
- •Многозадачность со скоростью света
- •Другие системы контроля версий
- •Чудеса ветвления
- •Кнопка босса
- •Грязная работа
- •Быстрые исправления
- •Бесперебойный рабочий процесс
- •Собрать все в кучу
- •Управление Ветками
- •Временные Ветки
- •Работайте как вам нравится
- •Уроки истории
- •Оставаясь корректным
- •Локальные изменения сохраняются
- •Переписывая историю
- •Создавая Историю
- •Когда же все пошло не так?
- •Из-за кого все пошло наперекосяк?
- •Личный опыт
- •Групповая работа в Git
- •Git через SSH, HTTP
- •Git через что угодно
- •Патчи: Общее применения
- •К сожалению, мы переехали
- •Удаленные Ветки
- •Несколько Удаленных Веток
- •Мои Настройки
- •Гроссмейстерство Git
- •Релизы исходников
- •Сохранение изменений
- •Слишком большой коммит
- •Этапные изменения
- •Не теряй HEAD
- •Охота за HEAD'ами
- •Git как основа
- •Опасные трюки
- •Улучшаем свой публичный образ
- •Раскрываем тайны
- •Невидимость
- •Целостность
- •Интеллект
- •Индексация
- •Голые репозитории
- •Происхождение Git
- •База данных объектов
- •Blobs
- •Деревья
- •Коммиты
- •Неотличимо от магии
- •Недостатки Git
- •Недостатки SHA1
- •Microsoft Windows
- •Несвязанные файлы
- •Кто и что редактировал ?
- •История файлов
- •Начальное Клонирование
- •Изменчивые Проекты
- •Глобальный счетчик
- •Пустые подкаталоги
- •Первоначальный коммит
- •Приложение А: Перевод этого руководства
Магия 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 <alice@example.com> 1234567890 -0800" LF "committer Bob <bob@example.com> 1234567890 -0800" LF 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, но они обновляться различными наборами команд.