TL; DR
Использование git filter-branch
.Вы можете использовать --index-filter
для скорости, но это сложнее в использовании;используя всего 50 коммитов, используйте --tree-filter
, который намного медленнее, но гораздо проще в использовании:
git filter-branch --tree-filter <fill this in> --tag-name-filter cat -- --all
Обычно вы должны делать это на копии (клон) оригиналарепозиторий, так как легко обойти ветку фильтра и простой способ восстановить , из которого удалить копию и начать заново.
Как только все заработает, удалите все refs/original/
имена, как описано в git filter-branch
документации .В конечном итоге репозиторий будет раздуваться (ветвь фильтра временно удваивает его размер).
Long
История в Git - это (есть?) Коммиты.Чтобы изменить историю, вам нужно скопировать старые коммиты (которые предоставляют старую историю) в новые, разные коммиты (которые предоставляют новую историю).Таким образом, ваша цель состоит в том, чтобы заменить все 50-иные коммиты новыми, которые совпадают с , за исключением , когда файл перемещен по другому пути.
Как вы упоминали, вы можете сделать это с интерактивным перебазированием, но это неприятно: перебазирование работает путем преобразования каждого коммита к копии в набор изменений (путем сравнения этого коммита с его родителем, чтобы увидеть, что изменилось), а затем применения тех же изменений к некоторым существующимcommit.
Существует довольно тяжелая команда, git filter-branch
, целью которой является копирование коммитов при применении какого-либо модификатора коммитов.У него много вариантов, потому что он очень медленный;но, по сути, он состоит из:
- Перечислите каждый коммит (по хэш-идентификатору), который будет задействован.В вашем случае это просто «каждый коммит».Кроме того, создайте пустую карту со старым хеш-идентификатором → новый хеш-идентификатор.
Затем, начиная с самого коренного (самого старого / самого наследственного) коммита:
- Извлеките коммит во временную рабочую область.
- Примените каждый из различных фильтров.
- Создайте новый коммит из результата.Используйте хэш-карту для сопоставления родительских идентификаторов, чтобы новая фиксация указывала на ранее скопированную новую фиксацию.Это дает команде новый хэш-идентификатор нового коммита.
- Добавить запись на карту из хэша старого коммита → хэш нового коммита.
Наконец, после выполнениявыше для каждой фиксации, которую нужно отфильтровать, переберите все ссылки , которые вы говорите, чтобы изменить (в основном имена веток, но и имена тегов, если вы используете --tag-name-filter
):
- Переименуйте исходную ссылку с
refs/whatever
на refs/original/refs/whatever
. - Создайте новый
refs/whatever
, используя новый хэш, найденный на карте.
В конце этого процесса у вас есть все исходные коммиты (с refs/original
для их обозначения) плюс все новые коммиты (с использованием имен веток).
Если у вас есть только одно имя ветки(и без тегов), единственное имя, которое вам нужно указать, это имя одной ветви, вероятно, master
, но --all
скажет Git просмотреть все ссылки, а --tag-name-filter cat
скажет Git, что изменение, которое оно должно сделатьк именам тегов при их обновлении, в конце концов, ничего не нужно менять.
--tree-filter
указывает git filter-branch
, что на шаге 1 (извлечение фиксации) он должен выполнить полное и полное извлечение во временный каталог, который git filter-branch
создаст самостоятельно.(Другие параметры фильтра пытаются обойтись гораздо более быстрым трюком «извлечение только для временного индекса».) Команда или команды, которые вы вводите для tree-filter
, запускаются в этом временном каталоге, поэтому, если вам нужно только переименоватьфайла, достаточно команды:
mv old-relative-path new-relative-path
(при условии, что система Unix / Linux-ish).