Русский ▾ Topics ▾ Latest version ▾ git-filter-branch last updated in 2.44.0

НАЗВАНИЕ

git-filter-branch — переписывание веток

ОБЗОР

git filter-branch [--setup <команда>] [--subdirectory-filter <каталог>]
	[--env-filter <команда>] [--tree-filter <команда>]
	[--index-filter <команда>] [--parent-filter <команда>]
	[--msg-filter <команда>] [--commit-filter <команда>]
	[--tag-name-filter <команда>] [--prune-empty]
	[--original <пространство-имён>] [-d <каталог>] [-f | --force]
	[--state-branch <ветка>] [--] [<параметры-rev-list>…​]

ПРЕДУПРЕЖДЕНИЕ

git filter-branch имеет множество подводных камней, которые могут привести к неочевидным искажениям предполагаемого переписывания истории (и могут оставить вам мало времени для исследования таких проблем из-за его ужасной производительности). Эти проблемы безопасности и производительности не могут быть исправлены с сохранением обратной совместимости, и поэтому его использование не рекомендуется. Пожалуйста, используйте альтернативный инструмент фильтрации истории, такой как git filter-repo. Если вам всё ещё нужно использовать git filter-branch, пожалуйста, внимательно прочитайте БЕЗОПАСНОСТЬПРОИЗВОДИТЕЛЬНОСТЬ), чтобы узнать о минных полях filter-branch, а затем бдительно избегайте как можно большего количества перечисленных там опасностей.

ОПИСАНИЕ

Позволяет переписать историю редакций Git, переписывая ветки, упомянутые в <параметрах-rev-list>, применяя пользовательские фильтры к каждой редакции. Эти фильтры могут изменять каждое дерево (например, удалять файл или выполнять переписывание на Perl для всех файлов) или информацию о каждом коммите. В остальном вся информация (включая исходные времена коммитов или информацию о слиянии) будет сохранена.

Команда перепишет только положительные ссылки, упомянутые в командной строке (например, если вы передадите a..b, будет переписана только b). Если вы не укажете фильтры, коммиты будут заново зафиксированы без каких-либо изменений, что обычно не имеет эффекта. Тем не менее, это может быть полезно в будущем для компенсации некоторых ошибок Git или подобного, поэтому такое использование разрешено.

ПРИМЕЧАНИЕ: Эта команда учитывает файл .git/info/grafts и ссылки в пространстве имён refs/replace/. Если у вас определены какие-либо grafts или заменяющие ссылки, выполнение этой команды сделает их постоянными.

ПРЕДУПРЕЖДЕНИЕ! Переписанная история будет иметь разные имена объектов для всех объектов и не будет сходиться с исходной веткой. Вы не сможете легко отправить и распространить переписанную ветку поверх исходной ветки. Пожалуйста, не используйте эту команду, если вы не знаете всех последствий, и в любом случае избегайте её использования, если для исправления вашей проблемы достаточно одного простого коммита. (Дополнительную информацию о переписывании опубликованной истории см. в разделе "ВОССТАНОВЛЕНИЕ ПОСЛЕ ПЕРЕМЕЩЕНИЯ ВЫШЕСТОЯЩЕЙ ВЕТКИ" в git-rebase[1].)

Всегда проверяйте, что переписанная версия верна: исходные ссылки, если они отличаются от переписанных, будут сохранены в пространстве имён refs/original/.

Обратите внимание, что поскольку эта операция очень затратна по вводу-выводу, возможно, будет хорошей идеей перенаправить временный каталог с диска с помощью параметра -d, например, на tmpfs. Сообщается, что ускорение очень заметно.

Фильтры

Фильтры применяются в порядке, указанном ниже. Аргумент <команда> всегда вычисляется в контексте оболочки с использованием команды eval (за заметным исключением фильтра коммита по техническим причинам). Перед этим переменная среды $GIT_COMMIT будет установлена так, чтобы содержать идентификатор переписываемого коммита. Кроме того, GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, GIT_AUTHOR_DATE, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL и GIT_COMMITTER_DATE берутся из текущего коммита и экспортируются в среду, чтобы повлиять на идентификаторы автора и коммиттера заменяющего коммита, создаваемого git-commit-tree[1] после выполнения фильтров.

Если любое вычисление <команды> возвращает ненулевой статус выхода, вся операция будет прервана.

Доступна функция map, которая принимает аргумент "исходный идентификатор sha1" и выводит "переписанный идентификатор sha1", если коммит уже был переписан, и «исходный идентификатор sha1» в противном случае; функция map может возвращать несколько идентификаторов на отдельных строках, если ваш фильтр коммитов выдал несколько коммитов.

ПАРАМЕТРЫ

--setup <команда>

Это не реальный фильтр, выполняемый для каждого коммита, а одноразовая настройка непосредственно перед циклом. Поэтому переменные, специфичные для коммита, ещё не определены. Функции или переменные, определённые здесь, могут быть использованы или изменены в следующих шагах фильтрации, за исключением фильтра коммитов по техническим причинам.

--subdirectory-filter <каталог>

Рассматривать только историю, которая затрагивает указанный подкаталог. Результат будет содержать этот каталог (и только его) в качестве корня проекта. Подразумевает Пересопоставление на предка.

--env-filter <команда>

Этот фильтр может использоваться, если вам нужно только изменить среду, в которой будет выполняться коммит. В частности, вы можете захотеть переписать переменные среды имя/адрес электронной почты/время автора/коммиттера (подробности см. в git-commit-tree[1]).

--tree-filter <команда>

Это фильтр для переписывания дерева и его содержимого. Аргумент вычисляется в оболочке с рабочим каталогом, установленным в корень переключённого дерева. Затем новое дерево используется как есть (новые файлы автоматически добавляются, исчезнувшие файлы автоматически удаляются — ни файлы .gitignore, ни какие-либо другие правила игнорирования НЕ ИМЕЮТ НИКАКОГО ЭФФЕКТА!).

--index-filter <команда>

Это фильтр для переписывания индекса. Он похож на фильтр дерева, но не переключает дерево, что делает его намного быстрее. Часто используется с git rm --cached --ignore-unmatch ..., см. ПРИМЕРЫ ниже. Для сложных случаев см. git-update-index[1].

--parent-filter <команда>

Это фильтр для переписывания списка родителей коммита. Он получит строку родителей в stdin и должен вывести новую строку родителей в stdout. Строка родителей имеет формат, описанный в git-commit-tree[1]: пустая для начального коммита, "-p родитель" для обычного коммита и "-p родитель1 -p родитель2 -p родитель3 …​" для коммита слияния.

--msg-filter <команда>

Это фильтр для переписывания сообщений коммитов. Аргумент вычисляется в оболочке с исходным сообщением коммита в стандартном вводе; его стандартный вывод используется как новое сообщение коммита.

--commit-filter <команда>

Это фильтр для выполнения коммита. Если этот фильтр указан, он будет вызван вместо команды git commit-tree с аргументами вида «<TREE_ID> [(-p <PARENT_COMMIT_ID>)…​]» и сообщением журнала в stdin. Идентификатор коммита ожидается в stdout.

В качестве специального расширения фильтр коммитов может выдавать несколько идентификаторов коммитов; в этом случае переписанные потомки исходного коммита будут иметь всех их в качестве родителей.

Вы можете использовать удобную функцию map в этом фильтре, а также другие удобные функции. Например, вызов skip_commit "$@" пропустит текущий коммит (но не его изменения! Если вы хотите этого, используйте вместо этого git rebase).

Вы также можете использовать git_commit_non_empty_tree "$@" вместо git commit-tree "$@", если вы не хотите сохранять коммиты с одним родителем, которые не вносят изменений в дерево.

--tag-name-filter <команда>

Это фильтр для переписывания имён меток. При передаче он будет вызываться для каждой ссылки метки, которая указывает на переписанный объект (или на объект метки, который указывает на переписанный объект). Исходное имя метки передаётся через стандартный ввод, а новое имя метки ожидается в стандартном выводе.

Исходные метки не удаляются, но могут быть перезаписаны; используйте "--tag-name-filter cat", чтобы просто обновить метки. В этом случае будьте очень осторожны и убедитесь, что у вас есть резервные копии старых меток на случай, если преобразование пойдёт не так.

Поддерживается почти правильное переписывание объектов меток. Если у метки есть прикреплённое сообщение, будет создан новый объект метки с тем же сообщением, автором и временной меткой. Если у метки есть прикреплённая подпись, подпись будет удалена. По определению невозможно сохранить подписи. Причина, по которой это "почти" правильно, заключается в том, что в идеале, если метка не изменилась (указывает на тот же объект, имеет то же имя и т.д.), она должна сохранять любую подпись. Это не так, подписи всегда будут удалены, так что имейте это в виду. Также нет поддержки изменения автора или временной метки (или сообщения метки, если на то пошло). Метки, которые указывают на другие метки, будут переписаны так, чтобы указывать на базовый коммит.

--prune-empty

Некоторые фильтры будут создавать пустые коммиты, которые не изменяют дерево. Этот параметр указывает git-filter-branch удалять такие коммиты, если у них ровно один или ноль необрезанных родителей; коммиты слияния поэтому останутся нетронутыми. Этот параметр нельзя использовать вместе с --commit-filter, хотя тот же эффект может быть достигнут использованием предоставленной функции git_commit_non_empty_tree в фильтре коммитов.

--original <пространство-имён>

Используйте этот параметр, чтобы установить пространство имён, где будут храниться исходные коммиты. Значение по умолчанию — refs/original.

-d <каталог>

Используйте этот параметр, чтобы установить путь к временному каталогу, используемому для переписывания. При применении фильтра дерева команде необходимо временно переключить дерево в какой-либо каталог, что может потребовать значительного места в случае больших проектов. По умолчанию она делает это в каталоге .git-rewrite/, но вы можете изменить этот выбор с помощью этого параметра.

-f
--force

git filter-branch отказывается запускаться, если существует временный каталог или уже есть ссылки, начинающиеся с refs/original/, если только не использовано принудительное выполнение.

--state-branch <ветка>

Этот параметр приведёт к загрузке сопоставления старых объектов с новыми из именованной ветки при запуске и сохранению в виде нового коммита в эту ветку при выходе, обеспечивая инкрементальность для больших деревьев. Если <ветка> не существует, она будет создана.

<параметры rev-list>…​

Аргументы для git rev-list. Все положительные ссылки, включённые этими параметрами, переписываются. Вы также можете указывать параметры, такие как --all, но вы должны использовать --, чтобы отделить их от параметров git filter-branch. Подразумевает Пересопоставление на предка.

Пересопоставление на предка

Используя аргументы git-rev-list[1], например, ограничители путей, вы можете ограничить набор переписываемых редакций. Однако положительные ссылки в командной строке выделяются: мы не позволяем им быть исключёнными такими ограничителями. Для этой цели они вместо этого переписываются так, чтобы указывать на ближайшего предка, который не был исключён.

КОД ЗАВЕРШЕНИЯ

В случае успеха статус выхода равен 0. Если фильтр не может найти ни одного коммита для переписывания, статус выхода равен 2. При любой другой ошибке статус выхода может быть любым другим ненулевым значением.

ПРИМЕРЫ

Предположим, вы хотите удалить файл (содержащий конфиденциальную информацию или нарушающий авторские права) из всех коммитов:

git filter-branch --tree-filter 'rm имя-файла' HEAD

Однако, если файл отсутствует в дереве какого-либо коммита, простой rm имя-файла завершится ошибкой для этого дерева и коммита. Поэтому вместо этого вы можете захотеть использовать rm -f имя-файла в качестве сценария.

Использование --index-filter с git rm даёт значительно более быструю версию. Как и при использовании rm имя-файла, git rm --cached имя-файла завершится ошибкой, если файл отсутствует в дереве коммита. Если вы хотите "полностью забыть" файл, не имеет значения, когда он попал в историю, поэтому мы также добавляем --ignore-unmatch:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch имя-файла' HEAD

Теперь вы получите переписанную историю, сохранённую в HEAD.

Чтобы переписать репозиторий так, чтобы он выглядел, как если бы foodir/ был его корнем проекта, и отбросить всю остальную историю:

git filter-branch --subdirectory-filter foodir -- --all

Таким образом, вы можете, например, превратить подкаталог библиотеки в собственный репозиторий. Обратите внимание на --, который отделяет параметры filter-branch от параметров редакций, и на --all, чтобы переписать все ветки и метки.

Чтобы установить коммит (который обычно находится на верхушке другой истории) в качестве родителя текущего начального коммита, чтобы вставить другую историю за текущую историю:

git filter-branch --parent-filter 'sed "s/^\$/-p <id-прививки>/"' HEAD

(если строка родителей пуста — что происходит, когда мы имеем дело с начальным коммитом — добавьте graftcommit в качестве родителя). Обратите внимание, что это предполагает историю с одним корнем (то есть не было слияния без общих предков). Если это не так, используйте:

git filter-branch --parent-filter \
	'test $GIT_COMMIT = <id-коммита> && echo "-p <id-прививки>" || cat' HEAD

или ещё проще:

git replace --graft $commit-id $graft-id
git filter-branch $graft-id..HEAD

Чтобы удалить из истории коммиты, автором которых является "Darl McBribe":

git filter-branch --commit-filter '
	if [ "$GIT_AUTHOR_NAME" = "Darl McBribe" ];
	then
		skip_commit "$@";
	else
		git commit-tree "$@";
	fi' HEAD

Функция skip_commit определена следующим образом:

skip_commit()
{
	shift;
	while [ -n "$1" ];
	do
		shift;
		map "$1";
		shift;
	done;
}

Магия shift сначала отбрасывает идентификатор дерева, а затем параметры -p. Обратите внимание, что это правильно обрабатывает слияния! Если Дарл совершил слияние между P1 и P2, оно будет правильно распространено, и все потомки слияния станут коммитами слияния с P1 и P2 в качестве родителей вместо коммита слияния.

ПРИМЕЧАНИЕ изменения, внесённые коммитами и не отменённые последующими коммитами, всё равно будут в переписанной ветке. Если вы хотите отбросить изменения вместе с коммитами, вам следует использовать интерактивный режим git rebase.

Вы можете переписывать сообщения журнала коммитов с помощью --msg-filter. Например, строки git svn-id в репозитории, созданном git svn, можно удалить таким образом:

git filter-branch --msg-filter '
	sed -e "/^git-svn-id:/d"
'

Если вам нужно добавить строки