Удалить большой файл из git
Я проверил загрузку файлов в ветке и объединил их, а затем пришлось удалить их, и теперь у меня остался большой файл .pack, от которого я не знаю, как избавиться.
Я удалил все файлы с помощью git rm -rf xxxxxx и также запустил эту --cached опцию.
Может ли кто-нибудь сказать мне, как я могу удалить большой файл .pack, который в настоящее время находится в следующем каталоге:
Мне просто нужно удалить ветку, которая у меня еще есть, но больше не используется? Или мне нужно что-то еще запустить?
Я не уверен, насколько это важно, но он показывает замок напротив файла.
Вот несколько отрывков из моей bash_history, которые должны дать представление о том, как мне удалось попасть в это состояние (предположим, что на данный момент я работаю над веткой git под названием 'my-branch', и у меня есть папка, содержащая больше папок / файлы):
Я думал, что также выполнил следующее, но оно не отображается в bash_history с другими:
Я также подумал, что запустил несколько команд git (например git gc ), чтобы попытаться привести в порядок файл пакета, но они также не отображаются в файле .bash_history.
Можете уточнить, как вы их удалили? Если они все еще находятся в истории коммитов, значит, они все еще находятся в ваших файлах пакета.
Проблема в том, что даже если вы удалили файлы, они все еще присутствуют в предыдущих версиях. В этом весь смысл git: даже если вы что-то удалите, вы все равно можете вернуть это, обратившись к истории.
То, что вы хотите сделать, называется перезаписью истории, и в ней задействована git filter-branch команда.
Чтобы ответить на ваш вопрос более прямо, вам в основном нужно запустить эту команду с unwanted_filename_or_folder соответствующей заменой:
Это удалит все ссылки на файлы из активной истории репо.
Следующий шаг, чтобы выполнить цикл GC, чтобы все ссылки на файл истекли и были удалены из файла упаковки. В этих командах ничего заменять не нужно.
Я пометил его как принятый, если это облегчит любому, кто придет к этому вопросу в будущем, хотя я фактически решил свою проблему в то время, создав свежий
Этот ответ указал мне правильное направление. Но для фактического удаления файлов необходимы еще 3 команды 1) git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin 2) git reflog expire --expire=now --all 3) git gc --prune=now
Сценарий А. Если ваши большие файлы были добавлены только в ветку, запускать не нужно git filter-branch . Вам просто нужно удалить ветку и запустить сборку мусора:
Сценарий B : Однако, судя по вашей истории bash, вы действительно слили изменения в master. Если вы никому не поделились изменениями ( git push пока нет ). Проще всего было бы сбросить мастер обратно до слияния с веткой, в которой были большие файлы. Это устранит все коммиты из вашей ветки и все коммиты, сделанные для мастера после слияния. Таким образом, вы можете потерять изменения - в дополнение к большим файлам - которые вы действительно хотели:
Затем выполните шаги из сценария A.
Сценарий C : если после слияния были другие изменения из ветки или изменения на главном сервере, которые вы хотите сохранить, было бы лучше перебазировать мастер и выборочно включить те коммиты, которые вы хотите:
В своем редакторе удалите строки, соответствующие коммитам, добавившим большие файлы, но оставьте все остальное как есть. Сохранить и выйти. Ваша основная ветка должна содержать только то, что вы хотите, и никаких больших файлов. Обратите внимание, что git rebase без -p исключения коммиты слияния будут исключены, поэтому после этого у вас останется линейная история для мастера . Возможно, это нормально для вас, но если нет, вы можете попробовать -p , но git help rebase говорит combining -p with the -i option explicitly is generally not a good idea unless you know what you are doing .
050d105 HEAD@: reset: moving to HEAD
050d105 HEAD@: Branch: renamed refs/heads/master-2 to refs/heads/master
050d105 HEAD@: reset: moving to HEAD
050d105 HEAD@: Branch: renamed refs/heads/master to refs/heads/master-2
050d105 HEAD@: Branch: renamed refs/heads/master-2 to refs/heads/master
050d105 HEAD@: Branch: renamed refs/heads/master to refs/heads/master-2
050d105 HEAD@: Branch: renamed refs/heads/master to refs/heads/master
050d105 HEAD@: reset: moving to HEAD~1
fcf55bd HEAD@: commit: add view/options
050d105 HEAD@: Branch: renamed refs/heads/master to refs/heads/master
HEAD@ файлы нужны что здесь лежат. пропали после команды git reset --hard HEAD~1
Отвечу чуть подробнее чем спрашиваете:
Конкретный файл (www/video/route.mp4):
Файл по маске (*.mp4):
Данный способ не оптимальный, но универсальный - проверит всю историю коммитов и выпилит из коммитов данные файлы/каталоги и перепишет все что дальше
Пуш форсом отправляем все ветки в репозиторий
git push origin --force --all
не забываем сообщить другим разрабам что надо сделать git reset --hard origin/.
Warning: опробуйте сначала на тестовом репозитории (склонируйте текущий куда нибудь и потренируйтесь). Не хочу чувствовать себя виноватым, если вы выпилите что-то исторически-ценное
Cannot rewrite branches: You have unstaged changes.
> Cannot rewrite branches: You have unstaged changes.
Невозможно переписать ветку: У вас есть не зафиксированные изменения
050d105 HEAD@: reset: moving to HEAD~1
fcf55bd HEAD@: commit: add view/options
050d105 HEAD@: Branch: renamed refs/heads/master to refs/heads/master
HEAD@ файлы нужны что здесь лежат. пропали после команды git reset --hard HEAD~1
Денис Юрьев,
я знаю что не советовали мне это вводить!
Я вчера ввел потому что попал лишний файл в коммит и файлы пропали как вернуть с коммита
файлы плиззззз помогите! Ваш совет помог спасибо большое, но не загрузился этот коммит, а файл удалился!
Посмотрите лог гита
git log -g
Найдите свой коммит, который потеряли
Возьмите хэш этого коммита (строка commit многобуквицифр - эти буквы и цифры и будут вашим хэшем) и введите команду, заменив hash-commit на нужный
git branch recover-branch hash-commit
так вы получите ветку recover-branch с коммитом, который пролюбили
Удаление файла passwords.txt изо всех коммитов
Подробнее в пункте "Удаление файла из каждого коммита"
Учтите что вы меняете историю и git push вам не разрешат сделать, надо будет делать git push -f. Остальные в команде должны будут сделать
Минимизация ущерба
Итак, вы случайно закоммитили файл с конфиденциальной информацией. Назовём этот файл .env . Сразу после того, как это случилось, надо задать себе пару вопросов:
- Отправлен ли коммит в удалённый репозиторий?
- Является ли удалённый репозиторий общедоступным?
▍Коммит пока не отправлен в удалённый репозиторий
Файлы останутся в рабочей копии репозитория, вы сможете внести в проект необходимые изменения.
Если же вы хотите сохранить коммит и вам нужно просто удалить из него определённые файлы, тогда поступите так:
Параметр --amend можно использовать только для работы с самым свежим коммитом. Если вы, после неудачного коммита, добавили ещё несколько, воспользуйтесь такой командой:
▍Коммит отправлен в удалённый репозиторий
Если вы уже отправили коммит в удалённый репозиторий, то, в первую очередь, вам нужно знать о том, чем отличаются публичные и приватные репозитории.
Если ваш репозиторий является приватным, и при этом он не доступен ботам или людям, которым вы не доверяете, вы можете просто внести поправки в последний коммит, воспользовавшись парой вышеприведённых команд.
Если вы отправили в репозиторий, после проблемного коммита, и другие коммиты, это не помешает вам убрать файлы с конфиденциальными данными из истории Git, воспользовавшись командой git filter-branch или инструментом BFG Repo-Cleaner.
Вот пример использования git filter-branch :
Но, делая это, учитывайте два важных аспекта подобных изменений, вносимых в репозиторий:
- Вы меняете историю Git. Если на текущее состояние репозитория полагаются другие люди, если от этого состояния зависят какие-то ветки того же репозитория, его форки, открытые PR, то это нарушит их работу. В подобных случаях относитесь к репозиторию как к общедоступному и постарайтесь не вносить изменения в его историю.
- Вам нужно будет очистить кеш. Вам понадобится обратиться в службу поддержки платформы, на которой хранится ваш репозиторий, и попросить очистить его кеш. Несмотря на то, что вы исправили проблемный коммит или переписали историю репозитория, старый коммит, содержащий конфиденциальные данные, останется в кеше. Для того чтобы к нему обратиться, нужно будет знать его ID, но к нему можно будет получить доступ до тех пор, пока кеш не очистят.
Нужно ли создавать новые секретные ключи в том случае, если их актуальные версии попали в публичный репозиторий?
Если кратко ответить на вопрос, вынесенный в заголовок, то — нужно. Если ваш репозиторий общедоступен, или если вы, по любой причине, полагаете, что он — не место для хранения секретных данных, вам необходимо будет счесть попавшие в него конфиденциальные данные скомпрометированными.
Даже если вы удалили эти данные из репозитория, вы ничего не сможете сделать с ботами и с форками репозитория. Как же поступить?
- Деактивируйте все ключи или пароли. Это надо сделать в первую очередь. После того, как вы деактивируете ключи, конфиденциальные сведения, ушедшие в общий доступ, оказываются бесполезными.
- Настройте файл .gitignore . Сделайте в .gitignore записи о файлах с конфиденциальной информацией для того чтобы Git не отслеживал бы состояние этих файлов.
- Подготовьте коммит, в котором нет файлов с конфиденциальной информацией.
- Отправьте изменения в репозиторий, снабдите коммит пояснениями о возникшей ситуации. Не пытайтесь скрыть ошибку. Все программисты, работающие над проектом, включая вас, по достоинству оценят наличие в репозитории коммита с разъяснениями ситуации и с описанием того, что именно было исправлено с помощью данного коммита.
Рекомендации по хранению конфиденциальных файлов в проектах, в которых для контроля версий применяется Git
Для того чтобы не допустить утечек конфиденциальной информации стоит придерживаться следующих рекомендаций.
▍Храните секретные данные в файле .env (или в другом подобном файле)
Ключи к API и другие подобные сведения стоит хранить в единственном файле .env . При таком подходе, если Git не отслеживает состояние файла .env , вы, добавив в этот файл новый ключ, не отправите его случайно в репозиторий.
Ещё одно преимущество такого подхода заключается в том, что так у вас будет доступ ко всем ключам через глобальную переменную process .
▍Используйте, если это возможно, ключи API
Скомпрометированные ключи API легко деактивировать, такие ключи легко создать заново. Если это возможно — используйте именно их, а не нечто вроде логинов и паролей.
▍Храните ключи API, пользуясь средствами вашего инструмента для сборки проектов
Ключи API обычно нужны при сборке приложений. Инструменты для сборки проектов, вроде Netlify, позволяют держать ключи в защищённых хранилищах. Такие ключи автоматически внедряются в приложение с использованием глобальной переменной process .
Управление переменными окружения
▍Добавьте запись о файле .env в файл .gitignore
Сделайте так, чтобы Git не отслеживал бы файлы, содержащие конфиденциальную информацию.
▍Подготовьте шаблонный файл .env.template
Наличие подобного шаблонного файла помогает тем, кто работает над проектом, добавлять в проект ключи API, избавляя их от необходимости чтения документации.
▍Не меняйте историю Git в удалённых репозиториях
Постарайтесь строго придерживаться этого правила. Если вы следовали вышеприведённым рекомендациям, то историю Git вам менять и не потребуется.
Итоги
Надеюсь, мой материал поможет вам в безопасной работе с конфиденциальными данными.
А вам случалось отправлять в общедоступный репозиторий что-то такое, что туда попадать не должно?
1.В начале забыл добавить в .gitignore папку target/ в которой хранятся скомпилированные *.jar *.class и прочие файлы. 2.позже через несколько коммитов обнаружил что папка .git весит 12+ мегабайт, по размеру предполагаю что это забытый мной jar файл (кода у меня ~15 килобайт, все .class файлы ~14 килобайт) 3.нашел этот файл руками он весит 12 МБ и лежит в папке
Прошел несколько вопросов на русском и английском stackoverflow + google пробовал следующее:
- git filter-branch --index-filter 'git rm --cached --ignore-unmatch FILENAME' --prune-empty -- --all
- git filter-branch --index-filter 'git rm --cached --ignore-unmatch FILENAME' HEAD
- git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch FILENAME' --prune-empty --tag-name-filter cat -- --all
Вместо FILENAME пробовал подставлять вот это:
- target/gs-rest-service-0.1.0.jar
- gs-rest-service-0.1.0.jar
- target/
- *.jar
В итоге у меня 2 вопроса: -Как выяснить что это весит 12МБ в папке .git и как это удалить?
вот ссылка на мо проект на github, можно скачать как zip-архив или клонировать:
(да я пытаюсь написать Rest-бэкэнд на Spring Boot'e для крестиков-ноликов)
Скажите, а вам очень дороги эти несколько коммитов с момента, когда вы файл закоммитили? Так-то можно просто откатиться ( rebase -i или reset --soft ) и заменить их все новым коммитом.
@NickVolynkin дело в том что я хочу научиться решать такую проблему. на работу устраиваюсь juniur-ом. хочу разобраться в вопросе (коммиты сносить я уже научился)
На самом деле, 12 метров в репозитории - это очень немного. Говорю вам как видевший двухгигабайтный репозиторий.
@PavelMayorov раз уж настало время страшных историй: репозиторий thirdparty, в нем сорцы всех библиотек на c++, используемых хотя бы в одном проекте в большой айтишной компании. Штук 50 их там было, места тоже гига полтора, но из-за количества файлов git clone творил непотребство с disk io.
1 ответ 1
вы можете посмотреть содержимое каталога target (во всех коммитах):
или информацию о конкретном файле target/gs-rest-service-0.1.0.jar :
удалить файл во всех коммитах можно, например, так:
p.s. если репозиторий был склонирован ещё куда-то/кем-то, то там/тому надо будет принудительно переключиться на отправленную вами переписанную историю:
или просто заново склонировать репозиторий в пустой каталог.
после того, как вы перезаписали историю, объект типа blob, содержащий удалённый файл, стал «осиротевшим». чтобы удалить его, надо воспользоваться командой gc (gargabe collection):
как показывает вывод команды $ du -sb .git (выполненной до и после $ git gc ), занимаемый каталогом .git объём изменился на 242997 байт (13254649-13011652). вероятно, именно столько занимал удалённый файл (в сжатом виде).
после того, как вы перезаписали свою локальную историю, удалённый файл (пока) не стал «осиротевшим» — на него есть ссылки из (пока не переписанной) истории подключенного репозитория (который на github-е находится). после того, как вы перепишете историю и на github-е, команда
должна будет удалить объект типа blob, содержащий этот «осиротевший» файл.
дополнение
полную очистку и упаковку всех объектов, чтобы добиться минимального размера каталога .git , можно выполнить примерно так:
ваш репозиторий (т.е., содержимое каталога .git ), после удаления того большого файла, и полной переупаковки, стал занимать у меня 187165 байт.
I checked a load of files in to a branch and merged and then had to remove them and now I'm left with a large .pack file that I don't know how to get rid of.
I deleted all the files using git rm -rf xxxxxx and I also ran the --cached option as well.
Can someone tell me how I can remove a large .pack file that is currently in the following directory:
Do I just need to remove the branch that I still have but am no longer using? Or is there something else I need to run?
I'm not sure how much difference it makes but it shows a padlock against the file.
Here are some excerpts from my bash_history that should give an idea how I managed to get into this state (assume at this point I'm working on a git branch called 'my-branch' and I've got a folder containing more folders/files):
I thought I also ran the following but it doesn't appear in the bash_history with the others :
I also thought I ran some git commands (like git gc ) to try to tidy up the pack file but they don't appear in the .bash_history file either.
Can you clarify how you removed them? If they are still in the commit history, then they's still be in your pack files.
7 Answers 7
The issue is that, even though you removed the files, they are still present in previous revisions. That's the whole point of git, is that even if you delete something, you can still get it back by accessing the history.
What you are looking to do is called rewriting history, and it involved the git filter-branch command.
To answer your question more directly, what you basically need to run is this command with unwanted_filename_or_folder replaced accordingly:
This will remove all references to the files from the active history of the repo.
Next step, to perform a GC cycle to force all references to the file to be expired and purged from the packfile. Nothing needs to be replaced in these commands.
I've marked it as accepted if that makes it easier for anyone coming to this question in future, although I actually solved my problem at the time by creating a fresh git repo
This answer pointed me in the right direction. But to actually delete the files 3 more commands are needed 1) git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin 2) git reflog expire --expire=now --all 3) git gc --prune=now
Scenario A: If your large files were only added to a branch, you don't need to run git filter-branch . You just need to delete the branch and run garbage collection:
Scenario B: However, it looks like based on your bash history, that you did merge the changes into master. If you haven't shared the changes with anyone (no git push yet). The easiest thing would be to reset master back to before the merge with the branch that had the big files. This will eliminate all commits from your branch and all commits made to master after the merge. So you might lose changes -- in addition to the big files -- that you may have actually wanted:
Then run the steps from the scenario A.
Scenario C: If there were other changes from the branch or changes on master after the merge that you want to keep, it would be best to rebase master and selectively include commits that you want:
In your editor, remove lines that correspond to the commits that added the large files, but leave everything else as is. Save and quit. Your master branch should only contain what you want, and no large files. Note that git rebase without -p will eliminate merge commits, so you'll be left with a linear history for master after . This is probably okay for you, but if not, you could try with -p , but git help rebase says combining -p with the -i option explicitly is generally not a good idea unless you know what you are doing .
Читайте также: