Git удалить файл из всех коммитов

Обновлено: 04.07.2024

Позже я обнаружил, что в коммит по ошибке был добавлен файл.

Как я могу удалить файл из последней фиксации?

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

Затем сбросьте ненужные файлы, чтобы исключить их из фиксации:

Если по какой-либо причине (как это было в моем случае) у вас возникнут проблемы с сбросом всех файлов из последней фиксации - как в git reset --soft HEAD^ - и ваш случай соответствует следующим условиям, вы можете сделать, как я объясню ниже.

  1. файл, который вы пытаетесь удалить, уже существовал в репозитории перед последней фиксацией
  2. вы можете отменить все изменения в файле с момента предыдущей фиксации ( HEAD

Затем вы можете отменить все изменения с момента предыдущей фиксации (HEAD

2), сохранить файл, обработать его ( git add filename ) и затем запустить git commit --amend --no-edit . Это приведет к фиксации «новых изменений» - фактически повторная фиксация файла до последней фиксации, как это было до последней фиксации.

Другой подход, который мы можем сделать, - это:

  1. Удалить файл
  2. Сделайте новую фиксацию с удаленным файлом
  3. Сделайте интерактивное перебазирование и раздавите оба коммитов
  4. От себя

Вы можете просто использовать эту команду:

Я скопировал текущие файлы в другую папку, а затем избавился от всех неопубликованных изменений:

Затем скопируйте вещи обратно. Зафиксировать, нажать.

Если вы еще не отправили свои изменения в git

Он сбросит все изменения и вернется к одной фиксации.

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

Сбросить ненужный файл

Это сработало для меня, чтобы удалить файл из репозитория битового ведра, который я сначала отправил в ветвь.

Если вы используете GitHub и еще не отправили коммит, GitHub Desktop легко решает эту проблему:

Если вам больше не нужен этот файл, вы можете сделать

Это отменит последнюю фиксацию в локальных репозиториях и переместит все обратно в рабочую область, как перед выполнением фиксации. Затем просто используйте любой инструмент Git UI как обычно (например, TortoiseGit, Git UI, Git Extensions . ), чтобы деактивировать файлы, которые мы не хотим фиксировать, а затем повторите фиксацию.

На самом деле, я думаю, что более быстрый и простой способ - использовать интерактивный режим git rebase.

4, как бы далеко вы ни зашли)

А затем вместо «выбрать» используйте «редактировать». Я не осознавал, насколько мощным является «редактирование».

Надеюсь, вы найдете это полезным.

git reset --soft HEAD^ отменяет вашу фиксацию, и когда вы набираете git status , он говорит вам, что делать:

Была такая же проблема, когда у меня есть изменения в локальной ветке, где я хотел вернуть только один файл. Для меня сработало -

( feature / target_branch ниже - это место, где у меня есть все мои изменения, включая те, которые я хотел отменить для определенного файла)

( feature / staging - это моя временная промежуточная ветка, в которую я буду нажимать все желаемые изменения, за исключением изменения этого одного файла)

Создайте локальную ветвь из моей origin / feature / target_branch - назовите ее feature / staging .

Моя рабочая локальная ветка feature / target_branch была объединена с ветвью feature / staging .

Отметьте функция / постановка , затем git reset --soft ORIG_HEAD (теперь все изменения из функции / постановки будут поэтапно, но не зафиксированы.)

Неустановил файл, который я ранее зарегистрировал, с ненужными изменениями

Изменена ветвь восходящего потока для feature / staging на origin / feature / target_branch .

Фиксировал остальные поэтапные изменения и отправил вверх по течению на мой удаленный origin / feature / target_branch

Если вы хотите удалить файлы из предыдущих коммитов, используйте фильтры

Если вы видите эту ошибку:

Невозможно создать новую резервную копию. Предыдущая резервная копия уже существует в refs / original / Принудительно перезаписать резервную копию с помощью -f

Просто удалите резервные копии ссылок на вашем локальном репо

Что-то, что сработало для меня, но все же думаю, что должно быть лучшее решение:

Просто оставьте изменение, которое вы хотите отменить, в другом коммите, проверьте другие

Выполните последовательность следующих команд:

Просто хотел дополнить верхний ответ, так как мне пришлось запустить дополнительную команду:

Использование git GUI может упростить удаление файла из предыдущей фиксации.

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

Вы можете отменить отметку файла, который был ошибочно зафиксирован, и затем нажать «Зафиксировать».

enter image description here

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

Оставит вам локальный файл по-прежнему. Если вам тоже не нужен файл локально, вы можете пропустить параметр --cached.

Если вся работа выполняется в вашей локальной ветке, вам нужно сохранить файл в более позднем коммите, и, как и при наличии чистой истории, я думаю, что более простой способ сделать это может быть:

Вы можете просто попробовать.

И создайте новую фиксацию.

Однако есть замечательная программа "gitkraken". что упрощает работу с git.

Следующее приведет к деактивации только того файла, который вы намеревались, что и просил OP.

Вы увидите что-то вроде следующего .

Изменения, которые необходимо зафиксировать: (используйте "git reset HEAD . ", чтобы отключить сцену)

изменено: / путь / к / файлу

Изменения, не запланированные для фиксации: (используйте «git add . », чтобы обновить то, что будет зафиксировано) (используйте «git checkout - . », чтобы отменить изменения в рабочем каталоге)

изменено: / путь / к / файлу

  • «Изменения, которые необходимо зафиксировать» - это предыдущая версия файла перед фиксацией. Это будет выглядеть как удаление, если файл никогда не существовал. Если вы зафиксируете это изменение, будет ревизия, которая вернет изменение в файл в вашей ветке.
  • «Изменения, не предназначенные для фиксации» - это внесенное вами изменение и текущее состояние файла.

На этом этапе вы можете сделать с файлом все, что захотите, например выполнить сброс до другой версии.

Когда вы будете готовы совершить:

Или (если у вас есть другие изменения, которые вы пока не хотите фиксировать)

Я объясню вам на примере.
Пусть A, B, C - 3 последовательных коммита. Фиксация B содержит файл, который не должен быть зафиксирован.

1.В начале забыл добавить в .gitignore папку target/ в которой хранятся скомпилированные *.jar *.class и прочие файлы. 2.позже через несколько коммитов обнаружил что папка .git весит 12+ мегабайт, по размеру предполагаю что это забытый мной jar файл (кода у меня

15 килобайт, все .class файлы

14 килобайт) 3.нашел этот файл руками он весит 12 МБ и лежит в папке

Прошел несколько вопросов на русском и английском stackoverflow + google пробовал следующее:

  1. git filter-branch --index-filter 'git rm --cached --ignore-unmatch FILENAME' --prune-empty -- --all
  2. git filter-branch --index-filter 'git rm --cached --ignore-unmatch FILENAME' HEAD
  3. 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.

вы можете посмотреть содержимое каталога 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 байт.



Удаление файлов с конфиденциальной информацией из Git-репозитория ( изображение большого размера )

Минимизация ущерба

Итак, вы случайно закоммитили файл с конфиденциальной информацией. Назовём этот файл .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 вам менять и не потребуется.

Итоги

Надеюсь, мой материал поможет вам в безопасной работе с конфиденциальными данными.

А вам случалось отправлять в общедоступный репозиторий что-то такое, что туда попадать не должно?

На коммите 4 я случайно добавил свой файл credentials.json и подтолкнул к репо.
На коммите 5 я удалил файл, сделал коммит и отправил это изменение в репозиторий.

В настоящее время файл не отображается в последнем коммите. Но если пользователь проверяет историю коммитов, он может перейти к коммиту 4 и проверить содержимое моего файла.

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

Примечание. -
я уже попробовал ниже команду: -

Не выдает никакой ошибки, но файл не удаляется из истории.

На самом деле файл находится в папке / src. Но странно то, что когда я даю команду ниже (включая полный путь):

полный путь / src / Credentials.json находится за пределами хранилища.

Это потому, что я уже удалил свой файл в коммите 5? Что я могу сделать в этом сценарии?
Является ли создание нового репо с нуля единственным вариантом?

3 ответа

Предполагая, что никто не клонировал хранилище, вы можете использовать git rebase -i , чтобы переписать историю и удалить файл.

Используйте git rebase -i <commit-hash-of-commit-3> . Откроется редактор со списком коммитов, начиная с коммита-4. Вы можете удалить коммит (удалив строку и сохранив файл), отредактировать его и т. Д.

Выберите «редактировать» для коммита-4. Сохраните файл. Вы вернетесь к оболочке с файлами, добавленными в git 'index', но еще не зафиксированными.

Теперь вы можете git rm credentials.json удалить файл и git commit --amend изменить коммит. ( обновляется )

Наконец git rebase --continue чтобы закончить. Вы вернетесь к оболочке, и файл больше не будет частью репозитория.

Если позже вы добавили коммит, который зависит от файла Credentials.json, вы можете получить ошибку во время фазы продолжения. Затем Git останавливается на проблемном коммите, и вы снова можете использовать команды git (сброс / добавление), чтобы изменить коммит и продолжить.

Я бы рекомендовал прочитать страницу справки GitHubs:

Вы можете использовать BFG Repo-Cleaner

bfg --delete-files Credentials.json my-repo.git

Вы можете использовать git filter-branch :

git filter-branch --force --index-filter "git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA" --prune-empty --tag-name-filter cat -- --all
(где путь, например, src/Credentials.json , Это тоже может вам помочь)

Стоит отметить, что если у кого-то уже есть копия репо, вы должны удалить ее и там! (или вы заставляете переклонировать / вытащить репо)

Не нужно создавать новый репо. git filter-branch работает.

Не используйте полный путь в git filter-branch , используйте относительный путь src/Credentials.json .

Кстати, bfg гораздо проще использовать для новичка. ( bfg принимает имя файла без пути.)

Читайте также: