Остановить git от бесшумно скрытых игнорируемых файлов (ошибка в git?)?

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

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

git init
mkdir ignored/
echo stuff > ignored/file
echo otherstuff > otherfile
git add -A
# Opps added my ignored folders files
# Forgeting to rm --cache
echo ignored/ > .gitignore ; git add .gitignore
git commit -m 'accidentally add ignored/file'
git status
touch dummyfile; git add dummyfile
# Remembered to rm --cache
git rm --cache -rf ignored/file #This file is now vulnerable to clobber
git commit -m 'add stuff'
echo somechange >> ignored/file

## Wait for it..
git checkout HEAD~
## somechange has been silently clobbered!!

# Please paste first paragraph, observe and then past the second.
# Note both commits have the correct ignore file and are not immune!

(cd в пустую папку, прежде чем вставлять код в терминал)

Во всяком случае, чтобы предотвратить этот тихий clobber?

Ответ 1

Это не ошибка (или, по крайней мере, разработчики git не считают ее одним).

Содержимое .gitignore - это не "файлы, которые следует игнорировать" или "имена путей, которые не следует трогать"; вместо этого они являются "путями, которые автоматически не добавляются, и их подавляют, пока они не отображаются как неотображенные" (что делает .gitignore бедным именем).

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

Для некоторых файлов вы можете использовать git update-index --assume-unchanged или (лучше) git update-index --skip-worktree, но в общем случае, если вы случайно сделали файл, которого не должны иметь, и вы хотите его игнорировать, вы должны "переписать историю", чтобы полностью избавиться от хранилища, чтобы получить хорошее поведение. Это не слишком сложно, если вы ничего не толкнули и не имеете никаких коммитов, содержащих нежелательный файл, но намного сложнее, если вы нажали или имели много таких коммитов.

См. также Git - Разница между "предполагать-без изменений" и "skip-worktree" .

[Текст добавлен в декабре 2016 года]

Технические данные

Для Git существует краткое, простое и сладкое определение отслеживаемого файла: Файл отслеживается тогда и только тогда, когда в нем есть запись.

"Принимать без изменений" и "пропускать рабочую строку" - это флаги, которые вы можете вручную установить или очистить в индексе. Они являются отдельными флагами и могут устанавливаться индивидуально, хотя неясно, что значит устанавливать оба. Цель "принять без изменений" состоит в том, чтобы просто сделать git быстрее, разрешив предположить, что файл не изменен и, следовательно, не нужно обновлять с помощью git add, в то время как флаг "skip worktree" состоит в том, чтобы сказать git "руки": не просто принимайте его без изменений, старайтесь не менять его. Это сложнее, чем может показаться на первый взгляд; более подробно об этом см. выше ссылка.

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

Если файл не отслеживается, его можно игнорировать и не игнорировать. Для них существует множество источников: не только верхний уровень .gitignore, который содержит список (по одному на строку) варианта git pathspec, который ограничен сопоставлением и отрицанием glob, но также и .gitignore файлов внутри каждый подкаталог в репозитории, файл $GIT_DIR/info/exclude, если он существует, и файл с именем core.excludesFile, если эта запись конфигурации существует. Чтобы узнать, проигнорирован ли файл и почему он игнорируется, используйте git check-ignore, доступный с git версии 1.8.2. Опция -v сообщает, какой файл управления помечен как файл игнорируется.

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