1. 概述
Git 已经成为广泛使用的分布式版本控制系统。在这个教程中,我们将探讨如何从 Git 仓库中移除文件或目录,但保留本地副本。
2. 问题介绍
通常,我们可以通过一个例子来理解这个问题。假设我们在处理一个名为 myRepo 的 Git 仓库:
$ 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
输出所示,仓库中有三个文件和一个 logs 目录。
现在,假设我们想要从仓库中移除文件 user-list.txt 和 logs 目录,但不希望从本地工作副本中删除它们。常见的场景是,我们可能已经提交了一些文件或目录,然后意识到应该忽略某些文件。因此,我们将从仓库中移除相关文件,保留本地副本,并在 .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 被标记为 "已删除"。由于其本地副本仍然存在,所以被标记为 "未跟踪"。
我们可以同样地移除 logs 目录,但由于它是一个目录,我们需要额外传递 -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 文件和 logs 目录。这次,我们先将它们添加到 .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'
值得注意的是,命令将删除 logs 目录下的所有文件,最终会从索引中删除空的 logs 目录。在这个例子中,logs 目录下只有一个文件。
现在,如果检查暂存的文件,已删除的文件将不再存在:
$ 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 的提交历史中。