参考资料:Git分支
向Git中提交文件的基本过程如下:
git status
git diff .
git add .
git commit
在git add
暂存文件时,实际上做了以下工作:
在git commit
提交文件时,则做了以下工作:
$ vi README.md
$ vi test.txt
$ git add .
$ git commit -m "first commit"
当执行完以上命令时,此时git仓库中有如下几个对象:两个blob对象,记录这两个文件快照,一个树文件,记录目录结构和blob的索引,一个提交对象,包含有提交信息和指向树文件的索引。如图所示:
之后每次修改提交,生成的提交对象中都会包含一个指向上次提交对象的指针,如图:
这样,多次提交就后就像一个链表一样对不同的版本建立联系。
Git的分支,就是指向提交对象的可变指针,当通过git init
创建一个Git仓库时,会默认创建一个名为master的分支,也叫作主分支。在每次提交时,会自动向前移动到最新提交对象上,此外,Git还提供了一个特殊的指针HEAD指向当前所在的本地分支,如果此时处于master分支,图示如下:
可以将HEAD理解为当前分支的别名,了解这些原理后,也就能很好的理解分支的创建和切换了。
创建分支使用git branch bname
命令,会在当前的提交对象上创建一个指针:
$ git branch
* master
$ git branch trunk
$ git branch
* master
trunk
使用git branch
可以查看本地分支,*表示当前处于的分支,此时Git的图示如下:
查看分支信息也是通过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 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 checkout master
该命令做了两件事:
如果在master分支下再来一次提交:
$ vi test.txt
$ git add .
$ git commit -m "5 times commit in master"
此时Git仓库中结构如下:
此时,出现了项目分叉,可以通过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分支:
如果要合并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仓库结构如下所示:
此时,如果切换到trunk2分支工作,并提交数次后,Git仓库对应记录结构如下:
此时如果合并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分支的直接祖先,因此如果出现这种情况,则会使用两个分支的末端和这两个分支的共同祖先分支进行三方合并,并且对合并后的结果做了一个新的快照并且自动创建一个新的提交指向它,如下图:
在合并分支时,如果两个分支对同一个文件做了修改,则可能会出现冲突,必须解决掉文件中冲突的内容,然后提交,就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
git reset HEAD
用来撤销暂存区修改的文件,可理解为恢复到当前分支最后一次提交。$ git status #查看出现冲突的文件
$ git add . #手动解决冲突后,将文件标记为已暂存
$ git commit #提交此次合并