Как удалить ненужные капли из моего репозитория git

124

У меня есть репозиторий GitHub с двумя ветками - master и release.

Ветвь выпуска содержала двоичные файлы дистрибутива, которые способствовали очень большому размеру репо (> 250 МБ), поэтому я решил все исправить.

Сначала я удалил ветку удаленного выпуска через git push origin :release

Затем я удалил ветку локального выпуска. Сначала я попробовал git branch -d release, но git сказал: «Ошибка: ветка release не является предком вашей текущей HEAD». что правда, поэтому я сделал, git branch -D releaseчтобы принудительно удалить его.

Но размер моего репозитория, как локально, так и на GitHub, по-прежнему был огромным. Итак, я просмотрел обычный список команд git, вроде git gc --prune=today --aggressive, безуспешно.

Следуя инструкциям Чарльза Бейли в SO 1029969, я смог получить список SHA1 для самых больших блобов. Затем я использовал сценарий из SO 460331, чтобы найти капли ... и пяти самых больших не существует, хотя обнаружены более мелкие капли, поэтому я знаю, что сценарий работает.

Я думаю, что эти блоги - это двоичные файлы из ветки выпуска, и они каким-то образом остались после удаления этой ветки. Как правильно от них избавиться?

kkrugler
источник
Какую версию Git вы используете? А вы пробовали stackoverflow.com/questions/1106529/… ?
VonC
git версии 1.6.2.3 Я пробовал gc и обрезал с различными аргументами. Я не пробовал repack -a -d -l, просто запустил, без изменений.
kkrugler
2
Новая информация - новый клон с GitHub больше не имеет лишних BLOB-объектов, и его размер уменьшился до "всего" 84 МБ с 250 МБ.
kkrugler

Ответы:

219

... и без лишних слов, могу я представить вам эту полезную команду "git-gc-all", которая гарантированно удалит весь ваш git-мусор, пока не появятся дополнительные переменные конфигурации:

git -c gc.reflogExpire=0 -c gc.reflogExpireUnreachable=0 -c gc.rerereresolved=0 -c gc.rerereunresolved=0 -c gc.pruneExpire=now gc

Вам также может потребоваться сначала запустить что-то подобное, о боже, git - это сложно !!

git remote rm origin
rm -rf .git/refs/original/ .git/refs/remotes/ .git/*_HEAD .git/logs/
git for-each-ref --format="%(refname)" refs/original/ | xargs -n1 --no-run-if-empty git update-ref -d

Вам также может потребоваться удалить некоторые теги, спасибо Zitrax:

git tag | xargs git tag -d

Я записал все это в скрипт: git-gc-all-ferocious .

Сэм Уоткинс
источник
1
Интересный. Хорошая альтернатива моему более общему ответу. +1
VonC
10
Это заслуживает большего количества голосов. Наконец, он избавился от множества объектов git, которые оставались бы другими методами. Спасибо!
Жан-Филипп Пелле,
1
Upvoted. Вау, я не знаю, что я только что сделал, но, похоже, это многое помогает. Можете подробнее рассказать, что он делает? У меня такое чувство, что все мои файлы objects. Что это такое и почему они (по-видимому) неактуальны?
Redsandro
2
@Redsandro, насколько я понимаю, эти команды «git rm origin», «rm» и «git update-ref -d» удаляют ссылки на старые коммиты для пультов и тому подобное, что может препятствовать сборке мусора. Параметры для "git gc" говорят ему не удерживать различные старые коммиты, иначе он будет удерживать их некоторое время. Например, gc.rerereresolved предназначен для «записей о конфликтующих слияниях, которые вы разрешили ранее», по умолчанию хранится в течение 60 дней. Эти параметры находятся на странице руководства git-gc. Я не эксперт по git и точно не знаю, что все эти вещи делают. Я нашел их из manpages и grepping .git для ссылок на коммиты.
Сэм Уоткинс,
1
Объект git - это сжатый файл или дерево или фиксация в вашем репозитории git, включая старые вещи из истории. git gc удаляет ненужные объекты. Он хранит объекты, которые все еще необходимы для вашего текущего репо, и его историю.
Сэм Уоткинс,
81

Как описано здесь , если вы хотите навсегда удалить все, на что ссылается только через reflog , просто используйте

git reflog expire --expire-unreachable=now --all
git gc --prune=now

git reflog expire --expire-unreachable=now --allудаляет все ссылки на недостижимые коммиты в reflog.

git gc --prune=now удаляет сами коммиты.

Внимание : только использование git gc --prune=nowне будет работать, поскольку эти коммиты все еще упоминаются в рефлоге. Следовательно, очистка рефлога обязательна. Также обратите внимание, что если вы rerereего используете, у него есть дополнительные ссылки, не очищенные этими командами. Подробнее git help rerereсм. Кроме того, любые коммиты, на которые ссылаются локальные или удаленные ветки или теги, не будут удалены, потому что они считаются ценными данными git.

jiasli
источник
14
Это сработало, но каким-то образом я потерял свои сохраненные тайники в процессе (ничего серьезного в моем случае, просто предупреждение для других)
Амро
1
почему бы и нет - агрессивный?
JoelFan
3
Я думаю, что для этого ответа необходимо четкое предупреждение, желательно вверху. Мое предложение редактирования было отклонено, потому что я думаю, мне следует предложить его автору в комментарии? Примите это изменение stackoverflow.com/review/suggested-edits/26023988 или добавьте предупреждение по-своему. Кроме того, это сбрасывает все ваши тайники . Это тоже следует отметить в предупреждении!
Иниго
Я тестировал с git версии 2.17, и спрятанные коммиты не будут удалены указанными выше командами. Вы уверены, что не выполняли никаких дополнительных команд?
Микко Ранталайнен
1
git fetch --pruneеще больше уменьшить размер за счет удаления локальных BLOB-объектов.
hectorpal
33

Как упоминалось в этом SO-ответе , git gcможет действительно увеличить размер репо!

Также эту ветку

Теперь у git есть механизм безопасности, чтобы не удалять объекты, на которые нет ссылок, сразу при запуске ' git gc'.
По умолчанию объекты, на которые нет ссылок, хранятся в течение 2 недель. Это сделано для того, чтобы упростить восстановление случайно удаленных ветвей или коммитов или избежать гонки, когда только что созданный объект, находящийся в процессе, но еще не упомянутый, может быть удален git gcпараллельным процессом.

Таким образом, чтобы дать этот льготный период упакованным, но не имеющим ссылки объектам, процесс переупаковки выталкивает эти объекты без ссылок из упаковки в их свободную форму, чтобы их можно было выдержать и в конечном итоге обрезать.
Однако объектов, на которые нет ссылок, обычно не так много. Наличие 404855 объектов, на которые нет ссылок, - это довольно много, и отправка этих объектов в первую очередь через клон - это глупо и полная трата пропускной способности сети.

В любом случае ... Чтобы решить вашу проблему, вам просто нужно запустить ' git gc' с --prune=nowаргументом, чтобы отключить этот льготный период и сразу же избавиться от тех объектов, на которые нет ссылок (безопасно, только если в то же время не выполняются другие действия git, которые должны быть легко обеспеченным на рабочей станции).

И BTW, используя ' git gc --aggressive' с более поздней версией git (или ' git repack -a -f -d --window=250 --depth=250')

В той же теме упоминается :

 git config pack.deltaCacheSize 1

Это ограничивает размер дельта-кеша одним байтом (фактически отключая его) вместо значения по умолчанию 0, что означает неограниченный. Благодаря этому я могу перепаковать этот репозиторий, используя указанную выше git repackкоманду в системе x86-64 с 4 ГБ ОЗУ и с использованием 4 потоков (это четырехъядерный процессор). Однако использование резидентной памяти увеличивается почти до 3,3 ГБ.

Если ваш компьютер SMP и у вас недостаточно оперативной памяти, вы можете уменьшить количество потоков до одного:

git config pack.threads 1

Кроме того, вы можете дополнительно ограничить использование памяти с помощью --window-memory argument" git repack".
Например, использование --window-memory=128Mдолжно сохранять разумную верхнюю границу использования памяти для дельта-поиска, хотя это может привести к менее оптимальному дельта-совпадению, если репо содержит много больших файлов.


Что касается ветки фильтров, вы можете (осторожно) рассмотреть этот сценарий

#!/bin/bash
set -o errexit

# Author: David Underhill
# Script to permanently delete files/folders from your git repository.  To use 
# it, cd to your repository's root and then run the script with a list of paths
# you want to delete, e.g., git-delete-history path1 path2

if [ $# -eq 0 ]; then
    exit 0
fi

# make sure we're at the root of git repo
if [ ! -d .git ]; then
    echo "Error: must run this script from the root of a git repository"
    exit 1
fi

# remove all paths passed as arguments from the history of the repo
files=$@
git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch $files" HEAD

# remove the temporary history git-filter-branch otherwise leaves behind for a long time
rm -rf .git/refs/original/ && git reflog expire --all &&  git gc --aggressive --prune
VonC
источник
stackoverflow.com/questions/359424/… также является хорошим началом filter-branchиспользования команд.
VonC
Привет, VonC - NI пробовал git gc prune = сейчас безуспешно. Это действительно похоже на ошибку git в том смысле, что после удаления ветки я оказался с не имеющими ссылки каплями локально, но их нет со свежим клоном репозитория GitHub ... так что это просто проблема локального репо. Но у меня есть дополнительные файлы, которые я хочу удалить, поэтому сценарий, на который вы ссылались выше, великолепен - спасибо!
kkrugler
19

git gc --prune=now, или низкий уровень git prune --expire now.

Якуб Наребски
источник
12

Каждый раз, когда ваша ГОЛОВА движется, git отслеживает это в файле reflog. Если вы удалили коммиты, у вас останутся «болтающиеся коммиты», потому что на них по-прежнему ссылается reflog~ 30 дней. Это подстраховка при случайном удалении коммитов.

Вы можете использовать git reflogкоманду удаления определенных коммитов, переупаковки и т. Д. Или просто команду высокого уровня:

git gc --prune=now
vdboor
источник
5

Вы можете использовать git forget-blob.

Использование довольно простое git forget-blob file-to-forget. Вы можете получить больше информации здесь

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

Он исчезнет из всех коммитов в вашей истории, рефлоге, тегах и т. Д.

Время от времени я сталкиваюсь с одной и той же проблемой, и каждый раз, когда мне приходится возвращаться к этому и другим постам, я автоматизировал этот процесс.

Благодарности таких участников, как Сэм Уоткинс

nachoparker
источник
2

Попробуйте использовать git-filter-branch - он не удаляет большие капли, но может удалить большие файлы, которые вы укажете, из всего репо. Для меня это уменьшает размер репо с сотен МБ до 12 МБ.

W55tKQbuRu28Q4xv
источник
6
Теперь , что это страшная команда :) Я должен дать ему попробовать , когда мой ГИТ-фу чувствует себя сильнее.
kkrugler
ты можешь сказать это снова. Я всегда с осторожностью отношусь к любым командам, которые управляют историей репозитория. Дела идут очень плохо, когда несколько человек нажимают и извлекают из этого репозитория, и внезапно куча объектов, которых ожидает git, не существует.
Джонатан Дюмен
1

Иногда причина того, что «gc» не приносит особой пользы, заключается в том, что существует незаконченная перебазировка или тайник, основанный на старой фиксации.

StellarVortex
источник
Или на старый коммит ссылаются HEAD, ORIG_HEAD, FETCH_HEAD, reflog или что-то еще, что git автоматически поддерживает, пытаясь убедиться, что он никогда не потеряет ничего ценного. Если вы действительно хотите потерять все это, вам придется приложить все усилия, чтобы сделать это.
Микко Ранталайнен
1

Чтобы добавить еще один совет, не забудьте использовать git remote prune для удаления устаревших веток ваших пультов перед использованием git gc.

вы можете увидеть их с помощью git branch -a

Это часто бывает полезно, когда вы получаете данные из github и разветвленных репозиториев ...

Танги
источник
1

Прежде чем делать git filter-branchи git gc, вам следует просмотреть теги, которые присутствуют в вашем репо. Любая реальная система, которая имеет автоматические теги для таких вещей, как непрерывная интеграция и развертывание, будет делать нежелательные объекты, на которые ссылаются эти теги, поэтому gcне может их удалить, и вы все равно будете задаваться вопросом, почему размер репо по-прежнему так велик.

Лучший способ избавиться от всего нежелательного - запустить git-filter&, git gcа затем переместить мастер в новое голое репо. Новое чистое репо будет иметь очищенное дерево.

v_abhi_v
источник