Git 分支本质是指向提交对象(commit object)的可变指针,默认是指向最后那个提交对象,每次提交后,都会自动向前移动。HEAD是特殊指针,指向当前所在的本地分支(可理解为当前分支的别称)。
Git 是如何保存数据的?
Git 保存的不是文件的一系列或变更集或者差异,而是一系列不同时刻的文件快照。
每次提交时,Git 会保存一个提交对象。该对象包含:
- 一个指向暂存内容快照的指针(树对象)
- 作者姓名、邮箱、提交解决信息
- 指向它的父对象(上一次提交对象)的指针
- 首次提交产生的提交对象没有父对象
- 其他普通提交操作产生的提交对象有一个父对象
- 由多个分支合并产生的提交对象有多个父对象
例子
假设有一个工作目录,包含三个将要被暂存、提交的文件。
暂存操作:- 为每一个文件计算校验和
- 把当前版本的文件快照保存到 Git 仓库中(使用 blob 对象来保存文件快照)
- 将校验和加入到暂存区域 等待提交
提交操作:
- Git 先计算每一个子目录的校验和
- 在 Git 仓库中把这些校验和保存为树对象
- Git 创建一个提交对象
此时,Git 仓库:
3 个 blob 对象(保存文件快照)
1 个树对象(记录着目录结构和 blob 对象索引)
1 个提交对象 (包含 树对象的指针,和所有提交信息)
修改后再次提交,此次产生的提交对象会包含一个指向上次提交对象(父对象)的指针
Git 分支
Git 分支,其本质仅仅时指向其中一个提交对象的可变指针。默认分支名字是 master。
A branch in Git is simply a lightweight movable pointer to one of these commits.
创建分支
创建分支只是创建了一个可以移动的新的指针。名为HEAD
的特殊指针,指向当前所在的本地分支
git branch <branchname>
: 创建一个分支git branch -b <branchname>
: 创建分支,并切换,创建:git branch <branchname>
, 切换:git checkout <branchname>
切换分支
git checkout
命令,切换分支,实际上是改变 HEAD
的指向,同时将工作目录恢复到该分支所指向的快照内容(最后一次提交时的样子)。git checkout testing
,切换到 testing 分支,此时 HEAD
指向 testing 分支
在testing
分支,修改再提交一次,testing
分支向前移动,HEAD
也随着提交自动向前移动,但master
分支保持不动。
合并分支
git merge
命令,合并指定分支到当前分支。
Fast-forward
, “快进模式”,由于当前分支所指向的提交是并入分支的直接上游,所有 Git 只是简单的将指针向前移动。
通俗来讲:当试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么在合并时,只会简单的将指针向前推进。'recursive' strategy
,在合并时,需要递归的查找两个分支的分叉点。Git 会使用两个分支的末端的快照(C4 、C5)和这两个分支的共同祖先(C2),做一个简单的三方合并。再将结果做一个新的快照并自动创建一个新的提交(C6)指向它。
分支合并冲突
如果在两个不同分支上,对同一个文件的同一个部分进行了不同的修改,分支合并时就会产生合并冲突。 此时,Git 做了合并,但没有自动创建一个新的合并提交。,需要解决冲突,在使用git add
命令来将其标记为冲突已解决,然后git commit
完成提交。具体步骤:
git status
查看因 包含合并冲突 而处于未合并(unmerged)状态的文件1
2
3
4
5
6
7
8
9git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: index.html
no changes added to commit (use "git add" and/or "git commit -a")未合并状态的文件中都会加入标准的冲突解决标记,需要打开文件,手动修改文件,解决冲突。
1
2
3
4
5
6
7<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
please contact us at support@github.com
</div>
>>>>>>> iss53:index.html解决了所有文件冲突后,
git add
将每个文件标记为 冲突已解决。注意: 一旦暂存这些冲突文件,Git 就会将它们标记为冲突已解决。所以最后是修改一个文件,暂存一个文件,以免漏掉。git commit
,提交合并。
分支管理
查看分支
git branch
不加任何参数,列出所有分支,*
代表当前分支(当前 HEAD 指针所指向的分支)1
2
3
4git branch
* develop
master
releasegit branch -v
,每个分支的最后一次提交1
2
3
4git branch -v
* develop b1e18237 【预警查询】 分页滑动,触发页面样式更新函数,屏幕自适应
master 3ea94fa9 Merge branch 'release' into 'master'
release a98f6667 Merge branch 'develop' into 'release'git branch --merged/--no-merged
,过滤分支列表中已经合并或尚未合并到当前分支的分支。1
2
3git branch --merged
* develop
release在列表中没有
*
号的分支,通常是已经将它们的分支合并到另一个分支,可已使用git branch -d
删除的。