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

4238

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

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

  • 11
    git clean -X звучит похоже, но это не применимо в этой ситуации (когда файлы все еще отслеживаются Git). Я пишу это для тех, кто ищет решение не следовать по неверному маршруту.
  • 26
    Единственный реальный ответ на этот вопрос приведен ниже, смотрите git update-index --assume-unchanged . Это решение 1) хранит файл на сервере (индекс), 2) позволяет свободно модифицировать его локально.
Показать ещё 3 комментария
Теги:
gitignore
git-rm

22 ответа

4712
Лучший ответ

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

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

git rm --cached <file>

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

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

  • 40
    Процесс, который работал для меня: 1. сначала зафиксировать ожидающие изменения 2. git rm --cached <file> и зафиксировать снова 3. добавить файл в .gitignore, проверить состояние git и снова зафиксировать
  • 87
    Очень важное добавление. Если игнорируемый файл будет изменен (но, несмотря на это, его не следует фиксировать), после изменения и выполнения git add . это будет добавлено к индексу. И следующий коммит передаст его в хранилище. Чтобы избежать этого, выполняйте сразу после того, как этот mataal произнес еще одну команду: git update-index --assume-unchanged <path&filename>
Показать ещё 27 комментариев
2379

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

Во-первых:

git rm -r --cached . 
git add .

Тогда:

git commit -am "Remove ignored files"
  • 1
    Можно было бы использовать немного больше экспозиции, но да, это было лучшим решением моей проблемы.
  • 166
    Чтобы подчеркнуть разницу между этим ответом и принятым: используя эти команды, вам не нужно знать, какие файлы затронуты. (Представьте себе временный каталог с большим количеством случайных файлов, которые должны быть удалены из индекса).
Показать ещё 18 комментариев
919

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

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

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

изменение: так как этот ответ был опубликован, новая опция была создана, и это должно быть предпочтительным. Вы должны использовать --skip-worktree который предназначен для измененных отслеживаемых файлов, которые пользователь больше не хочет фиксировать, и сохраняет --assume-unchanged для повышения производительности, чтобы git не проверял состояние больших отслеживаемых файлов. См. https://stackoverflow.com/questions/13630849/git-difference-between-assume-unchanged-and-skip-worktree для получения дополнительной информации...

git update-index --skip-worktree <file>
  • 137
    Это реальный ответ. Удивительно, на самом деле, очень просто, не загрязняет git status и на самом деле очень интуитивно. Благодарю.
  • 4
    Я пошел на достаточно хороший rm [...] . решение, так как, по крайней мере, я мог понять, как это работает. Я не нашел --assume-unchanged документации о том, что делают update-index & --assume-unchanged . Может кто-нибудь добавить, как это сравнивается с другим, в котором я хотел бы удалить все файлы, которые были бы проигнорированы? (Или ссылка на понятное объяснение?)
Показать ещё 17 комментариев
232
git ls-files --ignored --exclude-standard -z | xargs -0 git rm --cached
git commit -am "Remove ignored files"

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

  • 6
    Если вам нужно удалить их и из рабочего каталога, просто запустите git ls-files --ignored --exclude-standard | xargs git rm . Я считаю этот ответ лучшим! Потому что это очень понятно, Unix-способ, и делает желаемую вещь прямым способом, без компоновки побочных эффектов других, более сложных команд.
  • 5
    Отличный ответ; однако команда не будет выполнена, если у вас есть пути с пробелами в середине, например: «Мой dir / my_ignored_file.txt»
Показать ещё 9 комментариев
63

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

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

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

  • 2
    Отличное решение! Работает отлично и чувствует себя более корректно, чем удаление всех файлов, а затем добавление их обратно.
  • 4
    Я тоже нашел этот "самый чистый". Это может быть очевидно, но просто запуск первой части, git ls-files --ignored --exclude-standard , сам по себе позволяет сначала понять / проверить, какие файлы ваш новый .gitignore собирается исключить / удалить, прежде чем вы перейдете вперед и выполнить финальный git rm .
Показать ещё 2 комментария
55

Если вы не можете 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-модулей.
  • Антирекомендация: не используйте предположения без изменений, это закончится только слезами (потому что ложная ложь сама по себе может привести к плохим вещам, таким как ваши изменения будут потеряны навсегда).
  • 7
    git не удалил бы файл, если бы он был грязным во время удаления. И если он не грязный, поиск файла будет таким же простым, как git checkout <oldref> -- <filename> - но тогда он будет извлечен и проигнорирован.
  • 0
    Что касается вашей последней заметки (о --assume-unchanged ): либо это культ груза, и его следует отклонить, либо вы можете объяснить, почему (в чем я убежден), и это станет полезным.
50

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

  • 0
    Это отлично работает, если вы хотите игнорировать кучу файлов, которые ранее не игнорировались. Хотя, как вы сказали, возможно, есть лучший способ для этого.
  • 0
    Это именно то, что я сделал. Просто переместите файлы в папку за пределами git, затем выполните «git add.», «Git commit». (Это удалило файлы), затем добавьте gitignore, ссылаясь на файлы / папки, подтвердите снова, чтобы добавить файл gitignore в git, затем скопируйте / переместите обратно в папки, и их следует игнорировать. NB. Похоже, что файлы были удалены из GIT, поэтому, вероятно, удалят их из других проверок / извлечений, как упоминалось в вышеупомянутых решениях, но, поскольку вы изначально делаете их копии, это не является большой проблемой IMHO. просто дайте знать остальной команде ...
Показать ещё 2 комментария
43

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

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"

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

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

  • 0
    Это не удалит файлы из удаленного хранилища? Что если я захочу сохранить файлы как в локальном, так и в удаленном репозиториях, но заставить git «забыть» о них?
  • 0
    AFAIK, это не удалит файлы из истории, потому что мы не используем никаких команд изменения истории (исправьте меня, если я ошибаюсь). Это только добавляет новый коммит, удаляя файлы, игнорируемые в gitignore из git. Эти файлы будут в исторические коммиты
39

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

(В Linux) я хотел использовать сообщения, предлагающие подход ls-files --ignored --exclude-standard | xargs git rm -r --cached. Однако (некоторые из) файлов, которые должны быть удалены, имели встроенные символы newline/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-files, а аргумент -0 - xargs, чтобы безопасно/корректно использовать для "неприятных" символов в именах файлов.

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

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

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

EDIT: меня попросили добавить, что --- как и любая команда git rm - за ней должен следовать commit, чтобы сделать удаление перманентным, например. git commit -am "Remove ignored files".

  • 1
    Для меня это лучшее решение. Он имеет гораздо лучшую производительность, чем git add . , Он также содержит лучшие улучшения из некоторых комментариев выше.
  • 1
    Отличное решение и работает очень хорошо
Показать ещё 3 комментария
37

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

ПРЕДУПРЕЖДЕНИЕ: это приведет к удалению файла из всей истории

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

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

  • 8
    Это изменит все идентификаторы коммитов, тем самым нарушая слияния из веток за пределами вашей копии репозитория.
  • 17
    ВНИМАНИЕ: это удалит файл из всей вашей истории. Это было то, что я искал, хотя, чтобы удалить совершенно ненужный и слишком большой файл (вывод, который никогда не должен был быть зафиксирован), который был зафиксирован давным-давно в истории версий.
18
  • Обновите файл .gitignore - например, добавьте папку, в которую вы не хотите отслеживать .gitignore.

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

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


Совет шляпы @AkiraYamamoto для указания в правильном направлении.

  • 1
    Как насчет понижения рейтинга из-за того, что он на самом деле не будет работать, так как вам все равно нужен ключ -r для рекурсивного запуска rm :) (Кто-то не правильно скопировал)
  • 1
    Предупреждение: эта техника на самом деле не заставляет git игнорировать файл, а фактически заставляет git удалить файл. Это означает, что если вы используете это решение, то всякий раз, когда кто-либо другой делает git pull, файл будет удален. Так что это на самом деле не игнорируется. Посмотрите решение, предлагающее вместо этого git update-index --assume-unchanged, для решения исходного вопроса.
13

Я думаю, что, возможно, 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 должны отсутствовать. Это решение подходит для файлов конфигураций, когда каждый разработчик имеет собственную конфигурацию проекта.

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

8

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

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

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
6

Ответ от Matt Fear был самым эффективным IMHO. Следующее - это просто PowerShell script для тех, кто в 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 .
  • 0
    В какой ситуации этот список файлов не будет равен рекурсивному --cached?
5

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

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

5

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

  • 30
    git rm --cached удалит файл из индекса, не удаляя его с диска, поэтому нет необходимости перемещать / копировать его
3

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

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

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

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

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

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

2

Мне понравился ответ 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"
  • удалить проигнорированные файлы из индекса
  • stage.gitignore и файлы, которые вы только что удалили.
  • совершить
2

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

1

Это уже не проблема в последнем 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
  • 0
    Я рад, что Git сейчас делает это. Однако ОП спрашивал о том, чтобы не отслеживать изменения в файлах, присутствующих в .gitignore, а не в удаленных файлах, по-прежнему отображающих состояние.
0

В случае уже совершенного 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

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

-6

На mac:

$ git --version
git version 2.6.4

$ uname -a
Darwin MUSRV186016-382 14.5.0 Darwin Kernel Version 14.5.0: Sun Sep 25 22:07:15 PDT 2016; root:xnu-2782.50.9~1/RELEASE_X86_64 x86_64

1. Удалите файлы DS_Store из списка файлов, отслеживаемых git в ветке foo:

$ for file in $(git ls-tree -r foo --name-only | grep -i DS_Store); do git rm --cached $file; done

2. Commit:

$ git commit -m "Removed .DS_Store files"

3. Проверьте, что файлы больше не отслеживаются

$ git ls-tree -r foo --name-only
# There should not be anything coming back

4. Нажмите, чтобы удалить их из пульта:

$ git push

Ещё вопросы

Сообщество Overcoder
Наверх
Меню