Как удалить файлы из ветки git

Обновлено: 21.11.2024

Вы уже передавали в репозиторий закрытый ключ SSH, файл паролей или файл конфигурации с конфиденциальными данными?

Поделиться этой статьей

Введение

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

Для всех остальных: НЕ ПАНИКУЙТЕ! Сделайте глубокий вдох, встаньте из-за стола, пройдитесь несколько минут. Готовый? Хорошо, приступим!

Цель состоит в том, чтобы полностью стереть файл из репозитория Git, чтобы скрыть все следы вашей ужасной ошибки. Вы хотите быть человеком, который отправил ключи AWS в общедоступный репозиторий GitHub только для того, чтобы через 24 часа узнать, что около 2000 долларов США было потрачено на майнинг биткойнов?

Несколько методов

Просто git rm passwords.txt не поможет, так как этот файл все еще будет присутствовать во всех предыдущих коммитах. В зависимости от того, где находится файл, вы можете использовать несколько методов. Здесь представлен обзор сценариев в порядке возрастания сложности.

Все эти методы предполагают, что вы знакомы с консольными командами.

Они написаны для Linux, но должны работать в OS X и даже в Windows, если вы используете Git Bash.

Если вы уже отправили свои изменения, все может усложниться.

Если вы разработчик-одиночка, дерзайте. Но если вы работаете в команде, то сначала обсудите это с ними.

Если ваш код (с нежелательным файлом) уже находится в открытом доступе (GitHub, BitBucket. ), то вам может не повезти. Но дочитайте до конца.

Сценарий 1: файл находится в последней фиксации, и вы еще не отправили его

1. Вы хотите сохранить файл локально

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

Команды git reflog expire и git gc вызывают принудительную сборку мусора, чтобы файл не болтался где-то в вашем репозитории.

2. Вы не хотите хранить файл локально

Просто измените последний коммит.

Сценарий 2: файл находится ниже в истории, и вы еще не отправили его

Решение 1. Средство очистки репозиториев BFG

Загрузите «BFG Repo-Cleaner» здесь. Этот инструмент работает в 10-720 раз быстрее, чем любой другой метод, но вы не можете указать подкаталог, он удалит все файлы с одинаковым именем в любом каталоге.

Обычно BFG Repo-Cleaner защищает вашу самую последнюю фиксацию, но если вы знаете, что делаете (а вы знаете, верно?), вы даете ему опцию --no-blob-protection , что примерно равнозначно , делай что хочу и не оберегай меня от ошибок.

Решение 2: интерактивная перебазировка

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

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

Отредактируйте файл git-rebase-todo: измените первую команду с pick на edit.

Затем сохраните и закройте редактор. Перебазирование вернет коммиты к коммиту, добавившему нежелательный файл.

Решение 3. Git filter-ветка

Это запускает сценарий, указанный в --tree-filter (например, удаление определенного файла) при каждой фиксации. Это действительно МЕДЛЕННО! Это потому, что он должен проверять каждую фиксацию, запускать скрипт, фиксировать и переходить к следующей фиксации. Используйте это, когда есть теги или коммиты слияния между коммитом-нарушителем и HEAD, или когда файл-нарушитель существует в нескольких ветвях. Другими словами, используйте в крайнем случае!

В этом скрипте я использую тот же трюк, чтобы найти первую фиксацию, добавляющую нежелательный файл, а затем запускаю фильтр только для родителя этой фиксации, вплоть до HEAD текущего ветвь. git filer-branch всегда делает резервную копию и добавляет к исходной ветке префикс оригинального . Команда git for-each-ref очищает эти резервные ветки, потому что мы действительно хотим избавиться от этого надоедливого файла. --tag-name-filter cat позаботится о том, чтобы любые теги перемещались вместе с их коммитами.

Если вы хотите сделать это для всего репозитория (вы действительно не должны!):

Сценарий 3: вы уже отправили

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

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

Тогда: СДЕЛАЙТЕ РЕЗЕРВНУЮ КОПИИ.

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

Используйте один из способов, описанных выше, чтобы удалить файл.

Принудительно нажмите, но знайте, что вы делаете, и помните о последствиях, потому что нет возможности вернуться, если у вас нет резервной копии. --force-with-lease на самом деле должен быть там по умолчанию, потому что он сначала проверяет, не перезапишите ли вы работу других людей. См. также блог Atlassian для отличного объяснения.

Сценарий 4: коммиты уже есть на GitHub

В этом случае существует риск того, что кто-то все еще может получить доступ к нежелательному файлу даже после принудительной отправки.

Есть два способа сделать это:

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

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

GitHub может собирать кэшированные представления через некоторое время, но на это нельзя полагаться.

Лучше всего связаться со службой поддержки GitHub и сообщить им о репозитории и оскорбительном коммите, после чего они вручную удалят кешированные представления.

В следующем посте я объясню некоторые передовые методы, используемые в iText Software для обработки конфиденциальных файлов, необходимых в наших проектах.

Я только что столкнулся с ситуацией, которая кажется мне нелогичной с Git. У меня есть репозиторий с большим количеством коммитов, поэтому на данном этапе у него только одна ветка (master).

Предположим, что в этой ветке есть файл MyFile.txt. Теперь мне нужно создать другую ветку, потому что я хочу внести некоторые изменения в несколько файлов, и я не хочу делать это напрямую, поэтому я запускаю:

Сейчас схема выглядит так:

Хорошо, я переключился на ветку iss53, запускаю ls -l и MyFile.txt. Одно из изменений включает удаление файла MyFile.txt, поэтому:

Отлично, повторный запуск ls -l показывает, что файла MyFile.txt больше нет. Поэтому я переключаюсь обратно на ветку master:

Но. файл MyFile.txt также исчез. Моя логика говорит, что если я удалил файл в ветке, он должен применяться только к этой ветке, так почему же файл также удаляется из ветки master? Обратите внимание, что я еще не делал никаких коммитов, только ветвление.

Примечание. Оба изображения взяты из Git-scm.

2 ответа 2

Ответ в вашем вопросе:

Обратите внимание, что я еще не делал никаких коммитов, только ветвление.

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

Таким образом, файл будет снова восстановлен.

Возможно, для вас это не логично, но такое поведение может быть полезным. Представьте, что у вас есть 2 ветки: master и development, и вы всегда должны работать над development, прежде чем переносить изменения в master. Допустим, вы забыли переключиться на разработку перед тем, как начать работать, то — до совершения коммита — вы поняли, что находитесь на мастере. Решение простое: переключитесь на разработку и зафиксируйте.

Установка и настройка

Получение и создание проектов

Базовый снимок

Ветвление и слияние

Обмен проектами и их обновление

Осмотр и сравнение

Исправление

Отладка

Электронная почта

Внешние системы

Администратор сервера

Руководства

Администрирование

Связные команды

Проверьте свою версию git, запустив

git-rm - Удалить файлы из рабочего дерева и из индекса

ОБЗОР

ОПИСАНИЕ

Удалить файлы, соответствующие пути, из индекса или из рабочего дерева и индекса. git rm не удалит файл только из вашего рабочего каталога. (Нет возможности удалить файл только из рабочего дерева и при этом сохранить его в индексе; используйте /bin/rm, если вы хотите это сделать.) Удаляемые файлы должны быть идентичны вершине ветви, и никакие обновления их содержимого не могут быть размещены в индексе, хотя это поведение по умолчанию можно переопределить с помощью параметра -f. Если задан параметр --cached, подготовленное содержимое должно соответствовать либо концу ветки, либо файлу на диске, что позволяет удалить файл только из индекса. Когда используются разреженные проверки (см. git-sparse-checkout[1]), git rm удалит только пути в шаблонах разреженных проверок.

ВАРИАНТЫ

Файлы для удаления. Начальное имя каталога (например,dir для удаления dir/file1 и dir/file2 ) можно задать для удаления всех файлов в каталоге и рекурсивно всех подкаталогов, но для этого требуется явно заданный параметр -r.

Команда удаляет только те пути, которые известны Git.

Подстановка файлов соответствует границам каталогов. Таким образом, при наличии двух каталогов d и d2 существует разница между использованием git rm 'd*' и git rm 'd/*' , так как первый также удалит весь каталог d2 .

Подробнее см. в записи pathspec в gitglossary[7].

Переопределить проверку актуальности.

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

Разрешить рекурсивное удаление, если задано начальное имя каталога.

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

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

Выйти с нулевым статусом, даже если не найдено ни одного файла.

Разрешить обновление записей индекса за пределами конуса разреженной проверки. Обычно git rm отказывается обновлять записи индекса, пути которых не соответствуют конусу разреженной проверки. См. git-sparse-checkout[1] для получения дополнительной информации.

git rm обычно выводит одну строку (в виде команды rm) для каждого удаленного файла. Этот параметр подавляет этот вывод.

Pathspec передается вместо аргументов командной строки. Если точно - то используется стандартный ввод. Элементы Pathspec разделяются символами LF или CR/LF. Элементы Pathspec можно заключать в кавычки, как описано для переменной конфигурации core.quotePath (см. git-config[1]). См. также --pathspec-file-nul и global --literal-pathspecs .

Имеет смысл только с параметром --pathspec-from-file . Элементы Pathspec разделяются символом NUL, а все остальные символы воспринимаются буквально (включая символы новой строки и кавычки).

УДАЛЕНИЕ ФАЙЛОВ, ИСЧЕЗНУВШИХ ИЗ ФАЙЛОВОЙ СИСТЕМЫ

У git rm нет возможности удалить из индекса только те пути, которые исчезли из файловой системы. Однако, в зависимости от варианта использования, есть несколько способов сделать это.

Использование «git commit -a»

Если вы предполагаете, что ваша следующая фиксация должна записывать все изменения отслеживаемых файлов в рабочем дереве и записывать все удаления файлов, которые были удалены из рабочего дерева с помощью rm (в отличие от git rm ), используйте git commit -a , так как он автоматически заметит и запишет все удаления. Вы также можете получить аналогичный эффект без фиксации, используя git add -u .

Использование «git add -A»

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

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

а затем распакуйте новый код в рабочем дереве. В качестве альтернативы вы можете rsync внести изменения в рабочее дерево.

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

Другие способы

Если все, что вы действительно хотите сделать, это удалить из индекса файлы, которых больше нет в рабочем дереве (возможно, из-за того, что ваше рабочее дерево грязное и вы не можете использовать git commit -a ), используйте следующую команду :

ПОДМОДУЛИ

Только подмодули, использующие git-файл (что означает, что они были клонированы с помощью Git версии 1.7.8 или новее), будут удалены из рабочего дерева, поскольку их репозиторий находится внутри каталога .git суперпроекта. Если подмодуль (или один из вложенных в него) по-прежнему использует каталог .git, git rm переместит каталог git подмодулей в каталог git суперпроектов, чтобы защитить историю подмодуля. Если он существует, то подмодуль. раздел в файле gitmodules[5] также будет удален, а этот файл будет помещен в промежуточное состояние (если только не используются --cached или -n).

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

Если вы хотите удалить только локальное извлечение подмодуля из вашего рабочего дерева без фиксации удаления, используйте вместо этого git-submodule[1] deinit. Также см. gitsubmodules[7] для получения подробной информации об удалении подмодуля.

ПРИМЕРЫ

Удаляет из индекса все файлы *.txt, находящиеся в каталоге Documentation и любых его подкаталогах.

Обратите внимание, что в этом примере звездочка * взята из оболочки; это позволяет Git, а не оболочке, расширять пути к файлам и подкаталогам в каталоге Documentation/.

Я добавил файл с именем "file1.txt" в репозиторий Git.После этого я зафиксировал его, добавил пару каталогов с именами dir1 и dir2 и закоммитил их в репозиторий Git.

Теперь в текущем репозитории есть файлы "file1.txt", dir1 и dir2. Как я могу удалить «file1.txt», не затрагивая другие файлы, такие как dir1 и dir2?

git rm — правильный ответ, но помните, что файл все равно останется в истории. Если вы хотите удалить файл из-за того, что он содержал конфиденциальную информацию, вам нужно будет сделать что-то более радикальное. (Изменение истории, особенно для контента, который вы уже отправили, является решительным действием, и его следует по возможности избегать.)

Примечание: на GitHub теперь вы можете удалить файл напрямую из веб-интерфейса (даже не клонируя репозиторий). Смотрите мой ответ ниже.

24 ответа 24

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

Но если вы хотите удалить файл только из репозитория Git, а не из файловой системы, используйте:

И отправить изменения в удаленное хранилище

@SaulOrtega: Верно. Чтобы удалить файл из предыдущих коммитов (изменив предыдущую историю), см. страницу справки GitHub, посвященную удалению конфиденциальных данных.

Обратите внимание, что при этом файл также будет удален локально. Если вы хотите удалить его только из репо, выполните: git rm --cached file1.txt

Стоит отметить, что если этот файл содержит конфиденциальную информацию (например, учетные данные), вам следует немедленно изменить эти учетные данные. Цитируя GitHub: «После того, как вы отправили фиксацию на GitHub, вы должны рассматривать любые содержащиеся в ней данные как скомпрометированные. Если вы зафиксировали пароль, измените его! Если вы зафиксировали ключ, сгенерируйте новый».

git rm file.txt удаляет файл из репозитория, но также удаляет его из локальной файловой системы.

Чтобы удалить файл из репозитория и не удалять его из локальной файловой системы, используйте:
git rm --cached file.txt

В приведенной ниже точной ситуации я использую git для управления версиями веб-сайта моей компании, но каталог «mickey» был папкой tmp для обмена личным содержимым с разработчиком САПР. Когда ему понадобились ОГРОМНЫЕ файлы, я создал частную, несвязанную директорию и отправил туда файлы по ftpd, чтобы он мог получить их через браузер. Забыв, что сделал это, я позже выполнил git add -A из базового каталога веб-сайта. Впоследствии статус git показал, что новые файлы нуждаются в фиксации. Теперь мне нужно было удалить их из системы отслеживания и контроля версий git.

Приведенный ниже пример вывода основан на том, что только что произошло со мной, когда я непреднамеренно удалил файл .003. К счастью, меня не волнует, что случилось с локальной копией в .003 , но некоторые из других измененных в настоящее время файлов были обновлениями, которые я только что сделал на веб-сайте, и было бы эпично, если бы они были удалены из локальной файловой системы! "Локальная файловая система" = работающий веб-сайт (не лучшая практика, но реальность).

Обновление: этот ответ получает некоторый трафик, поэтому я подумал, что упомяну, что мой другой ответ Git содержит несколько замечательных ресурсов: На этой странице есть графика, которая помогает мне демистифицировать Git. Книга "Pro Git" есть в сети и мне очень помогает.

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