浅析Git 分支的新建与合并

分支的新建与合并

现在让我们来看一个简单的分支与合并的例子,实际工作中大体也会用到这样的工作流程:

  • 开发某个网站。
  • 为实现某个新的需求,创建一个分支。
  • 在这个分支上开展工作。

假设此时,你突然接到一个电话说有个很严重的问题需要紧急修补,那么可以按照下面的方式处理:

  • 返回到原先已经发布到生产服务器上的分支。
  • 为这次紧急修补建立一个新分支,并在其中修复问题。
  • 通过测试后,回到生产服务器所在的分支,将修补分支合并进来,然后再推送到生产服务器上。
  • 切换到之前实现新需求的分支,继续工作。

分支的新建与切换

首先,我们假设你正在项目中愉快地工作,并且已经提交了几次更新(见图 3-10)。

图 3-10. 一个简短的提交历史

现在,你决定要修补问题追踪系统上的 #53 问题。顺带说明下,Git 并不同任何特定的问题追踪系统打交道。这里为了说明要解决的问题,才把新建的分支取名为 iss53。要新建并切换到该分支,运行git checkout并加上-b参数:

$ git checkout -b iss53
Switched to a new branch 'iss53'

这相当于执行下面这两条命令:

$ git branch iss53
$ git checkout iss53

图 3-11 示意该命令的执行结果。

图 3-11. 创建了一个新分支的指针

接着你开始尝试修复问题,在提交了若干次更新后,iss53分支的指针也会随着向前推进,因为它就是当前分支(换句话说,当前的HEAD指针正指向iss53,见图 3-12):

$ vim index.html
$ git commit -a -m 'added a new footer [issue 53]'

图 3-12. iss53 分支随工作进展向前推进

现在你就接到了那个网站问题的紧急电话,需要马上修补。有了 Git ,我们就不需要同时发布这个补丁和iss53里作出的修改,也不需要在创建和发布该补丁到服务器之前花费大力气来复原这些修改。唯一需要的仅仅是切换回master分支。

不过在此之前,留心你的暂存区或者工作目录里,那些还没有提交的修改,它会和你即将检出的分支产生冲突从而阻止 Git 为你切换分支。切换分支的时候最好保持一个清洁的工作区域。稍后会介绍几个绕过这种问题的办法(分别叫做 stashing 和 commit amending)。目前已经提交了所有的修改,所以接下来可以正常转换到master分支:

$ git checkout master
Switched to branch 'master'

此时工作目录中的内容和你在解决问题 #53 之前一模一样,你可以集中精力进行紧急修补。这一点值得牢记:Git 会把工作目录的内容恢复为检出某分支时它所指向的那个提交对象的快照。它会自动添加、删除和修改文件以确保目录的内容和你当时提交时完全一样。

接下来,你得进行紧急修补。我们创建一个紧急修补分支hotfix来开展工作,直到搞定(见图 3-13):

$ git checkout -b hotfix
Switched to a new branch 'hotfix'
$ vim index.html
$ git commit -a -m 'fixed the broken email address'
[hotfix 3a0874c] fixed the broken email address
 1 files changed, 1 deletion(-)

图 3-13. hotfix 分支是从 master 分支所在点分化出来的

有必要作些测试,确保修补是成功的,然后回到master分支并把它合并进来,然后发布到生产服务器。用git merge命令来进行合并:

$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
 README | 1 -
 1 file changed, 1 deletion(-)

请注意,合并时出现了“Fast forward”的提示。由于当前master分支所在的提交对象是要并入的hotfix分支的直接上游,Git 只需把master分支指针直接右移。换句话说,如果顺着一个分支走下去可以到达另一个分支的话,那么 Git 在合并两者时,只会简单地把指针右移,因为这种单线的历史分支不存在任何需要解决的分歧,所以这种合并过程可以称为快进(Fast forward)。

现在最新的修改已经在当前master分支所指向的提交对象中了,可以部署到生产服务器上去了(见图 3-14)。

图 3-14. 合并之后,master 分支和 hotfix 分支指向同一位置。

在那个超级重要的修补发布以后,你想要回到被打扰之前的工作。由于当前hotfix分支和master都指向相同的提交对象,所以hotfix已经完成了历史使命,可以删掉了。使用git branch-d选项执行删除操作:

$ git branch -d hotfix
Deleted branch hotfix (was 3a0874c).

现在回到之前未完成的 #53 问题修复分支上继续工作(图 3-15):

$ 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(+)

图 3-15. iss53 分支可以不受影响继续推进。

值得注意的是之前hotfix分支的修改内容尚未包含到iss53中来。如果需要纳入此次修补,可以用git merge master把 master 分支合并到iss53;或者等iss53完成之后,再将iss53分支中的更新并入master

删除远程分支:

$ git push origin --delete <BranchName>

查看远程分支:

$ git branch -a

分支的合并

在问题 #53 相关的工作完成之后,可以合并回master分支。实际操作同前面合并hotfix分支差不多,只需回到master分支,运行git merge命令指定要合并进来的分支:

$ git checkout master
$ git merge iss53
Auto-merging README
Merge made by the 'recursive' strategy.
 README | 1 +
 1 file changed, 1 insertion(+)

请注意,这次合并操作的底层实现,并不同于之前hotfix的并入方式。因为这次你的开发历史是从更早的地方开始分叉的。由于当前master分支所指向的提交对象(C4)并不是iss53分支的直接祖先,Git 不得不进行一些额外处理。就此例而言,Git 会用两个分支的末端(C4 和 C5)以及它们的共同祖先(C2)进行一次简单的三方合并计算。图 3-16 用红框标出了 Git 用于合并的三个提交对象:

图 3-16. Git 为分支合并自动识别出最佳的同源合并点。

这次,Git 没有简单地把分支指针右移,而是对三方合并后的结果重新做一个新的快照,并自动创建一个指向它的提交对象(C6)(见图 3-17)。这个提交对象比较特殊,它有两个祖先(C4 和 C5)。

值得一提的是 Git 可以自己裁决哪个共同祖先才是最佳合并基础;这和 CVS 或 Subversion(1.5 以后的版本)不同,它们需要开发者手工指定合并基础。所以此特性让 Git 的合并操作比其他系统都要简单不少。

图 3-17. Git 自动创建了一个包含了合并结果的提交对象。

既然之前的工作成果已经合并到master了,那么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 status查阅:

$ 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")

任何包含未解决冲突的文件都会以未合并(unmerged)的状态列出。Git 会在有冲突的文件里加入标准的冲突解决标记,可以通过它们来手工定位并解决这些冲突。可以看到此文件包含类似下面这样的部分:

<<<<<<< HEAD
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
 please contact us at support@github.com
</div>
>>>>>>> iss53

可以看到=======隔开的上半部分,是HEAD(即master分支,在运行merge命令时所切换到的分支)中的内容,下半部分是在iss53分支中的内容。解决冲突的办法无非是二者选其一或者由你亲自整合到一起。比如你可以通过把这段内容替换为下面这样来解决:

<div id="footer">
please contact us at email.support@github.com
</div>

这个解决方案各采纳了两个分支中的一部分内容,而且我还删除了<<<<<<<=======>>>>>>>这些行。在解决了所有文件里的所有冲突后,运行git add将把它们标记为已解决状态(译注:实际上就是来一次快照保存到暂存区域。)。因为一旦暂存,就表示冲突已经解决。如果你想用一个有图形界面的工具来解决这些问题,不妨运行git mergetool,它会调用一个可视化的合并工具并引导你解决所有冲突:

$ git mergetool

This message is displayed because 'merge.tool' is not configured.
See 'git mergetool --tool-help' or 'git help config' for more details.
'git mergetool' will now attempt to use one of the following tools:
opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge p4merge araxis bc3 codecompare vimdiff emerge
Merging:
index.html

Normal merge conflict for 'index.html':
 {local}: modified file
 {remote}: modified file
Hit return to start merge resolution tool (opendiff):

如果不想用默认的合并工具(Git 为我默认选择了opendiff,因为我在 Mac 上运行了该命令),你可以在上方"merge tool candidates"里找到可用的合并工具列表,输入你想用的工具名。我们将在第七章讨论怎样改变环境中的默认值。

退出合并工具以后,Git 会询问你合并是否成功。如果回答是,它会为你把相关文件暂存起来,以表明状态为已解决。

再运行一次git status来确认所有冲突都已解决:

$ git status
On branch master
Changes to be committed:
 (use "git reset HEAD <file>..." to unstage)

    modified:  index.html

如果觉得满意了,并且确认所有冲突都已解决,也就是进入了暂存区,就可以用git commit来完成这次合并提交。提交的记录差不多是这样:

Merge branch 'iss53'

Conflicts:
 index.html
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
#    .git/MERGE_HEAD
# and try again.
#

如果想给将来看这次合并的人一些方便,可以修改该信息,提供更多合并细节。比如你都作了哪些改动,以及这么做的原因。有时候裁决冲突的理由并不直接或明显,有必要略加注解。

总结

到此这篇关于Git 分支的新建与合并的文章就介绍到这了,更多相关Git 分支内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅谈Git分支管理策略

    如果你严肃对待编程,就必定会使用"版本管理系统"(Version Control System). 眼下最流行的"版本管理系统",非Git莫属. 相比同类软件,Git有很多优点.其中很显著的一点,就是版本的分支(branch)和合并(merge)十分方便.有些传统的版本管理软件,分支操作实际上会生成一份现有代码的物理拷贝,而Git只生成一个指向当前版本(又称"快照")的指针,因此非常快捷易用. 但是,太方便了也会产生副作用.如果你不加注意,很可能

  • 在 Shell 提示符中显示 Git 分支名称的方法

    Git 的好处之一就是把代码的分支管理变成了一件极其便捷的事情,分支只保留差异,不用复制任何文件,不用连接网络,快速创建,用完即删.Git 分支与项目的复杂程度无关,不管你的项目多么复杂,创建 Git 分支永远都是瞬间的事情.同时,因为保留了父类分支的信息,所以分支的合并也变得异常简单. 当在一个项目中频繁使用多个分支时,可以使用 git status 命令查询自己现在正工作在哪个分支下面,不过难免有脑子发昏的时候,忘记自己在哪个分支下面,因而发生误操作之类的杯具. 那么把分支显示在 Shell

  • Git 创建分支提交远程分支详解

    Git 创建分支提交远程分支详解 1.创建本地分支 git branch 分支名,例如:git branch 2.0.1.20120806 注:2.0.1.20120806是分支名称,可以随便定义. 2.切换本地分支 git checkout 分支名,例如从master切换到分支:git checkout 2.0.1.20120806 3.远程分支就是本地分支push到服务器上.比如master就是一个最典型的远程分支(默认). git push origin 2.0.1.20120806 4.

  • Git 详细介绍查看、删除、重命名远程分支和tag

    Git 详细介绍查看.删除.重命名远程分支和tag 1. 查看远程 分支加上-a参数可以查看远程分支,远程分支会用红色表示出来: xiaosi@Qunar:~/code/qtown-score$ git branch -a FRESH-1606_qscore-20160503 * dev master remotes/origin/20151225-qtown-score-FRESH-1236 remotes/origin/2016-2qtscore remotes/origin/FRESH-1

  • git分支的创建、切换、合并及删除操作小结

    一.查看现存分支 查看现存分支 : git branch命令; git branch 从结果可以看出, 现在只有一个分支master; 二.创建分支 创建分支 : git branch 分之名称, 就可以创建一个分支, 创建完分支以后可以查看分支, 当前使用的分支会显示成为绿色, 前面带有 "*" , 如果不是当前使用的分支, 显示的是白色, 并且没有 "*" 前缀; git branch branch1 三.切换分支 切换分支 : git checkout 分支名

  • 浅析Git 分支的新建与合并

    分支的新建与合并 现在让我们来看一个简单的分支与合并的例子,实际工作中大体也会用到这样的工作流程: 开发某个网站. 为实现某个新的需求,创建一个分支. 在这个分支上开展工作. 假设此时,你突然接到一个电话说有个很严重的问题需要紧急修补,那么可以按照下面的方式处理: 返回到原先已经发布到生产服务器上的分支. 为这次紧急修补建立一个新分支,并在其中修复问题. 通过测试后,回到生产服务器所在的分支,将修补分支合并进来,然后再推送到生产服务器上. 切换到之前实现新需求的分支,继续工作. 分支的新建与切换

  • Git分支合并冲突解决的方法实现

    前2天群里发了张git历史图,如下: 根据提交历史,可以看出图中所有分支合并都采用merge的方式,具体merge是怎么操作的,可以阅读下边文章. 根据项目上的需求,如果要求git提交历史是比较简单的一条时间线,可以使用rebase的合并:如果没有这个要求,可以使用merge合并,其分支树是网状结构,但每个提交也都是比较明确清晰的.他们的区别,可参考git merge与 git rebase区别及实例: rebase合并:本质是先取消自己的提交,临时保存,然后把当前分支更新到最新的origin分

  • 浅析git 删除某次指定的提交问题

    Git 是一个分布式版本控制软件,与CVS.Subversion一类的集中式版本控制工具不同,它采用了分布式版本库的作法,不需要服务器端软件,就可以运作版本控制,使得源代码的发布和交流极其方便. git 是一个分布式版本控制软件,最初由林纳斯·托瓦兹创作,于 2005 年以 GPL 发布.最初目的是为更好地管理 Linux 内核开发而设计.应注意的是,这与 GNU Interactive Tools 有所不同. git 最初的开发动力来自于 BitKeeper 和 Monotone. git 最

  • git分支管理_动力节点Java学院整理

    分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN. 如果两个平行宇宙互不干扰,那对现在的你也没啥影响.不过,在某个时间点,两个平行宇宙合并了,结果,你既学会了Git又学会了SVN! 分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了.如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险. 现在有了分支,就不用怕了.你

  • 详解Android Studio Git分支实践

    新公司有些项目是用的 Git,以前公司都是 svn,为了练手 Git,我个人 APP 用到了,但是仅简单的 git pull/push 的使用,并未用到 Git 精髓,只有当项目中用到,才会紧迫去全面学习,所以说,在项目中进步是最快的. 开发场景:远程有主分支 master,提供用户使用的正式的稳定的版本:开发分支 dev,用于日常开发.dev 们可以在本地任意创建自己的 Local Branch,然后合并到本地 master 再提交到远程分支 dev. 命令行实践 高手都是直接用命令行,用命令

  • Git分支管理策略

    目录 一.创建测试项目 1.新建GitHub仓库 2.将本地仓库项目上传到GitHub 2.1.初始化本地仓库 2.2.把文件添加到暂存区 2.3.提交到本地仓库 2.4.关联远程GitHub仓库 2.5.将本地仓库推送到远程仓库 2.6.查看状态 二.管理分支 1.创建本地仓库新分支 2.查看新创建的分支是否成功 3.切换分支 4.查看当前分支 5.将创建的分支推送到远程仓库 6.修改文件 7.将修改后的文件提交到暂存区 8.提交到本地仓库 9.推送到远程仓库 10.查看文件状态 11.合并到

  • git分支(branch)操作相关命令及分支命令的使用

    分支(branch)操作相关命令 查看本地分支:$ git branch 查看远程分支:$ git branch -r 创建本地分支:$ git branch [name] ----注意新分支创建后不会自动切换为当前分支 切换分支:$ git checkout [name] 创建新分支并立即切换到新分支:$ git checkout -b [name] 删除分支:$ git branch -d [name] ---- -d选项只能删除已经参与了合并的分支,对于未有合并的分支是无法删除的.如果想强

随机推荐