Как удалить / удалить большой файл из истории фиксации в репозитории Git?
иногда я бросал DVD-rip в проект веб-сайта, а затем небрежно git commit -a -m ..., и, zap, РЕПО было раздуто на 2,2 гига. В следующий раз я внес некоторые изменения, удалил видеофайл и зафиксировал все, но сжатый файл все еще находится в репозитории, в истории.
Я знаю, что могу запускать ветви из этих коммитов и перебазировать одну ветвь на другую. Но что я должен сделать, чтобы объединить вместе 2 фиксации, чтобы большой файл не отображался в истории и был очищается в процедуре сбора мусора?
14 ответов:
использовать BFG Repo-Cleaner, более простая и быстрая альтернатива
git-filter-branchспециально разработан для удаления нежелательных файлов из истории Git.внимательно следуйте инструкция по применению, основная часть заключается именно в этом:
$ java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.gitлюбые файлы размером более 100 МБ (которые не находятся в вашем последний commit) будет удален из истории вашего репозитория Git. Затем вы можете использовать
git gcчтобы убрать мертвых данные:$ git gc --prune=now --aggressiveBFG является, как правило, не менее 10-50x быстрее, чем работает
git-filter-branch, и вообще более легкий для использования.полное раскрытие информации: я автор BFG Repo-Cleaner.
то, что вы хотите сделать, очень разрушительно, если вы опубликовали историю другим разработчикам. Смотрите "восстановление из апстрима перебазировать" в
git rebaseдокументация для необходимых шагов после восстановления вашей истории.у вас есть как минимум два варианта:
git filter-branchи интерактивный перебазирования, как описано ниже.используя
git filter-branchу меня была аналогичная проблема с громоздкими двоичными тестовыми данными из импорта Subversion и написал о удаление данных из репозитория Git.
скажите, что ваша история 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-rip, когда он присутствует, и не терпит неудачу в противном случае.--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и что фиксация "удалить 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:///...clone 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 для первых двух коммитов ("Index "и" Admin page") остались прежними, поскольку операция фильтрации не изменила эти коммиты. "Неосторожный" потерял
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-rip" бесполезно для вас. Таким образом, наш план идет в интерактивный rebase является сохранить страницу "admin," редактировать "нерадивых", и отбросить "удалить 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 # ...то есть мы удаляем строку с "Remove DVD-rip "и меняем операцию на" Careless " на
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первый удаляет оскорбительный файл из индекса. Второй изменяет или исправляет "неосторожный", чтобы быть обновленным индексом и
-C HEADпоручает git повторно использовать старое сообщение фиксации. Наконец,git rebase --continueидет вперед с остальной частью операция перебазирования.это дает история:
$ 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это то, что вы хотите.
почему бы не использовать эту простую, но мощную команду?
git filter-branch --tree-filter 'rm -f DVD-rip' HEADThe
--tree-filterопция запускает указанную команду после каждой проверки проекта, а затем возобновляет результаты. В этом случае вы удаляете файл с именем DVD-rip из каждого снимка, независимо от того, существует он или нет.посмотреть этой ссылке.
эти команды работали в моем случае:
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
(лучший ответ, который я видел на эту проблему:https://stackoverflow.com/a/42544963/714112, скопировано здесь, так как эта тема появляется высоко в рейтинге поиска Google, но это не так)
невероятно быстрая оболочка с одним вкладышем
этот сценарий оболочки отображает все объекты 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(,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
попробовав практически каждый ответ в SO, я, наконец, нашел этот драгоценный камень, который быстро удалил и удалил большие файлы в моем репозитории и позволил мне снова синхронизировать: http://www.zyxware.com/articles/4027/how-to-delete-files-permanently-from-your-local-and-remote-git-repositories
CD в локальную рабочую папку и выполните следующую команду:
git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch FOLDERNAME" -- --allзамените имя папки на файл или папку, которые вы хотите удалить из данного 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это очистит удаленный репозиторий.
git filter-branch --tree-filter 'rm -f path/to/file' HEADработал довольно хорошо для меня, хотя я столкнулся с той же проблемой, как описано здесь, который я решил, следуя предложение.в книге pro-git есть целая глава о переписывание истории - взгляните на
filter-branch/ удаление файла из каждой фиксации.
Сразу отметим, что эти команды могут быть очень разрушительными. Если больше людей работают над РЕПО, им всем придется тянуть новое дерево. Три средние команды не нужны, если ваша цель не уменьшить размер. Потому что ветка фильтра создает резервную копию удаленного файла, и он может оставаться там в течение длительного времени.
$ 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
Если вы знаете, что ваша фиксация была недавней, вместо того, чтобы проходить через все дерево, выполните следующие действия:
git filter-branch --tree-filter 'rm LARGE_FILE.zip' HEAD~10..HEAD
я столкнулся с этим с учетной записью bitbucket, где я случайно сохранил ginormous *.резервное копирование СПД моего сайта.
git filter-branch --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch MY-BIG-DIRECTORY-OR-FILE' --tag-name-filter cat -- --allRelpace
MY-BIG-DIRECTORYс соответствующей папкой, чтобы полностью переписать историю (включая теги).источник: http://naleid.com/blog/2012/01/17/finding-and-purging-big-files-from-git-history
использовать Git Extensions, это инструмент пользовательского интерфейса. Он имеет плагин под названием "Найти большие файлы", который находит файлы lage в репозиториях и позволяет удалять их постоянно.
Не используйте 'git filter-branch' перед использованием этого инструмента, так как он не сможет найти файлы, удаленные 'filter-branch' (Altough 'filter-branch' не удаляет файлы полностью из файлов пакета репозитория).
когда вы столкнетесь с этой проблемой,
git rmне хватит, так как git помнит, что файл существовал когда-то в нашей истории, и таким образом сохранит ссылку на него.чтобы сделать вещи хуже, перебазирование тоже нелегко, потому что любые ссылки на большой двоичный объект не позволят сборщику мусора git очистить пространство. Это включает в себя удаленные ссылки и reflog ссылки.
Я собрал
git forget-blob, небольшой скрипт, который пытается удалить все эти ссылки, а затем использует git filter-branch для перезаписи каждой фиксации в ветке.как только ваш blob полностью не задействован,
git gcизбавится от нееиспользование довольно просто
git forget-blob file-to-forget. Вы можете получить дополнительную информацию здесьЯ собрал это вместе благодаря ответам от переполнения стека и некоторых записей в блоге. Кредиты им!
Я в основном сделал то, что было на этот ответ: 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мое РЕПО (the
.git) изменился с 32 МБ до 388 КБ, что даже фильтр-ветка не могла очистить.