Git 学习笔记
GitHub浏览器快捷键:
t: 查找
先丢一堆图。。就是这么任性
目录
Git对象说明
1.Git对象模型
SHA :所有用来表示项目历史信息的文件,都是通过对对象做SHA1哈希得到的40个字符的对象名来进行索引
与SVN的区别
SVN,CVS:增量文件系统,存储每次提交之间的差异
Git:存储每次提交的文件的全部内容
对象
包括三个部分:类型(对象类型),大小(内容大小),内容
四种类型的对象:blob, tree commit tag
blob: 用来存储数据文件,通常是一个文件
tree: 有点像目录,管理一些tree或者blob
commit: 一个commit只指向一个tree,用来标记项目某个特定时间点的状态,包括一些关于时间点的元数据(时间戳,最近一次提交的作者,指向上次提交的指针等)
tag: 标记某个提交的方法
几乎所有的Git功能都是使用这四个简单的对象模型来完成,就像在本机的文件系统之上构建了一个小的文件系统
1. Blob
git show XX
![blob][]
一块二进制数据,不指向任何东西,没有任何属性,因为blob对象内容全部都是数据,如两个文件在一个目录树(或是一个版本仓库)中有同样的数据内容,那么它们将会共享同一个blob对象。Blob对象和其所对应的文件所在路径、文件名是否改被更改都完全没有关系。
2. Tree
git ls-tree 31adb53
一串直线blob对象或者其他tree对象的指针,一般表示内容之间的目录层次关系。
一个tree对象包括一串(list)条目,每一个条目包括:mode、对象类型、SHA1值 和名字(这串条目是按名字排序的)。它用来表示一个目录树的内容。
(注意:在submodules里,trees对象也可以指向commits对象. 请参见 Submodules 章节)
注意:所有的文件的mode位都是644 或 755,这意味着Git只关心文件的可执行位.
3. Commit
可以用git show -s --pretty=raw 31adb53f48cc43f72ee325cac509bf120357657e
查看
指向一个tree对象,并带有相关描述信息
4. Tag
一个标签对象包括一个对象名(译者注:就是SHA1签名), 对象类型, 标签名, 标签创建人的名字("tagger"), 还有一条可能包含有签名(signature)的消息. 你可以用 git cat-file
命令来查看这些信息:
Git目录与工作目录
Git目录:为你的项目存储所有历史和元信息的目录,包括所有对象。
Git工作目录:存储着你现在迁出(checkout)来用来编辑的文件。
Git索引
Git索引是一个在你的工作目录和项目仓库间的暂存区(Staging area),每次commit提交的内容就是当前索引(Index)里的内容
Git Start
1. Git安装
1.从源代码开始安装
2.通过一些应用包来安装
Linux,Mac,windows
2. 安装与初始化
设置名字和email,这些都是提交commit的时候的前签名
$ git config --global user.name "Scott Chacon" $ git config --global user.email "[email protected]"
git config --list:查看配置信息
- 获取git仓库 三种途径
- git clone [email protected]:develop/iLukou.git(配置SSH,Add SSH public key)
- git clone http://gitlab.lukou.com/develop/iLukou.git
- git init in your project
3.工作流程开始
- git add file(添加到file索引) / git commit -a(自动添加索引修改到索引并提交) / 缩写 gaa(git add .)
- git commit -m / 缩写 gcmsg xxx
- git push originName originBranch / 缩写 gp originName originBranch
4.分支合并
now in branch yyy
git branch 得到当前仓库的所有分支
git branch XXX 创建分支XXX
git checkout XXX (gco) 切换到XXX分支 (checkout 会改index,Workspace ,head to the last commit in XXX,所以需要先commit再co)
git merge xxx 合并xxx到yyy现有分支
git diff if 有冲突,diff来查看冲突,然后解决冲突再commit,commit之后,gitk(这个命令不知用)
git branch -d xxx 删除xxx
reset and checkout
reset
- git reset --hard commitID
change head to the commit ID,暂存区,工作区和引用指向的目录树三者一致
- git reset (--mixed) commitID
change head to the commitID,暂存区和引用指向的目录树相同,工作区不变
- git reset --soft commitID
change head to the commitID,暂存区和工作区不变
checkout
git checkout (commit) paths , commit可选,若省略则工作区与暂存区指向的内容相同,修改工作区,不修改HEAD指针
git checkout branch ,暂存区工作区和branch下上次commit时引用指向的目录树一致,改变HEAD指针,HEAd只有在切换到一个分支时才可以对提交进行跟踪,,否则仍然会进入分离头指针的状态,在分离头指针额状态下的提交并不能被引用关联到,从而可能丢失,所以该命令的主要作用是切换分支.
git checkout 汇总工作区,暂存区和HEAd的差异
- git checkout branch -- filename
维持HEAD的指向不变.用branch所指向的提交中的filename替换暂存区和工作区中相应的文件.会将暂存区和工作区中的filename直接覆盖
撒销一个合并
如果你觉得你合并后的状态是一团乱麻,想把当前的修改都放弃,你可以用下面的命令回到合并之前的状态:
$ git reset --hard HEAD
或者你已经把合并后的代码提交,但还是想把它们撒销:
$ git reset --hard ORIG_HEAD
但是刚才这条命令在某些情况会很危险,如果你把一个已经被另一个分支合并的分支给删了,那么 以后在合并相关的分支时会出错。
快速向前合并
还有一种需要特殊对待的情况,在前面没有提到。通常,一个合并会产生一个合并提交(commit), 把两个父分支里的每一行内容都合并进来。
但是,如果当前的分支和另一个分支没有内容上的差异,就是说当前分支的每一个提交(commit)都已经存在另一个分支里了,git 就会执行一个“快速向前"(fast forward)操作;git 不创建任何新的提交(commit),只是将当前分支指向合并进来的分支
查看git日志
git log
git diff branchA..branchB a到b的工作区改动
git diff branchB 当前工作目录与另一个分支的差别
分布式工作流程
两个分支的修改同一份内容
git pull oringinName branchName
git pull : 从远程分支抓取修改的内容,然后把他合并到当前分支(git fetch + git merge)
将修改推到公共仓库
git push ssh://yourserver.com/~you/proj.git master
和git-fetch命令一样git-push如果命令的执行结果不是"快速向前"(fast forward) 就会报错
Git标签
用git tag
创建标签制定某个提交
git tag tagName commitID
当这样的一条命令执行后,一个新的对象被添加到Git对象库中,并且标签引用就指向了 一个标签对象,而不是指向一个提交(commit). 这样做的好处就是:你可以为一个标签 打处签名(sign), 方便你以后来查验这是不是一个正确的提交(commit).
下面是一个创建标签对象的例子:
$ git tag -a stable-1 1b2e1d63ff
标签对象可以指向任何对象,但是在通常情况下是一个提交(commit). (在Linux内核代 码中,第一个标签对象是指向一个树对象(tree),而不是指向一个提交(commit)).
忽略文件
vim .gitignore
.gitignor文件同样可以像其它文件一样加到项目仓库里( 直接用 git add .gitignore 和 git commit等命令), 这样项目里的其它开发者也能共享同一套忽略 文件规则。
如果你想忽略规则只对特定的仓库起作用,你可以把这些忽略规则写到你的仓库下 .git/info/exclude文件中,或是写在Git配置变量core.excludesfile中指定的 文件里。
rebase
git checkout -b mywork rogin
基于远程分支,创建mywork分支
在分支mywork中,生成两次提交,此时,在origin分支,有其他人生成了两次提交,则两个分支都各自向前进了,出现了分叉,这时,
1.我们可以merge,合并origin分支生成一个新的提交,该提交指向两个父commit
2.也可以rebase,将mywork指向origin的最新提交
这些命令会把你的"mywork"分支里的每个提交(commit)取消掉,并且把它们临时 保存为补丁(patch)(这些补丁放到".git/rebase"目录中),然后把"mywork"分支更新 到最新的"origin"分支,最后把保存的这些补丁应用到"mywork"分支上。
当'mywork'分支更新之后,它会指向这些新创建的提交(commit),而那些老的提交会被丢弃。 如果运行垃圾收集命令(pruning garbage collection), 这些被丢弃的提交就会删除。
在rebase的过程中,也许会出现冲突(conflict). 在这种情况,Git会停止rebase并会让你去解决 冲突;在解决完冲突后,用"git-add"命令去更新这些内容的索引(index), 然后,你无需执行 git-commit,只要执行:
$ git rebase --continue
这样git会继续应用(apply)余下的补丁。
在任何时候,你可以用--abort参数来终止rebase的行动,并且"mywork" 分支会回到rebase开始前的状态。
$ git rebase --abort
交互式rebase
你亦可以选择进行交互式的rebase。这种方法通常用于在向别处推送提交之前对它们进行重写。交互式rebase提供了一个简单易用的途径让你在和别人分享提交之前对你的提交进行分割、合并或者重排序
。在把从其他开发者处拉取的提交应用到本地时,你也可以使用交互式rebase对它们进行清理。
如果你想在rebase的过程中对一部分提交进行修改,你可以在'git rebase'命令中加入'-i'或'--interactive'参数去调用交互模式。
这个命令会执行交互式rebase操作,操作对象是那些自最后一次从origin仓库拉取或者向origin推送之后的所有提交。
若想查看一下将被rebase的提交,可以用如下的log命令:
git rebase -i origin/master
一旦运行了'rebase -i'命令,你所预设的编辑器会被调用,其中含有如下的内容:
pick fc62e55 added file_size
pick 9824bf4 fixed little thing
pick 21d80a5 added number to log
pick 76b9da6 added the apply command
pick c264051 Revert "added file_size" - not implemented correctly
Rebase f408319..b04dc3d onto f408319
#
Commands:
p, pick = use commit
e, edit = use commit, but stop for amending
s, squash = use commit, but meld into previous commit
#
If you remove a line here THAT COMMIT WILL BE LOST.
However, if you remove everything, the rebase will be aborted.
#
这些信息表示从你上一次推送操作起有5个提交。每个提交都用一行来表示,行格式如下:
(action) (partial-sha) (short commit message)
1.如果指定进行'pick'操作,git会应用这个补丁,以同样的提交信息(commit message)保存提交。
2.如果指定进行'squash'操作,git会把这个提交和前一个提交合并成为一个新的提交。这会再次调用编辑器,你在里面合并这两个提交的提交信息。所以,如果你(在上一步)以如下的内容离开编辑器:
3.如果指定进行'edit'操作,git会完成同样的工作,但是在对下一提交进行操作之前,它会返回到命令行让你对提交进行修正,或者对提交内容进行修改。
$ git reset HEAD^
$ git add file1
$ git commit 'first part of split commit'
$ git add file2
$ git commit 'second part of split commit'
$ git rebase --continue
4.交互式rebase的最后一个作用是丢弃提交。如果把一行删除而不是指定'pick'、'squash'和'edit'中的任何一个,git会从历史中移除该提交。
交互式添加
git add -i
$>git add -i
staged unstaged path
1: unchanged +4/-0 assets/stylesheets/style.css
2: unchanged +23/-11 layout/book_index_template.html
3: unchanged +7/-7 layout/chapter_template.html
4: unchanged +3/-3 script/pdf.rb
5: unchanged +121/-0 text/14_Interactive_Rebasing/0_ Interactive_Rebasing.markdown
*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now>
我们可以进行数个有用的操作,包括取消文件的暂存(3: revert),加入未跟踪的文件(4: add untracked)和查看差异(6: diff)。这些功能都很易懂。还有一个很“酷”的功能,就是暂存补丁(staging patches)(5: patch)。
储藏
当你正在做一项复杂的工作时, 发现了一个和当前工作不相关但是又很讨厌的bug. 你这时想先修复bug再做手头的工作, 那么就可以用 git stash 来保存当前的工作状态, 等你修复完bug后,执行'反储藏'(unstash)操作就可以回到之前的工作里.
git stach save "work in progress for foo feature"
上面这条命令会保存你的本地修改到储藏(stash)中, 然后将你的工作目录和索引里的内容全部重置, 回到你当前所在分支的上次提交时的状态(git reset --hard HEAD)
after do some debug and git commit -a -m "fixed bug"
you can git stash apply
back to your work before
you can run several git stash
, to stash your work,
see the stash with git stash list
可以用类似'git stash apply stash@{1}'的命令来使用在队
列中的任意一个'储藏'(stashes). 'git stash clear‘则是用来清空这个队列.
Git树名
不用40个字节长的SHA串来表示一个提交(commit)或是其它git对象, 有很多种名字表示方法. 在Git里,这些名字就叫'树名'(treeish).
Sha短名
以下等价
980e3ccdaac54a0d4de358f3fe5d718027d96aae
980e3ccdaac54a0d4
980e3cc
分支,remote,标签
使用分支,remote,标签来代替sha串名,他们只是指向某个对象的指针,
日期标识符
追踪分支
在Git中,追踪分支是用于联系本地分支和远程分支的,如果你在’追踪分支'(Tracking Branches)上执行推送(push)或拉取(pull)时, 它会自动推送(push)或拉取(pull)到关联的远程分支上.
如果你经常要从远程仓库里拉取(pull)分支到本地,并且不想很麻烦的使用"git pull "这种格式; 那么就应当使用‘追踪分支'(Tracking Branches).
‘git clone‘命令会自动在本地建立一个'master'分支,它是'origin/master'的‘追踪分支’. 而'origin/master'就是被克隆(clone)仓库的'master'分支.
译者注: origin一般是指原始仓库地址的别名.
你可以在使用'git branch'命令时加上'--track'参数, 来手动创建一个'追踪分支'.
git branch --track experimental origin/experimental
当你运行下命令时:
$ git pull experimental
它会自动从‘origin'抓取(fetch)内容,再把远程的'origin/experimental'分支合并进(merge)本地的'experimental'分支.
当要把修改推送(push)到origin时, 它会将你本地的'experimental'分支中的修改推送到origin的‘experimental'分支里, 而无需指定它(origin).
搜索
git grep
git grep -c XXXX 文件名
Git的撤销操作-重置,签出
http://gitbook.liuhui998.com/4_9.html
git revert commitID
Git维护
git gc :压缩历史信息来节约磁盘和内存空间
git fsck :云心一些仓库的一致性检查,有问题就回报告,这项操作也有点耗时, 通常报的警告就是“悬空对象"(dangling objects).“悬空对象"(dangling objects)并不是问题, 最坏的情况只是它们多占了一些磁盘空间. 有时候它们是找回丢失的工作的最后一丝希望)
创建公共仓库Git
假设你个人的仓库在目录 ~/proj. 我们先克隆一个新的“裸仓库“,并且创建一个标志文件告诉git-daemon这是个公共仓库.
$ git clone --bare ~/proj proj.git
$ touch proj.git/git-daemon-export-ok
上面的命令创建了一个proj.git目录, 这个目录里有一个“裸git仓库" -- 即只有'.git'目录里的内容,没有任何签出(checked out)的文件.
下一步就是你把这个 proj.git 目录拷到你打算用来托管公共仓库的主机上. 你可以用scp, rsync或其它任何方式.
- 通过git协议导出git仓库
用git协议导出git仓库, 这是推荐的方法.
如果这台服务器上有管理员,TA们要告诉你把仓库放在哪一个目录中, 并且“git:// URL”除仓库目录部分外是什么.
你现在要做的是启动 git daemon; 它会监听在 9418端口. 默认情况下它会允许你访问所有的git目录(看目录中是否有git-daemon-export-ok文件). 如果以某些目录做为 git-daemon 的参数, 那么 git-daemon 会限制用户通过git协议只能访问这些目录.
你可以在inetd service模式下运行 git-daemon; 点击 git daemon 可以查看帮助信息.
- 通过http协议导出git仓库
git协议有不错的性能和可靠性, 但是如果主机上已经配好了一台web服务器,使用http协议(git over http)可能会更容易配置一些.
你需要把新建的"裸仓库"放到Web服务器的可访问目录里, 同时做一些调整,以便让web客户端获得它们所需的额外信息.
$ mv proj.git /home/you/public_html/proj.git
$ cd proj.git
$ git --bare update-server-info
$ chmod a+x hooks/post-update
(最后两行命令的解释可以点击这里查看: git update-server-info & githooks.)
拼好了proj.git的web URL, 任何人都可以从这个地址来克隆(clone)或拉取(pull) git仓库内容. 下面这个命令就是例子:
$ git clone http://yourserver.com/~you/proj.git
修改你的历史
交互式洐合是修改单个提交的好方法。
git filter-branch是修改大量提交的好方法
git bisect
二分查找问题分支
git blame
如果你要查看文件的每个部分是谁修改的, 那么 git blame 就是不二选择. 只要运行'git blame [filename]', 你就会得到整个文件的每一行的详细修改信息:包括SHA串,日期和作者:
Git是如何物理存储对象的
所有的对象都以SHA值为索引用gzip格式压缩存储, 每个对象都包含了对象类型, 大小和内容.
Git中存在两种对象 - 松散对象(loose object)和打包对象(packed object).
1. 松散对象
松散对象是一种比较简单格式. 它就是磁盘上的一个存储压缩数据的文件. 每一个对象都被写入一个单独文件中.
如果你对象的SHA值是ab04d884140f7b0cf8bbf86d6883869f16a46f65, 那么对应的文件会被存储在:
GIT_DIR/objects/ab/04d884140f7b0cf8bbf86d6883869f16a46f65
Git使用SHA值的前两个字符作为子目录名字, 所以一个目录中永远不会包含过多的对象. 文件名则是余下的38个字符.
2. 打包对象
另外一种对象存储方式是使用打包文件(packfile). 由于Git把每个文件的每个版本都作为一个单独的对象, 它的效率可能会十分的低. 设想一下在一个数千行的文件中改动一行, Git会把修改后的文件整个存储下来, 很浪费空间.(git GC)
查看Git对象
git-cat-file -t 54196cc2对象 (-p:对象内容 -t:对象类型)
git ls-tree 92b8b694 树
`