Git 基础
取得项目的Git 仓库
有两种取得Git 项目仓库的方法。第一种是在现存的目录下,通过导入所有文件来创建
新的Git 仓库。第二种是从已有的Git 仓库克隆出一个新的镜像仓库来。
从当前目录初始化
要对现有的某个项目开始用Git 管理,只需到此项目所在的目录,执行:
$ git init
初始化后,在当前目录下会出现一个名为.git 的目录,所有Git 需要的数据和资源都
存放在这个目录中。不过目前,仅仅是按照既有的结构框架初始化好了里边所有的文件和目
录,但我们还没有开始跟踪管理项目中的任何一个文件。
如果当前目录下有几个文件想要纳入版本控制,需要先用git add 命令告诉Git 开始对
这些文件进行跟踪,然后提交:
$ git add *.c
$ git add README
$ git commit -m 'initial project version'
酱紫经得到了一个实际维护着若干文件的Git 仓库。
从现有仓库克隆
如果想对某个开源项目出一份力,可以先把该项目的Git 仓库复制一份出来,这就需要
用到git clone 命令。(SVN用的是checkout命令)
即便服务器的磁盘发生故障,用任何一个克隆出来的客户端都可以重建服务器上的仓库,回到
当初克隆时的状态
克隆仓库的命令格式为git clone [url]。
如果希望在克隆
的时候,自己定义要新建的项目目录名称,可以在上面的命令最后指定:
git clone [url] [new Program name]
Git 支持许多数据传输协议。如git:// 协议、http(s):// 、user@server:/path.git 表示的SSH 传输协议
记录每次更新到仓库
工作目录下面的所有文件都不外乎这两种状态:已跟踪或未跟踪。已跟踪的文件
是指本来就被纳入版本控制管理的文件,在上次快照中有它们的记录,工作一段时间后,它
们的状态可能是未更新,已修改或者已放入暂存区。而所有其他文件都属于未跟踪文件。它
们既没有上次更新时的快照,也不在当前的暂存区域。初次克隆某个仓库时,工作目录中的
所有文件都属于已跟踪文件,且状态为未修改。
在编辑过某些文件之后,Git 将这些文件标为已修改。我们逐步把这些修改过的文件放到
暂存区域,然后等最后一次性提交暂存区域的所有文件更新,如此重复。
检查当前文件状态
要确定哪些文件当前处于什么状态,可以用git status 命令。如果在克隆仓库之后立即
执行此命令,会看到类似这样的输出:nothing to commit (working directory clean)
这说明你现在的工作目录相当干净。换句话说,当前没有任何跟踪着的文件,也没有任何
文件在上次提交后更改过。此外,上面的信息还表明,当前目录下没有出现任何处于未跟踪
的新文件,否则Git 会在这里列出来。最后,该命令还显示了当前所在的分支
Untracked files表示修改的,且在未跟踪文件列表中的文件
跟踪新文件
使用命令git add 开始跟踪一个新文件。
此时再运行git status 命令,会看到文件已被跟踪,并处于暂存状态
只要在“Changes to be committed” 这行下面的,就说明是已暂存状态
们修改下之前已跟踪过的文件,然后再次运行status 命令,会看
到这样的状态报告:
Changed but not updated说明已跟踪文件
的内容发生了变化,但还没有放到暂存区。要暂存这次更新,需要运行git add 命令。运行git add后,看到Changes to be committed。
忽略某些文件
一般我们总会有些文件无需纳入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 的数字)。
查看已暂存和未暂存的更新
如果要查看具体修改
了什么地方,可以用git diff 命令
该命令可以列出当前作的哪些更新还没有暂存?有哪些更新已经暂存起来准备好
了下次提交? git diff 会使用文件补丁的格式显示具体添加和删除的行。
此命令比较的是工作目录中当前文件和暂存区域快照之间的差异,也就是修改之后还没有
暂存起来的变化内容。
若要看已经暂存起来的文件和上次提交时的快照之间的差异,可以用git diff --cached
命令
提交更新
每次准备提交前,先用git status 看下,是不是都已暂存起来了,然后再运行提交命
令git commit
这种方式会启动文本编辑器以便输入本次提交的说明。
提交后它会告诉你,当前是在哪个分支
(master)提交的,本次提交的完整SHA-1 校验和是什么(463dc4f),以及在本次提交
中,有多少文件修订过,多少行添改和删改过。
记住,提交时记录的是放在暂存区域的快照,任何还未暂存的仍然保持已修改状态,可以
在下次提交时纳入版本管理。每一次运行提交操作,都是对你项目作一次快照,以后可以回
到这个状态,或者进行比较。
跳过使用暂存区域
Git 提
供了一个跳过使用暂存区域的方式,只要在提交的时候,给git commit 加上-a 选项,Git
就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过git add 步骤
移除文件
要从Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区
域移除),然后提交。可以用git rm 命令完成此项工作,并连带从工作目录中删除指定的
文件,这样以后就不会出现在未跟踪文件清单中了。
如果删除之前修改过并且已经放到暂存
区域的话,则必须要用强制删除选项-f(译注:即force 的首字母),以防误删除文件后
丢失修改的内容。
我们想把文件从Git 仓库中删除(亦即从暂存区域移除),但仍然希
望保留在当前工作目录中。换句话说,仅是从跟踪清单中删除。比如一些大型日志文件或者
一堆.a 编译文件,不小心纳入仓库后,要移除跟踪但不删除文件,以便稍后在.gitignore
文件中补上,用--cached 选项即可:git rm –cached filename
移动文件
当你看到Git 的mv 命令时一定会困惑不已。要在Git 中对文件改名,可以
这么做:
$ git mv file_from file_to
运行git mv 就相当于运行了下面三条命令:
$ mv README.txt README
$ git rm README.txt
$ git add README
查看提交历史
在提交了若干更新之后,又或者克隆了某个项目,想回顾下提交历史,可以使用git log
命令。
默认不用任何参数的话,git log 会按提交时间列出所有的更新,最近的更新排在最上
面。每次更新都有一个SHA-1 校验和、作者的名字和电子邮件地址、提交时
间,最后缩进一个段落显示提交说明。
git log 有许多选项可以帮助你搜寻感兴趣的提交,常用-p 选项展开显示每次提交的内容差异,用-2 则仅显示最近的两次更新,--stat,仅显示简要的增改行数统计,的--pretty 选项,可以指定使用完全不同于默认格式的方式展示提交历
史。比如用oneline 将每个提交放在一行显示,这在提交数很大时非常有用。另外还有
short,full 和fuller 可以用,展示的信息或多或少有些不同
$ git log --pretty=oneline
是format,可以定制要显示的记录格式,这样的输出便于后期编程提取分
析,像这样:
$ git log --pretty=format:"%h - %an, %ar : %s"
常用的格式占位符写法及其代表的意义:
选项说明
%H 提交对象(commit)的完整哈希字串
%h 提交对象的简短哈希字串
%T 树对象(tree)的完整哈希字串
%t 树对象的简短哈希字串
%P 父对象(parent)的完整哈希字串
%p 父对象的简短哈希字串
%an 作者(author)的名字
%ae 作者的电子邮件地址
%ad 作者修订日期(可以用-date= 选项定制格式)
%ar 作者修订日期,按多久以前的方式显示
%cn 提交者(committer)的名字
%ce 提交者的电子邮件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式显示
%s 提交说明
_作者(author)_和_提交者(committer)_之间究竟有何差别,其实作者指
的是实际作出修改的人,提交者指的是最后将此工作成果提交到仓库的人
用oneline 或format 时结合--graph 选项,可以看到开头多出一些ASCII 字符串表
示的简单图形,形象地展示了每个提交所在的分支及其分化衍合情况。
$ git log --pretty=format:"%h %s" --graph
限制输出长度
已经看到过-2 了,它只显示最近的两条提交,实际上,
这是-<n> 选项的写法,其中的n 可以是任何自然数,表示仅显示最近的若干条提交。不过
实践中我们是不太用这个选项的,Git 在输出所有提交时会自动调用分页程序(pager),
要看更早的更新只需翻到下页即可。
另外还有按照时间作限制的选项,比如--since 和--until
下面的命令列出所有最近
两周内的提交:
$ git log --since=2.weeks
用--author 选项显示指定作者的提交,用
--grep 选项搜索提交说明中的关键字。
选项说明
-(n) 仅显示最近的n 条提交
--since, --after 仅显示指定时间之后的提交。
--until, --before 仅显示指定时间之前的提交。
--author 仅显示指定作者相关的提交。
--committer 仅显示指定提交者相关的提交。
使用图形化工具查阅提交历史
,随Git 一同发布的gitk 就是这样一种
工具。它是用Tcl/Tk 写成的,基本上相当于git log 命令的可视化版本,凡是git log
可以用的选项也都能用在gitk 上。在项目工作目录中输入gitk 命令后就会启动
界面。
撤消操作
有些操作并不总是可以撤消的,所以请务必谨慎小心,一旦失
误,就有可能丢失部分工作成果。
修改最后一次提交
有时候我们提交完了才发现漏掉了几个文件没有加,或者提交信息写错了。想要撤消刚才
的提交操作,可以使用--amend 选项重新提交:
$ git commit --amend
此命令将使用当前的暂存区域快照提交。如果刚才提交完没有作任何改动,直接运行此命
令的话,相当于有机会重新编辑提交说明,而所提交的文件快照和之前的一样。
取消已经暂存的文件
可以使用git reset HEAD
<file>... 的方式取消暂存。
取消对文件的修改
git checkout -- filename
在用这条命令前,请务必确定真的不再需要保留刚才的修改。如果只是想回退版本,同时保
留刚才的修改以便将来继续工作
任何已经提交到Git 的都可以被恢复。即便在已经删除的分支中的提交,或者用
--amend 重新改写的提交,都可以被恢复
远程仓库的使用
远程仓库是指托管
在网络上的项目仓库,可能会有好多个,其中有些你只能读,另外有些可以写。同他人协作
开发某个项目时,需要管理这些远程仓库,以便推送或拉取数据,分享各自的工作进展。管
理远程仓库的工作,包括添加远程库,移除废弃的远程库,管理各式远程库分支,定义是否
跟踪这些分支
查看当前的远程库
要查看当前配置有哪些远程仓库,可以用git remote 命令,它会列出每个远程库的简短
名字。在克隆完某个项目后,至少可以看到一个名为origin 的远程库,Git 默认使用这个
名字来标识你所克隆的原始仓库
也可以加上-v 选项(译注:此为—verbose 的简写,取首字母),显示对应的克隆地
址
添加远程仓库
要添加一个新的远程仓库,可以指定一个简单的名字,以便将来引用,运行git remote
add [shortname] [url]
从远程仓库抓取数据
$ git fetch [remote-name]这里是远程仓库别名
此命令会到远程仓库中拉取所有你本地仓库中还没有的数据。运行完成后,你就可以在本
地访问该远程仓库中的所有分支,将其中某个分支合并到本地,或者只是取出某个分支
如果是克隆了一个仓库,此命令会自动将远程仓库归于origin 名下。所以,git fetch
origin 会抓取从你上次克隆以来别人上传到此远程仓库中的所有更新
fetch 命令只是将远端的数据拉到本地仓
库,并不自动合并到当前工作分支,只有当你确实准备好了,才能手工合并。
如果设置了某个分支用于跟踪某个远端仓库的分支(参见下节及第三章的内容),可以使
用git pull 命令自动抓取数据下来,然后将远端分支自动合并到本地仓库中当前分支
默认情况下git clone 命令本质上就是
自动创建了本地的master 分支用于跟踪远程仓库中的master 分支
所以一般我们运行git pull,目的都是要从原始克隆的远端仓库中抓取数
据后,合并到工作目录中当前分支
推送数据到远程仓库
项目进行到一个阶段,要同别人分享目前的成果,可以将本地仓库中的数据推送到远程
仓库。实现这个任务的命令很简单:git push [remote-name] [branch-name]。如果要把
本地的master 分支推送到origin 服务器上(再次说明下,克隆操作会自动使用默认的
master 和origin 名字),可以运行下面的命令:
$ git push origin master
只有在所克隆的服务器上有写权限,或者同一时刻没有其他人在推数据,这条命令才会如
期完成任务。如果在你推数据前,已经有其他人推送了若干更新,那你的推送操作就会被驳
回。你必须先把他们的更新抓取到本地,并到自己的项目中,然后才可以再次推送。
查看远程仓库信息
通过命令git remote show [remote-name] 查看某个远程仓库的详细信息
远程仓库的删除和重命名
在新版Git 中可以用git remote rename 命令修改某个远程仓库的简短名称
对远程仓库的重命名,也会使对应的分支名称发生变化
碰到远端仓库服务器迁移,或者原来的克隆镜像不再使用,又或者某个参与者不再贡献代
码,那么需要移除对应的远端仓库,可以运行git remote rm 命令
打标签
列显已有的标签
列出现有标签的命令非常简单,直接运行git tag 即可
显示的标签按字母顺序排列,所以标签的先后并不表示重要程度的轻重。
如果你只对1.4.2 系列的版本感兴趣,可以运行下面的命令:
$ git tag -l 'v1.4.2.*'
v1.4.2.1
v1.4.2.2
v1.4.2.3
v1.4.2.4
新建标签
Git 使用的标签有两种类型:轻量级的(lightweight)和含附注的(annotated)。轻量
级标签就像是个不会变化的分支,实际上它就是个指向特定提交对象的引用。而含附注标
签,实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,
电子邮件地址和日期,以及标签说明,标签本身也允许使用GNU Privacy Guard (GPG) 来
签署或验证。一般我们都建议使用含附注型的标签,以便保留相关信息;当然,如果只是临
时性加注标签,或者不需要旁注额外信息,用轻量级标签也没问题。
含附注的标签
创建一个含附注类型的标签非常简单,用-a (译注:取annotated 的首字母)指定标
签名字即可
$ git tag -a v1.4 -m 'my version 1.4'
-m 选项则指定了对应的标签说明,Git 会将此说明一同保存在标签对象中
签署标签
如果你有自己的私钥,还可以用GPG 来签署标签,只需要把之前的-a 改为-s (译注:
取Signed 的首字母)即可
轻量级标签
轻量级标签实际上就是一个保存着对应提交对象的校验和信息的文件。要创建这样的标
签,一个-a,-s 或-m 选项都不用,直接给出标签名字即可:
$ git tag v1.4-lw
验证标签
可以使用git tag -v [tag-name] (译注:取verify 的首字母)的方式验证已经签署的
标签。此命令会调用GPG 来验证签名,所以你需要有签署者的公钥,存放在keyring 中,
才能验证: