Почему есть два способа удалить файл из Git?


1170

Иногда git предлагает git rm --cachedудалить файл, иногда git reset HEAD file. Когда я должен использовать что?

РЕДАКТИРОВАТЬ:

D:\code\gt2>git init
Initialized empty Git repository in D:/code/gt2/.git/
D:\code\gt2>touch a

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       a
nothing added to commit but untracked files present (use "git add" to track)

D:\code\gt2>git add a

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   a
#
D:\code\gt2>git commit -m a
[master (root-commit) c271e05] a
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a

D:\code\gt2>touch b

D:\code\gt2>git status
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       b
nothing added to commit but untracked files present (use "git add" to track)

D:\code\gt2>git add b

D:\code\gt2>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   b
#

20
Почему? Я бы сказал, что это потому, что интерфейс командной строки git эволюционировал органически и никогда не подвергался серьезной реструктуризации для обеспечения согласованности. (Если вы не согласны, обратите внимание , как git rmможет как этап удаления , а также убрать из буфера в дополнение )
Roman Старков

3
@romkyns: я согласен, что интерфейс Git имеет несколько странностей, потому что он эволюционировал органически, но удаление, безусловно, является обратной функцией добавления, так что не логично rmли отменить add? Как вы думаете, как rmсебя вести?
Zaz

6
Единственный фактический ответ на ваш вопрос заключается в том, что сразу после a git initнет HEADсброса.
Майлз Рут

Лучшие документы для этого: help.github.com/articles/changing-a-remote-s-url
ScottyBlades

4
@Zaz, я выскажу свое мнение. rmподразумевает удаление в контексте unix. Это не противоположность добавления в индекс. Функция удаления файлов не должна быть перегружена функциями изменения промежуточного состояния. Если есть детали реализации, которые делают их удобными для объединения, это просто указывает на отсутствие вдумчивого уровня абстракции в git, что сделало бы понятным юзабилити.
Джошуа Голдберг

Ответы:


1894

git rm --cached <filePath> не удаляет файл, он фактически удаляет файл (ы) из репозитория (при условии, что он уже был зафиксирован ранее), но оставляет файл в рабочем дереве (оставляя файл без отслеживания).

git reset -- <filePath>будет отменять любые поэтапные изменения для данного файла (ов).

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

Обновление git 2.24
В этой новой версии git вы можете использовать git restore --stagedвместо git reset. Смотрите Git Docs .


71
Я бы сказал, git rm --cachedunstages файл, но не удаляет его из рабочего каталога.
Пьер де ЛЕСПИНАЙ

4
Удалить файл, подготовленный для добавления, чтобы он больше не был подготовлен, можно с уверенностью назвать «удаление файла, подготовленного для добавления», верно? Конечный результат - это не поэтапное удаление , это точно, поэтому я думаю, что недоразумение вполне понятно.
Роман Старков

4
Поэтому, как правило, можно использовать git rm --cached <filePath>для удаления некоторых файлов из репо после того, как они осознали, что их никогда не должно быть в репо: так что, скорее всего, запустите эту команду и затем добавите соответствующие файлы gitignore. Я прав?
Adrien Be

13
С таким большим количеством голосов, как по вопросу, так и по ответу, я бы сказал, что, очевидно, мы хотим иметь unstageкоманду git.
milosmns

4
Теперь «git status» советует: используйте «git restore --staged <file> ...» для удаления
yucer

334

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

git reset HEAD file(который по умолчанию использует --mixedфлаг) отличается тем, что в случае, когда файл уже находится в репо, он заменяет индексную версию файла на версию из репозитория (HEAD), фактически не внося изменений в него.

В случае неверсионного файла он собирается удалить весь файл, поскольку его не было в HEAD. В этом аспекте git reset HEAD fileи то git rm --cachedже самое, но они не совпадают (как объяснено в случае файлов уже в репо)

К вопросу о Why are there 2 ways to unstage a file in git?- в git никогда не бывает только одного способа что-либо сделать. в этом вся прелесть :)


7
И принятый ответ, и этот замечательный, и объясните, почему вы будете использовать один против другого. Но они не дают прямого ответа на неявный вопрос о том, почему git предлагает два разных метода. В первом случае в примере с OP только что был сделан git init. В этом случае git предлагает «git rm --cached», потому что в этот момент нет никаких коммитов в репозитории, и поэтому HEAD недопустим. "git reset HEAD - a" выдает: "fatal: не удалось разрешить 'HEAD' как действительный ref."
sootsnoot

5
с «git checkout», не потеряете ли вы все изменения, внесенные в файл? Это не то же самое, что unstaging файла, если я не понимаю.
Джон Дейган

there is never really only one way to do anything in git. that is the beauty of it- Хм ... почему? это всегда здорово, когда есть только один очевидный путь. это экономит много нашего времени и памяти в мозгу))
Ото Шавадзе

128

Довольно просто:

  • git rm --cached <file> заставляет git полностью прекратить отслеживание файла (оставляя его в файловой системе, в отличие от простого git rm*)
  • git reset HEAD <file> unstages любые изменения, сделанные в файле с момента последнего коммита (но не отменяет их в файловой системе, в отличие от того, что может указывать имя команды **). Файл остается под контролем ревизии.

Если файл ранее не контролировался ревизиями (то есть, вы растягиваете файл, который вы только что отредактировали git addв первый раз), тогда эти две команды имеют одинаковый эффект, следовательно, это «два способа сделать что-то» ».

* Имейте в виду предостережение, которое @DrewT упоминает в своем ответе относительно git rm --cachedфайла, который был ранее передан в хранилище. В контексте этого вопроса о файле, который был только что добавлен и еще не зафиксирован, не о чем беспокоиться.

** Мне было страшно долгое время использовать команду git reset из-за ее названия - и до сих пор я часто просматриваю синтаксис, чтобы убедиться, что я не облажался. ( обновление : я наконец-то нашел время, чтобы подвести итоги использования git resetстраницы tldr , так что теперь у меня есть лучшая ментальная модель того, как это работает, и краткий справочник, когда я забываю некоторые детали.)


Этоgit rm <file> --cached
новорожденный

8
Я действительно не думаю, что редактирование 4 августа 2015 года для этого ответа было общим улучшением. Возможно, это исправило техническую корректность (я не чувствую себя достаточно квалифицированным, чтобы оценить это), но я боюсь, что это сделало тон ответа гораздо менее доступным, введя такой язык, как ", снимает необходимость начать отслеживание файла, который в настоящее время не отслежен. ", и используя жаргон, как" index "и" HEAD ", именно такие вещи отпугивают новичков. Если кто-то может, отредактируйте, чтобы восстановить более дружественный для новичков язык.
Waldyrious

5
Согласитесь с @waldyrious. Оригинальный ответ, возможно, не был прямо из учебника git, но он ответил на вопрос на достаточном техническом уровне. Технические детали должны были быть разъяснены в комментариях, а не в виде редактирования, которое затенило первоначальное намерение.
Саймон Робб

Я отменил редактирование. Я полагаю, что сообщество провело достаточную проверку (в предыдущих комментариях и голосованиях по ним), что изменение нанесло ущерб ясности ответа.
Валдириус

Примечание. @DrewT предупреждает, что при использовании rm --cachedи нажатии любой, кто тянет одну и ту же ветку, будут фактически удалены файлы из их рабочего дерева.
Том Хейл

53

Этот поток немного староват, но я все же хочу добавить небольшую демонстрацию, так как это все еще не интуитивная проблема:

me$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   to-be-added
#   modified:   to-be-modified
#   deleted:    to-be-removed
#

me$ git reset -q HEAD to-be-added

    # ok

me$ git reset -q HEAD to-be-modified

    # ok

me$ git reset -q HEAD to-be-removed

    # ok

# or alternatively:

me$ git reset -q HEAD to-be-added to-be-removed to-be-modified

    # ok

me$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   to-be-modified
#   deleted:    to-be-removed
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   to-be-added
no changes added to commit (use "git add" and/or "git commit -a")

git reset HEAD(без -q) выдает предупреждение об измененном файле и его код выхода равен 1, что будет считаться ошибкой в ​​скрипте.

Edit: git checkout HEAD to-be-modified to-be-removedтакже работает для unstaging, но полностью удаляет изменения из рабочей области

Обновление git 2.23.0: время от времени команды меняются. Теперь git statusговорит:

  (use "git restore --staged <file>..." to unstage)

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


Спасибо, не совсем понятно из первых двух ответов (возможно, только из-за моего незнания терминологии), что git reset локально оставил изменения в файле (в отличие от git checkout, который их вернул).
суповая собака

Вы должны поместить предупреждение в начале о версии, потому что старая версия удаляет файлы в новых версиях
Луис Маурисио

@ LuisMauricio, какая команда удаляет файлы?
Даниэль Олдер

@DanielAlder sry, я только что проверил, это не удаляет, моя ошибка.
Луис Маурисио

36

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

git stash
git stash pop

это выполнит сброс в HEAD и повторно применяет ваши изменения, позволяя вам повторно подготовить отдельные файлы для фиксации. это также полезно, если вы забыли создать функциональную ветвь для запросов на извлечение ( git stash ; git checkout -b <feature> ; git stash pop).


3
Это чистое решение и гораздо менее тревожное, чем ввод "git rm"
Subimage

1
git stashимеет другие связанные преимущества, потому что он создает записи в reflog, которые будут доступны в будущем. если сомневаетесь, продолжайте и сделайте git stash(например git stash save -u "WIP notes to self"('-u' включает любые новые / неотслеживаемые файлы в коммите stash) ... затем попытайтесь git reflog show stashпросмотреть список коммитов stash и их sha. Я рекомендую оболочку псевдоним какalias grs="git reflog show stash"
cweekly

15

Эти 2 команды имеют несколько тонких отличий, если рассматриваемый файл уже находится в репозитории и находится под контролем версий (ранее зафиксирован и т. Д.):

  • git reset HEAD <file> unstages файл в текущем коммите.
  • git rm --cached <file>будет также разархивировать файл для будущих коммитов. Это unstaged, пока это не будет добавлено снова с git add <file>.

И есть еще одно важное отличие:

  • После запуска git rm --cached <file>и отправки ветки на удаленный компьютер любой пользователь, извлекающий ветку с пульта, получит ФАКТИЧЕСКИ удаленный файл из своей папки, даже если в вашем локальном рабочем наборе файл просто не будет отслежен (то есть физически не удален из папки).

Это последнее различие важно для проектов, которые включают в себя конфигурационный файл, в котором у каждого разработчика в команде разные настройки (т. Е. Разные базовые url, ip или настройки порта), поэтому, если вы используете git rm --cached <file>кого-то, кто тянет вашу ветку, придется вручную повторно создайте конфигурацию, или вы можете отправить их своим, и они смогут повторно отредактировать его обратно в свои настройки ip (и т. д.), потому что удаление влияет только на людей, которые тянут вашу ветку с пульта.


10

Допустим, вы прошли stageчерез весь каталог git add <folder>, но вы хотите исключить файл из поэтапного списка (т. Е. Список, который генерируется при запуске git status) и сохранить изменения в исключенном файле (вы работали над чем-то, и он не готов к фиксации, но Вы не хотите потерять свою работу ...). Вы можете просто использовать:

git reset <file>

Когда вы запустите git status, вы увидите, что какие бы файлы вы ни resetнаходились, unstagedа остальные файлы вы addedвсе еще в stagedсписке.


10

1.

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   a

(используйте "git rm --cached ..." для удаления)

  • Git это система указателей

  • у вас еще нет коммита, чтобы изменить указатель на

  • Единственный способ «извлечь файлы из корзины, на которую указывают», - это удалить файлы, которые вы сказали git, чтобы следить за изменениями.

2.

D:\code\gt2>git commit -m a
[master (root-commit) c271e05] a
0 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 a

git commit -ma

  • Вы совершили « спас »

3.

D:\code\gt2>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   b
#

(используйте «git reset HEAD ...» для удаления)

  • вы сделали коммит в своем коде в это время
  • Теперь вы можете сбросить указатель на ваш коммит « вернуться к последнему сохранению »

1
На самом деле это единственный ответ, который правильно отвечает на вопрос, ИМО. На самом деле он отвечает на вопрос, который заключается не в том, «в чем различия между« git rm --cached »и« git reset HEAD », а« почему git непоследовательно дает обе опции? », Ответ на этот вопрос заключается в том, что нет HEAD для сброса когда ты git initв первый раз.
Майлз Рут

5

Я удивлен, что никто не упомянул git reflog ( http://git-scm.com/docs/git-reflog ):

# git reflog
<find the place before your staged anything>
# git reset HEAD@{1}

Reflog - это история git, которая не только отслеживает изменения в репозитории, но также отслеживает действия пользователя (например, извлечение, возврат в другую ветку и т. Д.) И позволяет отменить эти действия. Таким образом, вместо того, чтобы unstaging файл, который был ошибочно подготовлен, где вы можете вернуться к точке, где вы не ставили файлы.

Это похоже на, git reset HEAD <file>но в некоторых случаях может быть более детальным.

Извините - на самом деле не отвечаю на ваш вопрос, а просто указываю еще один способ нестандартных файлов, которые я использую довольно часто (например, мне очень нравятся ответы Райана Стюарта и Вальдириуса.);) Надеюсь, это поможет.


5

Просто используйте:

git reset HEAD <filename>

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


3

Мне кажется, что git rm --cached <file>файл удаляется из индекса, не удаляя его из каталога, в котором обычная программа git rm <file>будет выполнять оба действия, точно так же, как ОС rm <file>удаляет файл из каталога без удаления версий.


1

Только для версий 2.23 и выше,

Вместо этих предложений, вы можете использовать git restore --staged <file>для того, чтобы unstageфайл (ы).


Он работает с обоими вариантами, --stageа также --staged.
dhana1310
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.