Git文件操作详解
检查当前文件状态
要查看哪些文件处于什么状态,可以用 git status 命令。 如果在克隆仓库后立即使用此命令,会看到类似这样的输出:
在 上一个笔记中我们提到过在工作目录下每个文件有两种状态(已跟踪或未跟踪),其中已跟踪分为,已修改,未修改或已放入暂存区,下面我们就详细的看一下文件的状态
我们在工作目录创建一个abc.txt文件 这个时候什么都不做,直接使用 git status 命令,会显示如下信息
这说明我们刚刚创建的文件(在没有执行任何命令的情况下)属于未跟踪文件。
跟踪新文件
我们使用 git add 命令,这个时候什么提示也没有,但是的的确确,我们将文件已经添加到了暂存区(staged或index);我们在使用 git status 命令
暂存已修改文件
现在我们来修改一个已被跟踪的文件。 如果你修改了一个名为 readme.txt 的已被跟踪的文件,然后运行 git status 命令,会看到下面内容:
也就是说,使用 git add 命令将文件添加到暂存区,文件就属于未修改状态了,当你修改了文件,这个时候文件就属于已修改状态,所以要重新使用 git add命令来添加;换句话说,使用 git add 命令将文件放到了暂存区是一个版本,当你修改了这个文件又是另一个版本了。当你修改了文件而未使用 git add添加到暂存区,直接使用 git commit 时,会将以前的版本提交到版本库,而不是你最新修改的。说白了使用 git commit 时会按照你暂存区中的数据版本为准。
状态简览
git status 命令的输出十分详细,但其用语有些繁琐。 如果你使用 git status -s 命令或 git status --short 命令,你将得到一种更为紧凑的格式输出。 运行 git status -s ,状态报告输出如下:
1.出现在 右边的 M 表示该文件 被修改了但是 还没放入暂存区
2.出现在 左边的 M 表示该文件 被修改了并 放入了暂存区
A:新添加到暂存区中的文件前面有 A 标记
??:新添加的未跟踪文件前面有 ?? 标记
例如,上面的状态报告显示: README 文件在工作区被修改了但是还没有将修改后的文件放入暂存区,lib/simplegit.rb 文件被修改了并将修改后的文件放入了暂存区。 而 Rakefile 在工作区被修改并提交到暂存区后又在工作区中被修改了,所以在暂存区和工作区都有该文件被修改了的记录。
忽略文件
一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。 通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。 在这种情况下,我们可以创建一个名为 .gitignore 的文件,列出要忽略的文件模式。 来看一个实际的例子:
文件 .gitignore 的格式规范如下:
GitHub 有一个十分详细的针对数十种项目及语言的 .gitignore 文件列表,你可以在 https://github.com/github/gitignore 找到它.
查看具体已暂存和未暂存的修改
可以用 git diff 命令。 你可能通常会用它来回答这两个问题:当前做的哪些更新还没有暂存? 有哪些更新已经暂存起来准备好了下次提交?尽管 git status 已经通过在相应栏下列出文件名的方式回答了这两个问题, git diff将通过文件补丁的格式显示具体哪些行发生了改变。
我个人觉得可以使用这个命令来查看你上次做了哪些更改,方便继续工作。例如你这个项目写到一半的时候,就去写别的项目了,可以使用这个命令来查看你这个项目写到了那个地方。或者说查看添加了那些代码,修改或删除了那些代码。
假如再次修改 abcc.txt 文件后暂存,然后编辑 readme.txt 文件后先不暂存, 运行 git status 命令将会看到:
个人理解就是,你哪些文件被修改了但是还没有放入暂存区。
若要查看 已暂存的将要添加到下次提交里的内容,可以用 git diff --cached 命令。(Git 1.6.1 及更高版本还允许使用 git diff --staged,效果是相同的,但更好记些。)
移除文件
另外如果你不小心删错了文件,这个时候还没有提交那么可以是用:
如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。
检查当前文件状态
要查看哪些文件处于什么状态,可以用 git status 命令。 如果在克隆仓库后立即使用此命令,会看到类似这样的输出:
$ git status
On branch master
nothing to commit, working directory clean
这说明你现在的工作目录相当干净。换句话说,所有已跟踪文件在上次提交后都未被更改过。 此外,上面的信息还表明,当前目录下没有出现任何处于未跟踪状态的新文件,否则 Git 会在这里列出来。 最后,该命令还显示了当前所在分支,并告诉你这个分支同远程服务器上对应的分支没有偏离。 现在,分支名是 “master”,这是默认的分支名。
在 上一个笔记中我们提到过在工作目录下每个文件有两种状态(已跟踪或未跟踪),其中已跟踪分为,已修改,未修改或已放入暂存区,下面我们就详细的看一下文件的状态
我们在工作目录创建一个abc.txt文件 这个时候什么都不做,直接使用 git status 命令,会显示如下信息
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
abcc.txt
nothing added to commit but untracked files present (use "git add" to track)
在状态报告中可以看到新建的 abcc.txt 文件出现在
Untracked files 下面。 未跟踪的文件意味着 Git 在之前的快照(提交)中没有这些文件;Git 不会自动将之纳入跟踪范围,除非你明明白白地告诉它“我需要跟踪该文件”, 这样的处理让你不必担心将生成的二进制文件或其它不想被跟踪的文件包含进来。
这说明我们刚刚创建的文件(在没有执行任何命令的情况下)属于未跟踪文件。
跟踪新文件
我们使用 git add 命令,这个时候什么提示也没有,但是的的确确,我们将文件已经添加到了暂存区(staged或index);我们在使用 git status 命令
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: abcc.txt
只要在
Changes to be committed 这行下面的,就说明是已暂存状态。 如果此时提交(
git commit),那么该文件此时此刻的版本将被留存在历史记录中。 你可能会想起之前我们使用 git init 后就运行了 git add (files) 命令,开始跟踪当前目录下的文件。 git add 命令使用文件或目录的路径作为参数;如果参数是目录的路径,该命令将递归地跟踪该目录下的所有文件。
暂存已修改文件
现在我们来修改一个已被跟踪的文件。 如果你修改了一个名为 readme.txt 的已被跟踪的文件,然后运行 git status 命令,会看到下面内容:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: abcc.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: readme.txt
文件
readme.txt 出现在
Changes not staged for commit 这行下面,说明已跟踪文件的内容发生了变化,但还没有放到暂存区(也就是说文件现在是已修改状态)。 要暂存这次更新,需要运行
git add命令。 这是个多功能命令:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等。 将这个命令理解为“添加内容到下一次提交中”而不是“将一个文件添加到项目中”要更加合适。 现在让我们运行
git add 将"
readme.txt "放到暂存区,然后再看看
git status 的输出:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: abcc.txt
modified: readme.txt
现在两个文件都已暂存,下次提交时就会一并记录到仓库。 假设此时,你想要在
readme.txt 里再加条注释, 重新编辑存盘后,准备好提交。 不过且慢,再运行
git status 看看:
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: abcc.txt
modified: readme.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: readme.txt
怎么回事? 现在
readme.txt 文件同时出现在暂存区和非暂存区。 这怎么可能呢? 好吧,实际上 Git 只不过暂存了你运行
git add命令时的版本, 如果你现在提交,
readme.txt 的版本是你最后一次运行
git add命令时的那个版本,而不是你运行
git commit时 ,在工作目录中的当前版本。 所以,运行了
git add 之后又作了修订的文件,需要重新运行
git add把最新版本重新暂存起来。
也就是说,使用 git add 命令将文件添加到暂存区,文件就属于未修改状态了,当你修改了文件,这个时候文件就属于已修改状态,所以要重新使用 git add命令来添加;换句话说,使用 git add 命令将文件放到了暂存区是一个版本,当你修改了这个文件又是另一个版本了。当你修改了文件而未使用 git add添加到暂存区,直接使用 git commit 时,会将以前的版本提交到版本库,而不是你最新修改的。说白了使用 git commit 时会按照你暂存区中的数据版本为准。
状态简览
git status 命令的输出十分详细,但其用语有些繁琐。 如果你使用 git status -s 命令或 git status --short 命令,你将得到一种更为紧凑的格式输出。 运行 git status -s ,状态报告输出如下:
$ git status -s
M README
MM Rakefile
A lib/git.rb
M lib/simplegit.rb
?? LICENSE.txt
M:修改过的文件前面有 M 标记( M 有两个可以出现的位置 )
1.出现在 右边的 M 表示该文件 被修改了但是 还没放入暂存区
2.出现在 左边的 M 表示该文件 被修改了并 放入了暂存区
A:新添加到暂存区中的文件前面有 A 标记
??:新添加的未跟踪文件前面有 ?? 标记
例如,上面的状态报告显示: README 文件在工作区被修改了但是还没有将修改后的文件放入暂存区,lib/simplegit.rb 文件被修改了并将修改后的文件放入了暂存区。 而 Rakefile 在工作区被修改并提交到暂存区后又在工作区中被修改了,所以在暂存区和工作区都有该文件被修改了的记录。
忽略文件
一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。 通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。 在这种情况下,我们可以创建一个名为 .gitignore 的文件,列出要忽略的文件模式。 来看一个实际的例子:
$ cat .gitignore
*.[oa]
*~
第一行告诉 Git 忽略所有以 .o 或 .a 结尾的文件。一般这类对象文件和存档文件都是编译过程中出现的。 第二行告诉 Git 忽略所有以波浪符(~)结尾的文件,许多文本编辑软件(比如 Emacs)都用这样的文件名保存副本。 此外,你可能还需要忽略 log,tmp 或者 pid 目录,以及自动生成的文档等等。 要养成一开始就设置好 .gitignore 文件的习惯,以免将来误提交这类无用的文件。
文件 .gitignore 的格式规范如下:
- 所有空行或者以 # 开头的行都会被 Git 忽略。
- 可以使用标准的 glob 模式匹配。
- 匹配模式可以以(/)开头防止递归。
- 匹配模式可以以(/)结尾指定目录。
- 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。
GitHub 有一个十分详细的针对数十种项目及语言的 .gitignore 文件列表,你可以在 https://github.com/github/gitignore 找到它.
查看具体已暂存和未暂存的修改
可以用 git diff 命令。 你可能通常会用它来回答这两个问题:当前做的哪些更新还没有暂存? 有哪些更新已经暂存起来准备好了下次提交?尽管 git status 已经通过在相应栏下列出文件名的方式回答了这两个问题, git diff将通过文件补丁的格式显示具体哪些行发生了改变。
我个人觉得可以使用这个命令来查看你上次做了哪些更改,方便继续工作。例如你这个项目写到一半的时候,就去写别的项目了,可以使用这个命令来查看你这个项目写到了那个地方。或者说查看添加了那些代码,修改或删除了那些代码。
假如再次修改 abcc.txt 文件后暂存,然后编辑 readme.txt 文件后先不暂存, 运行 git status 命令将会看到:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: abcc.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: readme.txt
要查看
尚未暂存的文件更新了哪些部分,不加参数直接输入
git diff:
$ git diff
diff --git a/readme.txt b/readme.txt
index 60f5ef2..c848f1d 100644
--- a/readme.txt
+++ b/readme.txt
@@ -3,3 +3,5 @@
我要删除这一行
+
+这是我新添加的
\ No newline at end of file
此命令比较的是工作目录中当前文件和暂存区域快照之间的差异, 也就是修改之后还没有暂存起来的变化内容。
个人理解就是,你哪些文件被修改了但是还没有放入暂存区。
若要查看 已暂存的将要添加到下次提交里的内容,可以用 git diff --cached 命令。(Git 1.6.1 及更高版本还允许使用 git diff --staged,效果是相同的,但更好记些。)
$ git diff --staged
diff --git a/abcc.txt b/abcc.txt
index 013b5bc..d8036c1 100644
--- a/abcc.txt
+++ b/abcc.txt
@@ -1,2 +1,2 @@
-Git is a distributed version control system.
+Git is a version control system.
Git is free software.
\ No newline at end of file
请注意,
git diff 本身只显示尚未暂存的改动,而不是自上次提交以来所做的所有改动。 所以有时候你一下子暂存了所有更新过的文件后,运行
git diff 后却什么也没有,就是这个原因。说白了只要你将你修改的文件暂存了,使用
git diff是什么也不显示的。
移除文件
要从 Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。 可以用 git rm 命令完成此项工作,并连带从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件清单中了。个人理解就是,要删除的文件状态必须是未修改状态并且不再暂存区。
如果只是简单地从工作目录中手工删除文件,运行 git status 时就会在 “Changes not staged for commit” 部分(也就是 未暂存清单)看到:
$ rm PROJECTS.md
$ git status
On branch master
Your branch is up-to-date with 'origin/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)
deleted: PROJECTS.md
no changes added to commit (use "git add" and/or "git commit -a")。
然后再运行 git rm 记录此次移除文件的操作:
$ git rm PROJECTS.md
rm 'PROJECTS.md'
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: PROJECTS.md
下一次提交时,该文件就不再纳入版本管理了。 如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项 -f(译注:即 force 的首字母)。 这是一种安全特性,用于防止误删还没有添加到快照的数据,这样的数据不能被 Git 恢复。
另外一种情况是,我们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。 换句话说,你想让文件保留在磁盘,但是并不想让 Git 继续跟踪。 当你忘记添加 .gitignore 文件,不小心把一个很大的日志文件或一堆 .a 这样的编译生成文件添加到暂存区时,这一做法尤其有用。 为达到这一目的,使用 --cached 选项:
$ git rm --cached README
另外如果你不小心删错了文件,这个时候还没有提交那么可以是用:
$ git checkout -- fileName.txt
git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。
移动文件
不像其它的 VCS 系统,Git 并不显式跟踪文件移动操作。 如果在 Git 中重命名了某个文件,仓库中存储的元数据并不会体现出这是一次改名操作。 不过 Git 非常聪明,它会推断出究竟发生了什么,至于具体是如何做到的,我们稍后再谈。
既然如此,当你看到 Git 的 mv 命令时一定会困惑不已。 要在 Git 中对文件改名,可以这么做:
$ git mv file_from file_to