2、Git深入理解
<pre><code> Git的使用
本次主要内容提纲:
(一).配置
1.注册
2.别名
3.颜色
4.编辑器
5.模糊匹配
6.自定义Git
(二).git的内部结构及工作原理
1.基本概念
2.基本结构
3.四种对象
4.三种状态
5.三种工作目录
(三).基本命令的使用
1.一般提交代码流程
2.查看Log日志
3.打标签Tag
4.reset的使用
5.撤销
Git 配置
1.配置用户信息
Git提供了一个叫做git config的工具,专门用来配置或读取相应的工作环境变量。这里主要介绍如何配置个人的户信息:
$ git config --global user.name xuhao
$ git config --global user.email xuhao@spt-tek.com
$ ssh-keygen -t rsa
2.显示git配置项:
git config --global --list
user.name //用户信息的配置
user.email
core.editor //文本编辑器
merge.tool //差异分析工具
core.paper &quot;less -N&quot; //配置显示方式
color.diff true //diff颜色配置
alias.co checkout //设置别名
git config --global alias.co checkout
git config --list //查看配置信息 !
git config user.name //直接查阅某个环境变量的设定
git config core.filemode false //忽略修改权限的文件
从代码仓库下载代码:
git clone ssh://项目地址 // git clone ssh://xuhao@gerrit2.spt-tek.com:29418/CarEngine
自定义Git
core.editor
Git默认会调用你的环境变量editor定义的值作为文本编辑器,如果没有定义的话,会调用Vim来创建和编辑提交以及标签信息, 你可以使用core.editor改变默认编辑器:
git config --global core.editor emacs // &quot;\&quot;D:\Program Files (x86)\gvim\vim73\gvim.exe\&quot;&quot; vim
git config --global core.editor &quot;\&quot;D:\Program Files (x86)\gvim\vim73\gvim.exe\&quot;&quot;
help.autocorrect
git config --global help.autocorrect 1 ( /0)
如果你把help.autocorrect设置成1(译注:启动自动修正),那么在只有一个命令被模糊匹配到的情况下,Git 会自动运行该命令
该配置项只在 Git 1.6.1及以上版本有效
外部的合并与比较工具
虽然 Git 自己实现了diff,而且到目前为止你一直在使用它,但你能够用一个外部的工具替代它,除此以外,你还能用一个图形化的工具来合并和解决冲突从而不必自己手动解决。有一个不错且免费的工具可以被用来做比较和合并工作,它就是P4Merge(译注:Perforce图形化合并工具),我会展示它的安装过程。 P4Merge可以在所有主流平台上运行,现在开始大胆尝试吧。对于向你展示的例子,在Mac和Linux系统上,我会使用路径名,在Windows上,/usr/local/bin应该被改为你环境中的可执行路径。 下载P4Merge:
http://www.perforce.com/perforce/downloads/component.html
首先把你要运行的命令放入外部包装脚本中,我会使用Mac系统上的路径来指定该脚本的位置,在其他系统上,它应该被放置在二进制文件p4merge所在的目录中。创建一个merge包装脚本,名字叫作extMerge,让它带参数调用p4merge二进制文件:
$ cat /usr/local/bin/extMerge
#!/bin/sh
/Applications/p4merge.app/Contents/MacOS/p4merge $*
diff包装脚本首先确定传递过来7个参数,随后把其中2个传递给merge包装脚本,默认情况下, Git 传递以下参数给diff:
path old-file old-hex old-mode new-file new-hex new-mode
由于你仅仅需要old-file和new-file参数,用diff包装脚本来传递它们吧。
$ cat /usr/local/bin/extDiff
#!/bin/sh
[ $# -eq 7 ] &amp;&amp; /usr/local/bin/extMerge &quot;$2&quot; &quot;$5&quot;
确认这两个脚本是可执行的:
$ sudo chmod +x /usr/local/bin/extMerge
$ sudo chmod +x /usr/local/bin/extDiff
现在来配置使用你自定义的比较和合并工具吧。这需要许多自定义设置:merge.tool通知 Git 使用哪个合并工具;mergetool.*.cmd规定命令运行的方式;mergetool.trustExitCode会通知 Git 程序的退出是否指示合并操作成功;diff.external通知 Git 用什么命令做比较。因此,你能运行以下4条配置命令:
$ git config --global merge.tool extMerge
$ git config --global mergetool.extMerge.cmd \
'extMerge &quot;$BASE&quot; &quot;$LOCAL&quot; &quot;$REMOTE&quot; &quot;$MERGED&quot;'
$ git config --global mergetool.trustExitCode false
$ git config --global diff.external extDiff
或者直接编辑~/.gitconfig文件如下:
[merge]
tool = extMerge
[mergetool &quot;extMerge&quot;]
cmd = extMerge &quot;$BASE&quot; &quot;$LOCAL&quot; &quot;$REMOTE&quot; &quot;$MERGED&quot;
trustExitCode = false
[diff]
external = extDiff
设置完毕后,运行diff命令:
$ git diff 32d1776b1^ 32d1776b1
命令行居然没有发现diff命令的输出,其实,Git 调用了刚刚设置的P4Merge,它看起来像图7-1这样:
Figure 7-1. P4Merge.
当你设法合并两个分支,结果却有冲突时,运行git mergetool,Git 会调用P4Merge让你通过图形界面来解决冲突。 设置包装脚本的好处是你能简单地改变diff和merge工具,例如把extDiff和extMerge改成KDiff3,要做的仅仅是编辑extMerge脚本文件:
$ cat /usr/local/bin/extMerge
/Applications/kdiff3.app/Contents/MacOS/kdiff3 $*
现在 Git 会使用KDiff3来做比较、合并和解决冲突。 Git预先设置了许多其他的合并和解决冲突的工具,而你不必设置cmd。可以把合并工具设置为:kdiff3、opendiff、tkdiff、 meld、xxdiff、emerge、vimdiff、gvimdiff。如果你不想用到KDiff3的所有功能,只是想用它来合并,那么kdiff3 正符合你的要求,运行:
1 $ git config --global merge.tool kdiff3
如果运行了以上命令,没有设置extMerge和extDiff文件,Git 会用KDiff3做合并,让通常内设的比较工具来做比较。
$ ls
HEAD --文件指向当前分支
branches/
config --- 文件包含了项目特有的配置选项
description ---文件仅供 GitWeb 程序使用
hooks/ ---介绍了的客户端或服务端钩子脚本
index --文件保存了暂存区域信息
info/
objects/目录存储所有数据内容
refs/ --目录存储指向数据 (分支) 的提交对象的指针
Git 是一套内容寻址文件系统。很不错。不过这是什么意思呢?这种说法的意思是,从内部来看,Git 是简单的 key-value 数据存储。它允许插入任意类型的内容,并会返回一个键值,通过该键值可以在任何时候再取出该内容。可以通过底层命令hash-object 来示范这点,传一些数据给该命令,它会将数据保存在 .git 目录并返回表示这些数据的键值。首先初使化一个 Git 仓库并确认objects 目录是空的:
$ mkdir test
$ cd test
$ git init
Initialized empty Git repository in /tmp/test/.git/
$ find .git/objects
.git/objects
.git/objects/info
.git/objects/pack
$ find .git/objects -type f
$
Git 初始化了 objects 目录,同时在该目录下创建了 pack 和 info 子目录,但是该目录下没有其他常规文件。我们往这个 Git 数据库里存储一些文本:
$ echo 'test content' | git hash-object -w --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4
参数 -w 指示 hash-object 命令存储 (数据) 对象,若不指定这个参数该命令仅仅返回键值。--stdin 指定从标准输入设备 (stdin) 来读取内容,若不指定这个参数则需指定一个要存储的文件的路径。该命令输出长度为 40 个字符的校验和。这是个 SHA-1 哈希值──其值为要存储的数据加上你马上会了解到的一种头信息的校验和。现在可以查看到 Git 已经存储了数据:
1
2 $ find .git/objects -type f
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
可以在 objects 目录下看到一个文件。这便是 Git 存储数据内容的方式──为每份内容生成一个文件,取得该内容与头信息的 SHA-1 校验和,创建以该校验和前两个字符为名称的子目录,并以 (校验和) 剩下 38 个字符为文件命名 (保存至子目录下)。
通过 cat-file 命令可以将数据内容取回。该命令是查看 Git 对象的瑞士军刀。传入 -p 参数可以让该命令输出数据内容的类型:
$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
test content
可以往 Git 中添加更多内容并取回了。也可以直接添加文件。比方说可以对一个文件进行简单的版本控制。首先,创建一个新文件,并把文件内容存储到数据库中:
$ echo 'version 1' &gt; test.txt
$ git hash-object -w test.txt
83baae61804e65cc73a7201a7252750c76066a30
接着往该文件中写入一些新内容并再次保存:
$ echo 'version 2' &gt; test.txt
$ git hash-object -w test.txt
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
数据库中已经将文件的两个新版本连同一开始的内容保存下来了:
$ find .git/objects -type f
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
再将文件恢复到第一个版本:
$ git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30 &gt; test.txt
$ cat test.txt
version 1
或恢复到第二个版本:
$ git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a &gt; test.txt
$ cat test.txt
version 2
需要记住的是几个版本的文件 SHA-1 值可能与实际的值不同,其次,存储的并不是文件名而仅仅是文件内容。这种对象类型称为 blob 。通过传递 SHA-1 值给cat-file -t 命令可以让 Git 返回任何对象的类型:
$ git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
blob
当你在一个新目录或已有目录内执行 git init 时,Git 会创建一个 .git 目录
直接记录快照,而非差异比较
Git 更像是把数据看作是对小型文件系统的一组快照。 每次你提交更新,或在 Git 中保存项目状态时,它主要对当时的全部文件制作一个快照并保存这个快照的索引。 为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个 快照流。
近乎所有操作都是本地执行
举个例子,要浏览项目的历史,Git 不需外连到服务器去获取历史,然后再显示出来——它只需直接从本地数据库中读取。 你能立即看到项目历史。 如果你想查看当前版本与一个月前的版本之间引入的修改,Git 会查找到一个月前的文件做一次本地的差异计算,而不是由远程服务器处理或从远程服务器拉回旧版本文件再来本地处理。
Git 保证完整性
Git 中所有数据在存储前都计算校验和,然后以校验和来引用
它通常是以十六进制为数制表示的形式,如:十六进制串: 0102030405060708 的效验和是: 24 (十六进制)
Git 用以计算校验和的机制:这是一个由 40 个十六进制字符(0-9 和 a-f)组成字符串,基于 Git 中文件的内容或目录结构计算出来
Git 一般只添加数据
你执行的 Git 操作,几乎只往 Git 数据库中增加数据。 很难让 Git 执行任何不可逆操作,或者让它以任何方式清除数据。 同别的 VCS 一样,未提交更新时有可能丢失或弄乱修改的内容;但是一旦你提交快照到 Git 中,就难以再丢失数据,特别是如果你定期的推送数据库到其它仓库的话。
三种状态
Git 有三种状态,你的文件可能处于其中之一:已提交(committed)、已修改(modified)和已暂存(staged)。 已提交表示数据已经安全的保存在本地数据库中。 已修改表示修改了文件,但还没保存到数据库中。 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,拷贝的就是这里的数据。
工作目录是对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。
暂存区域是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。 有时候也被称作“索引”,不过一般说法还是叫暂存区域。
$ ls
HEAD --文件指向当前分支
branches/
config --- 文件包含了项目特有的配置选项
description ---文件仅供 GitWeb 程序使用
hooks/ ---介绍了的客户端或服务端钩子脚本
index --文件保存了暂存区域信息
info/
objects/目录存储所有数据内容
refs/ --目录存储指向数据 (分支) 的提交对象的指针
基本命令
(1)git push
功能:向服务器的Git仓库中提交本地Git仓库已修改的文件或目录。
(2)git pull git push origin HEAD:refs/for/分支名
功能:从服务器中的Git仓库中获取最新代码,并与本地代码自动merge,如果服务器中的代码与本地要提交的代码有冲突,冲突部分会在文件中体现。
(3)git status
功能:查看Git仓库中的文件状态。
(4)git add .
功能:向本地Git仓库中添加修改文件或目录。
(5)git commit -m &quot;注释&quot; -a -m
git commit 提交的是暂存区里面的内容,也就是 Changes to be committed 中的文件。
git commit -a 除了将暂存区里的文件提交外,还提交 Changes bu not updated 中的文件。
功能:提交修改文件或目录到本地Git仓库。
(6) git diff
功能:查看修改文件与本地Git仓库中的文件的异同,即:你修改了文件中哪些部分。
(7) git log
(8)git checkout .
功能:从服务器的Git仓库中下载最新代码并覆盖掉所有本地文件,包括已经修改的。
(10)配置用户名:
git config --global user.name &quot;用户名&quot;。
(11)配置邮箱:
git config --global user.email 邮箱。
(12)给命令配置别名:
git config --global alias.co checkout
(13)显示某一版本更改详情:
git show 版本号。
(14)查看修改日志:
git log .
(15)git fetch:
从远程服务器端获取最新版本到本地,不会自动与本地代码merge(git pull = git fetch + git merge)。
Git中tag的用法
git标签分为两种类型:轻量标签和附注标签。轻量标签是指向提交对象的引用,附注标签则是仓库中的一个独立对象。建议使用附注标签。
# 创建轻量标签
$ git tag v0.1.2-light
# 创建附注标签
$ git tag -a v0.1.2 -m “0.1.2版本”
打标签
git tag -a v1.01 -m &quot;Relase version 1.01&quot;
注解:git tag 是打标签的命令,-a 是添加标签,其后要跟新标签号,-m 及后面的字符串是对该标签的注释。
查看标签
git tag
或者git tag -l
切换到标签
与切换分支命令相同,用git checkout [tagname]
查看标签信息
用git show命令可以查看标签的版本信息:
$ git show v0.1.2
提交标签到远程仓库
git push origin -tags
注解:就像git push origin master 把本地修改提交到远程仓库一样,-tags可以把本地的打的标签全部提交到远程仓库。
删除标签
误打或需要修改标签时,需要先将标签删除,再打新标签。
$ git tag -d v0.1.2 # 删除标签
参数d即delete的缩写,意为删除其后指定的标签。
给指定的commit打标签
打标签不必要在head之上,也可在之前的版本上打,这需要你知道某个提交对象的校验和(通过git log获取)。
# 补打标签
$ git tag -a v0.1.1 9fbc3d0
标签发布
通常的git push不会将标签对象提交到git服务器,我们需要进行显式的操作:
$ git push origin v0.1.2 # 将v0.1.2标签提交到git服务器
$ git push origin –tags # 将本地所有标签一次性提交到git服务器
删除远程标签
git push origin :refs/tags/v1.01
注解:就像git push origin :branch_1 可以删除远程仓库的分支branch_1一样, 冒号前为空表示删除远程仓库的tag。
注意:如果想看之前某个标签状态下的文件,可以这样操作
1.git tag 查看当前分支下的标签
2.git checkout v0.21 此时会指向打v0.21标签时的代码状态,(但现在处于一个空的分支上)
Git版本恢复命令reset
reset命令有3种方式:
1. git reset –mixed:此为默认方式,不带任何参数的git reset,即时这种方式,它回退到某个版本,只保留源码,回退commit和index信息
2. git reset –soft:回退到某个版本,只回退了commit的信息,不会恢复到index file一级。如果还要提交,直接commit即可
3. git reset –hard:彻底回退到某个版本,本地的源码也会变为上一个版本的内容
1. #回退所有内容到上一个版本
2. git reset HEAD^
3. #回退a.py这个文件的版本到上一个版本
4. git reset HEAD^ a.py
5. #向前回退到第3个版本
6. git reset –soft HEAD~3
7. #将本地的状态回退到和远程的一样
8. git reset –hard origin/master
9. #回退到某个版本
10. git reset 057d
11. #回退到上一次提交的状态,按照某一次的commit完全反向的进行一次commit
12. git revert HEAD
Git撤消操作总结
1 修改最后一次提交 git commit --amend
2 撤消已暂存的文件 git reset HEAD
3 撤消对文件的修改 git checkout -- &lt;file&gt;
4 Git撤消commit
git reset --hard HEAD^
5撤消未跟踪文件 git clean -dxf
git log
1.git log
如果不带任何参数,它会列出所有历史记录,最近的排在最上方,显示提交对象的哈希值,作者、提交日期、和提交说明。如果记录过多,则按Page Up、Page Down、↓、↑来控制显示;按q退出历史记录列表。
2.git log -n
如果不想向上面那样全部显示,可以选择显示前N条。
3.git log --stat -n
显示简要的增改行数统计,每次提交文件的变更统计,-n 同上,前n条,可省略。
4.git log -p -n
此命令同上,不过显示更全了。
5.git log --pretty=oneline
一行显示,只显示哈希值和提交说明。
6.gig lot --graph
ASCII 字符串表示的简单图形,形象地展示了每个提交所在的分支及其分化衍合情况
7.指定日期、关键字、作者
如两天前的提交历史:git log --since=2.days
如指定作者为&quot;BeginMan&quot;的所有提交:$ git log --author=BeginMan
如指定关键字为“init”的所有提交:$ git log --grep=init
如指定提交者为&quot;Jack&quot;的所有提交:$ git log --committer=Jack
注意作者与提交者的关系:作者是程序的修改者,提交者是代码提交人。
如指定2天前,作者为“BeginMan”的提交含有关键字'init'的前2条记录:$ git log --since=2.days --author=BeginMan --grep=init -2
注意:上面选项后面的参数可以带单双引号,如--author=&quot;BeginMan&quot;
git log 命令支持的选项
-p 按补丁格式显示每个更新之间的差异。
--stat 显示每次更新的文件修改统计信息。
--shortstat 只显示 --stat 中最后的行数修改添加移除统计。
--name-only 仅在提交信息后显示已修改的文件清单。
--name-status 显示新增、修改、删除的文件清单。
--abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符。
--relative-date 使用较短的相对时间显示(比如,“2 weeks ago”)。
--graph 显示 ASCII 图形表示的分支合并历史。
--pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。
checkout 和 reset 的区别
git checkout -- file;撤销对工作区修改;这个命令是以最新的存储时间节点(add和commit)为参照,覆盖工作区对应文件file;这个命令改变的是工作区
git reset HEAD -- file;清空add命令向暂存区提交的关于file文件的修改(Ustage);这个命令仅改变暂存区,并不改变工作区,这意味着在无任何其他操作的情况下,工作区中的实际文件同该命令运行之前无任何变化
$ echo 'test content' | git hash-object -w --stdin
$ find .git/objects -type f
$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
$ echo 'version 1' &gt; test.txt
$ git hash-object -w test.txt
$ echo 'version 2' &gt; test.txt
$ git hash-object -w test.txt</code></pre>