参考:
《Git权威指南》
1.逻辑
git的控制原理逻辑上可以看作三个部分,工作区、暂存区和分支。
git add将工作区文件保存到暂存区(标记为tracked)
git commit将暂存区文件保存到分支
git push就是将一个仓库的分支推送到另一个仓库的分支
git diff 比较工作区tracked文件和暂存区之间的差异
git diff --cached比较暂存区文件和HEAD的差异
git diff HEAD比较工作区tracked文件和HEAD的差异
git diff test比较工作区tracked文件和指定分支的差异
git status比较工作区所有文件和暂存区之间的差异
2.物理
物理上.git文件就是整个版本库,包括了暂存区(/index)、引用命名空间(/refs/)(包含了分支(/refs/head))、HEAD(/HEAD)、对象库(/objects/)。
暂存区中保存了一个目录树,树中的文件指向对象库中一个bolb对象,每个提交对应的目录树就是当时暂存区中的目录树。
命名空间中的分支指向该分支的最新提交,git reset可以改变指向的提交。
HEAD可以理解为“头指针”,是当前工作区的基础版本,当提交时,HEAD指向的提交将作为新提交的父提交。
Git放在对象库里的对象有4种类型:
①块(blob):文件的每一个版本表示一个blob。
Git追踪的是内容而不是文件,Git并不追踪那些与文件次相关的文件名或者是目录名。如果两个文件的内容完全一样,Git在对象库里只保存一份blob形式的内容副本,并且该文件具有唯一的SHA1值。文件内容改变,则Git会计算一个新的SHA1值,识别出它现在是一个不同的blob对象并把这个blob对象添加到对象库里。
②目录树(tree):一个目录树对象表示一层目录信息,Git通过目录树对象来追踪文件的路径名。
可以自己创建 tree 。通常 Git 根据你的暂存区域或 index 来创建并写入一个 tree 。因此要创建一个 tree 对象的话首先要通过将一些文件暂存从而创建一个 index 。
③提交(commit):一个提交(commit)对象保存版本库中每一次变化的元数据。
④标签:一个标签对象分配一个任意的且人类可读的名字给一个特定对象。
git cat-file -t <id>可以查看某个对象的类型,id可以只写开头几位。
3.命令
(1)git reset
git reset [-q] [<commit>] […]<paths>…
git reset [--soft | --mixed | --hard |--merge | --keep] [-q] [<commit>]
<commit>都是可选项,可以使用引用或提交ID,如果省略,则相当于使用了HEAD的指向作为提交ID
第一种在命令中包含路径<paths>,为了避免路径和引用(或提交ID)同名而发生冲突,可以在<paths>前用两个连续的减号作为分隔,这种用法不会重置引用,更不会改变工作区,而是用指定提交状态(<commit>)下的文件(<paths>)替换掉暂存区中的文件
git reset HEAD <paths>相当于取消之前执行的gitadd <paths>命令时改变的暂存区
第二种则会重置引用
--hard,如git reset --hard<commit>会执行上图的全部动作1、2、3
1.替换引用的指向,引用指向新的提交ID
2.替换暂存区,替换后暂存区的内容和引用指向的目录树一致
3.替换工作区,替换后工作区的内容和暂存区一致也和HEAD指向的目录树内容相同
--soft,如git reset --soft<commit>执行操作1,只更改引用的指向,不改变暂存区和工作区
--mixed或不用参数,如git reset <commit>会执行操作1、2,只更改引用的指向及重置暂存区,不改变工作区
git reset或git reset HEAD仅用HEAD指向的目录树重置暂存区,工作区不会收到影响,相当于之前用git add命令更新到暂存区的内容撤出暂存区,引用也没改变,因为重置到HEAD相当于没有重置
git reset --filename或git resetHEAD filename仅将文件filename的改动撤出暂存区,暂存区中其中文件不改变,相当于git add filename的反向操作
git reset --soft HEAD^工作区和暂存区不改变,但是引用向前回退一次,当对最新的提交说明或提交的更改不满意时,撤销最新的提交以便重新提交
git commit --amend用于对最新的提交进行重新提交以修补错误的提交说明或错误的提交文件,相当于执行了git reset --soft HEAD^和git commit -s -F .git/COMMIT_EDITMSG
git reset HEAD^或git reset--mixed HEAD^工作区不改变,但是暂存区会回退到上一次提交之前,引用也会回退一次
git reset --hard HEAD^彻底撤销最近的提交,引用回退到前一次,而且工作区和暂存区都会回退到上一次提交的状态,自上一次以来的提交全部丢失。
(2)git checkoutgit checkout [-g] [<commit>] [--]<paths>…
在命令中包含路径,为了避免路径和引用(或提交ID)同名而发生冲突,可以在前面用两个连续的减号作为分隔;<commit>是可选项,如果省略则相当于从暂存区(index)进行检出,重置的默认值是HEAD,而检出的默认值是暂存区,所以重置一般用于重置暂存区(除非使用--hard,否则不重置工作区),而检出命令主要覆盖工作区(如果<commit>不省略,也会替换暂存区中相应的文件)。这种用法不会改变HEAD头指针,主要用于指定版本的文件覆盖工作区中对应的文件,如果省略<commit>则会用暂存区的文件覆盖工作区的文件,否则用指定提交中的文件覆盖暂存区和工作区中对应的文件。
git checkout [<branch>]
不使用<paths>路径,会改变HEAD头指针,之所以后面的参数写为<branch>是因为只有HEAD切换到一个分支才可以对提交进行跟踪,否则仍然会进入“分离头指针”的状态,在该状态下的提交不能被引用关联到,可能丢失。主要作用是切换到分支,如果省略<branch>则相当于对工作区进行状态检查。
gitcheckout [-m] [[-b] --orphan <new_branch>] [start_point]用于创建和切换到新的分支,新的分支从<start_point>指向的提交开始创建。新分支和master分支一样,都是在refs/heads命名空间下的引用。
git checkout branch检出branch分支,要完成三个步骤,更新HEAD以指向branch分支,以及用branch指向的树更新暂存区和工作区
git checkout或git checkout HEAD汇总显示工作区、暂存区和HEAD的差异
git checkout -- filename用暂存区中的filename文件来覆盖工作区中的filename文件,相当于取消自上次执行gitadd filename以来的本地修改
git check branch --filename维持HEAD的指向不变,用branch所指向的提交中的filename替换暂存区和工作区中相应的文件(会将暂存区和工作区中的filename文件直接覆盖)
git checkout -- .或git checkout.取消所有本地的修改(相对于暂存区),相当于用暂存区的所有文件直接覆盖本地文件,并且不会再次确认。
(3)更多命令git branch -v 查看当前的分支
git branch -a查看所有分支(包括远程)git status -s -b显示工作区状态-s显示精简输出,-b同时显示当前工作分支的名称
git rev-parse <branch>显示应用对应的提交ID
git log --graph --online查看提交日志,通过对父提交的关联实现对提交历史的追溯
git log -l HEAD查看HEAD的指向
git log -l master查看master的指向
git log -l refs/heads/master查看refs/heads/master的指向
git stash保存当前工作进度,工作区尚未提交的改动(包括暂存区的改动)全都不见了
git clean -fd清除当前工作区中没有加入版本库的文件和目录(未跟踪的)
git rm --cached a.txt从暂存区删除文件,工作区不做改变
4.远程仓库
git init
创建工作区(在当前路径下添加.git文件夹)
(手动创建README.md文件)
git add README.md
将README.md文件添加到暂存区
git commit -m "first commit"
将暂存区中修改的提交到HEAD指向的master,message为"first commit"
git remote add origin https://github.com/yfraquelle/yfraquelle/YfR.git
添加远程仓库,名字为origin,地址为...
git push -u orgin master
指定master分支提交到origin仓库的对应分支(origin/master),同时指定origin为默认仓库,以后就可以不加任何参数
{
git push
推送当前分支
git push origin
将当前分支推送到origin仓库的对应分支
}
(在Github上手动创建d.txt并commit到dev分支)
fetch可以用status比较文件差异(fetch实际将远程文件版本下到本机,不做fetch看不到差异)
merge将新文件合并到本地
(pull直接将文件合并到本地)