Очистка истории бинарников add-in и дедупликация копий
Тяжёлые артефакты лежат в четырёх местах (см. src/addins/build.bat):
| Назначение | Путь |
|---|---|
| OneScript RU | src/ru/OInt/addins/*.zip |
| OneScript EN | src/en/OInt/addins/*.zip |
| Макет 1С RU | src/ru/BSL/OpenIntegrations/src/CommonTemplates/*/Template.addin |
| Макет 1С EN | src/en/BSL/OpenIntegrations/src/CommonTemplates/*/Template.addin |
Что уже делает Git
В одном коммите одинаковые байты хранятся одним blob (проверка: git ls-tree HEAD для всех четырёх путей одного компонента даёт один и тот же hash). Четыре копии раздувают рабочий каталог и checkout (~240 MB), но не умножают объект в pack для текущей ревизии.
История раздувается при каждой пересборке: старые версии zip/addin остаются в pack-файлах навсегда, пока не переписать историю.
Скрипты
| Файл | Назначение |
|---|---|
prune-addin-binaries-from-history.ps1 |
Windows: вырезать все версии zip/addin из истории, вернуть только актуальные файлы одним коммитом |
prune-addin-binaries-from-history.sh |
То же для Linux/macOS |
link-addin-duplicates.ps1 |
Опционально: заменить дубликаты на symlink (только RU как источник) |
Требования
- git-filter-repo ≥ 2.38 (
pip install git-filter-repo) - Чистое дерево (
git statusбез незакоммиченных изменений в затрагиваемых путях) - Согласование с командой: после переписывания истории нужен force push всех веток, все клонируют заново или делают fresh clone
Очистка истории (типовой сценарий)
cd R:\Repos\OpenIntegrations
.\service\git\prune-addin-binaries-from-history.ps1 -BackupDir D:\opi-addin-backup
# проверить diff, затем — см. раздел «После переписывания» ниже
Скрипт:
- Копирует все
*.zipиTemplate.addinв backup. - Запускает
git filter-repo --invert-pathsтолько по этим glob (лицензииTemplate.txtи.mdoне трогает). - Восстанавливает файлы из backup и создаёт коммит
chore: restore add-in binaries after history prune. - Запускает
git gc --prune=now.
git filter-repo удаляет remote origin — скрипт предлагает команду для повторного добавления.
После переписывания (обязательно)
git filter-repo переписывает все ветки и теги в локальном клоне. На remote попадает только то, что вы явно force-push'нули. Если отправить только main, старые теги и ветки (например stable) останутся на GitHub со старыми hash — в pack продолжит жить вся тяжёлая история (старые add-in, .deb/.rpm и т.д.). Обычный git clone по одной ветке будет казаться лёгким, а git clone --mirror — нет.
1. Force-push всех активных веток и тегов
cd R:\Repos\OpenIntegrations
# ветки (минимум main и stable; добавьте остальные активные)
git push --force-with-lease origin main
git push --force-with-lease origin stable
# все теги — без этого размер mirror/полного клона не уменьшится
git push --force origin '+refs/tags/*:refs/tags/*'
Проверка, что remote совпал с локальным (hash должны совпасть):
git rev-parse main stable 'refs/tags/2.1.0^{commit}'
git ls-remote origin refs/heads/main refs/heads/stable refs/tags/2.1.0
2. Обновить bare mirror (если используется)
После force-push на GitHub mirror нужно принудительно подтянуть refs и выбросить недостижимые объекты:
git -C R:\ointMirror\OpenIntegrations.git fetch --force --prune origin `
'+refs/heads/*:refs/heads/*' '+refs/tags/*:refs/tags/*'
git -C R:\ointMirror\OpenIntegrations.git gc --prune=now --aggressive
git -C R:\ointMirror\OpenIntegrations.git count-objects -vH
Для регулярного обновления mirror после любого переписывания истории:
git fetch --force --prune origin '+refs/heads/*:refs/heads/*' '+refs/tags/*:refs/tags/*'
git gc --prune=now
3. Убрать битые pack-файлы в локальном клоне (Windows)
После filter-repo иногда остаются .pack без пары .idx — git count-objects -vH показывает size-garbage с предупреждениями no corresponding .idx:
cd R:\Repos\OpenIntegrations
Get-ChildItem .git\objects\pack\pack-*.pack | Where-Object {
-not (Test-Path ($_.FullName -replace '\.pack$','.idx'))
} | Remove-Item
git gc --prune=now
git count-objects -vH
На Linux/macOS:
cd /path/to/OpenIntegrations
find .git/objects/pack -name 'pack-*.pack' ! -exec sh -c 'test -f "${1%.pack}.idx"' _ {} \; -print -delete
git gc --prune=now
git count-objects -vH
4. GitHub
- Размер на стороне GitHub уменьшится не сразу; иногда нужны тикеты в support или ожидание GC на стороне хостинга.
- Пока на remote старые теги,
git clone --mirrorбудет тянуть тяжёлый pack независимо от того, чтоmainуже переписан.
Symlink вместо четырёх копий (без LFS)
Идея: в Git хранить только src/ru/OInt/addins/*.zip, остальное — симлинки:
src/en/OInt/addins/OPI_MySQL.zip -> ../../ru/OInt/addins/OPI_MySQL.zip
src/ru/BSL/.../OPI_MySQL/Template.addin -> ../../../../../../ru/OInt/addins/OPI_MySQL.zip
Плюсы: один blob в коммите, меньше checkout на Linux/CI.
Минусы:
- Windows: нужны
git config core.symlinks trueи режим разработчика (или права на создание symlink); иначе вместо бинарника в рабочей копии окажется текстовый файл с путём. - EDT/1С: перед продакшен-сборкой проверьте, что загрузка макета
Template.addinи тесты 1С видят symlink (на Linux CI обычно ок).
Скрипт: link-addin-duplicates.ps1 (не входит в переписывание истории; запускать отдельно после согласования).
Альтернатива symlink: не хранить дубликаты в Git
Более надёжно для смешанной Windows/Linux команды:
- В
.gitignoreдобавитьsrc/en/OInt/addins/*.zipи**/CommonTemplates/*/Template.addin. - Оставить в репозитории только
src/ru/OInt/addins/*.zip. - В
build.batи CI перед тестами копировать zip в три остальных места (как уже делает сборка add-in).
Тогда в истории остаётся один путь на компонент; filter-repo проще. Минус: клон без шага CI/сборки не получит макеты 1С до копирования.
Почему не LFS
GitHub LFS считает bandwidth на download; при частых CI и клонах квота заканчивается быстрее, чем при обычном git pack, особенно если историю уже почистили.