经过一下午的git学习,感觉受益匪浅,不扯淡了,直接进入主题吧,以下是小二对git的一些见解。
首先取得项目的git仓库
有两种取得 Git 项目仓库的方法。第一种是在现存的目录下,通过导入所有文件来创建新的 Git仓库。第二种是从已有的 Git 仓库克隆出一个新的镜像仓库来(一般来说克隆用得比较普遍)。
第一种:要对现有的某个项目开始用Git 管理,只需到此项目所在的目录,执行:
$ git init
第二种:
克隆仓库的命令格式为git clone [url]。比如,要克隆 Ruby 语言的 Git 代码仓库Grit,可以用下面的命令:
$ git clone
git://github.com/schacon/grit.git如果希望在克隆的时候,自己定义要新建的项目目录名称,可以在上面的命令末尾指定新的名字:
$ git clone git://github.com/schacon/grit.gitmygrit
现在已经有了本例仓库,以下是一些常用的操作:
检查当前文件状态
要确定哪些文件当前处于什么状态,可以用git status命令。如果在克隆仓库之后立即执行此命令,会看到类似这样的输出:
$ git status
On branch masternothing to commit, working directory clean
现在让我们用 vim 创建一个新文件 README,保存退出后运行git status会看到该文件出现在未跟踪文件列表中:
$ vim README
$ git status
On branch master Untracked files: (use "git add ..." to include inwhat will be committed) README nothing added to commit butuntracked files present (use "git add" to track)
在状态报告中可以看到新建的README文件出现在“Untrackedfiles”下面。未跟踪的文件意味着Git在之前的快照(提交)中没有这些文件;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 stagedarea,同时未曾跟踪过的文件标记为需要跟踪。这样就好理解后续 add 操作的实际意义了。)
忽略某些文件
一般我们总会有些文件无需纳入 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 文件,不包括 subdir/TODO/TODO
# 忽略 build/ 目录下的所有文件build/
# 会忽略 doc/notes.txt 但不包括 doc/server/arch.txtdoc*.txt
查看已暂存和未暂存的更新
实际上git status的显示比较简单,仅仅是列出了修改过的文件,如果要查看具体修改了什么地方,可以用git diff命令。稍后我们会详细介绍git diff,不过现在,它已经能回答我们的两个问题了:当前做的哪些更新还没有暂存?有哪些更新已经暂存起来准备好了下次提交?git diff会使用文件补丁的格式显示具体添加和删除的行。
假如再次修改README文件后暂存,然后编辑benchmarks.rb文件后先别暂存,运行status命令将会看到:
$ git status
要查看尚未暂存的文件更新了哪些部分,不加参数直接输入git diff:
$ git diff
此命令比较的是工作目录中当前文件和暂存区域快照之间的差异,也就是修改之后还没有暂存起来的变化内容。
若要看已经暂存起来的文件和上次提交时的快照之间的差异,可以用git diff --cached命令。(Git 1.6.1 及更高版本还允许使用git diff --staged,效果是相同的,但更好记些。)
提交更新
现在的暂存区域已经准备妥当可以提交了。在此之前,请一定要确认还有什么修改过的或新建的文件还没有git add过,否则提交的时候不会记录这些还没暂存起来的变化。所以,每次准备提交前,先用git status看下,是不是都已暂存起来了,然后再运行提交命令git commit:
$ git commit -m "Story 182: Fix benchmarks for speed"
这种方式会启动文本编辑器以便输入本次提交的说明。(默认会启用 shell 的环境变量$EDITOR所指定的软件,一般都是 vim 或 emacs。当然也可以按照第一章介绍的方式,使用git config --global core.editor命令设定你喜欢的编辑软件。)
跳过使用暂存区域
尽管使用暂存区域的方式可以精心准备要提交的细节,但有时候这么做略显繁琐。Git提供了一个跳过使用暂存区域的方式,只要在提交的时候,给git commit加上-a选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过git add步骤:
$ git commit -a -m 'added new benchmarks'
从远程仓库抓取数据
正如之前所看到的,可以用下面的命令从远程仓库抓取数据到本地:
$ git fetch [remote-name]
此命令会到远程仓库中拉取所有你本地仓库中还没有的数据。运行完成后,你就可以在本地访问该远程仓库中的所有分支,将其中某个分支合并到本地,或者只是取出某个分支,一探究竟。
如果是克隆了一个仓库,此命令会自动将远程仓库归于 origin 名下。所以,git fetch origin会抓取从你上次克隆以来别人上传到此远程仓库中的所有更新(或是上次 fetch以来别人提交的更新)。有一点很重要,需要记住,fetch命令只是将远端的数据拉到本地仓库,并不自动合并到当前工作分支,只有当你确实准备好了,才能手工合并。
如果设置了某个分支用于跟踪某个远端仓库的分支,可以使用git pull命令自动抓取数据下来,然后将远端分支自动合并到本地仓库中当前分支。在日常工作中我们经常这么用,既快且好。实际上,默认情况下git clone命令本质上就是自动创建了本地的 master 分支用于跟踪远程仓库中的 master分支(假设远程仓库确实有 master 分支)。所以一般我们运行git pull,目的都是要从原始克隆的远端仓库中抓取数据后,合并到工作目录中的当前分支。
推送数据到远程仓库
项目进行到一个阶段,要同别人分享目前的成果,可以将本地仓库中的数据推送到远程仓库。实现这个任务的命令很简单:git push [remote-name] [branch-name]。如果要把本地的 master 分支推送到origin服务器上(再次说明下,克隆操作会自动使用默认的 master 和 origin名字),可以运行下面的命令:
$ git push origin master
分支的命令
那么,Git 又是如何创建一个新的分支的呢?答案很简单,创建一个新的分支指针。比如新建一个 testing 分支,可以使用git branch命令:
$ git branch testing
要切换到其他分支,可以执行git checkout命令。我们现在转换到新建的 testing分支:
$ git checkout testing
分支的新建与合并
现在,你决定要修补问题追踪系统上的 #53 问题。顺带说明下,Git并不同任何特定的问题追踪系统打交道。这里为了说明要解决的问题,才把新建的分支取名为 iss53。要新建并切换到该分支,运行git checkout并加上-b参数:
$ git checkout -b iss53
Switched to a new branch 'iss53'
这相当于执行下面这两条命令:
$ git branch iss53 $ git checkout iss53
有必要作些测试,确保修补是成功的,然后回到master分支并把它合并进来,然后发布到生产服务器。用git merge命令来进行合并:
$ git checkout master
$ git mergeiss53
在那个超级重要的修补发布以后,你想要回到被打扰之前的工作。由于当前hotfix分支和master都指向相同的提交对象,所以hotfix已经完成了历史使命,可以删掉了。使用git branch的-d选项执行删除操作:
$ git branch -diss53
遇到冲突时的分支合并
有时候合并操作并不会如此顺利。如果在不同的分支中都修改了同一个文件的同一部分,Git就无法干净地把两者合到一起(译注:逻辑上说,这种问题只能由人来裁决。)。如果你在解决问题 #53 的过程中修改了hotfix中修改的部分,将得到类似下面的结果:
$ git merge iss53
Auto-merging index.htmlCONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
任何包含未解决冲突的文件都会以未合并(unmerged)的状态列出。Git会在有冲突的文件里加入标准的冲突解决标记,可以通过它们来手工定位并解决这些冲突。可以看到此文件包含类似下面这样的部分:
<<<<<<< HEAD
contact : [email protected]
=======
please contact us at [email protected]
>>>>>>> iss53
可以看到=======隔开的上半部分,是HEAD(即master分支,在运行merge命令时所切换到的分支)中的内容,下半部分是在iss53分支中的内容。解决冲突的办法无非是二者选其一或者由你亲自整合到一起。比如你可以通过把这段内容替换为下面这样来解决:
please contact us at
解决冲突过后不需要重新合并,只需要add把解决后的文件放在暂存中,然后commit就可以。
PS: 希望这些有所帮助,有什么问题欢迎留言。