文档目的:
1.快速理清git的概念,能google解决方案;
2.规范git操作,包括开发模式,提交,合并
概念篇:
A.暂存区(stage或者index)
workspace <---> stage <---> HEAD
理解以下命令:
workspace和stage
git add xx 添加到暂存区
git diff -- xx 对比暂存区和工作区
git checkout -- xx 从暂存区取出到工作区,git add的逆操作
stage 和 HEAD
git diff --staged -- xx 对比暂存区和最新版(库)
git diff HEAD -- xx 对比工作区和最新版(库)
git reset HEAD -- xx 从库取出到暂存区
git ci -m ‘xx’提交暂存区到库
status
git status
git status -s 两列状态输出
.git/index 就是暂存区,里面保存了每一个文件的时间戳、长度等信息,但是不包含文件内容。
B.treeish
工作区--目录树
暂存区--虚拟的目录树
库(包括里面的各种分支和游标)--虚拟的目录树
git所有的文件、目录、提交都是一个git对象,有唯一ID标识。
git log --pretty=raw -1
commit 6fcd5346a1af366e7ac8226540616f8f51051537
tree 52373de4ba18af696766c95c84317b6f931b1693
parent 029b101afa14ae4b796b592979269b56c012ceb0
author rek <rek#ATdebian82x64.example.com> 1523199629 +0800
committer rek <rek#ATdebian82x64.example.com> 1523199629 +0800
bug fix
查看这些id的类型 git cat-file -t 6fcd5346a
其中第一行是本次提交的对象, 类型是commit
第二行是本次提交对应的目录树,类型是tree
第三行是本次提交的父提交,类型也是commit
查看提交的目录树的内容
git cat-file -p 52373d
100644 blob d6320439e849ed65b7015168481fa0dbc5a7b946 .gitignore
040000 tree c5575a786f24ea373f65ec32d7c443cf47942a44 3rdlib
040000 tree 48eaae1834333aeae40f35b145fbedc1ad0ac0d1 3rdlib_py
100644 blob 6b32146f32851dd3bf7cd9fa6b3db5d3358ae62f BLADE_ROOT
100644 blob 0022ea349ed0ad8a099cf041de8cc577f7e9a9de README.md
100644 blob f4dd3876e539638e33ed97a2a835f8e9efc2c1a0 TODO.org
040000 tree af615b8bdb37d9c8ece07c6726b08c405fbc3039 baseservice
100644 blob 876a1499c09dc083612f43c53c0ae71b9c30c5b1 bg_master.code-workspace
100644 blob dc5690e20e5463ca29d033e648aa5e9bde5d9fed dev-note.org
040000 tree eb7f97a9ebfcd7fdaadbca9e83905295a349d1c4 lib
100644 blob b74bee3e3a363debdf7b9c5eb44a16b697c96f93 port_register.txt
040000 tree 845a071a72cb9277a4b983068ffc920e04a5754f service
040000 tree f38cdb86ce927aa1b417c78f579932e2088ad114 tools
可以看到其实就是bg目录树中所有的文件(blob类型)和子目录(tree类型)组成的树。
这些对象ID就是计算160bit的SHA1哈希值的十六进制表示;即40位十六进制数字;放在.git/objects目录中(松散对象,可以打包存储,放在.git/objects/pack目录下,以节省磁盘空间),前2位做目录,后38位做文件名。
用文件内容hash值标识对象有个十分巨大的好处就是重命名(移动文件)时不会出现傻乎乎的删除和添加。
git cat-file -p 6fcd5346a1
tree 52373de4ba18af696766c95c84317b6f931b1693
parent 029b101afa14ae4b796b592979269b56c012ceb0
author rek <rek#ATdebian82x64.example.com> 1523199629 +0800
committer rek <rek#ATdebian82x64.example.com> 1523199629 +0800
bug fix
可以看到commit的内容是对应的树ID+父提交ID+提交信息;因此提交形成了一棵树。
git log --graph --raw
master xiqunpan 分支就是对某些提交的引用(游标),HEAD就是对这些游标的引用。
cat .git/HEAD
ref: refs/heads/master
cat .git/ refs/heads/master
6fcd5346a1af366e7ac8226540616f8f51051537
由此可见,HEAD就是当前分支(master)的一个别名(引用);切换到分支xiqunpan时,HEAD就是xiqunpan的别名。
操作篇:
任意跳转、各种反悔
git reset --hard HEAD^ 把HEAD(HEAD引用master,因此移动的其实是master)往前HEAD上一个提交移动(其实就是撤销提交)
往后移?呵呵
git reflog show master
git reset --hard master@{2}
(只有非裸仓库开启core.logallrefupdates选项时才有,而且,会过期!!)
回忆前面git reset HEAD -- xx 为什么能撤销暂存区(即git add的逆操作?)
git reset –soft | –mixed | –hard
--hard 移动HEAD,重置暂存区和工作区(啥都没了)
--soft 移动HEAD,不重置暂存区和工作区
--mixed 移动HEAD,重置暂存区,不动工作区(默认模式)
git reset HEAD 就是移动HEAD到HEAD(什么都没做),(选择性)重置暂存区,即撤销暂存区的更改。
暂存区就是一个没有名字的临时游标,所以她也是游标!!
每次git add 就会生成一个暂存区的游标,一般不操作这个游标。
但是也有例外:那就是stash 。
stash
简单讲,相当于
git add * 把所有的改动都放到暂存区,生成一个匿名游标
stage:ref to stash@{0} 给这个游标起个名字
git reset --hard HEAD 恢复暂存区和工作区到初始状态,这样子实现了工作暂存
执行git stash pop stash@{0}时,相当于
cur:ref to master
git reset --hard stash@{0}
git reset --mixed cur
语言描述就一句话:把那个游标的文件恢复到工作区,暂存区和库都不变
HEAD
讲完了游标,还有个HEAD,即游标的引用。这是分支的操作入口。
git checkout xiqunpan 把HEAD指向xiqunpan这个游标,用暂存区内容替换工作区
git checkout -- xx 把HEAD指向当前分支游标(什么都没做),选择性用暂存区内容替换工作区
git checkout . 把HEAD指向当前分支游标(什么都没做),用暂存区内容覆盖工作区(对比git add .)
git checkout把HEAD指向当前分支游标(什么都没做),那就汇总一下工作区、暂存区与HEAD的差异吧
tag
git tag -m ‘this is a tag’ tag_name
tag是什么东西就很好理解了,一个不在提交树上的commit。
git describe 数字版本控的福音
前面讲了跳转和反悔的最基本操作,有种炫技的感觉。但是,在一些场合还是非常有意义的。git的命令行封装了这些操作,使得操作更加便捷。但是原理还是上面这些基础命令。
git commit --amend 修补式提交,单步悔棋
git cherry-pick 选樱桃,筛选某些提交
git rebase <newbase> 变基,嫁接提交
git rebase -i <newbase> 交互式变基,可以压缩提交(这个在多人协作的时候是一个非常有用的特性)
git commit-tree A^{tree} 孤儿提交,抛弃历史
git revert HEAD 反转提交
通过前面的一波操作,会产生一堆没有指针指向的松散对象存在磁盘中:
git fsck
可以通过
git gc
进行清理(包括打包和删除没有引用的松散对象)
协作篇:
分支无处不在:
git branch -a
remotes/origin/HEAD -> origin/master
remotes/origin/master
remotes/origin/xiqunpan
remotes/upstream/master
remotes/upstream/xiqunpan
引用无处不在:
git show-ref
aec33feb623448246555e18dbfb4c1c8e20ea666 refs/heads/master
6fcd5346a1af366e7ac8226540616f8f51051537 refs/remotes/origin/HEAD
6fcd5346a1af366e7ac8226540616f8f51051537 refs/remotes/origin/master
b9f87167ddc9a90d764e3ced143f7ba7908bd77b refs/remotes/origin/xiqunpan
6fcd5346a1af366e7ac8226540616f8f51051537 refs/remotes/upstream/master
075743d26327e7daad51505c82b5c4c4b9762890 refs/remotes/upstream/xiqunpan
925c7aabcb4433f5616ae6248a3a333dd2fe7bba refs/tags/tt
其中以refs/heads开头的是分支,以refs/remotes开头的是远程分支,以refs/tags开头的是里程碑。upstream是我自己起的。
git pull && git push & git merge简单粗暴的操作
git fetch && git rebase && git push优雅的操作
fork http://git.digitalgd.com.cn/xiqunpan/bg(upstream,上游分支)到自己的仓库中,比如http://git.digitalgd.com.cn/rekii/bg
git clone git@193.112.141.128:rekii/bg.git 克隆到本地
这里的本地是指开发机(vim玩家),windows pc(ide玩家),mac(我)。
git checkout -b dev 在当前master分支下开一个临时分支,用于开发。
git pull upstream master 把upstream的master分支合并到当前master分支(为什么这里有pull,说好的不优雅呢?因为master分支根本就没有新代码,git fetch upstream master; git merge upstream/master master或者git rebase --onto master upstream/master 完全没必要)
git rebase master 把dev分支的修改rebase到master分支上
(git pull 的时候有一个--rebase选项,为何不能在master上直接修改,然后通过git pull upstream master --rebase进行rebase?因为提交时间戳反了)
git push origin master:master 把本地的master分支推到自己库里(origin)
push会报一个push.default is unset的告警,在2.0以前,push是默认matching模式,就是所有同名的分支都进行push;2.0以后,只push当前分支(更加保守)
在库里发起PR和code review
注意事项:
不要直接往upstream里推任何提交(建议关闭其他人的push权限,保留接受PR的权限)
rebase时建议合并提交,减少放在upstream/master的提交次数
不放心代码长时间存在本地?没关系,push的时候使劲--force好了
强迫症的福利:
一次性的clone,比如protobuf的编译
git clone --branch <rbranch> --depth=1 <repo-url> <directory>
--depth=1选项会 隐性地 只clone 1个分支,默认master。
只clone一个分支 -- 洁癖
git clone --single-branch --branch master git@193.112.141.128:rekii/bg.git
git branch -a
remotes/origin/HEAD -> origin/master
remotes/origin/master
需要注意的是,这种方式clone的库属于shallow clone。从1.9.0之后,shallow clone也可以支持数据交换(pull和push),因此还是非常有用的。逆转--single-branch的方法:
git config remote.origin.fetch +refs/heads/:refs/remotes/origin/
git fetch --unshallow
设置默认分支的方法:
git config branch.master.remote origin
git config branch.master.merge refs/heads/master
但是在我这里似乎需要改成
git config branch.odexchange.remote origin
git config branch.odexchange.merge odexchange
才能用
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。