记得知乎以前有个问题说:如果用一天的时间学习一门技能,选什么好?里面有个说学会Git是个很不错选择,今天就抽时间感受下Git的魅力吧。
Pro Git (Scott Chacon) 读书笔记:
第一章:Git起步
版本控制出现的原因及分类
类别 | 基本原理 | 优点 | 缺点 |
本地版本控制系统
|
自己把复制整个项目,改名或加上备份时间来区别
采用某种简单的数据库来记录文件的历次更新差异
|
简单 |
有时会混淆所在的工作目录,
丢了文件毛了数据就没有后退的路
|
集中化的版本控制系统
|
解决在不同系统上的开发者协同工作,( Centralized Version Control Systems,简称 CVCS )
诸如 CVS,Subversion 以及 Perforce 等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,
而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。
|
集中管理 权限分明 |
*服务器的单点故障。若是宕机一小时,
那么在这一小时内,谁都无法提交更新,
也就无法协同工作
|
分布式版本控制系统
|
Distributed Version Control System,简称 DVCS
诸如 Git,Mercurial,Bazaar 还有 Darcs 等,客户端并不只提取最新版本的文件快照,而是把原始的代码仓库完整地镜像下来。
这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。因为每一次的提取操作,
实际上都是一次对代码仓库的完整备份
|
1. Git 基础要点
Git 和其他版本控制系统的主要差别在于,Git 只关心文件数据的整体是否发生变化,而大多数其他系统则只关心文件内容的具体差异。这类系统(CVS,Subversion,Perforce,Bazaar等等)每次记录有哪些文件作了更新,以及都更新了哪些行的什么内容
Git 并不保存这些前后变化的差异数据。实际上,Git 更像是把变化的文件作快照后,记录在一个微型的文件系统中。每次提交更新时,它会纵览一遍所有文件的指纹信息并对文件
作一快照,然后保存一个指向这次快照的索引。为提高性能,若文件没有变化,Git 不会再次保存,而只对上次保存的快照作一连接。Git 的工作方式如下:
1.1 近乎所有操作都可本地执行
1.2. 时刻保持数据完整性
Git 使用 SHA-1 算法计算数据的校验和,通过对文件的内容或目录的结构计算出一个SHA-1 哈希值,作为指纹字符串。该字串由 40 个十六进制字符(0-9 及 a-f)组成,看起来就像是:
24b9da6552252987aa493b52f8696cd6d3b00373
Git 的工作完全依赖于这类指纹字串,所以你会经常看到这样的哈希值。实际上,所有保存在 Git 数据库中的东西都是用此哈希值来作索引的,而不是靠文件名。
1.3. 多数操作仅添加数据
1.4. 三种状态:已提交(committed),已修改(modified)和已暂存(staged)
已提交表示该文件已经被安全地保存在本地数据库中了;
已修改表示修改了某个文件,但还没有提交保存;
已暂存表示把已修改的文件放在下次提交时要保存的清单中。
基本的 Git 工作流程如下所示:
. 在工作目录中修改某些文件。
. 对这些修改了的文件作快照,并保存到暂存区域。
. 提交更新,将保存在暂存区域的文件快照转储到 git 目录中。
3. 初次运行 Git 前的配置
• /etc/gitconfig文件:系统中对所有用户都普遍适用的配置。若使用 git config 时用 --system 选项,读写的就是这个文件。
• ~/.gitconfig文件:用户目录下的配置文件只适用于该用户。若使用 git config 时用 --global 选项,读写的就是这个文件。
• 当前项目的 git 目录中的配置文件(也就是工作目录中的 .git/config 文件):这里的配置仅仅针对当前项目有效。每一个级别的配置都会覆盖上层的相同配置,所以
.git/config 里的配置会覆盖 /etc/gitconfig 中的同名变量。
• ~/.gitconfig文件:用户目录下的配置文件只适用于该用户。若使用 git config 时用 --global 选项,读写的就是这个文件。
• 当前项目的 git 目录中的配置文件(也就是工作目录中的 .git/config 文件):这里的配置仅仅针对当前项目有效。每一个级别的配置都会覆盖上层的相同配置,所以
.git/config 里的配置会覆盖 /etc/gitconfig 中的同名变量。
3.1 用户信息
$ git config --global user.name "zhongwencool"
$ git config --global user.email zhongwencool@gmail.com
如果用了 --global 选项,那么更改的配置文件就是位于你用户主目录下的那个,以后你所有的项目都会默认使用这里配置的用户信息。
如果要在某个特定的项目中使用其他名字或者电邮,只要去掉 --global 选项重新配置即可,新的设定保存在当前项目的 .git/config文件里。
3.2 文本编辑器
$ git config --global core.editor emacs
3.3 差异分析工具
$ git config --global merge.tool vimdiff
Git 可以理解 kdiff3,tkdiff,meld,xxdiff,emerge,vimdiff,gvimdiff,ecmerge,和 opendiff
3.4 查看配置信息
$ git config --list
有时候会看到重复的变量名,那就说明它们来自不同的配置文件(比如 /etc/gitconfig和 ~/.gitconfig),不过最终 Git 实际采用的是最后一个。
3.5 获取帮助
想了解 Git 的各式工具该怎么用,可以阅读它们的使用帮助,方法有三:
$ git help <verb>
$ git <verb> --help
$ man git-<verb>
#比如
$ git help config
第二章:Git基础
读完本章,
. 你就能初始化一个新的代码仓库,
. 做一些适当的配置;
. 开始或停止跟踪某些文件;
. 暂存或提交某些更新。我们还会展示如何让 Git 忽略某些文件,或是名称符合特定模式的文件;
. 如何既快且容易地撤消犯下的小错误;
. 如何浏览项目的更新历史,查看某两次更新之间的差异;
.如何从远程仓库拉数据下来或者推数据上去。
2.1 取得项目的 Git 仓库
2.1.1 从当前目录初始化
$ git init
在当前目录下会出现一个名为 .git 的目录,所有 Git 需要的数据和资源都存放在这个目录中。
不过目前,仅仅是按照既有的结构框架初始化好了里边所有的文件和目录,但我们还没有开始跟踪管理项目中的任何一个文件。
如果当前目录下有几个文件想要纳入版本控制,需要先用 git add 命令告诉 Git 开始对这些文件进行跟踪,然后提交:
$ git add *.c
$ git add README
$ git commit -m 'initial project version'
2.1.2 从现有仓库克隆
$ git clone git://github.com/schacon/grit.git
clone 而不是 checkout。这是个非常重要的差别,Git 收取的是项目历史的所有数据(每一个文件的每一个版本),服务器上有的数据克隆之后本地也都有了。实际上,即
便服务器的磁盘发生故障,用任何一个克隆出来的客户端都可以重建服务器上的仓库,回到当初克隆时的状态
便服务器的磁盘发生故障,用任何一个克隆出来的客户端都可以重建服务器上的仓库,回到当初克隆时的状态
Git 支持许多数据传输协议。之前的例子使用的是 git:// 协议,不过你也可以用http(s):// 或者 user@server:/path.git 表示的 SSH 传输协议。
2.2 记录每次更新到仓库
2.2.1 检查当前文件状态
$ git status
2.2.2 跟踪新文件
$ git add README
2.2.3 暂存已修改文件
运行 git add 命令(这是个多功能命令,根据目标文件的状态不同,此命令的效果也不同:可以用它开始跟踪新文件,
或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等)
2.2.4 忽略某些文件
文件 .gitignore 的格式规范如下:
• 所有空行或者以注释符号 # 开头的行都会被 Git 忽略。
• 可以使用标准的 glob 模式匹配。
• 匹配模式最后跟反斜杠(/)说明要忽略的是目录。
• 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。
所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。星号(*)匹配零个或多个任意字符;[abc] 匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,
要么匹配一个 b,要么匹配一个 c);问号(?)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配
(比如 [0-9] 表示匹配所有 0 到9 的数字)。
# 此为注释 – 将被 Git 忽略
*.a # 忽略所有 .a 结尾的文件
!lib.a # 但 lib.a 除外
/TODO # 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
build/ # 忽略 build/ 目录下的所有文件
doc/*.txt # 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt
2.2.5 查看已暂存和未暂存的更新
要查看尚未暂存的文件更新了哪些部分,不加参数直接输入 :
$ git diff
若要看已经暂存起来的文件和上次提交时的快照之间的差异,可以用
$ git diff --cached
命令。(Git 1.6.1 及更高版本还允许使用 git diff --staged
单单 git diff 不过是显示还没有暂存起来的改动,而不是这次工作和上次提交之间的差异。
所以有时候你一下子暂存了所有更新过的文件后,运行 git diff 后却什么也没有,就是这个原因。
2.2.6 提交更新
$ git commit
这种方式会启动文本编辑器以便输入本次提交的说明。(默认会启用 shell 的环境变量$EDITOR 所指定的软件,一般都是 vim 或 emacs。当然也可以按照第一章介绍的方式,
使用 git config --global core.editor 命令设定你喜欢的编辑软件。)
也可以使用 -m 参数后跟提交说明的方式,在一行命令中提交更新:
$ git commit -m "Story 182: Fix benchmarks for speed"
2.2.7 跳过使用暂存区域
尽管使用暂存区域的方式可以精心准备要提交的细节,但有时候这么做略显繁琐。Git 提供了一个跳过使用暂存区域的方式,
只要在提交的时候,给 git commit 加上 -a 选项,Git就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤
$ git commit -a -m 'added new benchmarks'
2.2.8 移除文件
$ git rm grit.gemspec
最后提交的时候,该文件就不再纳入版本管理了。如果删除之前修改过并且已经放到暂存区域的话,
则必须要用强制删除选项 -f(译注:即 force 的首字母),以防误删除文件后丢失修改的内容。
另外一种情况是,我们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。
换句话说,仅是从跟踪清单中删除。比如一些大型日志文件或者一堆 .a 编译文件,不小心纳入仓库后,要移除跟踪但不删除文件,
以便稍后在 .gitignore文件中补上,用 --cached 选项即可:
$ git rm --cached readme.txt
2.2.9 移动文件
$ git mv file_from file_to
其实,运行 git mv 就相当于运行了下面三条命令:
$ mv README.txt README
$ git rm README.txt
$ git add README
2.3 查看提交历史
在提交了若干更新之后,又或者克隆了某个项目,想回顾下提交历史,可以使用 git log命令。
每次更新都有一个 SHA-1 校验和、作者的名字和电子邮件地址、提交时间,最后缩进一个段落显示提交说明。
$ git log --pretty=oneline $ 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 提交说明
一些其他常用的选项及其释义
选项 说明
-p 按补丁格式显示每个更新之间的差异。
--stat 显示每次更新的文件修改统计信息。
--shortstat 只显示 --stat 中最后的行数修改添加移除统计。
--name-only 仅在提交信息后显示已修改的文件清单。
--name-status 显示新增、修改、删除的文件清单。
--abbrev-commit 仅显示 SHA- 的前几个字符,而非所有的 个字符。
--relative-date 使用较短的相对时间显示(比如,“ weeks ago”)。
--graph 显示 ASCII 图形表示的分支合并历史。
--pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。
2.4 撤消操作
2.4.1 修改最后一次提交
有时候我们提交完了才发现漏掉了几个文件没有加,或者提交信息写错了。想要撤消刚才的提交操作,可以使用 --amend 选项重新提交:
$ git commit --amend
此命令将使用当前的暂存区域快照提交。如果刚才提交完没有作任何改动,直接运行此命令的话,相当于有机会重新编辑提交说明,而所提交的文件快照和之前的一样。
$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend
上面的三条命令最终得到一个提交,第二个提交命令修正了第一个的提交内容。
2.4.2 取消已经暂存的文件
$ git reset HEAD benchmarks.rb
把提交到暂存的文件又撤回到本地
2.4.3 取消对文件的修改
$ git checkout -- benchmarks.rb
2.5 远程仓库的使用
2.5.1 查看当前的远程库
要查看当前配置有哪些远程仓库,可以用 git remote 命令,它会列出每个远程库的简短名字。在克隆完某个项目后,至少可以看到一个名为 origin 的远程库,
Git 默认使用这个名字来标识你所克隆的原始仓库:
$ git remote
也可以加上 -v 选项(译注:此为 —verbose 的简写,取首字母),显示对应的克隆地址:
$ git remote -v
bakkdoor git://github.com/bakkdoor/grit.git
cho45 git://github.com/cho45/grit.git
defunkt git://github.com/defunkt/grit.git
koke git://github.com/koke/grit.git
origin git@github.com:mojombo/grit.git
这样一来,我就可以非常轻松地从这些用户的仓库中,拉取他们的提交到本地。请注意,
上面列出的地址只有 origin 用的是 SSH URL 链接,所以也只有这个仓库我能推送数据上去
上面列出的地址只有 origin 用的是 SSH URL 链接,所以也只有这个仓库我能推送数据上去
2.5.2 添加远程仓库
$ git remote
origin
$ git remote add pb git://github.com/paulboone/ticgit.git
$ git remote -v
origin git://github.com/schacon/ticgit.git
pb git://github.com/paulboone/ticgit.git
现在可以用字串 pb 指代对应的仓库地址了。比如说,要抓取所有 Paul 有的,但本地仓库没有的信息,可以运行 git fetch pb:
$ git fetch pb
remote: Counting objects: , done.
remote: Compressing objects: % (/), done.
remote: Total (delta ), reused (delta )
Unpacking objects: % (/), done.
From git://github.com/paulboone/ticgit
* [new branch] master -> pb/master
* [new branch] ticgit -> pb/ticgit
现在,Paul 的主干分支(master)已经完全可以在本地访问了,对应的名字是 pb/master,你可以将它合并到自己的某个分支,或者切换到这个分支,看看有些什么有趣的更新。
2.5.3 从远程仓库抓取数据
$ git fetch [remote-name]
此命令会到远程仓库中拉取所有你本地仓库中还没有的数据。运行完成后,你就可以在本地访问该远程仓库中的所有分支,将其中某个分支合并到本地,或者只是取出某个分支,一探究竟。
如果是克隆了一个仓库,此命令会自动将远程仓库归于 origin 名下。所以,git fetch origin 会抓取从你上次克隆以来别人上传到此远程仓库中的所有更新(或是上次 fetch 以来别人提交的更新)。
有一点很重要,需要记住,fetch 命令只是将远端的数据拉到本地仓库,并不自动合并到当前工作分支,只有当你确实准备好了,才能手工合并。
可以使用 git pull 命令自动抓取数据下来,然后将远端分支自动合并到本地仓库中当前分支 实际上,默认情况下 git clone 命令本质上就是 自动创建了本地的 master 分支用于跟踪远程仓库中的 master 分支(假设远程仓库确实有
master 分支)。所以一般我们运行 git pull,目的都是要从原始克隆的远端仓库中抓取数据后,合并到工作目录中当前分支。
master 分支)。所以一般我们运行 git pull,目的都是要从原始克隆的远端仓库中抓取数据后,合并到工作目录中当前分支。
2.5.4 推送数据到远程仓库
$ git push [remote-name] [branch-name]
如果要把本地的 master 分支推送到 origin 服务器上(再次说明下,克隆操作会自动使用默认的 master 和 origin 名字)
$ git push origin master
2.5.5 查看远程仓库信息
git remote show [remote-name] 查看某个远程仓库的详细信息
$ git remote show origin
2.5.6 远程仓库的删除和重命名
$ git remote rename pb paul
对远程仓库的重命名,也会使对应的分支名称发生变化,原来的 pb/master 分支现在成了 paul/master
碰到远端仓库服务器迁移,或者原来的克隆镜像不再使用,又或者某个参与者不再贡献代码,那么需要移除对应的远端仓库,可以运行 git remote rm 命令:
$ git remote rm paul
2.6 打标签
Git 也可以对某一时间点上的版本打上标签。人们在发布某个软件版本(比如 v1.0 等等)的时候,经常这么做。本节我们一起来学习如何列出所有可用的标签,如何新建标签,以及各种不同类型标签之间的差别。
2.6.1 列显已有的标签
$ git tag
v0.
v1. $ git tag -l 'v1.4.2.*'
v1.4.2.
v1.4.2.
v1.4.2.
v1.4.2.
2.6.2 新建标签
Git 使用的标签有两种类型:轻量级的(lightweight)和含附注的(annotated)。轻量级标签就像是个不会变化的分支,实际上它就是个指向特定提交对象的引用。
而含附注标签,实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,电子邮件地址和日期,以及标签说明,标签本身也允许使用 GNU Privacy Guard (GPG) 来
签署或验证。一般我们都建议使用含附注型的标签,以便保留相关信息;当然,如果只是临时性加注标签,或者不需要旁注额外信息,用轻量级标签也没问题。
签署或验证。一般我们都建议使用含附注型的标签,以便保留相关信息;当然,如果只是临时性加注标签,或者不需要旁注额外信息,用轻量级标签也没问题。
2.6.3 含附注的标签
创建一个含附注类型的标签非常简单,用 -a (译注:取 annotated 的首字母)指定标
签名字即可:
签名字即可:
$ git tag -a v1. -m 'my version 1.4'
可以使用 git show 命令查看相应标签的版本信息,并连同显示打标签时的提交对象。
$ git show v1.
2.6.4 签署标签
如果你有自己的私钥,还可以用 GPG 来签署标签,只需要把之前的 -a 改为 -s (译注:取 Signed 的首字母)即可:
$ git tag -s v1. -m 'my signed 1.5 tag'
2.6.5 轻量级标签
轻量级标签实际上就是一个保存着对应提交对象的校验和信息的文件。要创建这样的标签,一个 -a,-s 或 -m 选项都不用,直接给出标签名字即可:
$ git tag v1.-lw $ git show v1.-lw
2.6.6 验证标签
可以使用 git tag -v [tag-name] (译注:取 verify 的首字母)的方式验证已经签署的标签。此命令会调用 GPG 来验证签名,所以你需要有签署者的公钥,存放在 keyring 中,才能验证:
2.6.8 分享标签
默认情况下,git push 并不会把标签传送到远端服务器上,只有通过显式命令才能分享标签到远端仓库。其命令格式如同推送分支,运行 git push origin [tagname] 即可:
$ git push origin v1.
如果要一次推送所有(本地新增的)标签上去,可以使用 --tags 选项:
$ git push origin --tags
2.7 技巧和窍门
2.7.1 自动完成
如果你用的是 Bash shell,可以试试看 Git 提供的自动完成脚本。下载 Git 的源代码,进入 contrib/completion 目录,会看到一个 git-completion.bash 文件。将此
文件复制到你自己的用户主目录中(译注:按照下面的示例,还应改名加上点:cp gitcompletion.bash /.git-completion.bash),并把下面一行内容添加到你的 .bashrc 文件中:
文件复制到你自己的用户主目录中(译注:按照下面的示例,还应改名加上点:cp gitcompletion.bash /.git-completion.bash),并把下面一行内容添加到你的 .bashrc 文件中:
source ~/.git-completion.bash
2.7.2 Git 命令别名
$ git config --global alias.co checkout
$ git config --global alias.br branch
$ git config --global alias.ci commit
$ git config --global alias.st status
使用这种技术还可以创造出新的命令,比方说取消暂存文件时的输入比较繁琐,可以自己
设置一下:
设置一下:
$ git config --global alias.unstage 'reset HEAD --'
这样一来,下面的两条命令完全等同:
$ git unstage fileA
$ git reset HEAD fileA
如果你觉得上面的东西太多,一下很难接受:那么其实你只想掌握git的基本操作:只要10分钟就可以: http://rogerdudler.github.io/git-guide/index.zh.html
下次再看:第三章 Git分支
哈哈:网上居然有个乐队叫Git Fresh... 每次看Dota搞笑视频时放的就是他们的歌:Booty Music..就是那个一直循环说Let's do it do it ,That‘s the way you like it 的歌,把git和这联系起来,那感觉好好笑。。。。
别装逼,我也会.....哈哈哈哈。。。。