Перемещение файла в истории git - PullRequest
0 голосов
/ 25 мая 2018

В моей функциональной ветке у меня около 50 коммитов.В первом коммите я создал файл, который сильно модифицируется при последующих коммитах.Теперь я понимаю, что было бы лучше сохранить этот файл в другом каталоге, поэтому я хотел бы вернуться к первому коммиту и просто создать его в нужном месте, чтобы сохранить историю в чистоте.Я могу сделать это с помощью интерактивного перебазирования, отредактировав первый коммит и переместив файл, но затем все последующие коммиты, которые касаются этого файла, вызовут конфликты, которые мне придется разрешать вручную.Есть ли способ сообщить каждому коммиту, что файл был перемещен, чтобы они просто автоматически применяли свои изменения в нужном месте?

1 Ответ

0 голосов
/ 25 мая 2018

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, целью которой является копирование коммитов при применении какого-либо модификатора коммитов.У него много вариантов, потому что он очень медленный;но, по сути, он состоит из:

  • Перечислите каждый коммит (по хэш-идентификатору), который будет задействован.В вашем случае это просто «каждый коммит».Кроме того, создайте пустую карту со старым хеш-идентификатором → новый хеш-идентификатор.
  • Затем, начиная с самого коренного (самого старого / самого наследственного) коммита:

    1. Извлеките коммит во временную рабочую область.
    2. Примените каждый из различных фильтров.
    3. Создайте новый коммит из результата.Используйте хэш-карту для сопоставления родительских идентификаторов, чтобы новая фиксация указывала на ранее скопированную новую фиксацию.Это дает команде новый хэш-идентификатор нового коммита.
    4. Добавить запись на карту из хэша старого коммита → хэш нового коммита.
  • Наконец, после выполнениявыше для каждой фиксации, которую нужно отфильтровать, переберите все ссылки , которые вы говорите, чтобы изменить (в основном имена веток, но и имена тегов, если вы используете --tag-name-filter):

    1. Переименуйте исходную ссылку с refs/whatever на refs/original/refs/whatever.
    2. Создайте новый 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).

...