Git Filter-Repo-это универсальный инструмент для переписывания истории, который включает в себя возможности, которые я не нашел нигде. Он примерно попадает в то же пространство инструмента, что и GIT-фильтровая маршрута, но без капитуляции, вызывающей плохую производительность, с гораздо большими возможностями, и с дизайном, который масштабируется по удобству использования за пределами тривиального переписывания. GIT Filter-Repo теперь рекомендуется проектом GIT вместо GIT Filter-Tranch.
В то время как большинство пользователей, вероятно, просто будут использовать фильтр-репо в качестве простого инструмента командной строки (и, вероятно, использовать только несколько его флагов), в его основном фильтре-репо содержится библиотека для создания инструментов переписывания истории. Таким образом, пользователи со специализированными потребностями могут использовать его для быстрого создания совершенно новых инструментов переписывания истории.
фильтр-репо требует:
git-filter-repo
-это сценарий Python с одним файлом, который был сделан для установки для базового использования на многих системах тривиальных: просто поместите этот файл в свой путь.
См. Установка.md для вещей, не имеющих базового использования или особых случаев. Более сложные инструкции необходимы только в том случае, если применяется одно из следующих.
Для комплексной документации:
Если вы предпочитаете учиться на примерах:
Это было более подробно рассмотрено в статье Git Rev News о фильтре-репо, но некоторые основные моменты для основных конкурентов:
Фильтровая ветвь чрезвычайно не распространена (множественные порядки меньше, чем это должно быть) для нетривиальных репозиториев.
Фильтровая ветвь пронизана GotChas, которые могут молча разобраться в вашем переписывании или, по крайней мере, сорвать ваши усилия по очистке », предоставив вам что-то более проблематичное и грязное, чем вы начали.
Фильтрой очень обременительна для использования для любого переписывания, которая даже немного нетривиальна.
Проект GIT заявил, что вышеупомянутые проблемы с фильтрационной ветвью не могут быть обратно совместно исправлены; Они рекомендуют прекратить использование фильтрации
Второжденные поклонники фильтрации могут быть заинтересованы в фильтре, но и фильтре (он же фильтр), повторная резумция фильтрации на основе фильтра-репо, которая является более эффективной (хотя и не так быстро или безопасна, как фильтр. репо).
Доступен шпаргалка, показывающее, как преобразовать примеры команд из руководства фильтрации в команды фильтра-репо.
Отличный инструмент для своего времени, но, хотя он делает некоторые вещи простыми, он ограничен несколькими видами переписываний.
Его архитектура не поддается обработке большего количества типов переписываний.
Его архитектура представляет некоторые недостатки и ошибки даже для его предполагаемого использования.
Поклонники BFG могут быть заинтересованы в BFG-ISH, повторном переосмыслении BFG на основе фильтра-Repo, который включает в себя несколько новых функций и ошибок по сравнению с BFG.
Доступен шпаргалка, показывающее, как преобразовать примеры команд из руководства BFG Repo Cleaner в команды фильтра-репо.
Допустим, мы хотим извлечь кусок репозитория, с целью объединить только этот кусок в какой -то другой репо. Для извлечения мы хотим:
Делать это с помощью фильтра-репо так же просто, как и следующая команда:
git filter-repo --path src/ --to-subdirectory-filter my-module --tag-rename ' ' : ' my-module- '
(Одиночные цитаты не нужны, но сделайте его более ясным человеку, что мы заменяем пустую строку в качестве префикса с помощью my-module-
)
BFG Repo Cleaner не способен на переписывание такого рода; На самом деле, все три типа разыскиваемых изменений находятся за пределами его возможностей.
Фильтровая ветвь поставляется с кучей предостережений (подробнее об этом ниже) даже после того, как вы выясните необходимые вызывы:
git filter-branch
--tree-filter ' mkdir -p my-module &&
git ls-files
| grep -v ^src/
| xargs git rm -f -q &&
ls -d *
| grep -v my-module
| xargs -I files mv files my-module/ '
--tag-name-filter ' echo "my-module-$(cat)" '
--prune-empty -- --all
git clone file:// $( pwd ) newcopy
cd newcopy
git for-each-ref --format= " delete %(refname) " refs/tags/
| grep -v refs/tags/my-module-
| git update-ref --stdin
git gc --prune=now
Некоторые могут заметить, что приведенный выше вызов фильтрации будет очень медленным из-за использования-плюс; В качестве альтернативы вы можете использовать опцию-index filter ranch, изменяя вышеупомянутые команды на:
git filter-branch
--index-filter ' git ls-files
| grep -v ^src/
| xargs git rm -q --cached;
git ls-files -s
| sed "s%$(printf \t)%&my-module/%"
| git update-index --index-info;
git ls-files
| grep -v ^my-module/
| xargs git rm -q --cached '
--tag-name-filter ' echo "my-module-$(cat)" '
--prune-empty -- --all
git clone file:// $( pwd ) newcopy
cd newcopy
git for-each-ref --format= " delete %(refname) " refs/tags/
| grep -v refs/tags/my-module-
| git update-ref --stdin
git gc --prune=now
Однако для любой команды фильтрации есть куча предостережений. Во-первых, некоторые могут быть задаются вопросом, почему я перечисляю пять команд здесь для фильтрации. Несмотря на использование-все и имен-имени, а также «Испания фильтрации», утверждая, что клона достаточно, чтобы избавиться от старых объектов, дополнительные шаги для удаления других тегов и сделать еще один GC все еще обязаны, чтобы Очистите старые предметы и избегайте смешивания новой и старой истории, прежде чем толкать где -нибудь. Другие предостережения:
Можно как бы взломать это вместе с чем -то вроде:
git fast-export --no-data --reencode=yes --mark-tags --fake-missing-tagger
--signed-tags=strip --tag-of-filtered-object=rewrite --all
| grep -vP ' ^M [0-9]+ [0-9a-f]+ (?!src/) '
| grep -vP ' ^D (?!src/) '
| perl -pe ' s%^(M [0-9]+ [0-9a-f]+ )(.*)$%1my-module/2% '
| perl -pe ' s%^(D )(.*)$%1my-module/2% '
| perl -pe s%refs/tags/%refs/tags/my-module-%
| git -c core.ignorecase=false fast-import --date-format=raw-permissive
--force --quiet
git for-each-ref --format= " delete %(refname) " refs/tags/
| grep -v refs/tags/my-module-
| git update-ref --stdin
git reset --hard
git reflog expire --expire=now --all
git gc --prune=now
Но это связано с некоторыми неприятными предостережениями и ограничениями:
Ни один из существующих инструментов фильтрации репозитории не сделал то, что я хотел; Все они потерпели неудачу для моих нужд. Ни один инструмент не предоставил ни одного из первых восьми признаков ниже, чем я хотел, и ни один инструмент не предоставил больше двух из последних четырех признаков:
[Отчет о запуске] Предоставьте пользователю анализ их репо, чтобы помочь им начать с того, что обрезать или переименоваться, вместо того, чтобы ожидать, что они угадают или найдут другие инструменты, чтобы выяснить это. (Изучено, например, запустив первый раз с специальным флагом, таким как -анализ.)
[Сохранить против удаления] вместо того, чтобы просто предоставлять пользователям способ легко удалить выбранные пути, а также предоставляют пользователям флаги, чтобы сохранить только определенные пути. Конечно, пользователи могли бы облегчить это, указав для удаления всех путей, кроме тех, которые они хотят сохранить, но необходимость указать все пути, которые когда -либо существовали в любой версии репозитория, иногда может быть довольно болезненной. Для фильтрации, используя трубопроводы, такие как git ls-files | grep -v ... | xargs -r git rm
может быть разумным обходным путем, но может стать громоздким и не так просты для пользователей; Кроме того, эти команды часто специфичны для операционной системы (можете ли вы обнаружить гном в фрагменте, который я предоставил?).
[Переименование] Должно быть легко переименовать пути. Например, в дополнение к тому, что он позволяет рассматривать какой -то подкаталог в качестве корня репозитория, также предоставит пользователям варианты, чтобы корень репозитория просто стал подкаталерием. И в целом позволяйте легко переименовать файлы и каталоги. Предоставьте проверки здравомыслия, если переименование приводит к тому, что несколько файлов существуют на одном и том же пути. (И добавьте специальную обработку, чтобы, если коммит просто скопировал OldName-> NewName без модификации, то фильтровать OldName-> NewName не запускает проверку здравомыслия и не умрет на этом коммите.)
[Более интеллектуальная безопасность] Написание копий оригинальных ссылок в специальное пространство имен в репо не обеспечивает удобный для пользователя механизм восстановления. Многие будут изо всех сил пытаться восстановить, используя это. Почти все, кого я когда -либо видел, выполняют операцию фильтрации репозитория, сделали это со свежим клоном, потому что вытирать клон в случае ошибки - значительно более легкий механизм восстановления. Настоятельно поощряйте этот рабочий процесс, обнаружив и освобождаясь, если мы не в свежем клоне, если только пользователь не переопределяется с помощью.
[Auto Sharink] автоматически удалить старый Cruft и перепаковать репозиторий для пользователя после фильтрации (если не переоценка); Это упрощает вещи для пользователя, помогает избежать смешивания старой и новой истории вместе и избегает проблем, в которых многоэтапный процесс сокращения репо, документированного в Manpage, на самом деле не работает в некоторых случаях. (Я смотрю на тебя, фильтровая маршрута.)
[Чистое разделение] Избегайте запутывания пользователей (и предотвращайте случайное повторное нагрузку старых вещей) из-за смешивания старого репо и переписанного репо вместе. (Это, в частности, проблема с фильтрационной маршрукой при использовании опции «tag-name filter», а иногда и проблема только при фильтрации подмножества ветвей.)
[Универсальность] предоставьте пользователю возможность расширить инструмент или даже писать новые инструменты, которые используют существующие возможности, и обеспечивают эту расширяемость таким образом, чтобы (а) избегали необходимости разворачивать отдельные процессы (что разрушит производительность), (б) избегает, чтобы пользователь указывает, что ОС-зависимые команды оболочки (которые не позволят пользователям обмена командами друг с другом), (c) использует преимущества богатых структур данных (потому что хэши, DICTS, списки и массивы чрезвычайно сложны в оболочке) и ( D) предоставляет разумные возможности манипуляции с струнными манипуляциями (которых крайне не хватает в оболочке).
[Старые ссылки на коммит] Предоставьте пользователям возможность использовать старые идентификаторы коммита с новым репозиторием (в частности, посредством картирования от старого в новую хэши с ссылками/ заменой/ ссылками).
[Последовательность последовательности сообщений] Если сообщения о совершении коммита относятся к другим коммитам по идентификатору (например, «Это возвращает к коммитированию 01234567890abcdef», «в Commit 0013deadbeef9a ...»), эти сообщения о коммит должны быть переписаны, чтобы обратиться к новым идентификаторам коммита.
[Стать-пустые обрезки] Коммуты, которые становятся пустыми из-за фильтрации, должны быть обрезаны. Если родитель коммита обрезан, первый не сбитый предок должен стать новым родителем. Если не существует предка, не существует, и коммит не был слиянием, то это становится новым корневым коммитом. Если не существует предка, не существующего, и коммит был слиянием, то в слиянии будет один меньше родителей (и, следовательно, сделает его, скорее всего, станет неэнергетическим коммитом, который сам по себе был бы обрезан, если бы у него не было собственных изменений в файле) Полем Особая вещь, которую следует отметить, заключается в том, что мы обретены, которые становятся пустыми, а не коммиты, которые начинаются пустыми. Некоторые проекты преднамеренно создают пустые коммиты по причинам управления версиями или публикацией, и их не следует удалять. (В качестве особого случая коммиты, которые начались пустыми, но чей родитель был обрезан, также считаются «пустыми».)
[Станьте обрезкой Degenerate] Обрезка коммитов, которые становятся пустыми, может привести к изменению топологии, и есть много особых случаев. Обычно коммиты Merge не удаляются, поскольку они необходимы для сохранения топологии графика, но обрезка родителей и других предков может в конечном итоге привести к потере одного или нескольких родителей. Простой случай уже был отмечен выше: если коммит слияния теряет достаточно родителей, чтобы стать неэнергетическим коммитом, и у него нет изменений в файле, то его тоже можно обрезать. Commits Merge также может иметь топологию, которая становится вырожденной: это может в конечном итоге, чтобы Merge_base служил оба родителя (если все вмешательство от первоначального репо было родительский В таких случаях, если слияние не имеет собственных изменений в файле, то коммит слияния также может быть обрезан. Однако, как мы делаем с пустой обрезкой, мы не обрезаем слияние, которые начали дегенерировать (что указывает на то, что он мог быть преднамеренным, например, с-не слияет), но только слияние, которые становятся вырожденными и не имеют изменения файлов их собственные.
[Скорость] фильтрация должна быть достаточно быстрой
Смотрите рекомендации.
Ожидается, что участники сообщества фильтров-репо будут придерживаться тех же стандартов, что и для проекта GIT, поэтому применяется кодекс поведения GIT.
Работа над фильтром-репо и его предшественником также привели к многочисленным улучшениям в быстром экспорте и быстром импорте (а иногда и других командах) в Core GIT, основываясь на вещах, необходимого для фильтрации, необходимо выполнять свою работу: