三、分支管理
1.创建和合并分支
①图解
每次提交,Git
都会把它们串成一条时间线。这条时间线就是一个分支。截至到目前,只有一条时间线,在Git
里,这个分支叫主分支,即master
分支。HEAD
严格来说不是指向提交,而是指向master
,master
才是指向提交的,所以HEAD
指向的就是当前分支。
一开始的时候,master
分支是一条线,Git
用master
指向最新的提交,再用HEAD
指向master
,就能确定当前分支,以及当前分支的提交点:
每次提交,master
分支都会向前移动一步,这样随着你不断提交,master
分支的线也越来越长。
当我们创建新的分支,例如dev
时,Git
新建了一个指针叫dev
,指向master
相同的提交,然后HEAD
指向dev
,就表示当前分支在dev
上:
你看,Git
创建一个分支很快,因为除了增加一个dev
指针,改改HEAD
的指向,工作区的文件都没有任何变化不过,从现在开始,对工作区的修改和提交就是针对dev
分支了,比如新提交一次后,dev
指针往前移动一步,而master
指针不变:
假如我们在dev
上的工作完成了,就可以把dev
合并到master
上。Git怎么合并呢?最简单的方法,就是直接把master
指向dev
的当前提交,就完成了合并:
所以Git合并分支也很快!就改改指针,工作区内容也不变合并完分支后,甚至可以删除dev
分支。删除dev
分支就是把dev
指针给删掉,删掉后,我们就剩下了一条master
分支:
②命令
首先,我们创建dev
分支,然后切换到dev
分支:
$ git checkout -b dev
Switched to a new branch 'dev'
git checkout
命令加上-b
参数表示创建并切换,相当于以下两条命令:
$ git branch dev
$ git checkout dev
Switched to branch 'dev'
然后,用git branch
命令查看当前分支:
$ git branch
* dev
master
git branch
命令会列出所有分支,当前分支前面会标一个*
号。
在dev
分支修改、添加、提交readme.md
,切换回master
(git checkout master
)分支后,再查看readme.md
文件,刚才添加的内容不见了!因为那个提交是在dev
分支上,而master
分支此刻的提交点并没有变:
现在,我们把dev
分支的工作成果合并到master
分支上:
$ git merge dev
Updating d46f35e..b17d20e
Fast-forward
readme.md | 1 +
1 file changed, 1 insertion(+)
git merge
命令用于合并指定分支到当前分支,通过上面的Fast-forward
信息,Git告诉我们这次合并是“快进模式”,也就是直接把master
指向dev
的当前提交,所以合并速度非常快。当然,也不是每次合并都能Fast-forward
,我们后面会讲其他方式的合并。合并完成后,就可以放心地删除dev
分支(git branch -d dev
)。
因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master
分支上工作效果是一样的,但过程更安全。
③switch
我们注意到切换分支使用git checkout <branch>
,而前面讲过的撤销修改则是git checkout -- <file>
,同一个命令,有两种作用,确实有点令人迷惑,因此,最新版本的Git提供了新的git switch
命令来切换分支。
创建并切换到新的dev
分支:git switch -c dev
切换到master
分支:git switch master
2.解决冲突
当master
分支和dev
分支各自都分别有新的提交,变成了这样:
这种情况下,Git
无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突,我们试试看:
$ git merge dev
Auto-merging readme.md
CONFLICT (content): Merge conflict in readme.md
Automatic merge failed; fix conflicts and then commit the result.
通过git status
可以告诉我们冲突的文件是哪个,然后查看readme.md
:
Git is a distributed version control system.
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1
Git用<<<<<<<
,=======
,>>>>>>>
标记出不同分支的内容,我们修改如下后保存:
Creating a new branch is quick and simple.
其实这就是一个解决冲突的过程,然后提交修改。现在,master
分支和feature1
分支变成了下图所示:
最后,删除dev
分支(git branch -d dev
)
3.分支管理策略
合并分支时,如果可能,Git
会用Fast forward
模式,但这种模式下,删除分支后,会丢掉分支信息。如果要强制禁用Fast forward
模式,Git
就会在merge
时生成一个新的commit
,这样,从分支历史上就可以看出分支信息。操作命令为git merge --no-ff -m "merge with no-ff" dev
。
通过git log --graph --pretty=oneline --abbrev-commit
以看到分支合并图。
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;那在哪干活呢?干活都在dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如1.0版本
发布时,再把dev
分支合并到master
上,在master
分支发布1.0版本
;你和你的小伙伴们每个人都在dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。所以,团队合作的分支看起来就像这样:
4.Bug分支
当工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug
,怎么办?
Git
提供了一个stash
功能,可以把当前工作现场“储藏”起来,命令为git stash
,使用完该命令后用git status
查看工作区是干净的。首先确定要在哪个分支上修复bug
,假定需要在master
分支上修复,就从master
创建临时分支issue-100:git checkout -b issue-100
,修改完毕后,切换到master
分支,并完成合并,最后删除issue-100
分支。
然后我们切换到刚才工作的分支dev
,使用git stash pop
恢复刚才工作的内容,同时我们需要将issue-100
修复的bug
同步到dev
分支,使用git cherry-pick <commitId>
,这样就完成了dev
上bug
的修复,同时还还原了之前的正在进行的工作。
5.多人协作
当你从远程仓库克隆时,实际上Git
自动把本地的master
分支和远程的master
分支对应起来了,并且,远程仓库的默认名称是origin
。要查看远程库的信息,用git remote
:
$ git remote
origin
或者,用git remote -v
显示更详细的信息
$ git remote -v
origin https://github.com/hucheng1997/learngti.git (fetch)
origin https://github.com/hucheng1997/learngti.git (push)
上面显示了可以抓取和推送的origin
的地址。如果没有推送权限,就看不到push的地址。
推送master
或其他分支到远程库:git push origin master/dev
,实际开发中一般需要将master
和dev
分支推送到远程库。
抓取分支:git pull origin master/dev
。
创建远程origin
的dev
分支到本地:git checkout -b dev origin/dev
。
如果是直接创建的dev
,在git pull
的时候会失败,此时需要执行命令git branch --set-upstream-to=origin/dev dev
。
多人协作的工作模式通常是这样:
- 首先,可以试图用
git push origin <branch-name>
推送自己的修改; - 如果推送失败,则因为远程分支比你的本地更新,需要先用
git pull
试图合并; - 如果合并有冲突,则解决冲突,并在本地提交;
- 没有冲突或者解决掉冲突后,再用
git push origin <branch-name>
推送就能成功!
如果git pull
提示no tracking information
,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>
。
四、标签管理
发布一个版本时,我们通常先在版本库中打一个标签(tag
),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。
1.创建标签
-
git tag <tagname> [commitId]
:用于新建一个标签,默认为HEAD
,也可以指定一个commit id;注:简洁查看历史记录
git log --pretty=oneline --abbrev-commit
-
git tag -a <tagname> -m "blablabla..."
:可以指定标签信息; -
git tag
:可以查看所有标签。 -
git show <tagname>
:可以查看所有标签
2.操作标签
-
删除
tag
:git tag -d v0.1
-
推送某个标签到远程:
git push origin v1.0
-
一次性推送全部尚未推送到远程的本地标签:
git push origin --tags
-
删除远程标签
先删除本地的
git tag -d v0.9
,然后删除远程:git push origin :refs/tags/v0.9