[Git] Git整理(二) 分支基础

参考资料:Git分支

Git工作原理

向Git中提交文件的基本过程如下:

git status
git diff .
git add .
git commit 

git add暂存文件时,实际上做了以下工作:

  • 1.为每一个需要暂存的文件计算校验和(SHA-1 哈希算法);
  • 2.把当前版本的文件快照保存到 Git 仓库中(Git 使用 blob 对象来保存它们);
  • 3.将校验和加入到暂存区域等待提交。

git commit提交文件时,则做了以下工作:

  • 1.首先会计算每一个子目录的校验和,然后将这个校验和在Git仓库中保存为一个树对象;
  • 2.创建一个提交对象,包含有git配置的基本信息、指向树对象的指针;
    这里举个例子:
$ vi README.md 
$ vi test.txt 
$ git add .
$ git commit -m "first commit"

当执行完以上命令时,此时git仓库中有如下几个对象:两个blob对象,记录这两个文件快照,一个树文件,记录目录结构和blob的索引,一个提交对象,包含有提交信息和指向树文件的索引。如图所示:
[Git] Git整理(二) 分支基础_第1张图片
之后每次修改提交,生成的提交对象中都会包含一个指向上次提交对象的指针,如图:
[Git] Git整理(二) 分支基础_第2张图片
这样,多次提交就后就像一个链表一样对不同的版本建立联系。
Git的分支,就是指向提交对象的可变指针,当通过git init创建一个Git仓库时,会默认创建一个名为master的分支,也叫作主分支。在每次提交时,会自动向前移动到最新提交对象上,此外,Git还提供了一个特殊的指针HEAD指向当前所在的本地分支,如果此时处于master分支,图示如下:
[Git] Git整理(二) 分支基础_第3张图片
可以将HEAD理解为当前分支的别名,了解这些原理后,也就能很好的理解分支的创建和切换了。

分支的操作

创建分支

创建分支使用git branch bname命令,会在当前的提交对象上创建一个指针:

$ git branch
* master
$ git branch trunk
$ git branch
* master
  trunk

使用git branch可以查看本地分支,*表示当前处于的分支,此时Git的图示如下:
[Git] Git整理(二) 分支基础_第4张图片

查看分支

查看分支信息也是通过git branch命令:

git branch 查看本地仓库所有分支
git branch -r 查看远程分支
git branch -a 查看所有的分支/本地分支和远程分支
git branch -vv 查看分支和它跟踪的远程分支以及最近一次提交

切换分支

使用git checkout bra切换分支,即移动HEAD指针:

$ git checkout trunk 
Switched to branch 'trunk'
$ git branch
  master
* trunk
$ 

此时Git仓库中图示如下:
[Git] Git整理(二) 分支基础_第5张图片
使用git checkout -b branname表示创建并切换到新建分支,等于git branch + git checkout
现在将分支切到了trunk分支上,如果再次提交文件:

$ vi test.txt 
$ git status 
On branch trunk
Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git checkout -- ..." to discard changes in working directory)
	modified:   test.txt
no changes added to commit (use "git add" and/or "git commit -a")
$ git add .
$ git commit -m "4 times commits"
[trunk 74eaed3] 4 times commits
 1 file changed, 1 insertion(+)
$ 

此时Git仓库图示如下:
[Git] Git整理(二) 分支基础_第6张图片
如果再次切换到master分支:

git checkout master

该命令做了两件事:

  • 1.将HEAD指向了master;
  • 2.将工作区中的内容恢复到了master分支最后一次提交时的样子,因此,切换分支后文件中的内容会改变;

如果在master分支下再来一次提交:

$ vi test.txt 
$ git add .
$ git commit -m "5 times commit in master"

此时Git仓库中结构如下:
[Git] Git整理(二) 分支基础_第7张图片
此时,出现了项目分叉,可以通过git log --oneline --decorate --graph --all查看所有的分支情况:

$ git log --oneline --decorate --graph --all
* dff6c9e (HEAD -> master) 5 times in master
| * 74eaed3 (trunk) 4 times commits
|/  
* 98aa7c4 4
* acc632a 3
* 2fb01ec (origin/master, origin/HEAD) Num # 2
* 5d81134 Num: #1
* 31acaff Update README.md
* 3d2eadd Initial commit
$ 

合并分支

在实际工作中,通过git merge可以对多个不同的分支进行合并,比如现在有如下图所示的Git分支:
[Git] Git整理(二) 分支基础_第8张图片
如果要合并master和trunk1 或trunk2,则使用如下命令:

$ vi test.txt 
$ git merge trunk1
Updating 96993c5..f55834f
Fast-forward
 test.txt | 2 ++
 1 file changed, 2 insertions(+)
$ 

Fast-forward表示快速合并,这是因为当前分支是所合并的直接上游分支,所以只是简单的指针移动,也就是说,如果一个分支顺着指针能走下去能够到达另一个分支,则这两者合并时只是简单的指针上移,不需要解决分歧,此种情况称为Fast-forward。
合并后Git仓库结构如下所示:
[Git] Git整理(二) 分支基础_第9张图片
此时,如果切换到trunk2分支工作,并提交数次后,Git仓库对应记录结构如下:
[Git] Git整理(二) 分支基础_第10张图片
此时如果合并master和trunk2:

$ git checkout master 
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 11 commits.
  (use "git push" to publish your local commits)
$ git merge trunk2
Merge made by the 'recursive' strategy.
 README.md | 3 +++
 1 file changed, 3 insertions(+)

可以看到,这里打印出recursive,这是因为由于master分支已经不再是trunk2分支的直接祖先,因此如果出现这种情况,则会使用两个分支的末端和这两个分支的共同祖先分支进行三方合并,并且对合并后的结果做了一个新的快照并且自动创建一个新的提交指向它,如下图:
[Git] Git整理(二) 分支基础_第11张图片

合并时遇到冲突

在合并分支时,如果两个分支对同一个文件做了修改,则可能会出现冲突,必须解决掉文件中冲突的内容,然后提交,就OK了。

$ git merge trunk2
CONFLICT (content): Merge conflict in text.txt
Automatic merge failed; fix conflicts and then commit the result.

任何因包含合并冲突而有待解决的文件,都会以未合并状态标识出来。 Git 会在有冲突的文件中加入标准的冲突解决标记,这样你可以打开这些包含冲突的文件然后手动解决冲突。 出现冲突的文件会包含一些特殊区段,以>>>>>标记,当修改完冲突内容后,使用如下命令提交这次合并:

$ git add .
$ git commit 

如果提交成功,则说明合并完成。

删除分支

如果要删除本地分支,使用git branch -d命令

$ git branch -d trunk1

可能由于删除分支上还有未提交的文件或者未合并,会提示删除失败,可以通过-D进行强制删除:

$ git branch -D trunk2 

总结

  • 在实际工作中,鼓励针对不同的任务创建不同的分支;
  • 分支的创建、切换使用很简单,两条命令就可以搞定:
$ git branch branchname
$ git checkout branchname
  • 以上两条可以用下条命令替换:
$ git checkout -b branchname
  • HEAD 表示一个指向当前分支的指针,或者当前分支的别名,之前学习过git reset HEAD 用来撤销暂存区修改的文件,可理解为恢复到当前分支最后一次提交。
  • 切换分支后,工作区中的内容可能会改变。
  • 合并分支时如果冲突,解决步骤如下:
$ git status    #查看出现冲突的文件
$ git add .     #手动解决冲突后,将文件标记为已暂存
$ git commit    #提交此次合并

你可能感兴趣的:(Git)