Как удалить / удалить большой файл из истории коммитов в репозитории Git?

708

Иногда я бросал DVD-рип в проект веб-сайта, затем небрежно git commit -a -m ..., и, зап, репо было раздутым на 2,2 гига. В следующий раз я сделал некоторые изменения, удалил видеофайл и зафиксировал все, но сжатый файл все еще находится в хранилище, в истории.

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

culebrón
источник
9
Эта статья должна помочь вам help.github.com/removing-sensitive-data
MBO
1
Обратите внимание, что если ваш большой файл находится в подкаталоге, вам необходимо указать полный относительный путь.
Йохан
1
Также связанные help.github.com/en/articles/…
frederj
Многие ответы ниже рекламируют BFG так же легко, как git filter-branch, но я обнаружил, что все наоборот.
2540625

Ответы:

605

Используйте BFG Repo-Cleaner , более простую и быструю альтернативу, git-filter-branchспециально разработанную для удаления ненужных файлов из истории Git.

Тщательно следуйте инструкциям по использованию , основная часть просто так:

$ java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git

Любые файлы размером более 100 МБ (которых нет в вашем последнем коммите) будут удалены из истории вашего репозитория Git. Затем вы можете использовать git gcдля очистки мертвых данных:

$ git gc --prune=now --aggressive

BFG обычно по крайней мере в 10-50 раз быстрее, чем бег git-filter-branch, и, как правило, проще в использовании.

Полное раскрытие: я являюсь автором BFG Repo-Cleaner.

Роберто Тайли
источник
4
@tony Стоит повторить всю процедуру клонирования и очистки, чтобы увидеть, повторяется ли сообщение с просьбой о извлечении, но это почти наверняка, потому что ваш удаленный сервер настроен на отклонение обновлений без ускоренной перемотки (т. е. он настроен на остановку вас). от потери истории - это именно то, что вы хотите сделать). Вам нужно изменить этот параметр на пульте или, если это не удалось, отправить обновленную историю репо в совершенно новое пустое репо.
Роберто Тайли
1
@RobertoTyley Спасибо. Я пробовал это 3 раза, и все вылилось в одно и то же сообщение. Поэтому я также думаю, что вы правы в том, что удаленный сервер настроен на отклонение обновлений без ускоренной пересылки. Я подумаю просто подтолкнуть обновленное репо к новому репо. Спасибо!
Тони
7
@RobertoTyley Отлично, вы экономите мое время, большое спасибо. Кстати, возможно, следует поступить git push --forceпосле ваших шагов, в противном случае удаленное репо все равно не изменилось.
li2
3
+1 к добавлению git push --force. Также стоит отметить: принудительные нажатия могут быть запрещены удаленным пользователем (по умолчанию gitlab.com этого не делает. Пришлось «снимать защиту» с ветки).
MatrixManAtYrService
25
Я думаю, что жаргон Трампа о том, что инструмент выводит, немного.
Крис
564

То, что вы хотите сделать, очень разрушительно, если вы опубликовали историю другим разработчикам. См. «Восстановление из исходной версии» в git rebaseдокументации, чтобы узнать о необходимых шагах после восстановления истории.

У вас есть как минимум два варианта: git filter-branchи интерактивная перебазировка, оба объяснены ниже.

С помощью git filter-branch

У меня была похожая проблема с объемными данными бинарных тестов из импорта Subversion и я писал об удалении данных из репозитория git .

Скажите, что ваша история с мерзавцами:

$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A     login.html
* cb14efd Remove DVD-rip
| D     oops.iso
* ce36c98 Careless
| A     oops.iso
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

Обратите внимание, что git lolaэто нестандартный, но очень полезный псевдоним. С помощью --name-statusпереключателя мы можем видеть модификации дерева, связанные с каждым коммитом.

В коммите «Неосторожный» (имя объекта SHA1 - ce36c98) файл oops.isoпредставляет собой DVD-рип, случайно добавленный и удаленный при следующем коммите cb14efd. Используя технику, описанную в вышеупомянутом сообщении в блоге, команда для выполнения:

git filter-branch --prune-empty -d /dev/shm/scratch \
  --index-filter "git rm --cached -f --ignore-unmatch oops.iso" \
  --tag-name-filter cat -- --all

Опции:

  • --prune-emptyудаляет коммиты, которые становятся пустыми ( т.е. не меняют дерево) в результате операции фильтрации. В типичном случае эта опция производит более чистую историю.
  • -dназывает временный каталог, который еще не существует, чтобы использовать для построения отфильтрованной истории. Если вы работаете в современном дистрибутиве Linux, указание дерева /dev/shmприведет к более быстрому выполнению .
  • --index-filterявляется основным событием и работает с индексом на каждом шаге в истории. Вы хотите удалить, oops.isoгде бы он ни находился, но он присутствует не во всех коммитах. Команда git rm --cached -f --ignore-unmatch oops.isoудаляет DVD-рип, когда он присутствует, и не дает сбоя в противном случае.
  • --tag-name-filterописывает, как переписать имена тегов. Фильтр cat- это операция идентификации. Ваш репозиторий, как и в приведенном выше примере, может не содержать тегов, но я включил эту опцию для полной общности.
  • -- указывает конец опций git filter-branch
  • --allСледующее --является сокращением для всех ссылок. Ваш репозиторий, как и в приведенном выше примере, может иметь только один ref (master), но я включил эту опцию для полной общности.

После некоторого сбивания история теперь:

$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A     login.html
* e45ac59 Careless
| A     other.html
|
| * f772d66 (refs/original/refs/heads/master) Login page
| | A   login.html
| * cb14efd Remove DVD-rip
| | D   oops.iso
| * ce36c98 Careless
|/  A   oops.iso
|   A   other.html
|
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

Обратите внимание, что добавляется только новый коммит «Неосторожный» other.htmlи что коммит «Remove DVD-rip» больше не находится в основной ветке. В ветке с пометкой refs/original/refs/heads/masterсодержатся ваши исходные коммиты на случай, если вы допустили ошибку. Чтобы удалить его, следуйте инструкциям в «Контрольном списке для сокращения хранилища».

$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --prune=now

Для более простой альтернативы клонируйте репозиторий, чтобы отбросить ненужные биты.

$ cd ~/src
$ mv repo repo.old
$ git clone file:///home/user/src/repo.old repo

Использование file:///...клона URL копирует объекты, а не только создает жесткие ссылки.

Теперь ваша история:

$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A     login.html
* e45ac59 Careless
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

Имена объектов SHA1 для первых двух коммитов («Индекс» и «Страница администратора») остались прежними, поскольку операция фильтрации не изменила эти коммиты. «Careless» потерял oops.isoи «Логин страница» получили новый родитель, так что их SHA1s сделал изменения.

Интерактивная перебазировка

С историей:

$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A     login.html
* cb14efd Remove DVD-rip
| D     oops.iso
* ce36c98 Careless
| A     oops.iso
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

Вы хотите удалить oops.isoиз «Неосторожного», как если бы вы никогда не добавляли его, а затем «Удалить DVD-рип» для вас бесполезно. Таким образом, наш план перехода к интерактивной перебазировке - сохранить «Страницу администратора», отредактировать «Неосторожный» и отказаться от «Удалить DVD-рип».

Запуск $ git rebase -i 5af4522запускает редактор со следующим содержимым.

pick ce36c98 Careless
pick cb14efd Remove DVD-rip
pick f772d66 Login page

# Rebase 5af4522..f772d66 onto 5af4522
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

Выполняя наш план, мы изменяем его

edit ce36c98 Careless
pick f772d66 Login page

# Rebase 5af4522..f772d66 onto 5af4522
# ...

То есть мы удаляем строку с «Удалить DVD-рип» и меняем операцию на «Неосторожно», editа не на pick.

При выходе из редактора при сохранении мы получаем командную строку со следующим сообщением.

Stopped at ce36c98... Careless
You can amend the commit now, with

        git commit --amend

Once you are satisfied with your changes, run

        git rebase --continue

Как говорится в сообщении, мы находимся в «небрежном» коммите, который хотим редактировать, поэтому мы запускаем две команды.

$ git rm --cached oops.iso
$ git commit --amend -C HEAD
$ git rebase --continue

Первый удаляет поврежденный файл из индекса. Второй изменяет или изменяет «Careless», чтобы он был обновленным индексом, и -C HEADдает команду git повторно использовать старое сообщение коммита. Наконец, мы git rebase --continueпродолжаем с остальной частью операции rebase.

Это дает историю:

$ git lola --name-status
* 93174be (HEAD, master) Login page
| A     login.html
* a570198 Careless
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

что ты хочешь

Грег Бэкон
источник
4
Почему я не могу нажать при использовании git filter-branch, не удалось отправить некоторые ссылки на «git@bitbucket.org: product / myproject.git». Чтобы предотвратить потерю истории, обновления без ускоренной перемотки были отклонены. Объедините удаленный изменения, прежде чем нажать снова.
Агунг Празетьо
11
Добавьте опцию -f(или --force) к своей git pushкоманде: «Обычно команда отказывается обновлять удаленную ссылку, которая не является предком локальной ссылки, используемой для ее перезаписи. Этот флаг отключает проверку. Это может привести к потере коммитов в удаленном репозитории; используйте это с осторожностью. ”
Грег Бэкон
5
Это удивительно подробный ответ, объясняющий использование git-filter-branch для удаления нежелательных больших файлов из истории, но стоит отметить, что с тех пор, как Грег написал свой ответ, был выпущен репо-очиститель BFG, который зачастую быстрее и проще использовать - см. мой ответ для деталей.
Роберто Тайли
1
После того, как я выполню одну из вышеуказанных процедур, удаленный репозиторий (на GitHub) НЕ удалит большой файл. Только местный делает. Я заставляю толкать и нада. Что мне не хватает?
Азатар
1
это также работает на dirs. ... "git rm --cached -rf --ignore-unmatch path/to/dir"...
Рыноп
198

Почему бы не использовать эту простую, но мощную команду?

git filter-branch --tree-filter 'rm -f DVD-rip' HEAD

--tree-filterОпция запускает указанную команду после каждой проверки проекта , а затем вновь заявляют результаты. В этом случае вы удаляете файл с именем DVD-rip из каждого снимка, независимо от того, существует он или нет.

Если вы знаете, какой коммит ввел огромный файл (скажем, 35dsa2), вы можете заменить HEAD на 35dsa2..HEAD, чтобы избежать переписывания слишком большого количества истории, таким образом избегая расходящихся коммитов, если вы еще не нажали. Этот комментарий любезно @ alpha_989 кажется слишком важным, чтобы оставить его здесь.

Смотрите эту ссылку .

Гэри Гаух
источник
3
Это хорошее решение! Я создал gist, в котором есть скрипт на python для вывода
punkdata
5
Гораздо лучше, чем BFG. Мне не удалось почистить файл из git с помощью bfg, но эта команда помогла
podarok
4
Это замечательно. Просто отметьте для других, что вам придется делать это для каждой ветви, если большой файл находится в нескольких ветвях.
Джеймс
2
На Windows я получил fatal: bad revision 'rm', который я исправил, используя "вместо '. Общая команда:git filter-branch --force --index-filter "git rm --cached -r --ignore-unmatch oops.iso" --prune-empty --tag-name-filter cat -- --all
Marcotama
2
Если вы знаете, commitкуда вы положили файл (скажем 35dsa2), вы можете заменить HEADна 35dsa2..HEAD. tree-filterгораздо медленнее, чем index-filterэтот способ, он не будет пытаться извлекать все коммиты и переписывать их. если вы используете HEAD, он попытается это сделать.
alpha_989
86

(Лучший ответ, который я когда-либо видел на эту проблему: https://stackoverflow.com/a/42544963/714112 , скопирован здесь, поскольку эта тема занимает высокое место в поисковом рейтинге Google, а другая нет)

Fast Чертовски быстрый однострочный корпус 🚀

Этот сценарий оболочки отображает все объекты BLOB-объектов в хранилище, отсортированные от наименьшего к наибольшему.

Для моего примера репо он работал примерно в 100 раз быстрее, чем другие, найденные здесь.
В моей надежной системе Athlon II X4 она обрабатывает репозиторий ядра Linux с 5,622,155 объектами всего за минуту .

Базовый сценарий

git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '/^blob/ {print substr($0,6)}' \
| sort --numeric-sort --key=2 \
| cut --complement --characters=13-40 \
| numfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest

Когда вы запустите код выше, вы получите хороший читабельный вывод, подобный этому:

...
0d99bb931299  530KiB path/to/some-image.jpg
2ba44098e28f   12MiB path/to/hires-image.png
bd1741ddce0d   63MiB path/to/some-video-1080p.mp4

🚀 Быстрое удаление файлов 🚀

Предположим, что вы хотите удалить файлы, aи bиз каждой достижимой фиксации HEADвы можете использовать эту команду:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' HEAD
Шридхар Сарнобат
источник
3
Если в вашем репо есть какие-либо теги, вы, вероятно, также захотите добавить флаг, --tag-name-filter catчтобы повторно пометить новые соответствующие коммиты по мере их переписывания, т. git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' --tag-name-filter cat HEAD
Е.
3
Инструкции для Mac и некоторая другая информация появляются в исходном связанном посте
nruth
3
git filter-branch --index-filter 'git rm --cached --ignore-unmatch <filename>' HEADрабочий по праву от летучей мыши
eleijonmarck
мой любимый ответ Небольшая настройка для Mac OS (с помощью команд git rev-list --objects --all \ | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \ | awk '/^blob/ {print substr($0,6)}' \ | sort --numeric-sort --key=2 \ | gnumfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
Флориан Освальд
крутой сценарий с rev-list, но он не работает для меня как псевдоним, есть идеи как это сделать?
Робин Маноли
47

Попробовав практически каждый ответ в SO, я наконец нашел этот драгоценный камень, который быстро удалил и удалил большие файлы в моем хранилище и позволил мне снова синхронизироваться: http://www.zyxware.com/articles/4027/how-to-delete -файлы-постоянно-из-ваш-местного и ЩИТОК-GIT-репозитории

CD в ​​вашу локальную рабочую папку и выполните следующую команду:

git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch FOLDERNAME" -- --all

замените FOLDERNAME файлом или папкой, которые вы хотите удалить из данного репозитория git.

После этого выполните следующие команды, чтобы очистить локальный репозиторий:

rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

Теперь внесите все изменения в удаленный репозиторий:

git push --all --force

Это очистит удаленный репозиторий.

Джастин
источник
Работал как шарм для меня.
Рамон Васконселос
3
Это сработало и для меня. Получает избавление от определенной папки (в моем случае, содержащей слишком большие файлы или репозитория Github) в хранилище, но сохраняет ее в локальной файловой системе, если она существует.
скиццо
Работал на меня! никакая истории не осталась , который потенциально запутанная (если кто - то где клон прямо сейчас), убедитесь , что у вас есть план по обновлению неработающих ссылок, зависимости и т.д.
Ruoho руотси
38

Эти команды работали в моем случае:

git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

Это немного отличается от приведенных выше версий.

Для тех, кому нужно отправить это в github / bitbucket (я проверял это только с bitbucket):

# WARNING!!!
# this will rewrite completely your bitbucket refs
# will delete all branches that you didn't have in your local

git push --all --prune --force

# Once you pushed, all your teammates need to clone repository again
# git pull will not work
Kostanos
источник
4
Чем он отличается от вышеописанного, почему он лучше?
Энди Хейден
1
По какой-то причине версия mkljun не уменьшила пространство для git в моем случае, я уже удалил файлы из индекса с помощью git rm --cached files. Предложение Грега Бэкона является более полным и почти таким же, как у этого рудника, но он пропустил индекс --force для случаев, когда вы используете фильтр-ветвь несколько раз, и он написал так много информации, что моя версия похожа на резюме этого
Костанос
1
Это действительно помогло, но мне нужно было использовать -fопцию не -rfздесь, git rm --cached -rf --ignore-unmatch oops.isoа git rm --cached -r --ignore-unmatch oops.isoв соответствии с @ lfender6445 ниже
drstevok
10

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

$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force
mkljun
источник
11
НЕ запускайте эти команды, если вы не хотите причинить себе огромную боль. Он удалил много моих исходных файлов исходного кода. Я предполагал, что это удалит некоторые большие файлы из моей истории коммитов в GIT (согласно исходному вопросу), однако я думаю, что эта команда предназначена для постоянной очистки файлов от вашего исходного дерева исходного кода (большая разница!). Моя система: Windows, VS2012, Git Source Control Provider.
Контанго
2
Я использовал эту команду: git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --allвместо первой из вашего кода
Kostanos
9

git filter-branch --tree-filter 'rm -f path/to/file' HEAD работал очень хорошо для меня, хотя я столкнулся с той же проблемой, как описано здесь , которую я решил, следуя этому предложению .

Книга pro-git содержит целую главу о переписывании истории - взгляните на filter-branchраздел / Удаление файла из каждого коммита .

Торстен Лоренц
источник
8

Если вы знаете, что ваш коммит был последним, а не проходил через все дерево, сделайте следующее: git filter-branch --tree-filter 'rm LARGE_FILE.zip' HEAD~10..HEAD

Soheil
источник
7

Я столкнулся с этим с помощью учетной записи bitbucket, где случайно хранил огромные резервные копии * .jpa моего сайта.

git filter-branch --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch MY-BIG-DIRECTORY-OR-FILE' --tag-name-filter cat -- --all

Перепроверьте MY-BIG-DIRECTORYпапку, о которой идет речь, чтобы полностью переписать историю ( включая теги ).

источник: https://web.archive.org/web/20170727144429/http://naleid.com:80/blog/2012/01/17/finding-and-purging-big-files-from-git-history/

lfender6445
источник
1
Этот ответ помог мне, за исключением того, что у сценария в ответе есть небольшая проблема, и он не ищет во всех ответвлениях. Но команда в ссылке сделала это отлично.
Али Б
5

Это удалит его из вашей истории

git filter-branch --force --index-filter 'git rm -r --cached --ignore-unmatch bigfile.txt' --prune-empty --tag-name-filter cat -- --all
искриться
источник
Это сработало для меня, спасибо!
Соня Бритс
Это работает в моем случае. Я запускаю это в вашей главной ветке.
С. Доменг
4

Я в основном сделал то, что было на этот ответ: https://stackoverflow.com/a/11032521/1286423

(для истории, я скопирую и вставлю это здесь)

$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force

Это не сработало, потому что я очень люблю переименовывать и перемещать вещи. Таким образом, некоторые большие файлы были в папках, которые были переименованы, и я думаю, что gc не смог удалить ссылку на эти файлы из-за ссылки в treeобъектах, указывающих на этот файл. Мое окончательное решение действительно убить это было:

# First, apply what's in the answer linked in the front
# and before doing the gc --prune --aggressive, do:

# Go back at the origin of the repository
git checkout -b newinit <sha1 of first commit>
# Create a parallel initial commit
git commit --amend
# go back on the master branch that has big file
# still referenced in history, even though 
# we thought we removed them.
git checkout master
# rebase on the newinit created earlier. By reapply patches,
# it will really forget about the references to hidden big files.
git rebase newinit

# Do the previous part (checkout + rebase) for each branch
# still connected to the original initial commit, 
# so we remove all the references.

# Remove the .git/logs folder, also containing references
# to commits that could make git gc not remove them.
rm -rf .git/logs/

# Then you can do a garbage collection,
# and the hidden files really will get gc'ed
git gc --prune --aggressive

Мое .gitхранилище изменилось с 32 МБ до 388 КБ, что даже фильтр-ветвь не смог очистить.

Dolanor
источник
4

git filter-branchэто мощная команда, которую вы можете использовать для удаления огромного файла из истории коммитов. Файл останется на некоторое время, и Git удалит его в следующей сборке мусора. Ниже представлен полный процесс удаления файлов из истории коммитов . В целях безопасности нижеприведенный процесс сначала запускает команды для новой ветви. Если результат - то, что вам нужно, то верните его обратно в ветку, которую вы действительно хотите изменить.

# Do it in a new testing branch
$ git checkout -b test

# Remove file-name from every commit on the new branch
# --index-filter, rewrite index without checking out
# --cached, remove it from index but not include working tree
# --ignore-unmatch, ignore if files to be removed are absent in a commit
# HEAD, execute the specified command for each commit reached from HEAD by parent link
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch file-name' HEAD

# The output is OK, reset it to the prior branch master
$ git checkout master
$ git reset --soft test

# Remove test branch
$ git branch -d test

# Push it with force
$ git push --force origin master
zhangyu12
источник
2

Используйте Git Extensions , это инструмент пользовательского интерфейса. Он имеет плагин под названием «Найти большие файлы», который находит файлы lage в репозиториях и позволяет удалять их постоянно.

Не используйте 'git filter-branch' перед использованием этого инструмента, так как он не сможет найти файлы, удаленные с помощью 'filter-branch' (хотя 'filter-branch' не удаляет файлы полностью из файлов пакета репозитория) ,

Nir
источник
Этот метод слишком медленный для больших репозиториев. Перечисление больших файлов заняло более часа. Затем, когда я иду, чтобы удалить файлы, через час это только 1/3 пути до обработки первого файла, который я хочу удалить.
kristianp
Да, медленно, но работает ли ... Вы знаете что-нибудь быстрее?
Nir
1
Не использовал его, но BFG Repo-Cleaner, согласно другому ответу на этой странице.
kristianp
2

Вы можете сделать это с помощью branch filterкоманды:

git filter-branch --tree-filter 'rm -rf path/to/your/file' HEAD

Джон Фоли
источник
2

В этой теме есть очень хорошие ответы, но многие из них устарели. Использование git-filter-branchбольше не рекомендуется, потому что это трудно использовать и ужасно медленно работает с большими репозиториями.

git-filter-repo гораздо быстрее и проще в использовании.

git-filter-repoскрипт Python, доступный на github: https://github.com/newren/git-filter-repo .

Вам нужен только один файл: скрипт Python3 git-filter-repo. Скопируйте его в путь, который включен в переменную PATH. В Windows вам, возможно, придется изменить первую строку скрипта (см. INSTALL.md). Вам нужно установить Python3 в вашей системе, но это не имеет большого значения.

Сначала вы можете запустить

git filter-repo --analyze

Это поможет вам определить, что делать дальше.

Вы можете удалить свой DVD-рип файл везде:

 git filter-repo --invert-paths --path-match DVD-rip

Фильтр-репо действительно быстрый. Задача, которая занимала на моем компьютере около 9 часов с помощью ответвления фильтра, была выполнена за 4 минуты с помощью фильтра-репо. С помощью filter-repo вы можете делать еще много приятных вещей. Обратитесь к документации для этого.

Предупреждение. Сделайте это с копией вашего хранилища. Многие действия фильтра-репо не могут быть отменены. filter-repo изменит хеши коммитов всех измененных коммитов (конечно) и всех их потомков вплоть до последних коммитов!

Донат
источник
1

Когда вы git rmстолкнетесь с этой проблемой, этого будет недостаточно, так как git помнит, что файл когда-то существовал в нашей истории, и, таким образом, сохранит ссылку на него.

Что еще хуже, перебазировка также не легка, потому что любые ссылки на blob не позволят сборщику мусора git очистить пространство. Это включает в себя удаленные ссылки и ссылки reflog.

Я собрал git forget-blobнебольшой скрипт, который пытается удалить все эти ссылки, а затем использует git filter-branch для перезаписи каждого коммита в ветке.

Как только ваш блоб полностью не git gcбудет ссылаться, избавится от него

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

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

Я собрал это воедино благодаря ответам из Stack Overflow и некоторым записям в блоге. Кредиты им!

nachoparker
источник
Вы должны получить это на доморощенном
Камерон E
0

Кроме git filter-branch(медленного, но чистого git-решения) и BFG (более простого и очень производительного), есть еще один инструмент для фильтрации с хорошей производительностью:

https://github.com/xoofx/git-rocket-filter

Из его описания:

Назначение git-rocket-filter аналогично команде git-filter-branch, предоставляя следующие уникальные возможности:

  • Быстрое переписывание коммитов и деревьев (порядка х10 до х100).
  • Встроенная поддержка как белого списка с --keep (хранит файлы или каталоги), так и черного списка с опциями --remove.
  • Использование .gitignore как шаблон для фильтрации дерева
  • Быстрый и простой C # Scripting как для фиксации, так и для фильтрации дерева
  • Поддержка сценариев в древовидной фильтрации для файла / каталога
  • Автоматически удаляет пустой / неизмененный коммит, включая коммиты слияния
Philippe
источник