Merge https://github.com/lordofbikes/gitmagic
[gitmagic/gitmagic.git] / ru / secrets.txt
blob0d108b8b1fc674b4dda380c5172dd19ce0710c78
1 == Раскрываем тайны ==
3 Мы заглянем под капот и объясним, как Git творит свои чудеса. Я опущу излишние детали. За более детальными описаниями обратитесь к http://www.kernel.org/pub/software/scm/git/docs/user-manual.html[руководству пользователя].
5 === Невидимость ===
7 Как Git может быть таким ненавязчивым? За исключением периодических коммитов и слияний, вы можете работать так, как будто и не подозреваете о каком-то управлении версиями. Так происходит до того момента, когда Git вам понадобится, и тогда вы с радостью увидите, что он наблюдал за вами все это время.
9 Другие системы управления версиями вынуждают вас постоянно бороться с загородками и бюрократией. Файлы могут быть доступны только для чтения, пока вы явно не укажете центральному серверу, какие файлы вы намереваетесь редактировать. С увеличением количества пользователей большинство базовых команд начинают выполняться всё медленнее. Неполадки с сетью или с центральным сервером полностью останавливают работу.
11 В противоположность этому, Git просто хранит историю проекта в подкаталоге .git вашего рабочего каталога. Это ваша личная копия истории, поэтому вы можете оставаться вне сети, пока не захотите взаимодействовать с остальными. У вас есть полный контроль над судьбой ваших файлов, поскольку Git в любое время может легко восстановить сохраненное состояние из .git.
13 === Целостность ===
15 Большинство людей ассоциируют криптографию с содержанием информации в секрете, но другой столь же важной задачей является содержание ее в сохранности. Правильное использование криптографических хеш-функций может предотвратить случайное или злонамеренное повреждение данных.
17 SHA1 хеш можно рассматривать как уникальный 160-битный идентификатор для каждой строки байт, с которой вы сталкиваетесь в вашей жизни. Даже больше того: для каждой строки байтов, которую любой человек когда-либо будет использовать в течение многих жизней.
19 Так как SHA1 хеш сам является последовательностью байтов, мы можем получить хеш строки байтов, содержащей другие хеши. Это простое наблюдение на удивление полезно: ищите «hash chains» (цепочки хешей). Позднее мы увидим, как Git использует их для эффективного обеспечения целостности данных.
21 Говоря кратко, Git хранит ваши данные в подкаталоге ".git/objects", где вместо нормальных имен файлов вы найдете только идентификаторы. Благодаря использованию идентификаторов в качестве имен файлов, а также некоторым хитростям с файлами блокировок и временными метками, Git преобразует любую скромную файловую систему в эффективную и надежную базу данных.
23 === Интеллект ===
25 Как Git узнаёт, что вы переименовали файл, даже если вы никогда не упоминали об этом явно? Конечно, вы можете запустить *git mv*; но это то же самое, что *git rm*, а затем *git add*.
27 Git эвристически находит файлы, которые были переименованы или скопированы между соседними версиями. На деле он может обнаружить, что участки кода были перемещены или скопированы между файлами! Хотя Git не может охватить все случаи, он всё же делает достойную работу, и эта функция постоянно улучшается. Если она не сработала, попробуйте опции, включающие более ресурсоемкое обнаружение копирования и подумайте об обновлении.
29 === Индексация ===
31 Для каждого отслеживаемого файла, Git записывает такую информацию, как размер, время создания и время последнего изменения, в файле, известном как «индекс». Чтобы определить, был ли файл изменен, Git сравнивает его текущие характеристики с сохраненными в индексе. Если они совпадают, то Git не станет перечитывать файл заново.
33 Поскольку считывание этой информации значительно быстрее, чем чтение всего файла, то если вы редактировали лишь несколько файлов, Git может обновить свой индекс почти мгновенно.
35 Мы отмечали ранее, что индекс это буферная зона. Почему набор свойств файлов выступает таким буфером? Потому что команда add помещает файлы в базу данных Git и в соответствии с этим обновляет эти свойства; тогда как команда commit без опций создает коммит, основанный только на этих свойствах и файлах, которые уже в базе данных.
37 === Происхождение Git ===
39 Это http://lkml.org/lkml/2005/4/6/121[сообщение в почтовой рассылке ядра Linux] описывает последовательность событий, которые привели к появлению Git. Весь этот тред — привлекательный археологический раскоп для историков Git.
41 === База данных объектов ===
43 Каждая версия ваших данных хранится в «базе данных объектов», живущей в подкаталоге .git/objects. Другие «жители» .git/ содержат вторичные данные: индекс, имена веток, теги, параметры настройки, журналы, нынешнее расположение «головного» коммита и так далее. База объектов проста и элегантна, и в ней источник силы Git.
45 Каждый файл внутри .git/objects это «объект». Нас интересуют три типа объектов: объекты «блобов», объекты деревьев и объекты коммитов.
47 === Блобы ===
49 Для начала один фокус. Выберите имя файла — любое имя файла. В пустом каталоге:
51  $ echo sweet > ВАШЕ_ИМЯ_ФАЙЛА
52  $ git init
53  $ git add .
54  $ find .git/objects -type f
56 Вы увидите +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+.
58 Откуда я знаю это, не зная имени файла? Это потому, что SHA1 хеш строки
60  «blob» SP «6» NUL «sweet» LF
62 равен aa823728ea7d592acc69b36875a482cdf3fd5c8d,
63 где SP это пробел, NUL — нулевой байт и LF — перевод строки. Вы можете проверить это, набрав
65   $ printf "blob 6\000sweet\n" | sha1sum
67 Git использует «адресацию по содержимому»: файлы хранятся в соответствии не с именами, а с хешами содержимого, — в файле, который мы называем «блоб-объектом». Хеш можно понимать как уникальный идентификатор содержимого файла, что означает обращение к файлам по их содержимому. Начальный «blob 6» — лишь заголовок, состоящий из типа объекта и его длины в байтах и упрощающий внутренний учет.
69 Таким образом, я могу легко предсказать, что вы увидите. Имя файла не имеет значения: для создания блоб-объекта используется только его содержимое.
71 Вам может быть интересно, что происходит с одинаковыми файлами. Попробуйте добавить копии своего файла с какими угодно именами. Содержание +.git/objects+ останется тем же независимо от того, сколько копий вы добавите. Git хранит данные лишь единожды.
73 Кстати, файлы в каталоге +.git/objects+ сжимаются с помощью zlib поэтому вы не сможете просмотреть их напрямую. Пропустите их через фильтр http://www.zlib.net/zpipe.c[zpipe -d], или введите
75  $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d
77 что выведет указанный объект в читаемом виде.
79 === Деревья ===
81 Но где же имена файлов? Они должны храниться на каком-то уровне. Git обращается за именами во время коммита:
83  $ git commit  # Введите какое-нибудь описание
84  $ find .git/objects -type f
86 Теперь вы должны увидеть три объекта. На этот раз я не могу сказать вам, что из себя представляют два новых файла, так как это частично зависит от выбранного вами имени файла. Далее будем предполагать, что вы назвали его «rose». Если это не так, то вы можете переписать историю, чтобы она выглядела как будто вы это сделали:
88  $ git filter-branch --tree-filter 'mv ВАШЕ_ИМЯ_ФАЙЛА rose'
89  $ find .git/objects -type f
91 Теперь вы должны увидеть файл
92 +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+, так как это
93 SHA1 хеш его содержимого:
95  «tree» SP «32» NUL «100644 rose» NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d
97 Проверьте, что этот файл действительно содержит указанную строку, набрав
99  $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch
101 С zpipe легко проверить хеш:
103  $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum
105 Проверка хеша с помощью cat-file сложнее, поскольку ее вывод содержит не только «сырой» распакованный файл объекта.
107 Этот файл — объект «дерево» (tree, прим. пер.): список цепочек, состоящих из типа, имени файла и его хеша. В нашем примере: тип файла — 100644, что означает, что «rose» это обычный файл; а хеш — блоб-объект, в котором находится содержимое «rose». Другие возможные типы файлов: исполняемые файлы, символические ссылки или каталоги. В последнем случае, хеш указывает на объект «дерево».
109 Если вы запускали filter-branch, у вас есть старые объекты которые вам больше не нужны. Хотя по окончании срока хранения они будут выброшены автоматически, мы удалим их сейчас, чтобы было легче следить за нашим игрушечным примером:
111  $ rm -r .git/refs/original
112  $ git reflog expire --expire=now --all
113  $ git prune
115 Для реальных проектов обычно лучше избегать таких команд, поскольку вы уничтожаете резервные копии. Если вы хотите иметь чистое хранилище, то обычно лучше сделать свежий клон. Кроме того, будьте осторожны при непосредственном вмешательстве в каталог +.git+: что если другая команда Git работает в это же время, или внезапно произойдет отключение питания? Вообще говоря, ссылки нужно удалять с помощью *git update-ref -d*, хотя обычно ручное удаление +refs/original+ безопасно.
117 === Коммиты ===
119 Мы рассмотрели два из трех объектов. Третий объект — «коммит» (commit). Его содержимое зависит от описания коммита, как и от даты и времени его создания. Для соответстия тому, что мы имеем, мы должны немного «подкрутить» Git:
121  $ git commit --amend -m Shakespeare  # Изменим описание коммита.
122  $ git filter-branch --env-filter 'export
123      GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800"
124      GIT_AUTHOR_NAME="Alice"
125      GIT_AUTHOR_EMAIL="alice@example.com"
126      GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800"
127      GIT_COMMITTER_NAME="Bob"
128      GIT_COMMITTER_EMAIL="bob@example.com"'  # Подделаем временные метки и авторов.
129  $ find .git/objects -type f
131 Теперь вы должны увидеть
132 +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+
133 который является SHA1 хешем его содержимого:
135  «commit 158» NUL
136  «tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9» LF
137  «author Alice <alice@example.com> 1234567890 -0800» LF
138  «committer Bob <bob@example.com> 1234567890 -0800» LF
139  LF
140  «Shakespeare» LF
142 Как и раньше, вы сами можете запустить zpipe или cat-file, чтобы увидить это.
144 Это первый коммит, поэтому здесь нет родительских коммитов, но последующие коммиты всегда будет содержать хотя бы одну строку, идентифицирующую родительский коммит.
146 === Неотличимо от магии ===
148 Секреты Git выглядят слишком простыми. Похоже, что вы могли бы объединить несколько shell-скриптов и добавить немного кода на C, чтобы сделать всё это в считанные часы: смесь базовых операций с файлами и SHA1-хеширование, приправленная блокировочными файлами и fsync для надеждности. По сути, это точное описание ранних версий Git. Тем не менее, помимо гениальных трюков с упаковкой для экономии места и с индексацией для экономии времени, мы теперь знаем, как ловко Git преображает файловую систему в базу данных, идеально подходящую для управления версиями.
150 Например, если какой-либо файл в базе данных объектов поврежден из-за ошибки диска, то его хеш теперь не совпадет, что привлечет наше внимание к проблеме. С помощью хеширования хешей других объектов, мы поддерживаем целостность на всех уровнях. Коммиты атомарны, так что в них никогда нельзя записать лишь часть изменений: мы можем вычислить хеш коммита и сохранить его в базу данных только сохранив все соответствующие деревья, блобы и родительские коммиты. База данных объектов нечувствительна к непредвиденным прерываниям работы, таких как перебои с питанием.
152 Мы наносим поражение даже самым хитрым противникам. Предположим, кто-то пытается тайно изменить содержимое файла в древней версии проекта. Чтобы база объектов выглядела неповрежденной, он также должен изменить хеш соответствующего блоб-объекта, поскольку это теперь другая последовательность байтов. Это означает, что нужно поменять хеши всех объектов деревьев, ссылающихся на этот файл; что в свою очередь изменит хеши всех объектов коммитов с участием таких деревьев; а также и хеши всех потомков этих коммитов. Вследствие этого хеш официальной головной ревизии будет отличаться от аналогичного хеша в этом испорченном хранилище. По цепочке несовпадающих хешей мы можем точно вычислить искаженный файл, как и коммит, где он изначально был поврежден.
154 Одним словом, невозможно подделать хранилище Git, оставив невредимыми двадцать байт, отвечающие последнему коммиту.
156 Как насчет известных характерных особенностей Git? Ветвление? Слияние? Теги? Очевидные подробности. Текущая «голова» хранится в файле +.git/HEAD+, содержащем хеш объекта коммита.
157 Хеш обновляется во время коммита, а также при выполнении многих других команд. С ветками всё аналогично: это файлы в +.git/refs/heads+. То же и тегами: они живут в +.git/refs/tags+, но их обновляет другой набор команд.