Git基础以及内部原理简介
基础
Git与SVN存储差异
Git存储的文件不同于CVS,CVS存储的的是每个版本的变动内容以及变动文件,而Git存储的是基于每个文件的快照,对每个文件生成一个摘要值,如果值没有发生变化,那么在该版本不会存储该值。
基于文件差异:
基于文件快照:
Git内部三种状态
Git内部三种状态:已提交,已修改,已存储
文件流转的三个工作目录
文件流转的三个工作目录:暂存区域,Git的工作目录,本地仓库
文件的四个状态
未执行git add
的文件,会被标记为untracked
.如果执行了git add
命令,那么文件会变为unmodified
,并存入暂存区.如果再对文件修改会变为modified
,再次执行git add
此时文件的状态会变为staged
.
如果我们编辑了暂存区的文件,那么会出现两次,一次是未暂存一次是已暂存.如果此时提交的话会提交没有修改过的文件,也就不是当前工作目录当中的文件.
提交只是提交已暂存的文件快照.
➜ git_test git:(master) ✗ git add .
➜ git_test git:(master) ✗ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: README.md
#
➜ git_test git:(master) ✗ vim README.md
➜ git_test git:(master) ✗ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: README.md
#
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: README.md
#
因此需要再次执行git add
才会将工作目录最新的文件再次放入到暂存区.
➜ git_test git:(master) ✗ git add .
➜ git_test git:(master) ✗ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: README.md
#
通常git commit
命令只会提交暂存区的文件,如果我们想提交已修改未暂存的文件那么需要使用git commit -a
这样会提交已修改但是未在暂存区的文件.
文件相关操作
查看Diff
在日常的开发当中必须要查看diff.无论是合分支还是看当前工作目录代码的变动.
执行默认的git diff
会比较工作目录与暂存区的代码.
如果需要比较暂存区与上次提交的代码进行diff那么git diff --cached
.
如果我们需要比较当前分支和master分支的diff,当master有新的提交历史时(也就是当前分支和master祖先分支进行比较),如果直接执行git diff master
,会显示当前的分支分支将master的最新提交的代码删除,并不是我们真正想要的diff,因此我们需要执行git diff master...branch_name
,这样会输出与master共有祖先提交的diff.
查看提交历史
git log
会输出每次提交的摘要,-p
可以输出每次提交的diff.
git log -p -2
,输出最近两次的提交diff.
Author:真正对代码改动的作者.
Commiter:将代码提交到分支的执行者
查看文件的修改
git blame
查看文件的每行是来自于哪次提交,帮助我们更快找出问题.
执行git blame README.md
文件,输出如下,以^
开头提交号为初次提交.
^80c5d53 (CoderLiu 2019-05-22 17:05:24 +0800 1) #456
^80c5d53 (CoderLiu 2019-05-22 17:05:24 +0800 2) #123
^80c5d53 (CoderLiu 2019-05-22 17:05:24 +0800 3) #test
69be85b5 (CoderLiu 2019-05-29 08:52:25 +0800 4) #test
撤销操作
首先我们应当查看文件的状态,无非是下述几种状态切换:
- 当我们需要将暂存区域的文件回退时,
git reset HEAD 文件
,回退后文件的状态变为未暂存. - 我们如果想将已修改未暂存的文件回退到当前的工作目录那么可以使用
git chekout 文件
,此时文件会回到未修改的状态.
没有提交至git的文件,撤销修改无法找到历史版本 - 撤销未push提交:通过
git reset --hard 0d1d7fc32
命令将项目还原至该提交,会重写提交历史. - 撤销已push提交:通过
git revert 0d1d7fc32
,会将0d1d7fc32的提交生成一个反向补丁,然后先提交本地再push到线上,不会重写任何提交历史.
别名
我们可以给对应的命令加入别名,对于习惯了SVN命令的同学还是很方便.
$ git config --global alias.co checkout
$ git config --global alias.br branch
$ git config --global alias.ci commit
$ git config --global alias.st status
$git config --global alias.last 'log -1 HEAD'
远程仓库
运行git remote add
添加远程仓库,可以将代码提交到远程仓库.一个项目也可以有多个远程仓库.
当然我们也可以将远程仓库进行重命名,git remote rename
,也可以删除远程仓库.
分支
我们可以在自己的独立分支进行开发,git branch [branch name]
创建了分支名称.git checkout [branch name]
检出相应的分支.
衍合与合并
衍合(git rebase
)按照每行的修改次序重放一遍修改.会根据当前分支的提交与要衍合的分支最新提交形成patch文件,然后将patch文件作为一次提交.提交历史记录会看出是线性的.
合并(git merge
):合并最终结果.没有形成patch,历史提交记录是分叉的.
一般情况下我们采用合并足够了,如果我们想产生一个线性的,干净的历史记录可以采用衍合.但是不要衍合公共分支,如果衍合了公共分*么会产生双重历史提交记录(包含了一个文件相同修改产生两次提交历史),会使历史提交记录错乱.
fetch与pull
fetch不会将代码进行合并,我们可以先fetch下来,比较下与远程分支的diff,然后执行merge命令.
pull会将代码进行自动合并.
储藏
可以将文件的修改作为一次储藏,就像邮件的存稿箱一样,并不是将文件直接进行提交.git statsh
可以将当前工作目录的文件进行储藏并不进行提交,这时你可以切换到其他分支继续进行开发.
我们可以进行多次储藏使用git statsh list
,查看所储藏的内容,这样我们可以切换分支,切换回来后我们可以再使用git apply
,这样我们可以使用最新的储藏,当有多个储藏时我们可以使用git stash apply aaa@qq.com{0}
应用指定的储藏.
内部原理
git内部的数据都存在于当前项目当中的.git
目录,这个目录包含了如下数据结构:
drwxrwxr-x 2 rong rong 4096 May 21 20:34 branches
-rw-rw-r-- 1 rong rong 308 May 29 08:52 COMMIT_EDITMSG
-rw-rw-r-- 1 rong rong 92 May 21 20:34 config #项目特有配置
-rw-rw-r-- 1 rong rong 73 May 21 20:34 description#GITWEB使用
-rw-rw-r-- 1 rong rong 23 May 21 20:34 HEAD#指向当前分支
drwxrwxr-x 2 rong rong 4096 May 21 20:34 hooks #钩子脚本
-rw-rw-r-- 1 rong rong 262 May 29 08:52 index #保存了暂存区域信息
drwxrwxr-x 2 rong rong 4096 May 21 20:34 info #保存了不希望在.gitignore文件全局管理的忽略模式的全局可执行文件
drwxrwxr-x 3 rong rong 4096 May 22 17:05 logs #提交日志文件目录
drwxrwxr-x 26 rong rong 4096 May 29 08:52 objects #存储所有数据内容
-rw-rw-r-- 1 rong rong 41 May 28 09:21 ORIG_HEAD
drwxrwxr-x 4 rong rong 4096 May 29 09:07 refs #指向数据
存储对象类型
- blob对象:二进制文件,存储的是数据对象,位于
.git/objects
. - tree对象:可以存储多个文件信息,blob文件名称,真实文件名称,文件权限以及文件类型,tree对象也可以存储tree对象
git cat-file -p 40060d4dfd175e434317feab4ce974017862ab3a
100644 blob 2a5a1ac1a7d7d78276113b370656e0cf287be699 README.md
100644 blob daf6a72025a1fa6db196da2106fc8dcf1850ec9e test.md
100644 blob 3d4db092fd0fd1d1123323e66b47ac05e775cae3 test2
- commit对象:存储提交的tree对象信息,以及提交日期提交作者等.
用途
HEAD文件里存着指向refs/heads/master目录的路径.refs目录当中的文件存着指向commit对象的指针.
refs/remotes记录最后一次推送至仓库的commit对象.
refs/tags 记录标签对应的任意对象.
当添加远程分支时,默认会在config文件当中添加如下信息:
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
url = aaa@qq.com:rtc/test.git
其中包含了本地分支和远程分支映射关系,以及远程仓库地址.+
表示可以强制更新.
上一篇: 如何用CSS3和Js制作响应式导航条
下一篇: css3的三种使用方法是什么