Вы можете (возможно) добиться того, чего хотите, но давайте начнем с этого:
как мне переписать эту строку через всю историю файла?
Рассмотрим этот философский вопрос: предположим, я говорю вам, что вместо 1993-2000 гг. = Президентство Клинтона, 2001-2008 гг. = Президентство Г.В.Буша, фактически это был 1993-2001 гг. = Клинтон, 2002-2008 гг. = Буш. Предположим далее, что я как-то загипнотизирую вас, чтобы поверить в это. Я действительно изменил историю, или вы просто используете неправильную историю? Что, если каким-то образом все тоже поверит в это?
Имея это в виду, учтите этот факт: никакой коммит Git никогда не может быть изменен , поскольку хеш-идентификатор коммита представляет собой криптографическую контрольную сумму содержимого этого коммита, которая включает файлы в снимке что фиксация держится вечно. Однако любой коммит Git можно скопировать в новый, несколько другой коммит с другим хеш-идентификатором.
Итак: что если мы скопируем каждый , фиксируем новый, немного другой, приводящий к новому, другому хранилищу, а затем каким-то образом убедим всех, что этот новый, другой хранилище является реальным хранилище и все они раньше использовали фальшивую историю?
Вот что делает git filter-branch
: он копирует каждый коммит 1 в новый, применяя фильтры, которые вы указали в первую очередь. Новый коммит имеет новый, отличный хеш-идентификатор, если что-либо в нем даже отличается от оригинала на один бит - и, конечно, в качестве копий ветки фильтра, если он внес изменение в предыдущий коммит, следующий коммит, который будет скопирован, должен измените родительский хэш-идентификатор, чтобы он использовал скопированный коммит вместо оригинала. Так что, как только где-нибудь произойдут какие-либо изменения, это будет распространяться на всю историю.
В результате ваш репозиторий теперь содержит два набора коммитов - две полные истории - и если вы изменили самую первую копию коммита, эти два набора коммитов не пересекаются. (Если вы оставили этот самый первый коммит один, два набора - две истории - объединяются в начале, а затем расходятся везде, где вы сделали свое первое изменение.) Фильтр-ветвь перезаписывает имена ветвей и, возможно, имена тегов (--tag-name-filter
), чтобы они ссылались на эту новую историю. Ваш репозиторий теперь верит в новую историю, а не в старую. (Имена refs/original/
запоминают исходную историю; после их удаления исходные коммиты становятся уязвимыми для сборщика мусора.)
Теперь вы должны убедить всех переключиться. Это большой флаг дня .
1 Точнее говоря, каждый достижимый коммит основывается на аргументах стиля git rev-list
, которые вы передаете git filter-branch
. Кроме того, некоторые фильтры специально пропускают некоторые коммиты, так что в скопированной истории зафиксировано меньше коммитов, чем в оригинале.
Фильтрующие механизмы
Самый простой фильтр - --tree-filter
. Этот фильтр работает, буквально извлекая каждый коммит во временный каталог, а затем выполняя любые команды, которые вы предоставляете, в этом временном каталоге. Чтобы вы могли использовать:
--tree-filter /tmp/edit-readme.sh
где /tmp/edit-readme.sh - ваша (исполняемая) программа, которая редактирует README.md
на месте. Обратите внимание, что мы используем здесь абсолютный путь (/tmp/edit-readme.sh
), поскольку Git находится в каком-то таинственном неизвестном временном каталоге, когда запускает фильтр дерева. (Даже если вы используете -d
, Git создает подкаталоги внутри любого аргумента, который вы использовали.)
Недостаток этого фильтра в том, что он чрезвычайно медленный. Извлечение, изменение и перестроение каждого коммита занимает много времени. Вы можете использовать опцию filter-branch -d
, чтобы использовать файловую систему в памяти, что значительно ускорит ее, но вам потребуется достаточно памяти для распаковки и восстановления каждого коммита.
Theбыстрый фильтр для этого --index-filter
, но его сложно использовать: фильтр-ветвь копирует каждый коммит только в индекс. 2 Ваша задача состоит в том, чтобы изменить индекс . Вы можете проверить индекс на наличие файла README.md
, и, если он существует, извлечь его, изменить его и поместить новый обратно в индекс. Git делает новые коммиты из того, что вы оставляете в индексе, после фильтра индекса.
Я оставляю вам право написать скрипт редактирования на основе индекса или дерева. Теперь у вас есть инструменты, чтобы переписать ваш репозиторий в новый, несовместимый репозиторий. Можете ли вы заставить всех остальных отказаться от своих существующих репозиториев в пользу этого нового, чтобы они поверили в фальшивую историю, - это совсем другой вопрос.