Git分支的新建与合并,管理
应用场景
让我们来看一个简单的分支新建与分支合并的例子,实际工作中你可能会用到类似的工作流。 你将经历如下步骤:
1. 开发某个网站。
2. 为实现某个新的用户需求,创建一个分支。
3. 在这个分支上开展工作。
正在此时,你突然接到一个电话说有个很严重的问题需要紧急修补。 你将按照如下方式来处理:
1. 切换到你的线上分支(production branch)。
2. 为这个紧急任务新建一个分支,并在其中修复它。
3. 在测试通过之后,切换回线上分支,然后合并这个修补分支,最后将改动推送到线上分支。
4. 切换回你最初工作的分支上,继续工作。
通过Git的分支,我们可以实现上述工作流程。
新建分支,切换分支已经在上一节记录中写过了。所以现在这里主要写合并相关的内容。
分支合并
快捷指令
首先介绍一个指令,带有 -b 参数的 git checkout 命令:
$ git checkout -b iss53
# Switched to a new branch "iss53"
它是下面两条命令的简写:
$ git branch iss53
$ git checkout iss53
fast-forward 合并
如下图所示,假设hotflix是上述提到的新用户需求,iss53是上述提到的严重问题,master是主线所在位置。hotflix和iss53都是在C2创建的。
要解决问题或者增加新需求,就用 git checkout 指令去对应的branch进行编辑修改。之后进行合并操作。
将 hotfix 分支合并回你的 master 分支来部署到线上。 你可以使用 git merge 命令来达到上述目的:
$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
index.html | 2 ++
1 file changed, 2 insertions(+)
在合并的时候,你应该注意到了“快进(fast-forward)”这个词。 由于你想要合并的分支 hotfix 所指向的提交 C4 是你所在的提交 C2 的直接后继, 因此 Git 会直接将指针向前移动。换句话说,当你试图合并两个分支时, 如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候, 只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward)”。
现在,如下图所示,最新的修改已经在 master 分支所指向的提交快照中,你可以着手发布该添加了hotfix的版本了。
关于这个紧急问题的解决方案发布之后,你准备回到被打断之前时的工作中。 然而,你应该先删除 hotfix 分支,因为你已经不再需要它了 —— master 分支已经指向了同一个位置。 你可以使用带 -d 选项的 git branch 命令来删除分支:
$ git branch -d hotfix
Deleted branch hotfix (3a0874c).
现在的状态
现在你可以切换回针对 #53 问题的那个分支(iss53 分支),继续修复问题。
$ git checkout iss53
Switched to branch "iss53"
$ vim index.html
$ git commit -a -m 'finished the new footer [issue 53]'
[iss53 ad82d7a] finished the new footer [issue 53]
1 file changed, 1 insertion(+)
你在 hotfix 分支上所做的工作并没有包含到 iss53 分支中。 如果你需要拉取 hotfix 所做的修改,你可以使用 git merge master 命令将 master 分支 合并入 iss53 分支,或者你也可以等到 iss53 分支完成其使命,再将其合并回 master 分支。
一次合并提交/diverged合并
假设你已经修正了 #53 问题,并且打算将你的工作合并入 master 分支。 为此,你需要合并 iss53 分支到 master 分支, 这和之前你合并 hotfix 分支所做的工作差不多。 你只需要检出到你想合并入的分支,然后运行 git merge 命令:
$ git checkout master
Switched to branch 'master'
$ git merge iss53
Merge made by the 'recursive' strategy.
index.html | 1 +
1 file changed, 1 insertion(+)
这和你之前合并 hotfix 分支的时候看起来有一点不一样。 在这种情况下,你的开发历史从一个更早的地方开始分叉开来(diverged)。 因为master 分支所在提交并不是 iss53 分支所在提交的直接祖先,Git 不得不做一些额外的工作。 出现这种情况的时候,如下图所示,Git 会使用两个分支的末端所指的快照(C4 和 C5)以及这两个分支的公共祖先(C2),做一个简单的三方合并。
和之前将分支指针向前推进所不同的是,Git 将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交(C6)指向它。 这个被称作一次合并提交,它的特别之处在于他有不止一个父提交。
既然你的修改已经合并进来了,就不再需要 iss53 分支了。 现在你可以在任务追踪系统中关闭此项任务,并删除这个分支。
$ git branch -d iss53
遇到冲突时的分支合并
有时候合并操作不会如此顺利。 如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,Git 就没法干净的合并它们。 如果你对 #53 问题的修改和有关 hotfix 分支的修改都涉及到同一个文件的同一处,在合并它们的时候就会产生合并冲突:
$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
此时 Git 做了合并,但是没有自动地创建一个新的合并提交。
Git 会暂停下来,等待你去解决合并产生的冲突。
你可以在合并冲突后的任意时刻使用 git status 命令来查看那些因包含合并冲突而处于未合并(unmerged)状态的文件:
$ 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.html
no changes added to commit (use "git add" and/or "git commit -a")
任何因包含合并冲突而有待解决的文件,都会以未合并状态标识出来。
Git 会在有冲突的文件中加入标准的冲突解决标记,这样你可以打开这些包含冲突的文件然后手动解决冲突。 出现冲突的文件会包含一些特殊区段。 总的来说,如果合并遇到冲突,就停下来用git status 查看文件,然后手动修改。
解决之后, 你可以再次运行 git status 来确认所有的合并冲突都已被解决。
确定之前有冲突的的文件都已经暂存了,这时不要忘记输入 git commit 来完成合并提交。
分支管理
现在已经创建、合并、删除了一些分支,让我们看看一些常用的分支管理工具。
git branch 命令不只是可以创建与删除分支。 如果不加任何参数运行它,会得到当前所有分支的一个列表:
$ git branch
iss53
* master
testing
**注意 master 分支前的 * 字符:它代表现在检出的那一个分支(也就是说,当前 HEAD 指针所指向的分支)。 ** 这意味着如果在这时候提交,master 分支将会随着新的工作向前移动。 如果需要查看每一个分支的最后一次提交,可以运行 git branch -v 命令:
$ git branch -v
iss53 93b412c fix javascript issue
* master 7a98805 Merge branch 'iss53'
testing 782fd34 add scott to the author list in the readmes
–merged 与 –no-merged 这两个有用的选项可以过滤这个列表中已经合并或尚未合并到当前分支的分支。 如果要查看哪些分支已经合并到当前分支,可以运行 git branch –merged:
$ git branch --merged
iss53
* master
因为之前已经合并了 iss53 分支,所以现在看到它在列表中。 在这个列表中分支名字前没有 * 号的分支通常可以使用 git branch -d 删除掉; 你已经将它们的工作整合到了另一个分支,所以并不会失去任何东西。
查看所有包含未合并工作的分支,可以运行 git branch –no-merged:
$ git branch --no-merged
testing
这里显示了其他分支。 因为它包含了还未合并的工作,尝试使用 git branch -d 命令删除它时会失败:
$ git branch -d testing
error: The branch 'testing' is not fully merged.
If you are sure you want to delete it, run 'git branch -D testing'.
如果真的想要删除分支并丢掉那些工作,如同帮助信息里所指出的,可以使用 -D 选项强制删除它。