Git底层模型
git底层为一个key-value存储系统,它将文件的内容通过hash计算获得一个全局唯一的hash码,作为代表该文件key,然后将其文件内容存为一个二进制文件,即为图中Hash Object。Hash Object中只有文件内容以及hash值作为文件名。文件名保存在Tree Object中,Tree Object 可以索引到其他Tree Object / Hash Object.Tree Object映射到现实文件系统中的文件夹。
每次提交时都会产生一个commit以及一个新的顶级虚拟Tree。
CommitA 中提交了三个文件File[A-C], 分别生成了Hash Object[A-C],其中有一个文件夹B,生成了TreeB。提交时产生虚拟顶级TreeA。
CommitB 时,修改了文件A、B的内容,于是CommitB基于修改后的文件A',B' 生成全新的Hash Object[A'|B'];,对于未修改的文件C则直接引用。
注意:
每次修改文件后生成的hashObject是基于整个文件生成的,是全量生成,而不是与之前的hashObject比较进行增量生成。
文件暂存区 index 类似于一个虚拟顶级Tree节点,文件被加到暂存区中,则会立刻生成hashObject,并被index索引。如果再次修改该文件,再次将其加入暂存区,则又会生成一个hashObject.之前的hashObject将不再被index索引,所以成为了一个无用对象,可以使用git fsck 查看,可以使用git prune清除。
Merge与cherry-pick/Rebase之间的区别
merge合并时,会查找共有的父提交,然后每个文件基于父提交中的文件进行比较,若出现双方修改则冲突。
Rebase/cherry-pick则是基于补丁的,是一种增量式记录,如git format-patch F,则会列出F相对于E修改的文件,将两个提交中所有修改的文件作为补丁。
在D处拣选F,git cherry-pick F。则会将F相对于E修改的所有文件(E、F均要)打包成patch,应用到D时,则以E的文件作为基本文件,进行D、E比较,若双方修改文件,则冲突。相对于以E作为D,F的共同提交,进行合并。
HEAD
HEAD默认会指向branch,branch再指向commit,若HEAD直接指向commit,则会除于头指针分离状态.此时所有提交都很有可能丢失
基本命令大全
-------------------------------------------------------------------------------------
git help //查看命令的帮助文档
git rev-parse master //查看引用指向的hash id
git rev-list --graph --format=%B //列出该commit及其之前的所有commit并展示
git rev-list --graph--format=%B.. //列出B及B的祖先提交,并排除A及A的祖先提交
git rev-list --graph--format=%B... //列出A与B的并集并排除A与B的交集
git rev-list --graph--format=%B^@ //A的所有祖先提交,将A排除在外
git rev-list --graph--format=%B^! //只列出A一个提交
git cat-file -t //查看hash id的类型,该id可以表示一个commit/hash Object/tree...
git cat-file -p //查看hash id的内容,当为hash Object时即为查看文件内容
-------------------------------------------------------------------------------------
~n //表示该提交向上遍历n个parent
git reflog show HEAD|master -n //查看HEAD/master之前的n个都指向哪些提交
HEAD|branch@{n} //代表该HEAD/branch之前第n个指向的提交
^{tree} // 代表提交对应的虚拟树节点
^{parent} //代表该提交的父提交
-------------------------------------------------------------------------------------
git diff --cached [] //比较暂存区与提交的区别,默认提交为HEAD
git diff //比较工作区所有已追踪文件与commit的区别
git diff //比较工作区与index的区别
git log --pretty //展示最近的提交
git st -s //精简展示变化, 两个标志位,第一个表示暂存区相对于版本库的编号,第二个表示工作区相对暂存区的变化
git archive -o now.zip //基于提交进行打包
-------------------------------------------------------------------------------------------------
git reset --soft //将当前分支&&HEAD重置到commit, 不改变暂存区和工作区
git reset [--mixed] //将当前分支&&HEAD以及暂存区重置到commit.
git reset --hard //将当前分支&&HEAD 暂存区 以及 工作区都重置到commit,此时所有工作区的变更会丢失,无法找回.
前三种方式都会将HEAD移向相应的commit,若HEAD此时指向branch则branch也会一起移动
git reset [--mixed] //将暂存区中filePath对应的文件内容替换成commit中的文件内容
git reset --hard //将暂存区与工作区中filePath所对应的文件内容替换成commit中的文件内容
----------------------------------------------------------------------------------------
git checkout [] //使用commit对应的file替换暂存区中的文件, commit默认为暂存区, filePath若为.则表示所有文件
git checkout //切换到一个分支
git checkout -b[|] //从提交/分支创建一个新分支并切换到该新分支,若不指定则表示HEAD指向的提交
git branch [|]//从提交/分支创建一个新分支但不切换分支,若不指定则表示HEAD指向的提交
git checkout //将HEAD直接指向commit,分支不会跟着移动,此时处于分离头指针状态.
--------------------------------------------------------------------------------------------
git stash //暂存工作区以及暂存区的工作 ==> 原理上会对暂存区以及工作区各做一次提交,分别记录文件内容.不会暂存未跟踪的新文件
git stash save "message" //给该stash一个描述,在查看暂存列表时会看到该描述
git stash list //查看所有暂存
git stash apply [stash@{n}|] //应用暂存,有可能出现冲突,默认应用栈顶,可以直接应用暂存id
git stash drop [stash@{n}] //移除暂存,默认移除栈顶
git stash pop [stash@{n}] //移除并应用暂存,默认移除栈顶,只建议在没有冲突时使用,避免冲突解决失败时,暂存丢失
git stash clear //清空所有暂存,不建议使用
----------------------------------------------------------------------------------------------
git rm //将从暂存区以及工作区中移除该文件
git add -i //命令式交互,用于工作区与暂存区互操作,可以将文件加入/移除暂存区(单纯移除,不会对工作区造成影响),使用回车确认
git add -- //将file加入暂存区
----------------------------------------------------------------------------------------------
git tag //对当前HEAD所指的提交打一个tag
git tag -a //使用tagName对HEAD所指的提交打一个tag并附加一个描述文件.
git tag -m //使用tagName对HEAD所指的提交打一个tag,并附加简单描述
git tag -s //打tag并应用作者签名
git tag -l //列出所有tag
git tag -l -n2 //列出所有tag并打印前两行描述
git tag -l v3.* //俩出所有以v3.开头的标签
git tag -d //移除一个tag
git push //将一个tag推送到远程库中
-----------------------------------------------------------------------------------------------
git cherry-pick //拣选
git rebase --onto //将..(不包含since),换基到newBranch上,若till不为一个branch,则会处于头指针分离的状态.底层实现: checkout到till => reset --hard newBranch => cherry-pick ..
git rebase <=> git rebase --onto HEAD //将featureA 换基到master上,则git checkout featureA => git rebase master
git rebase -i ... // ...与以上命令相同,提供交互文本,十分强大,推荐使用这个
----------------------------------------------------------------------------------------------
git remote add repoName url //创建一个远程版本库
git remote remove repoName //移除一个远程版本库
git remote -v /查看所有远程版本库
git remote -set-url --push //为push操作设置独立的url
一个版本库可以配置不同的拉取地址以及推送地址,大部分情况下,二者一致.
git fetch //同步远程版本库到本地,此时不会和本地分支合并
git push //向远程版本库推送分支
本地的每一个分支都可以通过设置branch..remote为其指定一个远程版本库,若不指定则默认为origin,在push/pull时会使用相关联的remote进行操作,具体url由remote给出.
指定push.default为simple则每次推送时,只推送当前分支,要成功推送的前提是远程版本库得存在相同名称的分支,第一次可以使用git push 在远程版本库中创建
git pull //需要当前分支指定branch..merge ,确定使用哪个远程分支来进行合并,否则报错.
git branch -r 查看所有远程分支
git branch 查看所有本地分支
------------------------------------------------------------------------------------------------
git format-patch -s HEAD~3..HEAD //使用format-patch创建补丁文件
cat *.patch | git am //应用补丁