git是linus为了管理Linux内核代码而设计的管理工具,成为目前最流行也是应用最为广泛的版本控制工具

<>git的基本结构



这张表格只是大概描述git的几个区域,在官网有一个动态的说明文档git的结构及命令
<http://ndpsoftware.com/git-cheatsheet.html>

* Workaspace:用户的操作空间,也就是我们编辑文档的目录
* Index :索引区,git add之后,会在索引区建立文档的一个索引,也可以理解为暂存区或者缓冲区;
* Repository:这里指的是本地的仓库,真正存储数据和索引的地方;
* Remote Repository:远程仓库,实际中应用最多的一种。
<>git的工作原理


git版本控制是基于类似快照的方式存在,当文件内容不发生变化时,则只建立快捷,当文件内容发生变化时,则通过复制文件+变化内容=新文件;文件在Repository中的存在形式是文件对象(包括数据+数据位置等信息)的hash值存在;
简单过程也可以用如下图表示:


* 当文件A发生变化,文件B不变,我们的工作目录依然是两个文件,git add之后,索引区会对文件A进行重新求hash值并记录,git
commit(提交)之后,文件A会重新保存一份,而文件B只是创建一个链接指向新的HEAD
* master则指向新的HEAD,此时HEAD值已经发生变化
* 图中黑色提交表示蓝色提交的父提交
<>git的配置文件

git有三种配置文件:

* 仓库特有:.git/config (git config)
* 全局配置:~/.gitconfig(git config --global)
* 系统配置:/etc/git/gitconfig(git config --system)
<>git对象类型

* Blob对象:文件的每个版本表现为一个BLOB文件,可以理解为文件的存在形式
* tree对象:每一级目录便是树状结构的一个节点,索引中的对象commit之后则创建树对象
* 提交对象:保存版本库一次变化的元数据,作者、日期、邮箱、日志等,每个对象都指定一个目录树对象
* 标签对象:用于对一个特定对象一个已读的名称
<>git中的文件分类

* 已追踪(tracked):使用git add命令添加至索引中的文件
* 被忽略(igonre):在版本库中通过“忽略文件列表[./.gitigonre]”明确声明的文件,支持正则表达,在仓库的根目录;
* 未追踪(untracked):除了以上两种的其他文件
<>git分支

在程序的编写提交过程中,可能涉及多个工作组同时对代码进行修改、提交,为此便出现了分支,其结构如图(一以下内容中黄色圆圈表示一个commit):


分支的命名规则:

1、可以使用“/”,但是不可以“/”结尾

2、不能以“-”开头

3、以位于“/”后面的组件,不能以“.”开头

4、不能使用连续的“.”;

5、不能包含空白字符;

6、不能使用^、~、?、*、[等正则意义的符号

7、必须唯一;

8、分支名字始终指向目标分支的最近提交


最简单的命令如下:
git branch [--set-upstream | --track | --no-track] [-l] [-f] <branchname>
[<start-point>]

git branch $BRANCHNAME $START-POINT
例如:实现上图中的分支结构
[root@localhost myproject]#git branch B1 B

例如:查看分支

[root@localhost myproject]# git branch -l
B1
* master

例如:切换到分支B1

[root@localhost myproject]# git checkout B1
Switched to branch ‘B1’
[root@localhost myproject]#git branch -l
* B1
master


这里有一个HEAD与master的概念,个人理解HEAD就如同一个指针函数,当你想回溯到哪个节点上,HEAD直接指向那个节点即可;而master则表示最近的一次commit(提交),可以用下图理解:


此时我们就切换到了分支B1的工作环境下,使用git cat-file -p HEAD便可看到B1的tree对象

<>GIT分支的合并(一)

正所谓“天下大势,分久必合”,分支势必有合并的一天,结构如下:


基本命令如下:
git merge -m <msg> $FRANCHNAME <commit>

这里就有两个概念“^”和“~”:


* ~:以当前提交向前的父提交,例如:~1表示父提交1,~2表示父提交2(分支合并提交中,表示父提交2)
* ^:以当前分支父提交递归,例如:^1~1表示父提交1的父提交

这样的表述可能更加模糊,从下图即可直观的看到两者之间的关系;


*
F(HEAD)的分支父提交仅且只有两个:E(HEAD^1)和D1(HEAD^2),这里1和2的顺序是根据master来的(递归父提交优先),此时如果你使用HEAD^3则会报错,因为没有其他的分支父提交

* D(HEAD~2)是E(HEAD^1)master线上的递归父提交,则表示为HEAD~2或者HEAD~1~1或者HEAD^1~1
* E(HEAD~1\HEAD^1)因为即是F(HEAD)的递归父提交,又是分支合并的父提交,则有两种表示方式,C(HEAD~3)同理也有两种表示方式

当然最简单的方法就是git log --graph或者git log --graph --pretty=oneline --abbrev-commit
查看整个tree对象,然后查看到每个commit对应的hash值,直接调用hash值即可 [root@localhost myproject]# git
log --graph --pretty=oneline --abbrev-commit * f50cb1f Merge branch 'devel' |\
| * 6beff3e v0.0.2-3 * | b9ffb4a Merge branch 'devel' |\ \ | |/ | * 5af2c01
v0.0.2-2 | * f31763f v0.0.2-1 * | fb84fb7 v0.0.4 * | cb22d49 v0.0.3 |/ *
2304fe2 v0.0.2 * 86777c6 v0.0.1
<>git分支合并(二)

分支合并除了merge这样的方式之外还有一种,叫做rebase。有人称此过程为变基,也有人称为衍合;下面从图例就可以看到主要区别:


上图是HEAD指向节点D时,分支B指向B2时,我们切换至B分支,然后将master从节点D rebase至分支B的B2节点上git rebase master
,则就会出现如上的结果;rebase与merge不同就是缺少了一个节点,在现有分支上将数据取&;
此时可以解释为:删除原有分支B节点,临时保存原数据以补丁追加至节点D之后。可以通过查看前后的各节点的hash值来确定
[root@localhost myproject]# git checkout B Switched to branch 'B'
[root@localhost myproject]# git rebase master First, rewinding head to replay
your work on top of it... Applying: B1 Applying: B2 [root@localhost myproject]#
git log --graph --pretty=oneline --abbrev-commit * 5fddaec B2 * 0835079 B1 *
b8dac7d D * 3beae4e C * 0cf96de B * 906ba55 A [root@localhost myproject]# git
checkout master Switched to branch 'master' [root@localhost myproject]# git log
--graph --pretty=oneline --abbrev-commit * b8dac7d D * 3beae4e C * 0cf96de B *
906ba55 A

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信