Как заставить Git "забыть" о файле, который отслеживался, но теперь находится в.gitignore?

Есть файл, который отслеживался git, но теперь файл находится на .gitignore список.

Тем не менее, этот файл продолжает отображаться в git status после того, как это отредактировано. Как вы заставляете git полностью забыть об этом?

34 ответа

Решение

.gitignore предотвратит добавление неотслеживаемых файлов (без add -f) к набору файлов, отслеживаемых git, однако git продолжит отслеживать любые файлы, которые уже отслеживаются.

Чтобы остановить отслеживание файла, вам необходимо удалить его из индекса. Это может быть достигнуто с помощью этой команды.

git rm --cached <file>

Удаление файла из заголовка ревизии произойдет при следующем коммите.

ВНИМАНИЕ: Хотя это не удалит физический файл из вашего локального, он будет удалять файлы с машин других разработчиков на следующем git pull,

Приведенная ниже серия команд удалит все элементы из индекса Git (не из рабочего каталога или локального репо), а затем обновит индекс Git, соблюдая при этом git игнорирует. PS. Индекс = Кэш

Первый:

git rm -r --cached . 
git add .

Затем:

git commit -am "Remove ignored files"

git update-index делает всю работу за меня:

git update-index --assume-unchanged <file>

Примечание: это решение на самом деле не зависит от .gitignore поскольку gitignore предназначен только для неотслеживаемых файлов.

редактировать: так как этот ответ был опубликован, новая опция была создана, и это должно быть предпочтительным. Вы должны использовать --skip-worktree который предназначен для измененных отслеживаемых файлов, которые пользователь больше не хочет фиксировать и сохраняет --assume-unchanged для производительности, чтобы предотвратить git, чтобы проверить состояние больших отслеживаемых файлов. См. /questions/40782498/git-raznitsa-mezhdu-predpolozhim-bez-izmenenij-i-propustit-rabochee-derevo/40782503#40782503 для получения дополнительной информации...

git update-index --skip-worktree <file>
git ls-files --ignored --exclude-standard -z | xargs -0 git rm --cached
git commit -am "Remove ignored files"

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

Ответ копирования / вставки git rm --cached -r .; git add .; git status

Эта команда будет игнорировать файлы, которые уже были переданы в Git-репозиторий, но теперь мы добавили их в .gitignore,

Уберите это, передайте, затем верните это обратно. Это работало для меня в прошлом. Вероятно, есть "более мерзкий" способ сделать это.

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

git ls-files --ignored --exclude-standard | sed 's/.*/"&"/' | xargs git rm -r --cached

Он перечисляет все ваши игнорируемые файлы, заменяет каждую строку вывода строкой в ​​кавычках вместо этого, чтобы обрабатывать пути с пробелами внутри, и передавать все в git rm -r --cached удалить пути / файлы / каталоги из индекса.

Используйте это когда:

1. Вы хотите распаковать много файлов, или

2. Вы обновили свой файл gitignore

Ссылка на источник: http://www.codeblocq.com/2016/01/Untrack-files-already-added-to-git-repository-based-on-gitignore/

Допустим, вы уже добавили / передали некоторые файлы в свой репозиторий git, а затем добавили их в свой.gitignore; эти файлы все еще будут присутствовать в вашем индексе репозитория. В этой статье мы увидим, как от них избавиться.

Шаг 1: внесите все ваши изменения

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

Шаг 2: Удалить все из хранилища

Чтобы очистить репо, используйте:

git rm -r --cached .
  • rm это команда удаления
  • -r позволит рекурсивное удаление
  • –Cached удалит только файлы из индекса. Ваши файлы все еще будут там.

rm Команда может быть неумолимой. Если вы хотите попробовать то, что он делает заранее, добавьте -n или же --dry-run флаг, чтобы проверить вещи.

Шаг 3: заново добавить все

git add .

Шаг 4: совершить

git commit -m ".gitignore fix"

Ваш репозиторий чистый:)

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

Если ты не можешь git rm отслеживаемый файл, потому что это может понадобиться другим людям (предупреждение, даже если вы git rm --cached, когда кто-то еще получит это изменение, его файлы будут удалены в их файловой системе). Это часто делается из-за переопределения файла конфигурации, учетных данных аутентификации и т. Д. Пожалуйста, посмотрите на https://gist.github.com/1423106 способы, которыми люди обошли проблему.

Подвести итоги:

  • Попросите ваше приложение найти пропущенный файл config-overide.ini и использовать его поверх зафиксированного файла config.ini (или, альтернативно, найдите ~/.config/myapp.ini или $MYCONFIGFILE)
  • Зафиксируйте файл config-sample.ini и проигнорируйте файл config.ini, при необходимости создайте скрипт или аналогичный файл для копирования.
  • Попробуйте применить магию gitattributes clean/smudge, чтобы применить и удалить изменения, например, размазать файл конфигурации как извлечение из альтернативной ветви и очистить файл конфигурации как извлечение из HEAD. Это сложная штука, я не рекомендую его для начинающего пользователя.
  • Сохраните файл конфигурации в выделенной для него ветке развертывания, которая никогда не объединяется с master. Когда вы хотите развернуть / скомпилировать / протестировать, вы сливаетесь с этой веткой и получаете этот файл. По сути, это подход smudge / clean, за исключением использования политик слияния людей и дополнительных git-модулей.
  • Антирекомендация: не используйте предположения без изменений, это закончится только слезами (потому что ложная ложь сама по себе приведет к плохим вещам, таким как ваши изменения будут потеряны навсегда).

Я сделал это с помощью git filter-branch. Точная команда, которую я использовал, была взята со страницы руководства:

ВНИМАНИЕ: это удалит файл из всей вашей истории

git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD

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

Что не сработало для меня

(Под Linux) я хотел использовать посты, предлагающие ls-files --ignored --exclude-standard | xargs git rm -r --cached подход. Тем не менее, (некоторые из) файлов, которые будут удалены, имели встроенную новую строку /LF/\n в их именах. Ни одно из решений:

git ls-files --ignored --exclude-standard | xargs -d"\n" git rm --cached
git ls-files --ignored --exclude-standard | sed 's/.*/"&"/' | xargs git rm -r --cached

справиться с этой ситуацией (получить ошибки о не найденных файлах).

Так что я предлагаю

git ls-files -z --ignored --exclude-standard | xargs -0 git rm -r --cached

Это использует -z аргумент для ls-файлов, а -0 аргумент к xargs для безопасного / правильного обслуживания "неприятных" символов в именах файлов.

На странице руководства git-ls-files(1) говорится:

Когда опция -z не используется, символы TAB, LF и обратная косая черта в путевых именах представляются как \t, \n и \\ соответственно.

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

РЕДАКТИРОВАТЬ: меня попросили добавить это --- как и любой git rm команда --- это должно сопровождаться коммитом, чтобы сделать удаление постоянным, например git commit -am "Remove ignored files",

Выполните следующие шаги для файла / папки:

Удалить файл:

  1. нужно добавить этот файл в.gitignore.
  2. необходимо удалить этот файл с помощью команды (git rm --cached file name).
  3. нужно запустить (git add.).
  4. необходимо (commit -m) "файл удален".
  5. и, наконец, (git push).

Например:

Я хочу удалить файл test.txt. Я случайно нажал на GitHub, чтобы удалить команды, которые будут выглядеть так:

1-е добавление test.txt в.gitignore

git rm --cached test.txt
git add .
git commit -m "test.txt removed"
git push

Удалить папку:

  1. нужно добавить эту папку в.gitignore.
  2. необходимо удалить эту папку с помощью команды (git rm -r --cached имя папки).
  3. нужно запустить (git add.).
  4. необходимо (commit -m) "папка удалена".
  5. и, наконец, (git push).

Например:

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

1-е добавление.idea в.gitignore

git rm -r --cached .idea
git add .
git commit -m ".idea removed"
git push
  1. Обновите свой .gitignore файл - например, добавьте папку, которую вы не хотите отслеживать .gitignore,

  2. git rm -r --cached . - Удалите все отслеживаемые файлы, включая нужные и нежелательные. Ваш код будет в безопасности, если вы сохранили локально.

  3. git add . - Все файлы будут добавлены обратно, кроме тех, что в .gitignore,


Шляпа подсказка @AkiraYamamoto, чтобы указать нам в правильном направлении.

Делайте следующие шаги поочередно, у вас все будет хорошо.

1.удалить ошибочно добавленные файлы из каталога / хранилища. Вы можете использовать команду "rm -r"(для linux) или удалить их, просматривая каталоги.

2. добавьте файлы / каталоги в gitignore файл сейчас и сохраните его.

3. Теперь удалите их из кеша git с помощью этих команд (если существует более одного каталога, удалите их один за другим, повторно выполнив эту команду)

git rm -r --cached path-to-those-files

4. Теперь сделайте коммит и нажмите, используйте эти команды. Это удалит эти файлы из git remote и заставит git перестать отслеживать эти файлы.

git add .
git commit -m "removed unnecessary files from git"
git push origin

Я думаю, что git не может полностью забыть о файле из-за его концепции ( раздел "Снимки, а не различия").

Эта проблема отсутствует, например, при использовании CVS. CVS хранит информацию в виде списка изменений на основе файлов. Информация для CVS - это набор файлов и изменений, внесенных в каждый файл с течением времени.

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

Эти 2 статьи были полезны для меня:

git предположить, что без изменений vs skip-worktree и как игнорировать изменения в отслеживаемых файлах с помощью Git

На основании этого я делаю следующее, если файл уже отслежен:

git update-index --skip-worktree <file>

С этого момента все локальные изменения в этом файле будут игнорироваться и не будут передаваться удаленно. Если файл изменяется на удаленном компьютере, конфликт может произойти, когда git pull, Тайник не сработает. Чтобы решить эту проблему, скопируйте содержимое файла в безопасное место и выполните следующие действия:

git update-index --no-skip-worktree <file>
git stash
git pull 

Содержимое файла будет заменено удаленным содержимым. Вставьте ваши изменения из безопасного места в файл и выполните снова:

git update-index --skip-worktree <file>

Если все, кто работает с проектом, выступят git update-index --skip-worktree <file>проблемы с pull должен отсутствовать. Это решение подходит для файлов конфигурации, когда каждый разработчик имеет свою собственную конфигурацию проекта.

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

С использованием git rm --cached Команда не отвечает на исходный вопрос:

Как вы заставляете git полностью забыть о [файле]?

Фактически, это решение приведет к удалению файла в каждом другом экземпляре хранилища при выполнении git pull!

Правильный способ заставить git забыть о файле задокументирован здесь GitHub.

Я рекомендую прочитать документацию, но в основном:

git fetch --all
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch full/path/to/file' --prune-empty --tag-name-filter cat -- --all
git push origin --force --all
git push origin --force --tags
git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
git reflog expire --expire=now --all
git gc --prune=now

просто замени full/path/to/file с полным путем файла. Убедитесь, что вы добавили файл в свой .gitignore,

Вам также нужно (временно) разрешить не-ускоренные пересылки в ваш репозиторий, так как вы изменяете свою историю git.

Переместите или скопируйте файл в безопасное место, чтобы не потерять его. Затем откройте файл и выполните коммит. Файл все равно будет отображаться, если вы вернетесь к одному из этих предыдущих коммитов или к другой ветке, где он не был удален. Однако, во всех будущих коммитах вы не увидите файл снова. Если файл находится в git ignore, вы можете переместить его обратно в папку, и git его не увидит.

Ответ от Мэтта Фира был самым эффективным ИМХО. Ниже приведен сценарий PowerShell для тех, кто в Windows удаляет только файлы из своего репозитория git, которые соответствуют их списку исключений.

# Get files matching exclusionsfrom .gitignore
# Excluding comments and empty lines
$ignoreFiles =  gc .gitignore | ?{$_ -notmatch  "#"} |  ?{$_ -match  "\S"} | % {
                    $ignore = "*" + $_ + "*"
                    (gci -r -i $ignore).FullName
                }
$ignoreFiles = $ignoreFiles| ?{$_ -match  "\S"}

# Remove each of these file from Git 
$ignoreFiles | % { git rm $_}

git add .

git rm --cached -r <YOUR_files_or_folders>

--кэш | удалять файлы только из индекса

В моем случае мне нужно было поставить .envrcфайл в файле .gitignore. а затем я использовал:

      git update-index --skip-worktree .envrc
git rm --cached .envrc

и файл был удален.

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

Но когда я использую команду git log -p содержимое файла (которое было секретными учетными данными S3 Amazon) показывало содержимое, которое было удалено, и я не хочу, чтобы этот контент никогда не отображался в истории git.

Затем я использовал эту команду:

       git filter-branch --index-filter 'git rm --cached --ignore-unmatch .envrc' HEAD

И я снова не вижу содержания.

Принятый ответ не "заставляет Git " забыть " о файле..." (исторически). Это только заставляет git игнорировать файл в настоящем / будущем.

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

Этот метод требует использования /.git/info/exclude(предпочтительный) ИЛИранее существовавших .gitignoreво всех коммитах, в которых есть файлы, которые нужно игнорировать / забыть. 1

Все методы принудительного применения git игнорируют поведение постфактум эффективно переписывают историю и, таким образом, имеют значительные разветвления для любых общедоступных / общих / совместных репозиториев, которые могут быть извлечены после этого процесса. 2

Общий совет: начните с чистого репо - все зафиксировано, ничего не ожидает в рабочем каталоге или индексе, и сделайте резервную копию!

Кроме того, комментарии / история изменений из этого ответа ( и истории изменений по этому вопросу), может быть полезной / просвещая.

#commit up-to-date .gitignore (if not already existing)
#this command must be run on each branch

git add .gitignore
git commit -m "Create .gitignore"

#apply standard git ignore behavior only to current index, not working directory (--cached)
#if this command returns nothing, ensure /.git/info/exclude AND/OR .gitignore exist
#this command must be run on each branch

git ls-files -z --ignored --exclude-standard | xargs -0 git rm --cached

#Commit to prevent working directory data loss!
#this commit will be automatically deleted by the --prune-empty flag in the following command
#this command must be run on each branch

git commit -m "ignored index"

#Apply standard git ignore behavior RETROACTIVELY to all commits from all branches (--all)
#This step WILL delete ignored files from working directory UNLESS they have been dereferenced from the index by the commit above
#This step will also delete any "empty" commits.  If deliberate "empty" commits should be kept, remove --prune-empty and instead run git reset HEAD^ immediately after this command

git filter-branch --tree-filter 'git ls-files -z --ignored --exclude-standard | xargs -0 git rm -f --ignore-unmatch' --prune-empty --tag-name-filter cat -- --all

#List all still-existing files that are now ignored properly
#if this command returns nothing, it's time to restore from backup and start over
#this command must be run on each branch

git ls-files --other --ignored --exclude-standard

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

git push origin --force --all
git push origin --force --tags
git for-each-ref --format="delete %(refname)" refs/original | git update-ref --stdin
git reflog expire --expire=now --all
git gc --prune=now

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

#fetch modified remote

git fetch --all

#"Pull" changes WITHOUT deleting newly-ignored files from working directory
#This will overwrite local tracked files with remote - ensure any local modifications are backed-up/stashed

git reset FETCH_HEAD

Сноски

1 Потому что/.git/info/exclude можно применить ко всем историческим коммитам, используя приведенные выше инструкции, возможно, подробности о получении .gitignoreфайл в исторические коммиты, которые нуждаются в этом, выходит за рамки этого ответа. Я хотел правильный.gitignoreбыть в корневом коммите, как будто это было первое, что я сделал. Другим может быть все равно, поскольку/.git/info/exclude может сделать то же самое независимо от того, где .gitignoreсуществует в истории коммитов, и очевидно, что переписывание истории - очень щекотливая тема, даже если вы знаете о разветвлениях.

FWIW, потенциальные методы могут включать git rebase или git filter-branchкоторый копирует внешний .gitignoreв каждый коммит, как и ответы на этот вопрос

2 Принудительное использование git ignore поведения постфактум путем фиксации результатов автономнойgit rm --cachedКоманда может привести к удалению недавно проигнорированного файла в будущих запросах с принудительно нажатого пульта ДУ. В--prune-empty флаг в следующих git filter-branchкоманда позволяет избежать этой проблемы, автоматически удаляя предыдущую фиксацию только индекса "удалить все игнорируемые файлы". Переписывание истории git также изменяет хэши коммитов, что нанесет ущерб будущим запросам из общедоступных / общих / совместных репозиториев. Пожалуйста, полностью осознайте последствия, прежде чем делать это для такого репо. В этом руководстве GitHub указывается следующее:

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

Альтернативные решения, не влияющие на удаленное репо:git update-index --assume-unchanged </path/file> или git update-index --skip-worktree <file>, примеры которых можно найти здесь.

Мне понравился ответ JonBrave, но у меня есть достаточно грязные рабочие каталоги, которые фиксируют -a меня немного пугает, так что вот что я сделал:

git config --global alias.exclude-ignored '! git ls-files -z --ignored --exclude-standard | xargs -0 git rm -r --cached && git ls-files -z --ignored --exclude-standard | xargs -0 git stage && git stage.gitignore && git commit -m "новый gitignore и удаление игнорируемых файлов из индекса"'

ломая это:

git ls-files -z --ignored --exclude-standard | xargs -0 git rm -r --cached 
git ls-files -z --ignored --exclude-standard | xargs -0 git stage 
git stage .gitignore 
git commit -m "new gitignore and remove ignored files from index"
  • удалить игнорируемые файлы из индекса
  • этап.gitignore и файлы, которые вы только что удалили
  • совершить

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

$ java -jar bfg.jar --strip-blobs-bigger-than 100M

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

$ java -jar bfg.jar --delete-files *.mp4

BFG в 10-1000 раз быстрее, чем git filter-branch, и, как правило, намного проще в использовании - для получения более подробной информации ознакомьтесь с полными инструкциями и примерами использования.

Источник: https://confluence.atlassian.com/bitbucket/reduce-repository-size-321848262.html

Если вы не хотите использовать интерфейс командной строки и работаете в Windows, очень простым решением является использование TortoiseGit, в меню которого есть действие "Удалить (сохранить локальное)", которое работает нормально.

Вот как я решил свою проблему:

git filter-branch --tree-filter 'rm -rf path/to/your/file' HEAD
git push

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

Для получения дополнительной информации вы можете обратиться к странице руководства filter-branch здесь

источник: https://docs.github.com/en/github/authenticating-to-github/removing-sensitive-data-from-a-repository#using-filter-branch

источник: https://thomas-cokelaer.info/blog/2018/02/git-how-to-remove-a-big-file-wrongly-committed/

Это больше не проблема в последнем git (v2.17.1 на момент написания).

.gitignore наконец игнорирует отслеженные, но удаленные файлы. Вы можете проверить это сами, запустив следующий скрипт. Финал git status Заявление должно сообщать "нечего совершать".

# Create empty repo
mkdir gitignore-test
cd gitignore-test
git init

# Create a file and commit it
echo "hello" > file
git add file
git commit -m initial

# Add the file to gitignore and commit
echo "file" > .gitignore
git add .gitignore
git commit -m gitignore

# Remove the file and commit
git rm file
git commit -m "removed file"

# Reintroduce the file and check status.
# .gitignore is now respected - status reports "nothing to commit".
echo "hello" > file
git status

Если у кого-то возникают проблемы с Windows, и вы хотите игнорировать всю папку, "cd" выберите "папку" и выполните "Git Bash Here".

git ls-files -z | xargs -0 git update-index --assume-unchanged

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

git rm -r --cached **/*.lock

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

Надеюсь, это поможет другим!

Специально для файлов на основе IDE, я использую это:

Например, slnx.sqlite, я просто полностью избавился от него следующим образом:

git rm {PATH_OF_THE_FILE}/slnx.sqlite -f
git commit -m "remove slnx.sqlite"

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

В случае уже совершенного DS_Store:

find . -name .DS_Store -print0 | xargs -0 git rm --ignore-unmatch

Проигнорируйте их:

echo ".DS_Store" >> ~/.gitignore_global
echo "._.DS_Store" >> ~/.gitignore_global
echo "**/.DS_Store" >> ~/.gitignore_global
echo "**/._.DS_Store" >> ~/.gitignore_global
git config --global core.excludesfile ~/.gitignore_global

Наконец, сделайте коммит!

Другие вопросы по тегам