git是linus为了管理Linux内核代码而设计的管理工具,成为目前最流行也是应用最为广泛的版本控制工具
git的基本结构
这张表格只是大概描述git的几个区域,在官网有一个动态的说明文档git的结构及命令
- 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
转载:https://blog.csdn.net/weixin_42867549/article/details/88563928