Git: Как удалить файл из индекса, не удаляя файлы из любого хранилища

163

Когда вы используете

git rm --cached myfile

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

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

Редактировать: уточнил, я надеюсь.

Флетчер Мур
источник
Возможно, вы могли бы расширить свой вариант использования. Индекс является промежуточной областью для следующего коммита, так почему вы хотите удалить файл из индекса, если не хотите удалять его из текущей ветви?
CB Bailey
1
Мне жаль. Я хотел сказать, что myfile по какой-то причине был зафиксирован и распространен давно. Теперь цель состоит в том, чтобы удалить его из версий, не удаляя его из систем людей. Причина, по которой это произошло, заключается в том, что я случайно установил версию файла конфигурации. Файл конфигурации необходим, поэтому я не хочу удалять его из сторонних систем.
Флетчер Мур
Я отредактировал вопрос, чтобы быть более понятным.
Флетчер Мур
3
git rm --cached не удалит файл из другого рабочего каталога. Файл будет удален только в том случае, если кто-то, работающий в этом каталоге, выполнит извлечение. Файл может быть легко восстановлен с помощью «git checkout HEAD @ {1} foo» (если выполняется сразу после извлечения).
Уильям Перселл,

Ответы:

119

Я не думаю, что коммит Git может записать намерение типа «прекратите отслеживать этот файл, но не удаляйте его».

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


Сохранить копию, применить удаление, восстановить

Вероятно, самое простое, что нужно сделать, - это попросить своих нижестоящих пользователей сохранить копию файла, вытащить удаление и восстановить файл. Если они вытягивают через rebase и «переносят» изменения в файл, они получат конфликты. Чтобы разрешить такие конфликты, используйте git rm foo.conf && git rebase --continue(если конфликтующий коммит имеет изменения помимо тех, что были в удаленном файле) или git rebase --skip(если конфликтующий коммит был изменен только на удаленный файл).

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

Если они уже извлекли ваш коммит удаления, они могут восстановить предыдущую версию файла с помощью git show :

git show @{1}:foo.conf >foo.conf

Или с Git Checkout (за комментарий Уильяма Перселла; но не забудьте удалить его из индекса!):

git checkout @{1} -- foo.conf && git rm --cached foo.conf

Если они предприняли другие действия с тех пор, как вы удалили ваше удаление (или они тянут с ребазой в отдельную ГОЛОВКУ), им может понадобиться что-то другое @{1}. Они могли бы использоватьgit log -g чтобы найти коммит непосредственно перед тем, как вытащить ваше удаление.


В комментарии вы упоминаете, что файл, который вы хотите «отследить, но сохранить», является своего рода файлом конфигурации, который необходим для запуска программного обеспечения (непосредственно из репозитория).

Сохранить файл как «по умолчанию» и вручную / автоматически активировать его

Если не совсем неприемлемо продолжать поддерживать содержимое файла конфигурации в хранилище, вы можете переименовать отслеживаемый файл из (например) foo.confв, foo.conf.defaultа затем проинструктировать пользователей cp foo.conf.default foo.confпосле применения коммита переименования. Или, если пользователи уже используют какую-либо существующую часть хранилища (например, сценарий или другую программу, сконфигурированную с помощью содержимого в хранилище (например, Makefileили аналогичное)) для запуска / развертывания вашего программного обеспечения, вы можете включить механизм запуска по умолчанию в запуск / запуск. процесс развертывания:

test -f foo.conf || test -f foo.conf.default &&
    cp foo.conf.default foo.conf

При наличии такого механизма по умолчанию пользователи должны иметь возможность получить коммит, который переименовывается foo.confвfoo.conf.default без необходимости делать какие - либо дополнительные работы. Кроме того, вам не нужно вручную копировать файл конфигурации, если в будущем вы сделаете дополнительные установки / репозитории.

В любом случае переписывание истории требует ручного вмешательства ...

Если недопустимо поддерживать содержимое в хранилище, вы, вероятно, захотите полностью удалить его из истории, например git filter-branch --index-filter …. Это равносильно переписыванию истории, что потребует ручного вмешательства для каждой ветви / репозитория (см. Раздел «Восстановление из исходной перебазировки» на справочной странице git rebase ). Специальная обработка, требуемая для вашего файла конфигурации, будет просто еще одним шагом, который нужно выполнить при восстановлении после перезаписи:

  1. Сохраните копию файла конфигурации.
  2. Восстановить от переписать.
  3. Восстановите файл конфигурации.

Проигнорируйте это, чтобы предотвратить повторение

Какой бы метод вы ни использовали, вы, вероятно, захотите включить имя файла конфигурации в .gitignoreфайл в хранилище, чтобы никто не мог случайно по ошибке git add foo.conf(это возможно, но требует -f/ --force). Если у вас есть более одного файла конфигурации, вы можете подумать о «перемещении» их всех в один каталог и игнорировании всего этого (под «перемещением» я имею в виду изменение того, где программа ожидает найти свои файлы конфигурации, и получение пользователей (или механизм запуска / развертывания) для копирования / перемещения файлов в новое место; очевидно, вы не захотите помещать файл в каталог, который вы будете игнорировать).

Крис Джонсен
источник
5
Невероятно тщательный ответ. Спасибо!
Флетчер Мур
Ответ Том Пауэр, приведенный ниже, кажется, противоречит вашему первому предложению.
Майк С
1
@MikeS: Эффект --{,no-}assume-unchangedявляется чисто локальным: его состояние не записывается напрямую в коммитах. Это может помочь предотвратить внесение репозиторием новых изменений в файл, но не удалит его из-под контроля версий. Если вы можете установить его для всех релевантных, связанных, не обнаженных репозиториев, то это может помочь вашей ситуации, но это не то, что вы можете напрямую толкнуть + вытянуть / перебазировать в другие связанные, не обнаженные репозитории (особенно те, которые вы не контролируете, согласно поясняющим комментариям первоначального аскера по этому вопросу: см. «системы людей» / «чужие системы»).
Крис Джонсен
1
I do not think a Git commit can record an intention like “stop tracking this file, but do not delete it”.- теперь можно, сgit rm --cached foo.conf
Ник Волынкин
1
@NickVolynkin: Разве вопрос уже не указывает на то, что этого недостаточно для цели спрашивающего: (автоматически) хранить файл, когда полученная фиксация загружается в другой репозиторий?
Крис Джонсен
86

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

http://gitready.com/intermediate/2009/02/18/temporarily-ignoring-files.html

работал нормально для меня и не упоминается до сих пор.

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

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

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

Если вы когда-нибудь хотели положить его обратно.

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

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

Том Пауэр
источник
7
Это не ответ на конкретную проблему, упомянутую. Он будет работать только для вашего локального репозитория, поэтому каждый пользователь должен сделать это сам. Это боль.
KPM
28

Чтобы удалить файл из индекса, используйте:

git reset myfile

Это не должно повлиять на вашу локальную или чужую копию.

Armand
источник
1
resetудаляет файл из индекса только в том случае, если файл отсутствует в текущем коммите HEAD, в противном случае он просто возвращает версию индекса в текущую версию HEAD.
CB Bailey
1
Возможно, я неправильно понял вопрос, но, похоже, он связан с удалением файла из индекса без каких-либо изменений.
Арманд
Как сказал Чарльз, сброс не «удаляет файл». Введите git help reset для получения дополнительной информации.
Саймон Б.
4
Это отвечает на вопрос «Как удалить файл из индекса без удаления файлов из любого хранилища». Хотя ОП на самом деле спрашивал: «Как мне удалить файл без удаления локальной копии».
Хевсонизм
@hewsonism ОП сказал «любая файловая система» (даже до каких-либо изменений).
jbobbins
19
  1. git rm --cached remove_file
  2. добавить файл в gitignore
  3. git add .gitignore
  4. git commit -m "Excluding"
  5. Радоваться, веселиться ;)
Анастасия Ермолик
источник
1
Это самое простое.
Жюльен
15

После выполнения git rm --cachedкоманды, попробуйте добавить myfileв .gitignoreфайл (создать, если он не существует). Это должно сказать Git игнорировать myfile.

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

awgy
источник
1

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

git log --pretty="format:" --name-only -n1 | xargs git checkout HEAD^1

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

Том Винер
источник
0

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

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

https://help.github.com/articles/remove-sensitive-data/

На самом деле вы можете перейти к шагу 3, если вы находитесь в своем локальном git-репозитории и вам не нужно выполнять пробный запуск. В моем случае мне были нужны только шаги 3 и 6, так как я уже создал свой файл .gitignore и находился в репозитории, над которым я хотел поработать.

Чтобы увидеть ваши изменения, вам может понадобиться перейти в корень GitHub вашего репозитория и обновить страницу. Затем перейдите по ссылкам, чтобы добраться до старого коммита, в котором когда-то был файл, чтобы увидеть, что он теперь удален. Для меня простое обновление старой страницы коммита не показывало изменения.

Сначала это выглядело устрашающе, но на самом деле было легко и сработало как шарм! :-)

SherylHohman
источник