1. Overview
1.概述
Git has become a widely used, distributed version control system. In this tutorial, let’s explore how to remove a file or directory from a Git repository but keep its local copy.
Git 已经成为一个广泛使用的分布式版本控制系统。在本教程中,让我们探讨如何从Git仓库中删除一个文件或目录,但保留其本地副本。
2. Introduction to the Problem
2.对问题的介绍
As usual, let’s understand the problem via an example. Let’s say we’re working on a Git repository myRepo:
像往常一样,让我们通过一个例子来理解这个问题。假设我们正在处理一个 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
We’ve cloned the repository to local, and as the ls output shows, we have three files and a logs directory in the repository.
我们已经将版本库克隆到本地,正如ls输出所显示的,我们在版本库中有三个文件和一个logs目录。
Now, let’s say we would like to remove the file user-list.txt and the logs directory from the Git repository. However, we don’t want to remove them from our local working copy.
现在,假设我们想从 Git 仓库中删除文件 user-list.txt 和 logs 目录。然而,我们并不想从本地工作副本中删除它们。
A common scenario would be we’ve committed some files or directories, then realized that we should ignore some files. Therefore, we’ll remove the related files from the repository, keep the local copies, and add the corresponding patterns to the .gitignore file so that Git won’t track those files anymore.
一个常见的情况是,我们提交了一些文件或目录,然后意识到我们应该忽略一些文件。因此,我们将从仓库中删除相关文件,保留本地副本,并在.gitignore文件中添加相应的模式,这样Git就不会再追踪这些文件。
We know the git rm user-list.txt command will remove the file from the repository. But, it removes the local file as well.
我们知道git rm user-list.txt命令会从版本库中删除该文件。但是,它同时也删除了本地文件。
Of course, we can move the file and directory to another directory, submit a commit, then copy them back to the local working directory. It solves the problem. However, this approach is inefficient, particularly when the file or directory is large.
当然,我们可以把文件和目录移到另一个目录,提交一个提交,然后把它们复制回本地工作目录。这就解决了问题。然而,这种方法效率很低,特别是当文件或目录很大的时候。
Next, let’s see how to solve this problem more efficiently.
接下来,让我们看看如何更有效地解决这个问题。
3. Using the git rm –cached Command
3.使用git rm -cached命令
We’ve mentioned that git rm FILE will remove files from the index and local working tree by default.
我们已经提到,git rm FILE 将默认删除索引和本地工作树中的文件。
However, the git rm command provides the –cached option to allow us only to remove files from the repository’s index and keep the local file untouched.
然而,git rm命令提供了-cached选项,允许我们只从版本库的索引中删除文件,而保持本地文件不被触动。
Next, let’s try it with the user-list.txt file:
接下来,让我们用user-list.txt文件试试。
$ git rm --cached user-list.txt
rm 'user-list.txt'
As the output above shows, the user-list.txt file has been removed. So now, let’s execute the git status command to verify it:
正如上面的输出显示,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
As we can see, the user-list.txt is “deleted“. Further, since its local copy is still there, it has been marked as “untracked”.
我们可以看到,user-list.txt是”deleted“。此外,由于它的本地副本仍然存在,它已被标记为 “未跟踪”。
We can similarly remove the logs directory. However, since it’s a directory, we need to additionally pass the -r (recursively) option to the git rm command:
我们同样可以删除logs目录。然而,由于它是一个目录,我们需要额外传递-r(递归)选项给git rm命令。
$ git rm --cached -r logs
rm 'logs/server.log'
Now, let’s commit our changes:
现在,让我们提交我们的修改。
$ 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
Then, let’s check the currently staged files using the git ls-files command:
然后,让我们使用git ls-files命令来检查当前的阶段性文件。
$ git ls-files -c
.gitignore
README.md
some-file.txt
As the output shows, the target file and directory are not there anymore. Also, the local copies are kept. Therefore, we’ve solved the problem.
正如输出显示的那样,目标文件和目录已经不存在了。同时,本地副本也被保留下来。因此,我们已经解决了这个问题。
If we like, we can add them to the .gitignore file to prevent Git from tracking them again.
如果我们愿意,我们可以把它们添加到.gitignore文件中,以防止Git再次追踪它们。
4. Remove All Files Defined in .gitignore
4.删除.gitignore中定义的所有文件
Sometimes, we want to check Git’s index and remove all files defined in .gitignore. Assume that we’re done with our .gitignore definition. Then, a straightforward way would be a three-step process:
有时,我们想检查Git的索引,并删除.gitignore中定义的所有文件。假设我们已经完成了.gitignore的定义。那么,一个直接的方法就是三步走。
- First, remove all files from the index: git rm -r –cached
- Then, stage all files again. Files defined in .gitignore will be ignored automatically: git add
- Commit our changes: git commit -m “a proper commit message”
Alternatively, we can find and remove only the files that are currently tracked but should be ignored. The git ls-files command can help us to find the files.
另外,我们可以只找到并删除那些当前被追踪但应该被忽略的文件。git ls-files命令可以帮助我们找到这些文件。
Let’s revert our previous commit and remove the user-list.txt file and logs directory again. This time, let’s first add them to the .gitignore file:
让我们恢复之前的提交,再次删除user-list.txt文件和logs目录。这一次,让我们先把它们添加到.gitignore文件中。
$ cat .gitignore
user-list.txt
logs/
Next, let’s find out the files we’d like to remove from the Git index:
接下来,让我们找出我们想从 Git 索引中删除的文件。
$ git ls-files -i -c -X .gitignore
logs/server.log
user-list.txt
As we can see, the command above listed the staged files we would like to remove.
我们可以看到,上面的命令列出了我们想删除的阶段性文件。
Now, let’s combine the git rm –cached and git ls-files commands to remove them in one shot:
现在,让我们结合git rm -cached 和git ls-files命令,一次性删除它们。
$ git rm --cached $(git ls-files -i -c -X .gitignore)
rm 'logs/server.log'
rm 'user-list.txt'
It’s worth mentioning that the command will delete all files under the logs directory, so finally, it will delete the empty logs directory from the index. So, in this example, we have only one file under the logs directory.
值得一提的是,该命令将删除logs目录下的所有文件,所以最后,它将从索引中删除空的logs目录。所以,在这个例子中,我们在logs目录下只有一个文件。
Now, if we check the staged files, the deleted ones are gone:
现在,如果我们检查阶段性文件,被删除的文件就不见了。
$ git ls-files -c
.gitignore
README.md
some-file.txt
And of course, user-list.txt and logs/ are still in our local working tree:
当然,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. The Removed Files Are Still in the Git History
5.被删除的文件仍在Git历史中
We’ve solved our problem using the git rm –cached command. However, We should keep in mind that we’ve merely removed the file from Git’s tracking index. We can still see the file and its content in Git’s commit history. For example, we can still see user-list.txt‘s content by checking a previous commit:
我们已经用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
Knowing this is important since sometimes we’ve forgotten to add some sensitive files to the .gitingore file, such as credentials. But we’ve committed them and pushed the changes to the remote repository. After we realize it, we may want to wipe the sensitive files completely from Git history.
知道这一点很重要,因为有时我们忘了在.gitingore文件中添加一些敏感文件,如证书。但我们已经提交了它们,并将修改推送到远程仓库。在我们意识到这一点后,我们可能想把这些敏感文件从 Git 历史记录中彻底清除。
If this is the case, we need to remove the files from Git’s commit history.
如果是这种情况,我们需要从Git的提交历史中删除这些文件。
6. Conclusion
6.结语
In this article, we’ve shown how to remove a file or directory from a Git repository but keep its local copy through examples.
在这篇文章中,我们通过实例展示了如何从 Git 仓库中删除一个文件或目录,但保留其本地副本。
Also, we’ve addressed a quick way to remove all files defined in the .gitignore file. Finally, we should remember that the files removed by git rm –cached still live in Git’s commit history.
此外,我们还解决了一个快速删除.gitignore文件中定义的所有文件的方法。最后,我们应该记住,被git rm -cached删除的文件仍然存在于 Git 的提交历史中。