Git详解之二 Git基础(第一部分)

Git 基础

      读完本章你就能上手使用 Git 了。本章将介绍几个最基本的,也是最经常使用的 Git 命令,之后绝大多数时间里用到的也就是这几个命令。读完本章,你就能初始化一个新的代码仓库,作一些适当配置;开始或中止跟踪某些文件;暂存或提交某些更 新。咱们还会展现如何让 Git 忽略某些文件,或是名称符合特定模式的文件;如何既快且容易地撤消犯下的小错误;如何浏览项目的更新历史,查看某两次更新之间的差别;以及如何从远程仓库 拉数据下来或者推数据上去。python

2.1  取得项目的 Git 仓库

       有两种取得 Git 项目仓库的方法。第一种是在现存的目录下,经过导入全部文件来建立新的 Git 仓库。第二种是从已有的 Git 仓库克隆出一个新的镜像仓库来。git

在工做目录中初始化新仓库github

要对现有的某个项目开始用 Git 管理,只需到此项目所在的目录,执行:正则表达式

$ git init

初始化后,在当前目录下会出现一个名为 .git 的目录,全部 Git 须要的数据和资源都存放在这个目录中。不过目前,仅仅是按照既有的结构框架初始化好了里边全部的文件和目录,但咱们尚未开始跟踪管理项目中的任何一个文件。(在第九章咱们会详细说明刚才建立的.git 目录中究竟有哪些文件,以及都起些什么做用。)shell

若是当前目录下有几个文件想要归入版本控制,须要先用 git add 命令告诉 Git 开始对这些文件进行跟踪,而后提交:vim

$ git add *.c $ git add README 
$ git commit -m 'initial project version'

稍后咱们再逐一解释每条命令的意思。不过如今,你已经获得了一个实际维护着若干文件的 Git 仓库。服务器

从现有仓库克隆框架

       若是想对某个开源项目出一份力,能够先把该项目的 Git 仓库复制一份出来,这就须要用到 git clone 命令。若是你熟悉其余的 VCS 好比 Subversion,你可能已经注意到这里使用的是 clone 而不是 checkout。这是个很是重要的差异,Git 收取的是项目历史的全部数据(每个文件的每个版本),服务器上有的数据克隆以后本地也都有了。实际上,即使服务器的磁盘发生故障,用任何一个克隆出来 的客户端均可以重建服务器上的仓库,回到当初克隆时的状态(虽然可能会丢失某些服务器端的挂钩设置,但全部版本的数据仍旧还在,有关细节请参考第四章)。ssh

克隆仓库的命令格式为 git clone [url]。好比,要克隆 Ruby 语言的 Git 代码仓库 Grit,能够用下面的命令:编辑器

$ git clone git://github.com/schacon/grit.git

      这会在当前目录下建立一个名为“grit”的目录,其中包含一个 .git 的目录,用于保存下载下来的全部版本记录,而后从中取出最新版本的文件拷贝。若是进入这个新建的grit 目录,你会看到项目中的全部文件已经在里边了,准备好后续的开发和使用。若是但愿在克隆的时候,本身定义要新建的项目目录名称,能够在上面的命令末尾指定新的名字:

$ git clone git://github.com/schacon/grit.git mygrit

惟一的差异就是,如今新建的目录成了 mygrit,其余的都和上边的同样。

Git 支持许多数据传输协议。以前的例子使用的是

 git:// 协议,不过你也能够用 
 http(s):// 
 ssh://user@server:/path.git 表示的 SSH 传输协议.

咱们会在第四章详细介绍全部这些协议在服务器端该如何配置使用,以及各类方式之间的利弊。

2.2  记录每次更新到仓库

     如今咱们手上已经有了一个真实项目的 Git 仓库,并从这个仓库中取出了全部文件的工做拷贝。接下来,对这些文件做些修改,在完成了一个阶段的目标以后,提交本次更新到仓库。

请记住,工做目录下面的全部文件都不外乎这两种状态:已跟踪或未跟踪。已跟踪的文件是指原本就被归入版本控制管理的文件,在上次快照中有它们的记 录,工做一段时间后,它们的状态多是未更新,已修改或者已放入暂存区。而全部其余文件都属于未跟踪文件。它们既没有上次更新时的快照,也不在当前的暂存 区域。初次克隆某个仓库时,工做目录中的全部文件都属于已跟踪文件,且状态为未修改。

在编辑过某些文件以后,Git 将这些文件标为已修改。咱们逐步把这些修改过的文件放到暂存区域,直到最后一次性提交全部这些暂存起来的文件,如此重复。因此使用 Git 时的文件状态变化周期如图 2-1 所示。

图 2-1. 文件的状态变化周期

检查当前文件状态

要肯定哪些文件当前处于什么状态,能够用 git status 命令。若是在克隆仓库以后当即执行此命令,会看到相似这样的输出:

$ git status # On branch master nothing to commit (working directory clean)

这说明你如今的工做目录至关干净。换句话说,当前没有任何跟踪着的文件,也没有任何文件在上次提交后更改过。此外,上面的信息还代表,当前目录下没 有出现任何处于未跟踪的新文件,不然 Git 会在这里列出来。最后,该命令还显示了当前所在的分支是 master,这是默认的分支名称,实际是能够修改的,如今先不用考虑。下一章咱们就会详细讨论分支和引用。

如今让咱们用 vim 编辑一个新文件 README,保存退出后运行 git status 会看到该文件出如今未跟踪文件列表中:

$ vim README $ git status 
# On branch master 
# Untracked files:
# (use "git add ..." to include in what will be committed) # #

就是在“Untracked files”这行下面。Git 不会自动将之归入跟踪范围,除非你明明白白地告诉它“我须要跟踪该文件”,于是不用担忧把临时文件什么的也纳入版本管理。不过如今的例子中,咱们确实想要跟踪管理 README 这个文件。

跟踪新文件

使用命令 git add 开始跟踪一个新文件。因此,要跟踪 README 文件,运行:

$ git add README

此时再运行 git status 命令,会看到 README 文件已被跟踪,并处于暂存状态:

$ git status # On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) # #	new file: README #

只要在 “Changes to be committed” 这行下面的,就说明是已暂存状态。若是此时提交,那么该文件此时此刻的版本将被留存在历史记录中。你可能会想起以前咱们使用git init 后就运行了 git add 命令,开始跟踪当前目录下的文件。在git add 后面能够指明要跟踪的文件或目录路径。若是是目录的话,就说明要递归跟踪该目录下的全部文件。(译注:其实git add 的潜台词就是把目标文件快照放入暂存区域,也就是 add file into staged area,同时不曾跟踪过的文件标记为须要跟踪。这样就好理解后续 add 操做的实际意义了。)

暂存已修改文件

如今咱们修改下以前已跟踪过的文件 benchmarks.rb,而后再次运行 status 命令,会看到这样的状态报告:

$ git status # On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) # #	new file: README # #

文件 benchmarks.rb 出如今 “Changed but not updated” 这行下面,说明已跟踪文件的内容发生了变化,但尚未放到暂存区。要暂存此次更新,须要运行git add 命令(这是个多功能命令,根据目标文件的状态不一样,此命令的效果也不一样:能够用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等)。如今让咱们运行git add 将 benchmarks.rb 放到暂存区,而后再看看 git status 的输出:

$ git add benchmarks.rb 
$ git status # On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) # #

如今两个文件都已暂存,下次提交时就会一并记录到仓库。假设此时,你想要在 benchmarks.rb 里再加条注释,从新编辑存盘后,准备好提交。不过且慢,再运行git status 看看:

$ vim benchmarks.rb 
$ git status # On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) # #	ne

怎么回事?benchmarks.rb 文件出现了两次!一次算未暂存,一次算已暂存,这怎么可能呢?好吧,实际上 Git 只不过暂存了你运行 git add 命令时的版本,若是如今提交,那么提交的是添加注释前的版本,而非当前工做目录中的版本。因此,运行了git add 以后又做了修订的文件,须要从新运行 git add 把最新版本从新暂存起来:

$ git add benchmarks.rb 
$ git status # On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) #

忽略某些文件

通常咱们总会有些文件无需归入 Git 的管理,也不但愿它们总出如今未跟踪文件列表。一般都是些自动生成的文件,好比日志文件,或者编译过程当中建立的临时文件等。咱们能够建立一个名为 .gitignore 的文件,列出要忽略的文件模式。来看一个实际的例子:

$ cat .gitignore
 *.[oa] *~

第一行告诉 Git 忽略全部以 .o 或 .a 结尾的文件。通常这类对象文件和存档文件都是编译过程当中出现的,咱们用不着跟踪它们的版本。第二行告诉 Git 忽略全部以波浪符(~)结尾的文件,许多文本编辑软件(好比 Emacs)都用这样的文件名保存副本。此外,你可能还须要忽略 log,tmp 或者 pid 目录,以及自动生成的文档等等。要养成一开始就设置好 .gitignore 文件的习惯,以避免未来误提交这类无用的文件。

文件 .gitignore 的格式规范以下:

全部空行或者以注释符号 # 开头的行都会被 Git 忽略。

可使用标准的 glob 模式匹配。 * 匹配模式最后跟反斜杠(/)说明要忽略的是目录。 * 要忽略指定模式之外的文件或目录,能够在模式前加上惊叹号(!)取反。

所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。星号(*)匹配零个或多个任意字符;[abc] 匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(?)只匹配一个任意字符;若是在方括号中使用短划线分隔两个字符,表示全部在这两个字符范围内的均可以匹配(好比[0-9] 表示匹配全部 0 到 9 的数字)。

咱们再看一个 .gitignore 文件的例子:

# 此为注释 – 将被 Git 忽略 *.a # 忽略全部 .a 结尾的文件 !lib.a # 但 lib.a 除外 /TODO # 仅仅忽略项目根目录下的 TODO 文件,不包

查看已暂存和未暂存的更新

实际上 git status 的显示比较简单,仅仅是列出了修改过的文件,若是要查看具体修改了什么地方,能够用 git diff命令。稍后咱们会详细介绍git diff,不过如今,它已经能回答咱们的两个问题了:当前作的哪些更新尚未暂存?有哪些更新已经暂存起来准备好了下次提交? git diff 会使用文件补丁的格式显示具体添加和删除的行。

假如再次修改 README 文件后暂存,而后编辑 benchmarks.rb 文件后先别暂存,运行 status 命令,会看到:

$ git status
 # On branch master # Changes to be committed: 
 # (use "git reset HEAD ..." to unstage) # 
 #	new file: README # # Changed but not updated: 
 # (use "git add ..." to update what will be committed) # 
 #	modified: benchmarks.rb #

要查看还没有暂存的文件更新了哪些部分,不加参数直接输入 git diff:

$ git diff diff --git a/benchmarks.rb b/benchmarks.rb index 3cb747f..da65585 100644 --- a/benchmarks.rb +++ b/benchmarks.rb @@ -36,6 +36,10 @@ def main @commit.parents[0].parents[0].parents[0] end + run_code(x, 'commits 1') do + git.commits.size + end + run_code(x, 'commits 2') do log = git.commits('master', 15) log.size

此命令比较的是工做目录中当前文件和暂存区域快照之间的差别,也就是修改以后尚未暂存起来的变化内容。

若要看已经暂存起来的文件和上次提交时的快照之间的差别,能够用 git diff --cached 命令。(Git 1.6.1 及更高版本还容许使用git diff --staged,效果是相同的,但更好记些。)来看看实际的效果:

$ git diff 
--cached diff --git a/README b/README new file mode 100644 index 0000000..03902a1 --- /dev/null +++ b/README2 @@ -0,0 +1,5 @@ +grit + by Tom Preston-Werner, Chris Wanstrath + http://github.com/mojombo/grit + +Grit is a Ruby library for extracting information from a Git repository

请注意,单单 git diff 不过是显示尚未暂存起来的改动,而不是此次工做和上次提交之间的差别。因此有时候你一会儿暂存了全部更新过的文件后,运行git diff 后却什么也没有,就是这个缘由。

像以前说的,暂存 benchmarks.rb 后再编辑,运行 git status 会看到暂存先后的两个版本:

$ git add benchmarks.rb 
$ echo '# test line' >> benchmarks.rb 
$ git status # On branch master # # Changes to be committed: # #	modified: benchmarks.rb # # Changed but not updated: # #	modified: benchmarks.rb #

如今运行 git diff 看暂存先后的变化:

$ git diff 
diff --git a/benchmarks.rb b/benchmarks.rb index e445e28..86b2f7c 100644 --- a/benchmarks.rb +++ b/benchmarks.rb @@ -127,3 +127,4 @@ end main() ##pp Grit::GitRuby.cache_client.stats +# test line

而后用 git diff --cached 查看已经暂存起来的变化:

$ git diff --cached 
diff --git a/benchmarks.rb b/benchmarks.rb index 3cb747f..e445e28 100644 --- a/benchmarks.rb +++ b/benchma

提交更新

      如今的暂存区域已经准备稳当能够提交了。在此以前,请必定要确认还有什么修改过的或新建的文件尚未 git add 过,不然提交的时候不会记录这些还没暂存起来的变化。因此,每次准备提交前,先用git status 看下,是否是都已暂存起来了,而后再运行提交命令 git commit:

$ git commit

      这种方式会启动文本编辑器以便输入本次提交的说明。(默认会启用 shell 的环境变量 $EDITOR 所指定的软件,通常都是 vim 或 emacs。固然也能够按照第一章介绍的方式,使用git config --global core.editor 命令设定你喜欢的编辑软件。)

编辑器会显示相似下面的文本信息(本例选用 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 ..." to unstage) # # new file: README # modified: benchmarks.rb ~ ~ ~ ".git/COMMIT_EDITMSG" 10L, 283C

能够看到,默认的提交消息包含最后一次运行 git status 的输出,放在注释行里,另外开头还有一空行,供你输入提交说明。你彻底能够去掉这些注释行,不过留着也不要紧,多少能帮你回想起此次更新的内容有哪些。(若是以为这还不够,能够用-v 选项将修改差别的每一行都包含到注释中来。)退出编辑器时,Git 会丢掉注释行,将说明内容和本次更新提交到仓库。

另外也能够用 -m 参数后跟提交说明的方式,在一行命令中提交更新:

$ git commit -m "Story 182: Fix benchmarks for speed" [master]: created 463dc4f: "Fix benchmarks for speed" 2 files changed, 3 insertions(+), 0 deletions(-) create mode 100644 README

好,如今你已经建立了第一个提交!能够看到,提交后它会告诉你,当前是在哪一个分支(master)提交的,本次提交的完整 SHA-1 校验和是什么(463dc4f),以及在本次提交中,有多少文件修订过,多少行添改和删改过。

记住,提交时记录的是放在暂存区域的快照,任何还未暂存的仍然保持已修改状态,能够在下次提交时归入版本管理。每一次运行提交操做,都是对你项目做一次快照,之后能够回到这个状态,或者进行比较。

跳过使用暂存区域

尽管使用暂存区域的方式能够精心准备要提交的细节,但有时候这么作略显繁琐。Git 提供了一个跳过使用暂存区域的方式,只要在提交的时候,给 git commit 加上-a 选项,Git 就会自动把全部已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤:

$ git status # On branch master # # Changed but not updated: # #modified: benchmarks.rb #
$ git commit -a -m 'added new benchmarks' 
 [master 83e38c7] added new benchmarks 1 files changed, 5 insertions(+), 0 deletions(-)

看到了吗?提交以前再也不须要 git add 文件 benchmarks.rb 了。

移除文件

      要从 Git 中移除某个文件,就必需要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),而后提交。能够用git rm 命令完成此项工做,并连带从工做目录中删除指定的文件,这样之后就不会出如今未跟踪文件清单中了。

若是只是简单地从工做目录中手工删除文件,运行 git status 时就会在 “Changed but not updated” 部分(也就是_未暂存_清单)看到:

$ rm grit.gemspec
$ git status # On branch master # # Changed but not updated: # (use "git add/rm ..." to update what will be committed) # # deleted: grit.gemspec #

而后再运行 git rm 记录这次移除文件的操做:

$ git rm grit.gemspec rm 'grit.gemspec' 
$ git status # On branch master # # Changes to be committed: # (use "git reset HEAD ..." to unstage) # # deleted: grit.gemspec #

       最后提交的时候,该文件就再也不归入版本管理了。若是删除以前修改过而且已经放到暂存区域的话,则必需要用强制删除选项 -f(译注:即 force 的首字母),以防误删除文件后丢失修改的内容。

      另一种状况是,咱们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然但愿保留在当前工做目录中。换句话说,仅是从跟踪清单中删除。好比一些大型日志文件或者一堆.a 编译文件,不当心归入仓库后,要移除跟踪但不删除文件,以便稍后在 .gitignore 文件中补上,用 --cached 选项便可:

$ git rm --cached readme.txt

后面能够列出文件或者目录的名字,也可使用 glob 模式。比方说:

$ git rm log/\*.log

注意到星号 * 以前的反斜杠 \,由于 Git 有它本身的文件模式扩展匹配方式,因此咱们不用 shell 来帮忙展开(译注:实际上不加反斜杠也能够运行,只不过按照 shell 扩展的话,仅仅删除指定目录下的文件而不会递归匹配。上面的例子原本就指定了目录,因此效果等同,但下面的例子就会用递归方式匹配,因此必须加反斜 杠。)。此命令删除全部log/ 目录下扩展名为 .log 的文件。相似的好比:

$ git rm \*~

会递归删除当前目录及其子目录中全部 ~ 结尾的文件。

相关文章
相关标签/搜索