在前面的实战部分,已经体验过怎么将文件添加到本地的git仓库了。
工作目录(实战中的
learngit
文件夹)下的每一个文件都不外乎这两种状态:已跟踪 或 未跟踪。
已跟踪:已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后, 它们的状态可能是未修改,已修改或已放入暂存区。简而言之,已跟踪的文件就是 git 已经知道的文件。
未跟踪:除了已跟踪的文件,都是未跟踪文件,它们既不存在于上次快照的记录中,也没有被放入暂存区。初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态,因为 git 刚刚检出了它们, 而你尚未编辑过它们。
编辑过某些文件之后,由于自上次提交后你对它们做了修改,git 将它们标记为已修改文件。 在工作时,你可以选择性地将这些修改过的文件放入暂存区,然后提交所有已暂存的修改,如此反复。下图为文件的状态变化周期
接着往下看,看完还不懂,提上你40米长的大刀来砍我。
检查当前文件状态
这里再次使用learngit
目录,在上面的实践结束后,该目录下只有一个readme.txt
文件,我们使用git status
命令查看一下文件处于什么状态,使用此命令,会看到类似这样的输出:
$ git status
On branch master # 说明当前所在分支 master 为默认分支名
nothing to commit, working tree clean # emmm~ 你细细拼一下这什么意思
这个输出表明自实践结束后,还未对目录里的文件进行过任何修改,也没有出现任何处于未跟踪状态的新文件,否则 git 会在这里列出来,最后,该命令还显示了当前所在分支(之后会介绍分支)。
现在,在learngit
目录下,新建一个test.txt
文件,文件内容为:
this is a test file
此时,使用git status
命令,就能看到一个新的未被跟踪的文件:
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt
nothing added to commit but untracked files present (use "git add" to track)
在状态报告中可以看到新建的 test.txt
文件出现在 Untracked files
下面。未跟踪的文件意味着 git 仓库中没有这些文件;git 不会自动将之纳入跟踪范围。现在搞懂未跟踪文件的意思了吧。
如果想要将
test.txt
也加入git仓库,让git进行管理,就要先将test.txt
加入暂存区,进行跟踪。
已经提到很多次暂存区了,那么究竟什么是暂存区?
工作区、暂存区和版本库
工作区(Working Directory):
就是在电脑里能看到的目录,比如learngit
文件夹就是一个工作区:
版本库(Repository):
工作区有一个隐藏目录.git
,这个不算工作区,而是Git的版本库。
Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master
,以及指向master
的一个指针叫HEAD
。
分支和HEAD
的概念我们以后再讲。
前面有讲过使用git add
命令将文件加入暂存区,实际上是把要提交的文件的所有修改添加到暂存区,然后执行git commit
就可以一次性把暂存区的所有修改提交到本地仓库的分支上。
暂存区(Stage或Index):
一般存放在 .git
目录下的 index 文件(.git/index
)中,所以我们把暂存区有时也叫作索引(index
)。暂存区可以理解为一个虚拟工作区,这个虚拟工作区会跟踪工作区的文件变化(增删改等操作)。这个工作区的位于.git 文件夹下的index
文件中。
需要理解一点:当需要对工作区的修改提交到版本库前,暂存区会与工作区进行差异比较,如果工作区与暂存区的文件不一致,那么需要同步工作区的修改到暂存区,然后才可以提交到版本库。从这个意义讲,暂存区可以说是工作区和版本库的桥梁。好处自然是可以在真正提交到版本库之前做任意的操作,在需要真正提交的时候 commit 到版本库 。当执行完git commit
命令时,暂存区会被清空。
实践出真知,我们来实践一下:
在之前的操作中,添加了一个新的文件test.txt
,之后就没有其他操作了,现在,我们修改一下readme.txt
文件,比如加上一句话:
Git is a version control system.
Git is free software.
Git has a mutable index called stage.
再用git status
查看一下状态:
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: readme.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt
no changes added to commit (use "git add" and/or "git commit -a")
git非常清楚的告诉了我们,readme.txt
被修改了,而test.txt
的状态是未被跟踪的。
现在,使用git add
命令,把readme.txt
和test.txt
都添加到暂存区,使用git status
再次查看一下状态:
# 将readme.txt和test.txt一次性添加到暂存区
$ git add readme.txt test.txt
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: readme.txt
new file: test.txt
此时,暂存区的状态就变成这样了:
所以,git add
命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git commit
就可以一次性把暂存区的所有修改提交到分支。
$ git commit -m 'understand how stage works'
[master 8ec32e3] understand how stage works
2 files changed, 3 insertions(+), 1 deletion(-)
create mode 100644 test.txt
一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的:
$ git status
On branch master
nothing to commit, working tree clean
现在版本库变成了这样,暂存区就没有任何内容了:
搞定收工!
跟踪新文件
使用命令git add
开始跟踪一个文件,这个已经讲过很多次了,就不赘述了,不过要注意的是文件名不能写错:
# 这里要写文件的全名
$ git add test.txt
# 文件名没有写全,或者写错,会出现下面的信息
$ git add test
fatal: pathspec 'test' did not match any files
$ git add test.tx
fatal: pathspec 'test.tx' did not match any files
暂存已修改的文件
暂存已修改的文件,使用的命令是:
没错,还是git add
,惊不惊喜意不意外?这个前面也已经提到了,就不赘述了。
git add
命令是个多功能命令:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等。将这个命令理解为“精确地将内容添加到下一次提交中”而不是“将一个文件添加到项目(仓库)中”要更加合适。
这里有一个需要注意的点:同一个文件,同时出现在暂存区和非暂存区。
我们来重现一下这个问题:
首先,修改readme.txt
文件,将内容改为:
Git is a version control system.
Git is free software.
Git has a mutable index called stage.
Git is so good
使用git add
命令将readme.txt
放入暂存区,使用git add
命令查看:
$ git add readme.txt
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: readme.txt
再次修改readme.txt
文件:
Git is a version control system.
Git is free software.
Git has a mutable index called stage.
Git is so good
Linus is god
此时再次运行git status
命令看看:
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: readme.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: readme.txt
现在readme.txt
文件同时出现在暂存区和非暂存区。实际上 git 只不过暂存了你最后一次运行 git add
命令时的版本。 如果你现在提交,你在git add
命令之后的修改,不会被提交到仓库中。 所以,运行了 git add
之后又进行过修改的文件,需要重新运行 git add
把最新版本重新暂存起来:
$ git add readme.txt
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: readme.txt
现在把readme.txt
提交到仓库吧
$ git commit -m 'update readme.txt'
[master 0086f1a] update readme.txt
1 file changed, 3 insertions(+), 1 deletion(-)
状态简述
git status
命令的输出十分详细,显得有些繁琐。 git 有一个选项可以帮你缩短状态命令的输出,这样可以以简洁的方式查看更改。 如果你使用 git status -s
命令或 git status --short
命令,你将得到一种格式更为紧凑的输出。你可能会看到以下输出:
$ git status -s
M helloGit.txt
MM README.md
M test.txt
A testC.c
?? testDoc.doc
新添加的未跟踪文件前面有 ??
标记,新添加到暂存区中的文件前面有 A
标记,修改过的文件前面有 M
标记。输出中有两栏,左栏指明了暂存区的状态,右栏指明了工作区的状态。例如,上面的状态报告显示: helloGit.txt
文件在工作区已修改但尚未暂存,而 test.txt
文件已修改且已暂存。 README.md
文件已修改,暂存后又作了修改,因此该文件的修改中既有已暂存的部分,又有未暂存的部分
是不是有点晕?没关系,我们再实践一下,重现上面的输出。
上面的输出涉及了5个文件,我们learngit
目录中已经有两个了,再新建三个文件app.txt
、google.txt
、ali.txt
,文件内容任意就行。
-
将
app.txt
、google.txt
添加到暂存区中$ git add app.txt google.txt # 单独提交 app.txt文件 $ git commit -m 'commit app.txt' app.txt
再修改
app.txt
文件内容 修改
test.txt
文件,并使用git add
命令将其加入暂存区修改
readme.txt
文件,并使用git add
命令将其加入暂存区,之后再次进行修改-
使用
git status -s
命令查看状态$ git status -s M app.txt A google.txt MM readme.txt M test.txt ?? ali.txt
如果步骤1中没有单独提交
app.txt
文件,那么输出应该是这样的:$ git status -s AM app.txt A google.txt MM readme.txt M test.txt ?? ali.txt
小结:使用git status -s
查看状态,状态会以两列显示,左栏表示暂存区的状态,右栏表示工作区的状态。
完美复现了,现在将他们提交到仓库,以免忘记,影响下次实验:
$ git commit -a -m 'update file content and add app.txt google.txt'
[master b9cb70f] update file content and add app.txt google.txt
4 files changed, 7 insertions(+), 2 deletions(-)
create mode 100644 app.txt
create mode 100644 google.txt
忽略文件
一般来讲,总有不需要放入git仓库进行管理的文件,也不想看到它出现在未跟踪列表,在这种情况下,我们可以创建一个名为 .gitignore
的文件,列出要忽略的文件的模式。 来看一个实际的 .gitignore
例子:
$ cat .gitignore # cat命令可以查看文件内容
*.[oa] # 文件内容
*~ # 文件内容
第一行告诉 git 忽略所有以 .o
或 .a
结尾的文件。一般这类对象文件和存档文件都是编译过程中出现的。 第二行告诉 git 忽略所有名字以波浪符~
结尾的文件,许多文本编辑软件(比如 Emacs)都用这样的文件名保存副本。 此外,你可能还需要忽略 log,tmp 或者 pid 目录,以及自动生成的文档等等。 要养成一开始就为你的新仓库设置好 .gitignore 文件的习惯,以免将来误提交这类无用的文件。
文件 .gitignore
的格式规范如下:
- 所有空行或者以
#
开头的行都会被 Git 忽略。 - 可以使用标准的 glob 模式匹配,它会递归地应用在整个工作区中。
- 匹配模式可以以(
/
)开头防止递归。 - 匹配模式可以以(
/
)结尾指定目录。 - 要忽略指定模式以外的文件或目录,可以在模式前加上叹号(
!
)取反。
所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。 星号(*
)匹配零个或多个任意字符;[abc]
匹配任何一个列在方括号中的字符 (这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c); 问号(?
)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符, 表示所有在这两个字符范围内的都可以匹配(比如 [0-9]
表示匹配所有 0 到 9 的数字)。 使用两个星号(**
)表示匹配任意中间目录,比如 a/**/z
可以匹配 a/z
、 a/b/z
或 a/b/c/z
等。
我们再看一个 .gitignore
文件的例子:
# 忽略所有的 .a 文件
*.a
# 跟踪所有的 lib.a,即便你在前面忽略了 .a 文件
!lib.a
# 只忽略当前目录下的 TODO 文件,而不忽略 subdir/TODO
/TODO
# 忽略任何目录下名为 build 的文件夹
build/
# 忽略 doc/notes.txt,但不忽略 doc/server/arch.txt
doc/*.txt
# 忽略 doc/ 目录及其所有子目录下的 .pdf 文件
doc/**/*.pdf
在最简单的情况下,一个仓库可能只根目录下有一个 .gitignore
文件,它递归地应用到整个仓库中。 然而,子目录下也可以有额外的 .gitignore
文件。子目录中的 .gitignore
文件中的规则只作用于它所在的目录中。
Tip:GitHub 有一个十分详细的针对数十种项目及语言的 .gitignore
文件列表, 你可以在 https://github.com/github/gitignore 找到它。
实践:在做完上一个示例之后,我们使用git status -s
命令查看文件状态
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
ali.txt
nothing added to commit but untracked files present (use "git add" to track)
git 会一直提示ali.txt
未被跟踪,这是不是很烦人,假如这个文件,就是个临时文件,没有价值的文件,我们不想让git管理它,那么,这个时候,我们可以新建一个.gitignore
文件,将它加入git仓库进行管理。
首先,新建.gitignore
文件。这里说一下,在Windows系统下,是不能直接新建以.
开头的文件,除非你这么命名.gitignore.txt
(这个是可行的),那么假如你新建的是.gitignore.txt
文件,并添加了如下内容:
ali.txt
那看看git是怎么看待这个文件的:
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore.txt
ali.txt
nothing added to commit but untracked files present (use "git add" to track)
没错,就是个普通文件,它并没有让git忽略掉ali.txt
文件,那咋办?没错,你可能会想到手动改后缀名呗,那我们来看一下:
坑爹的Windows就是不让你改,想砸电脑啊有木有?那可千万别砸,大好几千呢,总有解决问题的方法的。
之前介绍过Notepad++这款软件,这个软件真的超好用,没装的装一下。打开Notepad++,新建文件(中文界面,都是各界精英肯定会),然后在文件中输入一下内容:
ali.txt
没错,就上面一行,然后Ctrl + S
保存,这个时候,重点来了(敲黑板):
然后去learngit
这个目录下看看,有没有.gitignore
文件,接下来就是见证奇迹的时刻:
.gitignore
诞生了,现在来看看git是怎么看待这个文件的:
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
nothing added to commit but untracked files present (use "git add" to track)
发现没有,ali.txt
文件不见了,这说明我们的.gitignore
文件生效了,这个时候,我们只需要把.gitignore
文件添加到暂存区,然后commit提交到git仓库就行了。当然你不暂存,不提交,都是可以的,它照样可以生效,只是不暂存不提交,你之后每次使用git status
命令,都能看见它,你说烦不烦,所以还是乖乖暂存提交吧
$ git add .gitignore
$ git commit -m 'add file .gitignore '
[master f2e4019] add file .gitignore
1 file changed, 1 insertion(+)
create mode 100644 .gitignore
# 在查看一下文件状态
$ git status
On branch master
nothing to commit, working tree clean
我们再来看看ali.txt
是不是还在我们磁盘上:
嗯,还在,说明我真的没有删它,它真的是被git给忽略掉了。OK,示例结束。
这里有一个奇葩的需求:让.gitignore
忽略自己,经典的我忽略我自己,可还行。
但是 .gitignore
作为一个仓库结构的一部分,本身就应该是存在的,如果什么都不想忽略,那么就没必要建一个文件了。真碰到这种需求,总归还是要解决的。如果把.gitignore
加到.gitignore
文件中,是不起作用的。
解决办法:
编辑当前项目下的./git/info/exclude
文件,然后将需要忽略提交的.gitignore
文件路径写入就行了,注意写入文件的路径是相对项目根目录而言的。
查看已暂存和未暂存的修改
如果你想知道某个文件具体修改了什么地方,可以用 git diff
命令。
现在我们再次修改readme.txt
文件后暂存,然后编辑test.txt
文件后先不暂存,运行git status
命令将会看到:
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: readme.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: test.txt
要查看尚未暂存的文件更新了哪些部分,不加参数直接输入 git diff
:
$ git diff
diff --git a/test.txt b/test.txt
index 879fceb..9e431f3 100644
--- a/test.txt
+++ b/test.txt
@@ -1,2 +1,3 @@
this is a test file
-add some file # 前面是-说明是删除的内容
\ No newline at end of file # 在文件末尾没有换行符,这个是编辑器和系统的问题,我没有乱说,我有图为证,后面上Linux下的图
+add some file # 前面是+说明这是新增的内容 至于为什么先删后加,通过和Linux系统的对比,我认为是系统或者编辑器的问题,如果有知道真相的小伙伴,可以告诉我,帮我纠正一下,共同进步,后面上Linux下的图
+test git diff # 前面是+说明这是新增的内容
\ No newline at end of file
此命令比较的是修改之后还没有暂存起来的变化内容。
想要查看已暂存文件修改了什么内容,可以用git diff --staged
命令。这条命令将比对已暂存文件与最后一次提交的文件差异:
$ git diff --staged
diff --git a/readme.txt b/readme.txt
index 7cc6fe7..67386fd 100644
--- a/readme.txt
+++ b/readme.txt
@@ -4,4 +4,5 @@ Git has a mutable index called stage.
Git is so good
Linus is god
add app.txt file
-add google.txt file
\ No newline at end of file
+add google.txt file
+test git diff
\ No newline at end of file
Linux系统下,同样这两个文件,同样的内容,同样的操作方式:
可以看到,只是系统和编辑器不同,Windows多出了几行无关的内容(警告信息),并且出现了先删除后添加的情况,所以我猜测是系统和编辑器的不同造成的,如果不是,希望知情的小伙伴可以告诉我真正原因!
回归正题,请注意,git diff
本身只显示尚未暂存的改动,而不是自上次提交以来所做的所有改动。 所以有时候你一下子暂存了所有更新过的文件,运行 git diff
后却什么也没有,就是这个原因。
像之前说的,暂存 test.txt
后再修改(自己随便修改一下),可以使用 git status
查看已被暂存的修改或未被暂存的修改。 如果我们的环境(终端输出)看起来如下:
$ git add test.txt
# 修改test.txt文件
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: readme.txt
modified: test.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: test.txt
这里test.txt
出现在了暂存和未暂存的两种状态,现在运行git diff
看暂存前后的变化:
$ git diff
warning: LF will be replaced by CRLF in test.txt.
The file will have its original line endings in your working directory # 可能会出现警告信息,请忽略它
diff --git a/test.txt b/test.txt
index 117f3a6..940c7b0 100644
--- a/test.txt
+++ b/test.txt
@@ -1,3 +1,4 @@
this is a test file
add some file
test git diff
+# test line
然后用git diff --cached
查看已经暂存起来的变化(--staged
和cached
作用是一样的):
$ git diff --cached
# readme.txt
diff --git a/readme.txt b/readme.txt
index 7cc6fe7..bcb9d8f 100644
--- a/readme.txt
+++ b/readme.txt
@@ -4,4 +4,5 @@ Git has a mutable index called stage.
Git is so good
Linus is god
add app.txt file
-add google.txt file
\ No newline at end of file
+add google.txt file
+test git diff
# test.txt 重点看这里 是不是没有# test line这一行内容
diff --git a/test.txt b/test.txt
index 879fceb..117f3a6 100644
--- a/test.txt
+++ b/test.txt
@@ -1,2 +1,3 @@
this is a test file
-add some file
\ No newline at end of file
+add some file
+test git diff
这里我们先不提交,因为下面我们就要来讲提交了。
然而并没有!
分析文件差异也可以使用图形化的工具或外部 diff 工具来比较差异,比如DIffMerge,其他工具,自己去找一下。
还可以使用 git difftool
命令来调用 emerge 或 vimdiff 等软件输出 diff 的分析结果。 使用 git difftool --tool-help
命令来看你的系统支持哪些 git Diff 插件。git difftool
会依次输出所有文件的分析结果,如果想看某一个文件的diff结果,就加上文件全名,比如:xxx路径下的test.txt,应该写成:git difftool xxx/text.txt
提交更新
在提交之前,务必确认还有什么已修改或者新建的文件还没有git add
过,否则提交的时候不会记录这些尚未暂存的变化。 这些已修改但未暂存的文件只会保留在本地磁盘。所以,每次准备提交前,先用 git status
看下,你所需要的文件是不是都已暂存起来了, 然后再运行提交命令 git commit
:
$ git commit
直接运行该命令,会启动你选择的文本编辑器来输入提交说明,如果不输入提交说明,就会提交失败。
编辑器会显示类似下面的文本信息(默认使用的是vim编辑器,可配置自己的编辑器):
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# Changes to be committed:
# modified: readme.txt
# modified: test.txt
#
# Changes not staged for commit:
# modified: test.txt
#
可以看到,默认的提交消息包含最后一次运行 git status
的输出,放在注释行里,另外开头还有一个空行,供你输入提交说明。 你完全可以去掉这些注释行,不过留着也没关系,多少能帮你回想起这次提交更新的内容有哪些。
更详细的内容修改提示可以用 git commit -v
查看,这会将你所作的更改的 diff 输出呈现在编辑器中,以便让你知道本次提交具体作出哪些修改。
Tip:启动的编辑器是通过 Shell 的环境变量 EDITOR
指定的,一般为 vim 或 emacs。 当然也可以 使用 git config --global core.editor
命令设置你喜欢的编辑器。例如:git config --global core.editor vim
另外,你也可以在 git commit
命令后添加 -m
选项,将提交信息与命令放在同一行,如下所示:
$ git commit -m 'commit by xxx'
[master c8731ea] commit by xxx
2 files changed, 4 insertions(+), 2 deletions(-)
现在已经进行了一次提交! 可以看到,提交后它会告诉你,当前是在哪个分支(master
)提交的,本次提交的完整 SHA-1 校验和(c8731ea
)是什么,以及在本次提交中,有多少文件修订过,多少行添加和删改过。
跳过使用暂存区提交更新
git 提供了一个跳过使用暂存区域的方式, 只要在提交的时候,给 git commit
加上 -a
选项,git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add
步骤:
$ git status # 查看文件状态 ,可以看到test.txt未被暂存
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: test.txt
no changes added to commit (use "git add" and/or "git commit -a")
# 提交未被暂存的文件
$ git commit -a -m 'use commit -a -m command to commit'
warning: LF will be replaced by CRLF in test.txt.
The file will have its original line endings in your working directory
[master d1e9096] use commit -a -m command to commit
1 file changed, 1 insertion(+)
# 再次查看文件状态
$ git status
On branch master
nothing to commit, working tree clean
提交之前不再需要 git add
文件了。 这是因为 -a
选项使本次提交包含了所有修改过的文件。 这很方便,但是要小心,有时这个选项会将不需要的文件添加到提交中。
移除文件
要从 git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。 可以用 git rm
命令完成此项工作,并连带从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件清单中了。
如果只是简单地从工作目录中手工删除文件,运行 git status
时就会在 “Changes not staged for commit” 部分(也就是 未暂存清单)看到:
$ rm test.txt # 从硬盘中删除test.txt文件,见图
$ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: test.txt
no changes added to commit (use "git add" and/or "git commit -a")
然后再运行 git rm
记录此次移除文件的操作:
$ git rm test.txt # 从暂存区和硬盘中删除test.txt
rm 'test.txt'
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: test.txt
# 提交,提交之后,上面 deleted 信息就不见了
$ git commit -m 'delete test.txt'
[master a8de87f] delete test.txt
1 file changed, 4 deletions(-)
delete mode 100644 test.txt
提交之后,test.txt
文件就不在纳入版本管理,也就是从仓库删除了。如果要删除之前修改过或已经放到暂存区的文件,则必须使用强制删除选项 -f
(译注:即 force 的首字母),否则会报错。 这是一种安全特性,用于防止误删尚未添加到快照的数据,强制删除的数据不能被 git 恢复。
另外一种情况是,我们想把文件从 git 仓库中删除(或从暂存区域移除),但仍然希望保留在当前工作目录中。 换句话说,你想让文件保留在磁盘,但是并不想让 git 继续跟踪。 当你忘记添加 .gitignore
文件,不小心把一个很大的日志文件或一堆 .a
这样的编译生成文件添加到暂存区时,这一做法尤其有用。 为达到这一目的,使用 --cached
选项:
$ git rm --cached readme.txt
rm 'readme.txt'
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: readme.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
readme.txt
# 提交
$ git commit -m 'delete readme.txt from repository and stage'
[master cfef1ea] delete readme.txt from repository and stage
1 file changed, 8 deletions(-)
delete mode 100644 readme.txt
# 查看文件状态,readme.txt为未跟踪状态,并没有从本地硬盘删除
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
readme.txt
nothing added to commit but untracked files present (use "git add" to track)
git rm
命令后面可以列出文件或者目录的名字,也可以使用 glob
模式(正则表达式)。比如:
$ git rm log/\*.log
此命令删除 log/
目录下扩展名为 .log
的所有文件。 类似的比如:
$ git rm \*~
该命令会删除所有名字以 ~
结尾的文件。
移动文件
要在 git 中对文件改名,可以这么做:
$ git mv file_from file_to
此时查看文件状态信息
# 先修改文件名,比如:把app.txt改为application.txt
$ git mv app.txt application.txt
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: app.txt -> application.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
readme.txt
# 记得提交一下
$ git commit -m 'rename app.txt -> application.txt'
其实,运行 git mv
就相当于运行了下面三条命令:
$ mv app.txt application.txt
$ git rm app.txt
$ git add application.txt
分开操作,git 也会意识到这是一次重命名,所以不管何种方式结果都一样。 两者唯一的区别是,mv
是一条命令而非三条命令,直接用 git mv
方便得多。 不过有时候用其他工具批处理重命名的话,要记得在提交前删除旧的文件名,再添加新的文件名。