Git 的工作就是创建和保存你的项目的快照及与之后的快照进行对比。本章将对有关创建与提交你的项目的快照的命令作介绍。
这里有个重要的概念,Git 有一个叫做“索引”的东东,有点像是你的快照的缓存区。这就使你能够从更改的文件中创建出一系列组织良好的快照,而不是一次提交所有的更改。
简而言之,使用 git add
添加需要追踪的新文件和待提交的更改,
然后使用 git status
和 git diff
查看有何改动,
最后用 git commit
将你的快照记录。这就是你要用的基本流程,绝大部分时候都是这样的。
git add添加文件到缓存
在 Git 中,在提交你修改的文件之前,你需要把它们添加到缓存。如果该文件是新创建的,你可以执行
git add
将该文件添加到缓存,但是,即使该文件已经被追踪了 —— 也就是说,曾经提交过了 ——
你仍然需要执行
回到我们的 Hello World 示例,初始化该项目之后,我们就要用 git add
将我们的文件添加进去了。
我们可以用 git status
看看我们的项目的当前状态。
$ git status -s?? README
?? hello.rb
我们有俩尚未被追踪的文件,得添加一下。
$ git add README hello.rb
现在我们再执行 git status
,就可以看到这俩文件已经加上去了。
$ git status -sA README
A hello.rb
新项目中,添加所有文件很普遍,可以在当前工作目录执行命令:git add .
。
因为 Git 会递归地将你执行命令时所在的目录中的所有文件添加上去,所以如果你将当前的工作目录作为参数,
它就会追踪那儿的所有文件了。如此,git add .
就和 git add README hello.rb
有一样的效果。
此外,效果一致的还有 git add *
,不过那只是因为我们这还木有子目录,不需要递归地添加新文件。
好了,现在我们改个文件,再跑一下 git status
,有点古怪。
$ vim README$ git status -sAM README
A hello.rb
“AM” 状态的意思是,这个文件在我们将它添加到缓存之后又有改动。这意味着如果我们现在提交快照,
我们记录的将是上次跑 git add
的时候的文件版本,而不是现在在磁盘中的这个。
Git 并不认为磁盘中的文件与你想快照的文件必须是一致的 —— (如果你需要它们一致,)得用 git add
命令告诉它。
一言以蔽之,
当你要将你的修改包含在即将提交的快照里的时候,执行 git add
。
任何你没有添加的改动都不会被包含在内 —— 这意味着你可以比绝大多数其他源代码版本控制系统更精确地归置你的快照。
请查看《Pro Git》中 git add
的 “-p” 参数,以了解更多关于提交文件的灵活性的例子。
git status查看你的文件在工作目录与缓存的状态
正如你在 git add
小节中所看到的,你可以执行 git status
命令查看你的代码在缓存与当前工作目录的状态。我演示该命令的时候加了 -s
参数,以获得简短的结果输出。
若没有这个标记,命令 git status
将告诉你更多的提示与上下文欣喜。
以下便是同样状态下,有跟没有 -s
参数的输出对比。简短的输出如下:
$ git status -sAM README
A hello.rb
而同样的状态,详细的输出看起来是这样的:
$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: README
# new file: hello.rb
#
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: README
#
你很容易发现简短的输出看起来很紧凑。而详细输出则很有帮助,提示你可以用何种命令完成你接下来可能要做的事情。
Git 还会告诉你在你上次提交之后,有哪些文件被删除、修改或者存入缓存了。
$ git status -sM README
D hello.rb
你可以看到,在简短输出中,有两栏。第一栏是缓存的,第二栏则是工作目录的。
所以假设你临时提交了 README 文件,然后又改了些,并且没有执行 git add
,你会看到这个:
$ git status -sMM README
D hello.rb
一言以蔽之,执行 git status
以查看在你上次提交之后有啥被修改或者临时提交了,
从而决定自己是否需要提交一次快照,同时也能知道有什么改变被记录进去了。
git diff显示已写入缓存与已修改但尚未写入缓存的改动的区别
git diff
有两个主要的应用场景。我们将在此介绍其一,
在 检查与比较 一章中,我们将介绍其二。
我们这里介绍的方式是用此命令描述已临时提交的或者已修改但尚未提交的改动。
git diff #尚未缓存的改动
如果没有其他参数,git diff
会以规范化的 diff 格式(一个补丁)显示自从你上次提交快照之后尚未缓存的所有更改。
$ vim hello.rb$ git status -s
M hello.rb
$ git diffdiff --git a/hello.rb b/hello.rb
index d62ac43..8d15d50 100644
--- a/hello.rb
+++ b/hello.rb@@ -1,7 +1,7 @@
class HelloWorld
def self.hello
- puts "hello world"+ puts "hola mundo"
end
end
所以,git status
显示你上次提交更新至后所更改或者写入缓存的改动,
而 git diff
一行一行地显示这些改动具体是啥。
通常执行完 git status
之后接着跑一下 git diff
是个好习惯。
git diff --cached #查看已缓存的改动
git diff --cached
命令会告诉你有哪些内容已经写入缓存了。
也就是说,此命令显示的是接下来要写入快照的内容。所以,如果你将上述示例中的 hello.rb
写入缓存,因为 git diff
显示的是尚未缓存的改动,所以在此执行它不会显示任何信息。
$ git status -s
M hello.rb
$ git add hello.rb $ git status -sM hello.rb
$ git diff$
如果你想看看已缓存的改动,你需要执行的是git diff --cached
。
$ git status -sM hello.rb
$ git diff$ $ git diff --cacheddiff --git a/hello.rb b/hello.rb
index d62ac43..8d15d50 100644
--- a/hello.rb
+++ b/hello.rb@@ -1,7 +1,7 @@
class HelloWorld
def self.hello
- puts "hello world"+ puts "hola mundo"
end
end
git diff HEAD 查看已缓存的与未缓存的所有改动
如果你想一并查看已缓存的与未缓存的改动,可以执行 git diff HEAD
——
也就是说你要看到的是工作目录与上一次提交的更新的区别,无视缓存。
假设我们又改了些 ruby.rb
的内容,那缓存的与未缓存的改动我们就都有了。
以上三个 diff
命令的结果如下:
$ vim hello.rb $ git diffdiff --git a/hello.rb b/hello.rb
index 4f40006..2ae9ba4 100644
--- a/hello.rb
+++ b/hello.rb@@ -1,7 +1,7 @@
class HelloWorld
+ # says hello
def self.hello
puts "hola mundo"
end
end
$ git diff --cacheddiff --git a/hello.rb b/hello.rb
index 2aabb6e..4f40006 100644
--- a/hello.rb
+++ b/hello.rb@@ -1,7 +1,7 @@
class HelloWorld
def self.hello
- puts "hello world"+ puts "hola mundo"
end
end
$ git diff HEADdiff --git a/hello.rb b/hello.rb
index 2aabb6e..2ae9ba4 100644
--- a/hello.rb
+++ b/hello.rb@@ -1,7 +1,8 @@
class HelloWorld
+ # says hello
def self.hello
- puts "hello world"+ puts "hola mundo"
end
end
git diff --stat 显示摘要而非整个 diff
如果我们不想要看整个 diff 输出,但是又想比 git status
详细点,
就可以用 --stat
选项。该选项使它显示摘要而非全文。上文示例在使用 --stat
选项时,输出如下:
$ git status -sMM hello.rb
$ git diff --stat
hello.rb | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
$ git diff --cached --stat
hello.rb | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
$ git diff HEAD --stat
hello.rb | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
你还可以在上述命令后面制定一个目录,从而只查看特定文件或子目录的 diff
输出。
简而言之,
执行 git diff
来查看执行 git status
的结果的详细信息 ——
一行一行地显示这些文件是如何被修改或写入缓存的。
git commit记录缓存内容的快照
现在你使用 git add
命令将想要快照的内容写入了缓存,
执行 git commit
就将它实际存储快照了。
Git 为你的每一个提交都记录你的名字与电子邮箱地址,所以第一步是告诉 Git 这些都是啥。
$ git config --global user.name 'Your Name'$ git config --global user.email you@somedomain.com
让我们写入缓存,并提交对 hello.rb
的所有改动。在首个例子中,我们使用 -m
选项以在命令行中提供提交注释。
$ git add hello.rb $ git status -sM hello.rb
$ git commit -m 'my hola mundo changes'
[master 68aa034] my hola mundo changes
1 files changed, 2 insertions(+), 1 deletions(-)
现在我们已经记录了快照。如果我们再执行 git status
,会看到我们有一个“干净的工作目录”。
这意味着我们在最近一次提交之后,没有做任何改动 —— 在我们的项目中没有未快照的工作。
$ git status
# On branch master
nothing to commit (working directory clean)
如果你漏掉了 -m
选项,Git 会尝试为你打开一个编辑器以填写提交信息。
如果 Git 在你对它的配置中找不到相关信息,默认会打开 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:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: hello.rb
#
~
~
".git/COMMIT_EDITMSG" 9L, 257C
在此,你在文件头部添加实际的提交信息。以“#”开头的行都会被无视 ——Git 将 git status
的输出结果放在那儿以提示你都改了、缓存了啥。
通常,撰写良好的提交信息是很重要的。以开放源代码项目为例,多多少少以以下格式写你的提示消息是个不成文的规定:
简短的关于改动的总结(25个字或者更少)
如果有必要,更详细的解释文字。约 36 字时换行。在某些情况下,
第一行会被作为电子邮件的开头,而剩余的则会作为邮件内容。
将小结从内容隔开的空行是至关重要的(除非你没有内容);
如果这两个待在一起,有些 git 工具会犯迷糊。
空行之后是更多的段落。
- 列表也可以
- 通常使用连字符(-)或者星号(*)来标记列表,前面有个空格,
在列表项之间有空行,不过这些约定也会有些变化。
# 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:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: hello.rb
#
~
~
~
".git/COMMIT_EDITMSG" 25L, 884C written
提交注解是很重要的。因为 Git 很大一部分能耐就是它在组织本地提交和与他人分享的弹性, 它很给力地能够让你为逻辑独立的改变写三到四条提交注解,以便你的工作被同仁审阅。因为提交与推送改动是有区别的, 请务必花时间将各个逻辑独立的改动放到另外一个提交,并附上一份良好的提交注解, 以使与你合作的人能够方便地了解你所做的,以及你为何要这么做。
git commit -a 自动将在提交前将已记录、修改的文件放入缓存区
如果你觉得 git add
提交缓存的流程太过繁琐,Git 也允许你用 -a
选项跳过这一步。
基本上这句话的意思就是,为任何已有记录的文件执行 git add
——
也就是说,任何在你最近的提交中已经存在,并且之后被修改的文件。
这让你能够用更 Subversion 方式的流程,修改些文件,然后想要快照所有所做的改动的时候执行 git commit -a
。
不过你仍然需要执行 git add
来添加新文件,就像 Subversion 一样。
$ vim hello.rb$ git status -s
M hello.rb
$ git commit -m 'changes to hello file'
# On branch master
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: hello.rb
#
no changes added to commit (use "git add" and/or "git commit -a")$ git commit -am 'changes to hello file'
[master 78b2670] changes to hello file
1 files changed, 2 insertions(+), 1 deletions(-)
注意,如果你不缓存改动,直接执行 git commit
,Git 会直接给出 git status
命令的输出,提醒你啥也没缓存。我已将该消息中的重要部分高亮,它说没有添加需要提交的缓存。
如果你使用 -a
,它会缓存并提交每个改动(不含新文件)。
现在你就完成了整个快照的流程 ——改些文件,然后用 git add
将要提交的改动提交到缓存,
用 git status
和 git diff
看看你都改了啥,最后 git commit
永久地保存快照。
简而言之,执行 git commit
记录缓存区的快照。如果需要的话,这个快照可以用来做比较、共享以及恢复。