Перейти к основному содержимому

Удалить файл из репозитория Git, не удаляя его локально

· 6 мин. чтения

1. Обзор

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

2. Введение в проблему

Как обычно, давайте разберемся с проблемой на примере. Допустим, мы работаем с репозиторием Git myRepo :

$ ls -l
total 12
drwxr-xr-x 2 kent kent 60 May 12 23:00 logs/
-rw-r--r-- 1 kent kent 26 May 11 13:22 README.md
-rw-r--r-- 1 kent kent 21 May 11 13:22 some-file.txt
-rw-r--r-- 1 kent kent 16 May 12 22:40 user-list.txt

Мы клонировали репозиторий на локальный, и, как видно из вывода ls , у нас есть три файла и каталог журналов в репозитории.

Теперь предположим, что мы хотели бы удалить файл user-list.txt и каталог журналов из репозитория Git. Однако мы не хотим удалять их из нашей локальной рабочей копии.

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

Мы знаем, что команда git rm user-list.txt удалит файл из репозитория. Но он также удаляет локальный файл.

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

Далее, давайте посмотрим, как решить эту проблему более эффективно.

3. Использование команды git rm –cached

Мы упоминали, что git rm FILE по умолчанию удалит файлы из индекса и локального рабочего дерева .

Однако команда git rm предоставляет параметр –cached , позволяющий нам удалять файлы только из индекса репозитория и оставлять локальный файл нетронутым.

Далее попробуем с файлом user-list.txt :

$ git rm --cached user-list.txt
rm 'user-list.txt'

Как видно из приведенного выше вывода, файл user-list.txt был удален. Итак, теперь давайте выполним команду git status , чтобы проверить это:

$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: user-list.txt

Untracked files:
(use "git add <file>..." to include in what will be committed)
user-list.txt

Как мы видим, файл user-list.txt « удален ». Кроме того, поскольку его локальная копия все еще существует, он помечен как «неотслеживаемый».

Аналогичным образом мы можем удалить каталог журналов . Однако, поскольку это каталог, нам нужно дополнительно передать параметр -r (рекурсивно) команде git rm :

$ git rm --cached -r logs
rm 'logs/server.log'

Теперь давайте зафиксируем наши изменения:

$ git commit -m 'remove user-list.txt and logs'
[master ee8cfe8] remove user-list.txt and logs
2 files changed, 4 deletions(-)
delete mode 100644 logs/server.log
delete mode 100644 user-list.txt

Затем давайте проверим текущие подготовленные файлы с помощью команды git ls-files :

$ git ls-files -c
.gitignore
README.md
some-file.txt

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

При желании мы можем добавить их в файл .gitignore , чтобы Git больше не мог их отслеживать.

4. Удалите все файлы, определенные в .gitignore

Иногда мы хотим проверить индекс Git и удалить все файлы, определенные в .gitignore . Предположим, что мы закончили с нашим определением .gitignore . Тогда простым способом будет трехэтапный процесс:

  • Сначала удалите все файлы из индекса: git rm -r –cached
  • Затем снова подготовьте все файлы. Файлы, определенные в .gitignore , будут автоматически игнорироваться: git add
  • Зафиксируйте наши изменения: git commit -m «правильное сообщение фиксации»

В качестве альтернативы мы можем найти и удалить только те файлы, которые в настоящее время отслеживаются, но их следует игнорировать . Команда git ls-files может помочь нам найти файлы.

Давайте вернем нашу предыдущую фиксацию и снова удалим файл user-list.txt и каталог журналов . На этот раз давайте сначала добавим их в файл .gitignore :

$ cat .gitignore
user-list.txt
logs/

Далее давайте выясним, какие файлы мы хотели бы удалить из индекса Git:

$ git ls-files -i -c -X .gitignore
logs/server.log
user-list.txt

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

Теперь давайте объединим команды git rm –cached и git ls-files , чтобы удалить их одним выстрелом :

$ git rm --cached $(git ls-files -i -c -X .gitignore)
rm 'logs/server.log'
rm 'user-list.txt'

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

Теперь, если мы проверим поставленные файлы, удаленные исчезнут:

$ git ls-files -c
.gitignore
README.md
some-file.txt

И, конечно же, user-list.txt и logs/ все еще находятся в нашем локальном рабочем дереве:

$ ls -l
total 12
drwxr-xr-x 2 kent kent 60 May 13 00:45 logs/
-rw-r--r-- 1 kent kent 26 May 11 13:22 README.md
-rw-r--r-- 1 kent kent 21 May 11 13:22 some-file.txt
-rw-r--r-- 1 kent kent 16 May 13 00:45 user-list.txt

5. Удаленные файлы остаются в истории Git

Мы решили нашу проблему с помощью команды git rm –cached . Однако мы должны помнить, что мы просто удалили файл из индекса отслеживания Git . Мы по-прежнему можем видеть файл и его содержимое в истории коммитов Git. Например, мы все еще можем увидеть содержимое user-list.txt , проверив предыдущую фиксацию:

$ git show 668fa2f user-list.txt
commit 668fa2f...
Author: ...
Date: ...

add user-list.txt and some-file.txt

diff --git a/user-list.txt b/user-list.txt
new file mode 100644
index 0000000..3da7fab
--- /dev/null
+++ b/user-list.txt
@@ -0,0 +1,3 @@
+kent
+eric
+kevin

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

Если это так, нам нужно удалить файлы из истории коммитов Git .

6. Заключение

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

Кроме того, мы рассмотрели быстрый способ удаления всех файлов, определенных в файле .gitignore . Наконец, мы должны помнить, что файлы, удаленные git rm –cached, все еще живут в истории коммитов Git.