首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >git指南--我想xxx,该用什么命令?

git指南--我想xxx,该用什么命令?

原创
作者头像
rek
发布2019-07-04 16:03:35
5140
发布2019-07-04 16:03:35
举报
文章被收录于专栏:rek的云+专栏rek的云+专栏

文档目的:

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

  • master

  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

  • master

  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 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档