Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 23 additions & 23 deletions book/10-git-internals/sections/maintenance.asc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

Время от времени Git выполняет автоматическую сборку мусора.
Чаще всего эта команда ничего не делает.
Однако, если у вас накопилось слишком много «рыхлых» объектов (не в pack-файлах), или, наоборот, отдельных pack-файлов, Git запускает полноценный сборщик -- `git gc` (здесь «gc» это сокращение от «garbage collect», что означает «сборка мусора»).
Однако если у вас накопилось слишком много «рыхлых» объектов (не в pack-файлах), или, наоборот, отдельных pack-файлов, Git запускает полноценный сборщик -- `git gc` (здесь `gc` -- это сокращение от «garbage collect», что означает «сборка мусора»).
Эта команда выполняет несколько действий: собирает все «рыхлые» объекты и упаковывает их в pack-файлы; объединяет несколько упакованных файлов в один большой; удаляет недостижимые объекты, хранящиеся дольше нескольких месяцев.

Сборку мусора можно запустить вручную следующим образом:
Expand All @@ -22,7 +22,7 @@ $ git gc --auto
Нужно иметь примерно 7000 несжатых объектов или более 50 pack-файлов, чтобы запустился настоящий `gc`.
Эти значения можно изменить с помощью параметров `gc.auto` и `gc.autopacklimit` соответственно.

Ещё одно действие, выполняемое `gc` -- упаковка ссылок в единый файл.
Ещё одно действие, выполняемое `gc`, -- упаковка ссылок в единый файл.
Предположим, репозиторий содержит следующие ветки и теги:

[source,console]
Expand Down Expand Up @@ -50,7 +50,7 @@ cac0cab538b970a37ea1e769cbbde608743bc96d refs/tags/v1.0

При обновлении ссылки Git не будет редактировать этот файл, а добавит новый файл в `refs/heads`.
Для получения хеша, соответствующего нужной ссылке, Git сначала проверит наличие файла ссылки в каталоге `refs`, а к файлу `packed-refs` обратится только в случае отсутствия оного.
Так что, если вы не можете найти ссылку в каталоге `refs`, скорее всего она упакована в файле `packed-refs`.
Так что если вы не можете найти ссылку в каталоге `refs`, скорее всего, она упакована в файле `packed-refs`.

Обратите внимание, последняя строка файла начинается с `^`.
Это означает, что предыдущая строка является аннотированным тегом, а текущая строка -- это коммит, на который указывает аннотированный тег.
Expand All @@ -63,7 +63,7 @@ cac0cab538b970a37ea1e769cbbde608743bc96d refs/tags/v1.0
Как же в таком случае вернуть свои коммиты обратно?

Ниже приведён пример, в котором мы сбрасываем ветку `master` с потерей данных до более раннего состояния, а затем восстанавливаем потерянные коммиты.
Для начала, давайте посмотрим, как сейчас выглядит история изменений:
Для начала давайте посмотрим, как сейчас выглядит история изменений:

[source,console]
----
Expand All @@ -88,14 +88,14 @@ fdf4fc3344e67ab068f836878b6c4951e3b15f3d First commit
----

Итак, теперь два последних коммита по-настоящему потеряны -- они не достижимы ни из одной ветки.
Необходимо найти SHA-1 хеш последнего коммита и создать ветку, указывающую на него.
Необходимо найти SHA-1-хеш последнего коммита и создать ветку, указывающую на него.
Сложность в том, чтобы узнать этот самый SHA-1, ведь вряд ли вы его запомнили, да?

Зачастую самый быстрый способ -- использование команды `git reflog`.
Дело в том, что во время вашей работы Git записывает все изменения HEAD.
Каждый раз при переключении веток и коммитов изменений, добавляется запись в reflog.
reflog также обновляется командой `git update-ref` -- это, кстати, хорошая причина использовать именно эту команду, а не вручную записывать SHA-1 в ref-файлы, как было показано в <<r_git_refs>>.
Вы можете посмотреть где находился указатель HEAD в любой момент времени, запустив `git reflog`:
Дело в том, что во время вашей работы Git записывает все изменения `HEAD`.
Каждый раз при переключении веток и записи коммитов изменений добавляется запись в `reflog`.
`reflog` также обновляется командой `git update-ref` -- это, кстати, хорошая причина использовать именно эту команду, а не вручную записывать SHA-1 в ref-файлы, как было показано в <<r_git_refs>>.
Вы можете посмотреть где находился указатель `HEAD` в любой момент времени, запустив `git reflog`:

[source,console]
----
Expand All @@ -105,8 +105,8 @@ ab1afef HEAD@{1}: commit: Modify repo.rb a bit
484a592 HEAD@{2}: commit: Create repo.rb
----

Здесь мы видим два коммита, на которые когда-то указывал HEAD, однако информации не так уж и много.
Для получения информации в более удобном виде, можно воспользоваться командой `git log -g`, которая выведет лог записей из reflog в привычном формате:
Здесь мы видим два коммита, на которые когда-то указывал `HEAD`, однако информации не так уж и много.
Для получения информации в более удобном виде можно воспользоваться командой `git log -g`, которая выведет лог записей из `reflog` в привычном формате:

[source,console]
----
Expand Down Expand Up @@ -152,7 +152,7 @@ $ rm -Rf .git/logs/
----

В этом случае два первых коммита недоступны ниоткуда.
Так как данные reflog хранятся в каталоге `.git/logs/`, которую мы только что удалили, то теперь у нас нет reflog.
Так как данные `reflog` хранятся в каталоге `.git/logs/`, которую мы только что удалили, то теперь у нас нет `reflog`.
Как теперь восстановить коммиты?
Один из вариантов -- использование утилиты `git fsck`, проверяющую внутреннюю базу данных на целостность.
Если выполнить её с ключом `--full`, будут показаны все объекты, недостижимые из других объектов:
Expand All @@ -176,10 +176,10 @@ dangling blob 7108f7ecb345ee9d0084193f147cdad4d2998293

Git -- замечательный инструмент с кучей классных возможностей, но некоторые из них способны стать источником проблем; например, команда `git clone` загружает проект вместе со всей историей, включая все версии всех файлов.
Это нормально, если в репозитории хранится только исходный код, так как Git хорошо оптимизирован под такой тип данных и может эффективно сжимать их.
Однако, если когда-либо в проект был добавлен большой файл, каждый, кто потом захочет клонировать проект, будет вынужден скачивать этот файл, даже если он был удалён в следующем коммите.
Он будет в базе всегда, просто потому, что он доступен в истории.
Однако если когда-либо в проект был добавлен большой файл, каждый, кто потом захочет клонировать проект, будет вынужден скачивать этот файл, даже если он был удалён в следующем коммите.
Он будет в базе всегда -- просто потому что он доступен в истории.

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

Expand Down Expand Up @@ -246,10 +246,10 @@ size-garbage: 0
Давайте же исправим это!

Для начала найдём проблемный файл.
В данном случае, мы уже знаем, что это за файл.
В данном случае мы уже знаем, что это за файл.
Но если бы не знали, как можно было бы определить, какие файлы занимают много места?
При вызове `git gc` все объекты упаковываются в один pack-файл, но, несмотря на это, определить самые крупные файлы можно, запустив служебную команду `git verify-pack` и отсортировав её вывод по третьей колонке, в которой записан размер файла.
Так как нас интересуют самые крупный файлы, оставим три последние строки с помощью `tail`:
При вызове `git gc` все объекты упаковываются в один `pack`-файл, но, несмотря на это, определить самые крупные файлы можно, запустив служебную команду `git verify-pack` и отсортировав её вывод по третьей колонке, в которой записан размер файла.
Так как нас интересуют самые крупные файлы, оставим три последние строки с помощью `tail`:

[source,console]
----
Expand All @@ -261,10 +261,10 @@ dadf7258d699da2c8d89b09ef6670edb7d5f91b4 commit 229 159 12
82c99a3e86bb1267b236a4b6eff7868d97489af1 blob 4975916 4976258 1438
----

Большой объект внизу списка, его размер -- 5 МБ.
Большой объект в низу списка, его размер -- 5 МБ.
Для того чтобы узнать, что это за файл, воспользуемся командой `rev-list`, которая уже упоминалась в разделе <<ch08-customizing-git#r_enforcing_commit_message_format>> главы 8.
Если передать ей ключ `--objects`, она выдаст хеши всех коммитов, а также хеши объектов и соответствующие им имена файлов.
Воспользуемся этим для определения имени выбранного объекта:
Воспользуемся ей для определения имени выбранного объекта:

[source,console]
----
Expand Down Expand Up @@ -296,15 +296,15 @@ Ref 'refs/heads/master' was rewritten

Опция `--index-filter` похожа на `--tree-filter`, использовавшуюся в разделе <<ch07-git-tools#r_rewriting_history>> главы 7, за исключением того, что вместо передачи команды, модифицирующей файлы на диске, мы используем команду, изменяющую файлы в индексе.

Вместо удаления файла чем-то вроде `rm file`, мы используем `git rm --cached`, так как нам надо удалить файл из индекса, а не с диска.
Причина, по которой мы делаем именно так -- скорость: нет необходимости извлекать каждую ревизию на диск, чтобы применить фильтр, а это может очень сильно ускорить процесс.
Вместо удаления файла чем-то вроде `rm file` мы используем `git rm --cached`, так как нам надо удалить файл из индекса, а не с диска.
Причина, по которой мы делаем именно так, -- скорость: нет необходимости извлекать каждую ревизию на диск, чтобы применить фильтр, а это может очень сильно ускорить процесс.
Если хотите, можете использовать и `tree-filter` для получения аналогичного результата.
Опция `--ignore-unmatch` команды `git rm` отключает вывод сообщения об ошибке в случае отсутствия файлов, соответствующих шаблону.
Ещё один момент: мы указали команде `filter-branch` переписывать историю, начиная с коммита `7b30847`, потому что мы знаем, что именно в нём впервые появилась проблема.
По умолчанию перезапись начинается с самого первого коммита, что потребовало бы гораздо больше времени.

Теперь история не содержит ссылок на данный файл.
Однако, в reflog и в новом наборе ссылок, добавленном Git в `.git/refs/original` после выполнения `filter-branch`, ссылки на него всё ещё присутствуют, поэтому необходимо их удалить, а затем переупаковать базу.
Однако в reflog и в новом наборе ссылок, добавленном Git в `.git/refs/original` после выполнения `filter-branch`, ссылки на него всё ещё присутствуют, поэтому необходимо их удалить, а затем переупаковать базу.
Необходимо избавиться от всех возможных ссылок на старые коммиты перед переупаковкой:

[source,console]
Expand Down
Loading