Как удалить / удалить большой файл из истории фиксации в репозитории 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 --aggressive
BFG является, как правило, не менее 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' HEAD
The
--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 -- --all
Relpace
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 КБ, что даже фильтр-ветка не могла очистить.