在开发软件的时候, 可能很多人会同时为同一个软件开发性能或者修复 bug, 然而如果都在主分支来进行开发, 引起抵触的概率将会大大增加, 而且也不利于保护, 如果你同时批改多个 bug 该怎么办? 所幸,git 的分支性能很好的帮忙咱们解决了这个问题, 它能够帮忙咱们同时进行多个性能的开发和版本治理.
请在浏览这篇文章之前, 务必先浏览深入浅出 git——数据模型, 这样能力更好的帮忙你了解 git 中的分支. 想晓得为什么 git 中新建一个分支那么快, 代价那么小吗? 接下来咱们就来揭开分支神秘的面纱。
这次我会只显示 commit objects 来简化它, 并且为了让它更容易了解我会给他们取个别名来代替本来的测验和. 所以对于提交记录, 咱们会失去一个像上面这样的图.
相熟图论的应该留神到了下面的是一个有向无环图 (DAG), 这意味着从一个节点开始沿着边的方向不会通过雷同的节点.
在咱们的例图中能够清晰的发现存在三个不同的分支, 咱们别离用红色 (蕴含 A,B,C,D,E), 蓝色(A,B,F,G), 以及绿色(A,B,H,I,J) 来标记它们
这就是定义分支的一种形式 - 蕴含所有的提交列表. 然而这不是 git 应用的形式,git 应用更简略更便宜的形式,git 只跟踪分支上的最初一次提交, 而不是持有某个分支的所有列表并更新它们, 只须要晓得分支的最初一次提交, 而后依据图的有向边就能够获取整个提交列表. 例如要定义咱们的蓝色分支, 只须要晓得蓝色分支的最初一次提交是 G, 如果咱们须要蓝色分支蕴含的所有提交的列表, 就从 G 沿着图有向边遍历即可.
这就是 git 治理分支的形式, 通过放弃执行提交记录的指针即可, 接下来咱们会进行一个演示.
首先通过 git init
初始化一个空仓库, 而后查看.git 目录下存在的文件
.git
|-- HEAD
|-- config
|-- description
|-- hooks
| |-- applypatch-msg.sample
| |-- commit-msg.sample
| |-- fsmonitor-watchman.sample
| |-- post-update.sample
| |-- pre-applypatch.sample
| |-- pre-commit.sample
| |-- pre-push.sample
| |-- pre-rebase.sample
| |-- pre-receive.sample
| |-- prepare-commit-msg.sample
| |-- update.sample
|-- info
| -- exclude
|-- objects
| |-- info
| |-- pack
|-- refs
|-- heads
|-- tags
这次咱们关注 refs
这个子目录, 这个中央是 git 保留分支指针的地儿. 当咱们没有提交任何货色的时候,refs
目录下只存在两个空目录, 当初咱们提交几个文件
echo "Hello Java" > helloJava.txt
git add .
git commit -m "Hello Java Commit"
echo "Hello Php" > helloPhp.txt
git add .
git commit -m "Hello Php Commit"
echo "Hello Python" > helloPython.txt
git add .
git commit -m "Hello Python Commit"
当咱们执行 git branch
的时候咱们能够看到上面这样的输入
* master
意味着咱们当初处于 master 分支上 (这个是当咱们第一次提交的时候 git 主动给咱们创立的), 此时refs
目录下是这样
.git/refs
|-- heads
| `-- master
`-- tags
咱们看到 refs/heads 子目录中有一个文件,它就像咱们的分支一样被命名为 master, 咱们应用 cat 命令查看下文件内容
$ cat .git/refs/heads/master
49cd903b2bf247de040118ce60d1931ff587e801
而应用 git log
命令咱们能够看到咱们的提交记录是这样的
commit 49cd903b2bf247de040118ce60d1931ff587e801 (HEAD -> master)
Author: zhu.yang <zhu.yang@xxx.com>
Date: Tue Jan 8 17:48:36 2019 +0800
Hello Python Commit
commit dd7c1bc9c125067f5658bcc6bc35567d07bc4f35
Author: zhu.yang <zhu.yang@xxx.com>
Date: Tue Jan 8 17:48:31 2019 +0800
Hello Php Commit
commit c6bd5c991dbcf9c50bbab682796ab3e06672f5a7
Author: zhu.yang <zhu.yang@xxx.com>
Date: Tue Jan 8 17:48:30 2019 +0800
Hello Java Commit
从下面能够看进去一个分支仅仅只是一个文本文件, 其中记录了这个分支最初一次提交的校验和. 也就是指向 commit 的一个指针
当初咱们新建一个 feature
分支并切换到新建的这个分支下面
git checkout -b feature
应用 tree 命令在来看看.git/refs 的样子
.git/refs
|-- heads
| |-- feature
| |-- master
|-- tags
同样的咱们应用 cat 命令查看下.git/refs/heads/feature 文件的校验和
$ cat .git/refs/heads/feature
49cd903b2bf247de040118ce60d1931ff587e801
咱们会发现和 master 文件中的内容统一, 当初为止咱们没有往 feature 分支提交任何内容
这就是 git 创立一个分支那么快以及不便的起因所在,git 仅仅只是创立了一个蕴含最近一次提交校验和的文件而已.
当初咱们的仓库外面就有 2 个分支了, 然而 git 怎么晓得咱们以后检出的分支是哪个分支呢? 这里其实存在一个非凡的指针叫做HEAD, 它之所以非凡是因为它并不指向具体的 commit object, 而是指向分支,git 应用它来跟踪最近检出的分支.
$ cat .git/HEAD
ref: refs/heads/feature
如果咱们执行
git checkout master
而后查看 HEAD, 会发现以后分支是 master, 而后 HEAD 会指向 master
$ cat .git/HEAD
ref: refs/heads/master
这就是 git 的分支模型, 很简略然而很重要, 理解它有助于了解在这个图上的其余操作(merge,rebase,checkout,revert…)。