一. 理解git中的HEAD和master
在这里解释一下git中HEAD和master的关系。
在Git里,主分支即master分支(git默认分支的名字是master)。HEAD严格来说不是指向提交,而是指向分支master,master指向提交的,
HEAD指向当前所在分支
。
-
一开始,在master分支上,master指向最新的提交,HEAD指向master分支。(这样就能确定当前分支,以及当前分支的提交点)
-
让我们新建一个dev分支并切换过去
git checkout -b dev
时,发现HEAD此时指向dev分支
且此时新建的dev分支指向master相同的提交 -
在dev上进行一些修改(比如修改testing.txt),然后提交,dev指针向前移动一步,指向当前最新的提交,master不变,
HEAD指向当前所在的分支
即dev.
-
让我们切换回master看看。
查看master,发现testing.txt文件并未发生变化,工作目录恢复成 master 分支所指向的快照内容。
当你切换分支的时候,Git 会重置你的工作目录,使其看起来像回到了你在那个分支上最后一次提交的样子。里是引用
并且HEAD 指向当前分支并随之移动,HEAD指回master分支。
(图虽然和前面有点小差(把testing换成dev分支),但是就是这么个意思)
-
dev分支上修改完了,下面用
git merge
命令合并这两个分支
因为现在在master分支上,要合并dev分支上的内容所以用
git merge dev
命令。
这时候master指向了dev最新的一次提交,HEAD还是指向当前所在分支master
-
合并完分支后,dev分支就可以删除了
git branch -d dev
,只剩下master分支
二. 分支操作
话不多说,开始实战吧,实战背景
假设你正在你的iss53分支上进行开发,然后接到一个紧急任务,需要建立一个新的分支来修复bug,修复完成后合并到master,再切回iss53继续工作
创建分支
创建iss53分支并切换到iss53分支(注意:创建分支的基础是以当前分支为基础的)
$ git checkout -b iss53
Switched to a new branch 'dev'
git checkout
命令加上-b参数表示创建并切换,相当于以下两条命令:
$ git branch iss53
$ git checkout iss53
Switched to branch 'dev'
查看分支
git branch
命令查看当前分支,前面带*的就是你现在在的分支
$ git branch
* iss53
master
git branch //查看本地分支
git branch -r //查看远程分支
git branch -a //查看本地和远程的所有分支
合并分支
- 你继续在iss53上工作,并且做了一些提交,在此过程中iss53分支不断的向前推进,因为你检出到该分支,也就是你的HEAD指针指向了iss53分支
检出分支中“检出”二字,你可以简单的理解为切换分支
本质上,checkout操作是移动HEAD指针,将HEAD指针指向要切换到的分支的指针处。
$ vim index.txt
如图所示:
-
现在你接到那个紧急问题的电话
你不必着急将iss53写完,有了git用不着将那个紧急问题和iss53混在一起,只需要重新建立一个分支就好了。
那么建立一个新的分支之前首先要切回你原来的master分支,在master分支的基础上建立一个新的分支
切回master分支的时候要注意
工作目录和暂存区中那些还没有被提交的修改可能会与你即将检出的分支产生冲突阻止你切换分支
,所以,在切换分支前,请保持一个干净的状态。方法有三:- 把你的修改全部提交
-
用
git stash
命令(关于stash命令将在下篇文章中详细介绍) -
修补提交(commit amending)
假定你已经把你的修改全部提交了,并切换回 master 分支。
$ git add index.txt
$ git commit -a -m 'added on [issue 53]'
$ git checkout master
Switched to branch 'master'
切换成功后在master分支上,此时的工作目录和你在开始iss53问题前一模一样
当你切换分支的时候,Git 会重置你的工作目录,使其看起来像回到了你在那个分支上最后一次提交的样子。
接下来你要修复这个紧急问题,来建立一个hotfix分支,在该分支上解决问题。
$ git checkout -b hotfix
Switched to a new branch 'hotfix'
$ vim index.html
$ git commit -a -m 'fixed the broken email address'
[hotfix 1fb7853] fixed the broken email address
1 file changed, 2 insertions(+)
快进(fast-forward)合并
现在你在hotfix分支上修改完成,并且运行正确,可以将hotfix分支合并回你的master分支了。
使用
git merge
命令来达到上述目的
$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
index.html | 2 ++
1 file changed, 2 insertions(+)
由于你想要合并的分支 hotfix 所指向的提交 C4 是你所在的提交 C2 的直接后继, 因此 Git 会直接将指针向前移动。
换句话说,当你试图合并两个分支时, 如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候, 只会简单的将指针向前推进(指针右移)。
因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward)”。
三方合并
然后切换回iss53来继续刚刚的问题。并做一些修改。现在已经成功运行iss53了。打算将hotfix分支合并到master分支上,检出你想合并入的分支,运行
git merge
即可
$ git checkout iss53
Switched to branch "iss53"
$ vim index.txt
$ git commit -a -m 'finished the new footer [issue 53]'
[iss53 ad82d7a] finished the new footer [issue 53]
1 file changed, 1 insertion(+)
$ git checkout master
Switched to branch 'master'
$ git merge iss53
Merge made by the 'recursive' strategy.
index.txt | 1 +
1 file changed, 1 insertion(+)
这和你之前合并 hotfix 分支的时候看起来有一点不一样。 在这种情况下,你的开发历史从一个更早的地方开始分叉开来(diverged)。
master 分支所在提交并不是 iss53 分支所在提交的直接祖先,Git 不得不做一些额外的工作。 出现这种情况的时候,Git 会使用两个分支的末端所指的快照(C4 和 C5)以及这两个分支的公共祖先(C2),做一个简单的三方合并。
用带参数的
git log
也可以看到分支的合并情况
$ git log --graph --pretty=oneline --abbrev-commit
删除分支
当解决完紧急问题后,因为hotfix分支已经不再需要它了,那么就可以删除它了,使用
git branch -d hotfix
命令来删除分支。
$ git branch -d hotfix
Deleted branch hotfix (3a0874c).
现在修改合并进来,也不再需要iss53分支了,那么也可以删除iss53这个分支了。
$ git branch -d iss53
解决冲突
人生不如意之事十之八九,假设你在iss53上同样修改了在hotfix上做了修改的文件
index.txt
,关键它们对同一个部分做了不同的修改,这个时候就会产生合并冲突。
$ git merge iss53
Auto-merging index.txt
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
此时 Git 做了合并,但是没有自动地创建一个新的合并提交。 Git 会暂停下来,等待你去解决合并产生的冲突。
查看状态,查看冲突文件
$ git 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.txt
no changes added to commit (use "git add" and/or "git commit -a")
$ git cat index.txt
Git has a mutable index called stage.
Git tracks changes of files.
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1
Git用
<<<<<<<,=======,>>>>>>>
标记出不同分支的内容
也就是你的 master 分支所在的位置,因为你在运行 merge 命令的时候已经检出到了这个分支)在这个区段的上半部分(======= 的上半部分),而 iss53 分支所指示的版本在 ======= 的下半部分。 为了解决冲突,你必须选择使用由 ======= 分割的两部分中的一个,或者你也可以自行合并这些内容。
我们修改如下后保存:
Creating a new branch is quick and simple.
修改完成后记得提交
$ git status //查看状态看冲突文件是否暂存
On branch master
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes to be committed:
modified: index.txt
$ git add index.txt
$ git commit -m "conflict fixed"
[master cf810e4] conflict fixed
工作完成
版本回退
说到git 怎么能不介绍一下版本回退呢。
改bug改着,分支合并着运行不了了,哪怕现在改的一些东西没有了,让我回到那个能运行的也好
reset简直就是救命稻草啊!
-
git reset
命令恢复到版本
$ git reset --hard HEAD^
- git reset –hard commit_id通过版本号恢复到特定版本
$ git reset --hard 3628164
版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。
-
用
git log
命令查看版本号
$ git log
commit cb926e7ea50ad11b8f9e909c05226233bf755030
Author: Michael Liao <askxuefeng@gmail.com>
Date: Mon Aug 19 17:51:55 2013 +0800
到此,这篇文章就已经结束了,希望对大家git的学习有所帮助!